CtrlK
BlogDocsLog inGet started
Tessl Logo

wiring-sbx-mcp-servers

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

Quality

91%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Wiring sbx MCP servers

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.

Register a server

sbx mcp add <name> ... writes a host-side registration. Three input shapes, three execution locations:

You passRunsNotes
--url <endpoint>Remotely; gateway connects to itPlain remote MCP endpoint
--local --url <metadata>On the host, via DockerMetadata 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 processFor 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.

Choose static or dynamic mode

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 (--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.
  • Dynamic (flag omitted): nothing pre-loads, but the gateway exposes those three discovery tools so the agent can search the catalog and attach servers mid-session. Registering a new server with 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.

Authorize OAuth-backed servers

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.

Built-in gateway tools

These belong to the gateway, not to any registered server, so they show up in tool lists and policy decisions alongside real server tools:

ToolPurpose
mcp-execExecutes a tool by name through the gateway
code-modeSpins up an ephemeral JS tool that calls selected tools through the gateway
mcp-findSearches the registered catalog, dynamic mode only
mcp-addAttaches a registered server to the running sandbox, dynamic mode only
mcp-config-setPer-session config override for an attached server, dynamic mode only
<server>-authorizeStarts/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.

Manage registrations

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.

Write an MCP Cedar policy

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:

PointFires onMatch against
Registrationsbx mcp addThe chosen name plus resolved attributes, chiefly resource.identityURL
UseEach gateway requestRegistered 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.

Traps

  • 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.
  • Listings aren't gated. 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.
  • Server groups don't exist in MCP policy. Every rule names one server (or matches by attribute); there's no set-membership shorthand across servers.
  • Principal clauses are dead weight. 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.

Last verified

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.

Repository
slurpyb/sbx-agent
Last updated
First committed

Is this your skill?

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.