Use it

MCP for AI agents

Give your AI agent a place to run code that isn't your Mac. PyBox offers one tool, run_python, over the Model Context Protocol. Every call runs in a fresh sealed box and returns a receipt, and the program's output always comes back as data.

Connect your agent#

Terminal
$ claude mcp add pybox -- pybox mcp

To let the agent use numpy, pandas, matplotlib and an image you built, and run for up to 30 seconds:

Terminal
$ claude mcp add pybox -- pybox mcp --images stdlib,data,tools --max-timeout 30

Add this to claude_desktop_config.json (Settings → Developer → Edit Config), then restart Claude Desktop. Use the full path from which pybox, because desktop apps don't see your shell's PATH.

claude_desktop_config.json
{
  "mcpServers": {
    "pybox": {
      "command": "/full/path/to/pybox",
      "args": ["mcp", "--images", "stdlib,data", "--max-timeout", "30"]
    }
  }
}

Add this to .cursor/mcp.json in a project (or ~/.cursor/mcp.json for all projects):

.cursor/mcp.json
{
  "mcpServers": {
    "pybox": { "command": "pybox", "args": ["mcp", "--images", "stdlib,data"] }
  }
}

For Cursor's command-line agent, allow just this tool in .cursor/cli.json: {"permissions": {"allow": ["Mcp(pybox:run_python)"]}}.

Most MCP clients accept the same shape:

mcp config
{
  "mcpServers": {
    "pybox": { "command": "pybox", "args": ["mcp"] }
  }
}

Running from a clone without installing? Use "command": "python3", "args": ["-I", "-B", "/path/to/pybox/tools/serve_mcp.py"].

The VM must be running (pybox vm start) for calls to execute. Without it, the agent still sees the tool and each call returns an error with cleanup NOT_ATTEMPTED. The server never starts the VM and never falls back to running code unsealed.

Tested with#

ClientWhat happened (21 October 2026)
Claude Code 2.1.289 (claude -p)Ran numpy in data and got a CSV back; reported cleanup REMOVED. Unprompted, it noted that the memory peak came from a single reading and might be low (that run predates final readings)
Cursor command-line agentMade a matplotlib chart in data and got it back (13,796 bytes); in a second session, printed a table with tabulate from a custom image and made another chart. Every box was removed
PyBox's own MCP client13 protocol checks (M5), plus images, files and the server's ceilings

Not yet tested: Claude Desktop, the official SDKs, and other apps. Claude Desktop loads MCP servers only when it starts, and the test session was running inside Claude Desktop, so it couldn't restart it. All tests ran on the machine PyBox was built on, in a fresh test installation. PyBox implements MCP protocol version 2025-11-25.

You set the ceilings#

The person who configures the server decides what an agent may ask for. An agent can only ask for less.

Server optionDefaultMeaning
--max-timeout S2The longest run an agent may request, up to 30 seconds
--images LISTstdlibWhich images an agent may use: stdlib, data, or ones you built. Images

The tool's schema shows the agent exactly these limits, and every call is checked against them again on your Mac.

The tool#

run_python arguments
{
  "code": "import pandas as pd\nprint(pd.read_csv('/tmp/input/sales.csv').sum())",
  "image": "data",
  "timeout_s": 10,
  "files": [{ "name": "sales.csv", "content": "region,sales\nnorth,120\n" }],
  "return_files": true
}
ArgumentRule
code (required)Python source, at most 65,536 UTF-8 bytes
timeout_sMore than 0, at most the server's --max-timeout (default 2)
max_output_bytes1–8,192 per stream (default 4,096)
imageOne of the server's --images (default stdlib)
filesUp to 16 input files, {name, content, encoding} with encoding utf-8 (default) or base64; 256 KiB in total. They appear in /tmp/input/
return_filestrue to get back what the program writes to /tmp/output/

Any other argument is rejected. There is no way for an agent to pick a path on your Mac, a shell command, environment variables, or the unsealed runner.

Files#

Returned files arrive in structuredContent.files. Names of files that weren't brought back appear in the receipt with unusual characters escaped, at most 16 listed, so they can't crowd out the trusted parts of the receipt. Each returned file has its name, bytes and sha256, and its content, as text when it's valid UTF-8 and base64 otherwise. File contents are capped at 48 KiB in total so the whole result fits the protocol's frame. Larger files are still listed, with their size and SHA-256, marked omitted. Like stdout, returned files are the program's work: untrusted data. See files in and out for how they travel.

Output is data, never instructions#

MCP uses standard output for its own messages. If a program's output went straight there, a program could forge a protocol message. PyBox never lets that happen: the program's output is nested inside the tool result as a string.

In a recorded test, a program printed a fake protocol response with id 999. It arrived only inside capture.stdout, and the client received no message with id 999.

This protects the protocol, not the model. Your agent still reads that output and those files. Treat them like any untrusted text: they may contain instructions aimed at the model. PyBox keeps them clearly labelled; it can't make a model ignore them.

What the result looks like#

Each call returns structuredContent with {schema_version, execution, delivery}, plus files when you asked for them, and the same JSON as text. execution is the receipt. If it ever can't be delivered, the error still carries the real cleanup status. delivery says whether anything was trimmed to fit. isError is true when the program failed, timed out, was cancelled, or cleanup wasn't confirmed. Read the receipt to see which.

Limits#

LimitValue
Runs at once1. A second call gets a "busy" error immediately; nothing is queued
Calls per session32 (256 messages)
Largest message in512 KiB
Largest result out64 KiB of receipt, 256 KiB per response
A stalled clientSession closes after 5 seconds of not reading

Cancelling#

A cancellation from the client, closing the connection, or stopping the server all stop the running program and clean up its box. A cancelled call gets no response, as the protocol expects; the server reports the cleanup result on its error stream.

Checked against the source on 21 October 2026.

esc