Skip to content

MCP server configuration

Each MCP server the agent uses is one [mcp.<name>] table in box.toml. A stdio server is a program that the box starts in its own sandbox. An http server runs somewhere else, and the agent reaches it through the box’s egress gateway.

Audience: an operator with a working box from getting started who adds an MCP server to it. To let the agent use the server, write its rules as in write policy for an MCP server.

A stdio server and an http server:

[mcp.fetch]
type = "stdio"
command = ["mcp-server-fetch"]
[mcp.fetch.filesystem]
read = ["~/.local/share/uv/tools/mcp-server-fetch", "~/.local/share/uv/python"]
[mcp.github]
type = "http"
destinations = ["api.githubcopilot.com"]
secret.ref = "env://GITHUB_MCP_TOKEN"

The table’s name, fetch or github, is the server name. Policy reads it as context.input.server.

KeyTypeDefaultMeaning
typestringrequired"stdio".
commandstring arrayrequiredElement 0 is the program, and the rest are its arguments. The box places an alias named for the program in its bin/, on the agent’s PATH. The harness runs this command, word for word, to reach the server.
workspacepaththe agent’s workspaceThe server’s initial working directory.
envtableemptyVariables the server receives. The server also receives PATH and HOME from the host OS’s shell that runs box run, unless env sets them, and a placeholder for each env:// credential.
filesystemtableemptyThe paths the server’s own system calls reach. See Filesystem.
network.contain_egressbooltrueHow the server reaches the network. See Network.

The box starts the server when the agent’s harness runs the server’s full command, and only when policy permits shell:spawn for it. The box runs the command and arguments from box.toml.

The harness’s command for the server must be the same words, in the same order, as command. The harness runs the alias with its arguments, and the box starts the server whose command matches. A harness entry that sets only the program reaches only a server whose command is the program alone. For a server whose command has arguments, set the same arguments in the harness. Connect the harness shows the settings.

The program and the server name hold only letters, digits, ., _, and -. Each server has its own name and its own full command. A program cannot be zsh, bash, sh, python3, or python.

Two or more servers can start through one program, such as npx or uvx, when their full commands differ:

[mcp.playwright]
type = "stdio"
command = ["npx", "-y", "@playwright/mcp"]
[mcp.github]
type = "stdio"
command = ["npx", "-y", "@modelcontextprotocol/server-github"]

The box places one alias, bin/npx, for both servers. The arguments the harness passes select the server:

The harness runsThe box starts
npx -y @playwright/mcpplaywright
npx -y @modelcontextprotocol/server-githubgithub
npx -y some-other-serverNothing. The launch is refused.

Words that mean the same thing to the server are still different words. --port=3000 and --port 3000 don’t match, and neither do ~/data and /Users/me/data. Write each argument the same way in box.toml and in the harness. An argument that holds ${OTEL_EXPORTER_OTLP_ENDPOINT}, ${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}, or ${OTEL_EXPORTER_OTLP_PROTOCOL} matches both the name as written and the value the box puts in its place. The endpoint values hold a port that is new on each run, so a harness with a fixed configuration passes the name, as written in box.toml.

A box whose servers share a program stores a newer record than earlier box builds read. An earlier build refuses that box directory.

[mcp.<name>.filesystem] holds six lists. Each entry is a path, and ~ is the operator’s home.

ListEntryThe server can
readdirectory or fileRead it, and everything under a directory.
writedirectory or fileWrite it, and everything under a directory. write does not grant read.
read_filefileRead the file.
write_fileexisting fileWrite the file.
listdirectoryList the directory’s entries.
denyfile, directory, or absent pathReach nothing there, even under a path another list grants.

A server with no filesystem table reaches its own program and the runtime minimum. On macOS, a server that runs on an interpreter, such as Node or Python, needs read for that interpreter’s install directory.

The table takes no metadata or exec list, and box run refuses either one by name. On macOS, a server can test whether a path exists and read its metadata across the operator’s home. Linux does not provide that same discovery access.

On Linux and macOS, a server can execute helpers and load native code from its readable and writable grants. For a Python extension outside the runtime minimum, grant read to its directory or read_file to the extension file. Grant access to any additional libraries and helpers it needs. A directory grant includes its descendants, so it also permits execution of programs there. File permissions, explicit denials, and protected paths still apply.

On Linux, each write grant also needs a covering read grant. See direct access by operating system. A server can execute code it writes within those grants. Automatic temporary directories do not acquire this execution permission. The agent retains its separate execution restrictions. A filesystem grant does not grant network access.

A credential store, such as ~/.aws or ~/.ssh, is reachable only from an entry that names it exactly. box run prints each credential store a server’s lists name.

A grant in this table is direct access, outside every fs:* decision.

[mcp.<name>.network] holds one key, contain_egress.

ValueThe server’s own connections
trueGo through the egress gateway. The box sets HTTPS_PROXY for the server. Policy decides each net:connect and http:request, the gateway attaches credentials, and the decision log records each one.
falseGo straight to the network. The gateway attaches no credential, and the decision log records one egress:native entry when the server starts.

false applies to a server whose client does not read HTTPS_PROXY, such as a client that signs in through single sign-on:

[mcp.sso-tools]
type = "stdio"
command = ["sso-tools-mcp"]
env = { PATH = "~/.local/share/sso-tools/bin:/usr/bin:/bin" }
[mcp.sso-tools.network]
contain_egress = false
[mcp.sso-tools.filesystem]
read = ["~/.local/share/sso-tools", "~/.config/sso-tools"]
write = ["~/.config/sso-tools"]

contain_egress sets the server’s own connections. Policy decides every request the agent sends to the server in both modes.

KeyTypeDefaultMeaning
typestringrequired"http".
destinationsstring arrayrequiredThe server’s hosts. Each is a host, host:port, a host with a path prefix, or a *. wildcard host.
secret.refstringnoneThe credential the gateway attaches to each request to the server: env://NAME or aws://PROFILE.
secret.headerstringAuthorizationThe header the credential goes in.
secret.prefixstringBearer on AuthorizationThe text before the credential.
secret.placementstringheaderheader, basic_auth, or query_param.
secret.paramstringnoneThe query parameter’s name, for placement = "query_param".
secret.injectstringphantomFor env://: phantom attaches the credential to a request that carries the placeholder, and always attaches it to every request to the destinations.
secret.phantom_prefixstringstrands_box_For env://: the start of the placeholder value.

With secret.ref = "env://NAME", box run reads NAME from its own environment. The agent gets a placeholder in NAME, and the gateway swaps in the real value on each request to the server.

box run exits with an error before the agent starts when a table breaks a rule on this page.

MessageCause
A configuration error that names [mcp.<name>]A key is unknown, a required key is missing, or the name or the program breaks a rule on this page.
`[mcp.<a>]` and `[mcp.<b>]` declare the same command "<command>"Two tables have the same full command. Remove one, or change its arguments.
MCP server "<name>" failed during <step>The server failed while the box read its tool list. stderr names the step.

The harness gets these errors when it starts a server, and the box keeps running:

MessageCause
no MCP server declares "<command>"; servers on <program>: <name>, ...The harness ran a command that matches no command on that program. Set the harness’s command and arguments to the command of one of the listed servers in box.toml.
<name> (its `command` does not expand) in that listThe server’s command holds a ${...} name the box does not fill in. Use one of the three OTEL_EXPORTER_OTLP_ names.
no MCP server declares "<command>"; no `[mcp.<name>]` table in box.toml declares the program "<program>"The harness ran a program that no [mcp.<name>] table names.

For a request the policy refuses, see troubleshooting.