MCP mode
PIBOX_MCP_MODE=1. Exposes an MCP (Model Context Protocol) server over streamable HTTP, with run_prompt, list_files, read_file, write_file and delete_file as tools, so other agents can drive Pi as a tool.
MCP is the one mode that is not a foreground mode. It runs next to whatever else the container is doing.
| Foreground | MCP placement |
|---|---|
API mode (PIBOX_API_MODE=1) | Mounted at /mcp/ on the API port, with no extra process |
| Telegram, cron, or none | A sidecar server on PIBOX_MCP_MODE_PORT (default 8081), serving at the port root / |
The slashless /mcp spelling reaches the same handler as /mcp/ without a redirect.
Setup
Next to cron: scheduled jobs running on their own, and the same box reachable as a tool while they run.
# docker-compose.yml
services:
pibox:
image: psyb0t/pibox:latest
ports:
- "127.0.0.1:8081:8081"
environment:
- PIBOX_CRON_MODE=1
- PIBOX_CRON_MODE_FILE=/home/aicode/.aicodebox/cron.yaml
- PIBOX_MCP_MODE=1
- PIBOX_MCP_MODE_TOKEN=some-long-random-string
- PIBOX_PROVIDER_BASE_URL=https://api.z.ai/api/anthropic
- PIBOX_PROVIDER_API=anthropic-messages
- PIBOX_PROVIDER_API_KEY=your-z-ai-api-key
- PIBOX_PROVIDER_MODEL=your-model-id
volumes:
- ~/.aicodebox:/home/aicode/.aicodebox
- ~/workspaces:/workspace
Cron is the foreground process, so the container’s lifetime follows the scheduler, and MCP rides along in the background. Swap the cron flags for PIBOX_TELEGRAM_MODE=1 and the same holds for the bot.
In API mode you publish no second port, because MCP is already at /mcp/ on the API port. See API mode.
Tools
| Tool | What it does |
|---|---|
run_prompt | Runs a prompt through Pi and returns the reply text |
list_files | Lists a directory under the workspace root |
read_file | Reads a file |
write_file | Writes a file |
delete_file | Removes a file |
run_prompt takes prompt plus optional workspace, model, system_prompt, append_system_prompt, no_continue, resume, thinking and json_schema. Unlike /run, it starts a fresh session by default (no_continue is true).
The file tools resolve their path under the workspace root and reject anything that climbs out of it. This does not sandbox run_prompt: Pi runs with the container’s own permissions.
Auth
PIBOX_MCP_MODE_TOKEN=<token> is the bearer in the Authorization: Bearer ... header, or ?apiToken=... for clients that cannot set headers.
Two things that will bite you if you assume otherwise:
- Empty means no auth at all. Not “no access”, open. Anyone who can reach the port gets
run_prompt, andrun_promptruns code. Leave it unset only when the port is bound to loopback or an internal network. - No fallback to
PIBOX_API_MODE_TOKEN. MCP has its own bearer, and setting the API token does nothing for it.
Reverse proxies and public hosts
MCP keeps DNS rebinding protection enabled. Loopback hosts and origins work by default. If a reverse proxy, tunnel, or public DNS name forwards MCP, allow the exact values it sends. A browser MCP client also needs its exact Origin, including the scheme.
PIBOX_MCP_MODE_ALLOWED_HOSTS=localhost,localhost:*,127.0.0.1,127.0.0.1:*,[::1],[::1]:*,mcp.example.net
PIBOX_MCP_MODE_ALLOWED_ORIGINS=http://localhost:*,http://127.0.0.1:*,http://[::1]:*,https://mcp.example.net
An unexpected Host returns 421, and an unexpected browser Origin returns 403. Setting either variable replaces the loopback defaults, so keep the loopback entries as above. Keep the port bound to loopback when a local proxy terminates TLS. Do not disable the protection or allow broad wildcards for an internet-facing endpoint.
MCP mode environment variables
| Var | Default | What it does |
|---|---|---|
PIBOX_MCP_MODE | 0 | Expose MCP, mounted at /mcp/ in API mode or as a sidecar elsewhere |
PIBOX_MCP_MODE_PORT | 8081 | Port the sidecar MCP server binds to (ignored when mounted inside API) |
PIBOX_MCP_MODE_TOKEN | empty | Bearer token for MCP. Empty means no auth. No fallback to PIBOX_API_MODE_TOKEN |
PIBOX_MCP_MODE_ALLOWED_HOSTS | loopback hosts | Comma-separated MCP Host allowlist. Add each proxy host name |
PIBOX_MCP_MODE_ALLOWED_ORIGINS | loopback HTTP origins | Comma-separated MCP browser Origin allowlist. Add each proxy origin |
Every
PIBOX_*variable is an alias for theAICODEBOX_*equivalent read by the base image. If both are set,AICODEBOX_*wins.
Connecting a client
The sidecar:
claude mcp add --transport http pibox http://host:8081/ \
--header "Authorization: Bearer some-long-random-string"
Inside API mode:
claude mcp add --transport http pibox http://host:8080/mcp/ \
--header "Authorization: Bearer some-long-random-string"
For clients that only speak stdio, use the OpenClaw bridge described in Agent integrations.