MCP server
boxer's MCP server: the boxer_run and boxer_status tools, the brief resource, and lifecycle behaviour.
boxer mcp gives any MCP client two tools, boxer_run and boxer_status, and one resource.
boxer mcp [--harness NAME]It serves MCP over stdio as newline-delimited JSON-RPC 2.0, protocol 2025-06-18 (or 2025-03-26
and 2024-11-05 when the client asks for one). boxer_run calls run side by side and each answers
when it finishes, so ping and other calls are answered during a long run. It answers
initialize, ping, tools/list, tools/call, resources/list and resources/read. It ignores
notifications. Any harness that supports MCP can use it, including harnesses boxer has no
specific integration for.
--harness selects the [harness.<name>] override layer, so per-harness settings such as mode
apply here too.
Registering it
{
"mcpServers": {
"boxer": { "command": "boxer", "args": ["mcp"] }
}
}boxer install <harness> writes this entry into the file that harness reads.
Tools
boxer_run
| Field | Type | |
|---|---|---|
command | string | A POSIX shell line, run with sh -c in the guest. Give this or task |
task | string | A [tasks] entry to run, with its environment and timeout, as boxer run --task does |
cwd | string | Optional. The directory to run in. Defaults to the server's working directory. A directory in another repository is refused; another worktree of the same one is not |
Returns stdout and stderr interleaved, followed by [exit N]. Output over 128 KB keeps its first
and last 64 KB, with a line saying how many bytes were left out between them. Stdin is empty. isError is set when
the exit is non-zero or when boxer refused. A refusal is the same boxer: <reason> text, with
cause and fix lines, that the CLI prints. The sandbox is created first when
create_on includes run.
boxer_status
| Field | Type | |
|---|---|---|
cwd | string | Optional |
One line with the scope, isolation, state, image, worktree and mount path, then one line per
forwarded guest port with its URL, else http://127.0.0.1:<host port>. When no
sandbox exists yet it says so and names the image it would boot.
How cwd is interpreted
A client may pass a host path or a guest path. boxer accepts both:
- A host path inside the worktree becomes the matching guest directory.
- A path under the guest mount maps back to the host worktree.
- Anything else falls back to the server's own working directory, and the result starts with a
note:line saying so.
Resource
The server has one resource, boxer://brief, of type text/markdown. It holds the same brief that
boxer brief prints and the hooks inject, resolved for the server's working directory. The brief
lists:
- the mount
- the mode
- the intercept and passthrough lists
- the declared tasks, with what each is for
boxer generates the brief on each read. Any other URI is an error.
Lifecycle signals
For harnesses with no hook API, boxer treats MCP lifecycle messages as session start and end. Both
signals apply only when create_on includes mcp, which it does by default.
initializeprovisions the server's scope in the background, so the sandbox is warm before the first command. It is skipped undersessionandsubagentisolation, because MCP carries no session id.- stdin EOF records the scope's last use, so
gc's idle clock starts at the session's end.
Register the MCP server even on a harness where hooks already enforce sandboxing. You still get these signals.
Running someone else's MCP server in the sandbox
Some MCP servers must run next to the code. For example, next-devtools-mcp talks to a running
Next.js dev server, so it must run in the same sandbox. boxer run passes stdio through
unchanged, so you can wrap the server command with it:
{
"mcpServers": {
"next-devtools": {
"command": "boxer",
"args": ["run", "--", "npx", "-y", "next-devtools-mcp@latest"]
}
}
}The harness stays on the host and talks to the server inside the guest. The server needs no changes.