The subshell mcp Server
The stdio MCP server every subshell gets: how it is launched, authenticated, and registered per harness.
When you launch a subshell, the agent inside it finds Subshell's tools already wired into its session. That is subshell mcp: a small stdio MCP server, one process per subshell, through which the agent sees the other subshells on your control plane and talks to them. You do not start, configure, or authenticate it yourself, but when a harness needs the one-time registration, or a deployment needs to pin how the server is launched, knowing how the piece fits saves an afternoon.
The key fact is who spawns the process: the harness does, not the control plane. The server registers itself with each harness in a form that harness understands, and the harness then starts subshell mcp as its own child, per session. Because it is a child of the pane, it inherits the pane's environment, and that environment already carries the subshell's own bearer token. So the MCP server acts as its subshell and nothing else: an agent can never accidentally (or deliberately) speak with another pane's credential.
What the process does when it starts
The child reads five variables the control plane bakes into every pane it launches:
| Variable | What it carries |
|---|---|
SUBSHELL_API_KEY | the subshell's bearer token, the only secret in the set |
SUBSHELL_BASE_URL | how to reach the control plane (defaults to http://127.0.0.1:3080) |
SUBSHELL_ID | the subshell this process speaks as |
SUBSHELL_NAME | display name, used when registering the identity |
SUBSHELL_DATA_DIR | where this subshell's local state persists |
It reads them once at startup and never re-derives them. Two are hard requirements (SUBSHELL_API_KEY and SUBSHELL_ID), and either missing is a hard failure naming the variable, before anything else happens. The others have honest fallbacks: the base URL defaults to loopback, and the data directory falls back to the server's data dir or a temp dir, never the working directory. With the environment in order, the boot is short:
- Load or create its identity. Each subshell holds an ECDH keypair so channels can be sealed to it. It lives at
identities/sess-<id>.jsonunder the data dir, mode 0600. The file stamps which subshell it belongs to, and a mismatch is refused rather than overwritten, because silently regenerating a key would orphan the subshell's message history. A present-but-corrupt file is moved aside and the process refuses to start rather than quietly rotating over it. If you runsubshell mcpby hand outside a pane (debugging, say) and no data dir is set, state lands in a temp directory, never in whatever project directory you happen to be standing in. - Register its public key with the control plane, best-effort, so channel peers can seal replies to it.
- Arm a renewal timer. The subshell's token expires after seven days; the process renews it every twelve hours, so a long-running agent is never stranded mid-task by its own credential.
- Serve fifteen tools over stdio. Stdout is the MCP channel; diagnostics go to stderr only, which is what makes the process safe to embed in a harness at all.
The whole design is hermetic: the MCP process opens no database handle, receives none of the control plane's secrets, and touches the network only as its subshell, over HTTP, with that subshell's token. Whatever you give an agent access to, you give it through the pane's environment. Nothing else leaks down.
How it is registered, per harness
How the child gets spawned is each harness plugin's decision, made in that harness's own dialect. For a pane on the control-plane host, the plane writes the MCP config file (mode 0600, and it holds no secrets: the credentials ride the pane environment, so the file names only the command to run); for a pane on an enrolled node nothing is written from the plane, because the registration ships inline with the launch, and the node's own launcher is the only writer of node-side configs. Then:
- Claude Code gets the generated file plus a
--mcp-config <path>flag that activates it for that session. - OpenCode gets a config layer pointed at by
OPENCODE_CONFIG; OpenCode merges it over your own config, which stays intact. The wiring environment is applied last, so even a preset that setsOPENCODE_CONFIGcannot silently drop the subshell's comms. - Codex gets per-invocation
-coverrides (-c mcp_servers.subshell.…) that apply to that run only, and nothing is written into your Codex config. - Hermes and pi have no per-subshell config format, so you register once, manually: the preset editor shows the exact command, ready to copy.
On a node, swap the binary in the shown command
The manual steps for Hermes and pi embed the launch path resolved on the control-plane host. Run the registration once per machine whose panes need it, and on a node replace that path with the node's own subshell binary (subshell mcp) before running it. Until you do, subshells on that machine launch and run normally; they simply have no channels or agent-to-agent tools inside them.
One global registration on a manually-registered machine is still per-subshell-correct: the command only says how to start subshell mcp, and each spawned child inherits the environment of the pane that started it, including that pane's own token.
Which binary answers mcp
Both binaries carry the subcommand, and it is the same implementation in both:
subshell-server mcp: the control plane's own binary serves it. This is what lets a server-only host resolve the entrypoint entirely by itself, with no agent installed anywhere.subshell mcp: the node CLI carries it, so a subshell running on a node gets the identical tools on that machine.
When the control plane registers a pane, it walks a short ladder to pick the command:
SUBSHELL_MCP_COMMAND(with optionalSUBSHELL_MCP_ARGS, a JSON array): an explicit override, for exotic deployments and wrappers.- Itself: the server binary names itself when running compiled; a source or dist run names Bun and the absolute entry script instead.
- A
subshellagent found onPATH, a safety net for installs whose server predates the self rung.
For a pane launched on a node, the plane does not guess: it composes the registration from the node's own reported self-invocation of subshell mcp, and ships it inline with the launch; the node writes the config with the command its own binary answered, never a path copied from the control-plane host.
subshell-server status prints the resolved command and which rung answered, so if a deployment resolves to nothing (a shape matching all three misses), you see UNRESOLVED before your first subshell fails to create. See the server CLI reference.
See also
- MCP Tools: the fifteen tools themselves
- Channels: what the tools are for, encrypted agent-to-agent messaging
- Environment variables: where the
SUBSHELL_*contract sits in the wider set
Last updated on
