Register Model Context Protocol servers on the host with sbx mcp and expose them to sandboxes through the built-in MCP gateway — registration, OAuth, static vs dynamic mode, and Cedar MCP policy. Use when adding an MCP server to a sandbox, deciding between --static-mcp and dynamic discovery, wiring OAuth credentials for a remote server, or writing a governance policy for MCP tool calls.
74
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
sbx mcp registers a server once on the host; the sandbox gateway is what the
agent inside actually talks to. This is not the same move as adding an MCP
server to Claude Code's own config — that configures the agent's client
directly, per session, with no host-side credential store and no policy layer.
Registering with sbx mcp buys you reuse across sandboxes, OAuth tokens that
survive the sandbox, live attach without a restart, and (for governed orgs)
Cedar-enforced access control. Available from sbx 0.38.0, where MCP
management became first-class.
Registering a server does not attach it to anything. Attachment happens at
sandbox creation with --static-mcp, or at runtime with sbx mcp load.
sbx mcp add <name> ... writes a host-side registration. Three input shapes,
three execution locations:
| You pass | Runs | Notes |
|---|---|---|
--url <endpoint> | Remotely; gateway connects to it | Plain remote MCP endpoint |
--local --url <metadata> | On the host, via Docker | Metadata URL must resolve to an OCI stdio package — server.json/server.yaml from a registry or manifest. An npm-type package listed in that metadata is rejected, not translated; use --command instead |
--command <exe> --args ... | On the host, as a plain process | For local dev, private servers, or custom docker run flags. --dir sets the working directory and only pairs with --command |
Server names are letters, numbers, dots, hyphens, underscores. Host-run
servers — both --command and --local-resolved ones — execute outside
sandbox isolation: full host filesystem and network reach, host Docker
isolation if the command itself is a container. Don't mount host paths or
hand a host-run server credentials it doesn't need.
Whether sbx run/sbx create gets --static-mcp decides the sandbox's MCP
mode, and that choice sticks for the sandbox's life — it is not something you
flip on a later reconnect.
--static-mcp notion,linear or repeated flags): the named
servers pre-load, and the gateway does not expose mcp-find, mcp-add, or
mcp-config-set to the agent at all. Every name must already be registered
— sbx mcp add does not happen implicitly.sbx mcp add while a dynamic
sandbox is running refreshes its searchable catalog immediately — no
restart needed, though the agent still has to call mcp-add to attach it.Passing a different --static-mcp list when you reconnect to an existing
sandbox does not replace the original set. To add one more server to an
already-running sandbox — static or dynamic — use sbx mcp load <name> --sandbox <name>. Connected sessions get a tool-list update with no
reconnect required, and the attachment survives sandbox restarts.
sbx mcp add starts the OAuth flow automatically for a server that needs it.
Pass --skip_auth to register without authorizing yet. Tokens live in the
host OS credential store, never in the sandbox.
For a provider that doesn't support Dynamic Client Registration, supply the client you registered with them:
$ sbx mcp add slack --url https://slack.example.com/mcp --client-id <ID>If the server publishes no OAuth metadata at all, add
--oauth-authorization-server <path-or-URL> pointing at an RFC 8414 document
with authorization_endpoint and token_endpoint. Both flags require --url.
There is deliberately no --client-secret flag. For a confidential client,
store the secret first, under the convention mcp:<server>.client_secret:
$ sbx secret set mcp:slack.client_secret
$ sbx mcp add slack --url https://slack.example.com/mcp --client-id <ID>Skip that step and registration still succeeds — it just registers with
authorization silently skipped. Store the secret afterward and run sbx mcp auth <server> to finish it; there's no error to tell you authorization didn't
happen.
--scope (repeatable) records default scopes at registration; sbx mcp auth <server> --scope ... overrides them for one authorization. Requested scopes
must be a subset of what the authorization server advertises, when it
advertises any.
Manage tokens from the host at any time — sbx mcp auth status <server>,
sbx mcp auth <server>, sbx mcp auth rm <server>, each also taking --all
for every OAuth-backed registration and --format=json for scripting.
Every OAuth-backed server the gateway exposes also gets a helper tool,
<server>-authorize. Until the server is authorized, that helper is the
only tool visible for it — the agent has to call it before anything else
from that server appears.
These belong to the gateway, not to any registered server, so they show up in tool lists and policy decisions alongside real server tools:
| Tool | Purpose |
|---|---|
mcp-exec | Executes a tool by name through the gateway |
code-mode | Spins up an ephemeral JS tool that calls selected tools through the gateway |
mcp-find | Searches the registered catalog, dynamic mode only |
mcp-add | Attaches a registered server to the running sandbox, dynamic mode only |
mcp-config-set | Per-session config override for an attached server, dynamic mode only |
<server>-authorize | Starts/restarts OAuth for that server, even if it's already valid |
code-mode-generated tools are per-gateway-instance: shared by whoever is
connected, gone when the gateway restarts. Local stdio servers never get an
-authorize helper — there's no token to refresh.
sbx mcp ls, sbx mcp inspect <name>, sbx mcp rm <name>. Removing an
OAuth-backed server drops the access token first, then the registration — but
a pre-registered client's secret and identity binding stay in the host
credential store so re-adding the same client doesn't need re-entry. sbx mcp rm prints the sbx secret rm commands if you actually want those gone too.
To drop just the token and keep the registration, use sbx mcp auth rm
instead of rm.
Governed orgs write MCP access as Cedar in the MCP namespace — same
mechanism as other AI Governance policy, not the local network preset system.
There's no local-policy equivalent for MCP: if enforcement is active for a
user, registration and every governed request are default-deny until a
permit matches, full stop.
Policy bites at two separate points, and a rule for one does not cover the other:
| Point | Fires on | Match against |
|---|---|---|
| Registration | sbx mcp add | The chosen name plus resolved attributes, chiefly resource.identityURL |
| Use | Each gateway request | Registered name, tool/resource/prompt identity, tool annotations |
A registration permit that already exists doesn't retroactively legalize a
server someone registered before the rule landed, and conversely a use-time
forbid doesn't erase an existing registration — sbx mcp ls still shows it,
sbx mcp load still loads it, only the gateway request gets denied. To fully
withdraw a server, forbid both register (by identityURL) and the use-time
actions (invokeTool, readResource, getPrompt) for every name it was ever
registered under.
Built-in gateway tools (mcp-add, code-mode, <server>-authorize, …) are
MCP::Primordial resources under the invokePrimordial action — a separate
resource type from MCP::Tool, so a policy governing a server's tools does
not automatically reach its authorize helper. Govern it explicitly:
forbid (principal, action == MCP::Action::"invokePrimordial", resource)
when { resource in MCP::Primordial::"example-authorize" };For per-call confirmation, annotate a permit with @requireApproval("reason").
A match triggers an MCP elicitation to the requesting client, and the
annotation string is the only context the user sees — never raw tool
arguments:
@requireApproval("non-read-only tool call")
permit (principal, action == MCP::Action::"invokeTool", resource)
when { resource in MCP::Server::"example" && resource.readOnly == false };readOnly defaults to false for a tool that doesn't declare it, so an
unannotated tool falls under this pattern rather than skipping it.
sbx mcp add can never satisfy @requireApproval. It has no session to
elicit through, so a registration permit carrying that annotation is a
guaranteed denial, not a prompt. Keep @requireApproval off registration
rules.code-mode calls can't relay an elicitation either. A tool invoked from
inside a generated code-mode tool hits the same wall — denied, not
prompted — even if the same tool called directly would ask for confirmation.listTools, listResources, and
subscribeResource are in the schema but not evaluated by Cedar. An agent
can see a tool in its list that a forbid will still block the moment it's
called.container-stdio can be registered but never run locally. It's a valid
value for resource.type in a register decision — an OCI stdio server
resolved without --local — but the local gateway has no path to attach
or execute it. Don't write a permit expecting it to work end to end.principal in ..., principal.role,
principal.tenant don't match anything — policy scope supplies the
principal. Target users through org/team scope, not Cedar.resource.command/resource.args can be empty even for a real
host-run server, when the resolution path didn't capture command details.
Match resource.type == "local-stdio" if the rule must catch host-run
servers regardless of what command detail is available.Verified against the sbx docs of 2026-08-07 (re-fetched unchanged on 2026-08-10): mcp-gateway.md, the MCP access-controls
and policy-reference pages. sbx mcp and the gateway are new as of 0.38.0 —
check for a newer release note before relying on flag behavior here. Nothing
in this file is reverse-engineered; the docs describe the CLI flags, gateway
tools, and Cedar surface directly. Cross-reference governing-sbx-fleets for
non-MCP org policy and running-sbx-sandboxes for the rest of sbx run/sbx create.
cd8f798
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.