How to run an MCP server in a sandbox
Call sbx.mcp.start([{ id: "github" }]), wait for sbx.mcp.ready(), and give your agent each server's HTTPS URL and bearer header.
On Runtime a catalog MCP server starts in one call, and its API key never enters the sandbox. A setting that holds a key names a Runtime secret: the server sees only a placeholder, and the egress proxy outside the microVM adds the real value to its HTTPS requests to that secret's own hosts. The catalog shipped on 24 September 2026 with servers for GitHub, Postgres, a Playwright browser, filesystem, fetch, git, time, memory, Notion, Context7, Brave Search, Exa, Firecrawl and Supabase, all MIT or Apache-2.0. A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while the servers wait for calls (pricing).
Start servers from the catalog
Store the token once as a secret, then start the servers by id:
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.secrets.set("GITHUB_TOKEN", { value: process.env.GITHUB_TOKEN ?? "", hosts: ["api.github.com"],});await using sbx = await runtime.sandboxes.create({ timeoutSeconds: 3600 });await sbx.mcp.start([ { id: "github", secrets: { GITHUB_PERSONAL_ACCESS_TOKEN: "GITHUB_TOKEN" } }, { id: "fetch" },]);const gateway = await sbx.mcp.ready(); // waits until every server is installedfor (const server of gateway.servers) console.log(server.name, server.status, server.url);console.log(gateway.headers); // { Authorization: "Bearer ..." }Pythonimport osfrom withruntime import Runtimeruntime = Runtime()runtime.secrets.set("GITHUB_TOKEN", value=os.environ.get("GITHUB_TOKEN", ""), hosts=["api.github.com"])with runtime.sandboxes.create(timeout_seconds=3600) as sbx: sbx.mcp.start([ {"id": "github", "secrets": {"GITHUB_PERSONAL_ACCESS_TOKEN": "GITHUB_TOKEN"}}, {"id": "fetch"}, ]) gateway = sbx.mcp.ready() for server in gateway["servers"]: print(server["name"], server["url"]) print(gateway["headers"])Terminalruntime secrets set GITHUB_TOKEN --host api.github.com < token.txtruntime sandbox mcp "${id}" start github fetch --secret github.GITHUB_PERSONAL_ACCESS_TOKEN=GITHUB_TOKENruntime sandbox mcp "${id}" # state, URLs and the header to sendruntime sandbox mcp "${id}" stopThe CLI's start also prints a claude mcp add line for each server, so
Claude Code can use them at once. runtime sandbox mcp catalog (or
runtime.mcp.catalog()) lists every server with its licence, pinned version,
settings and the hosts it calls.
Connect an agent to the URLs
Each server answers at its own Streamable HTTP URL, reached through a private preview link. Every request also carries the gateway's bearer token. Any MCP client that speaks Streamable HTTP connects with those two values. Under the MCP specification a client sends each message as an HTTP POST to one endpoint and accepts either a JSON answer or an event stream:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.mcp.start([{ id: "time" }]);const gateway = await sbx.mcp.ready();const url = gateway.servers[0]?.url;const reply = await fetch(url!, { method: "POST", headers: { ...gateway.headers, "content-type": "application/json", accept: "application/json, text/event-stream", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "probe", version: "1.0.0" }, }, }),});console.log(reply.status, reply.headers.get("content-type"));In practice you hand the URL and header to your agent framework's MCP client rather than writing JSON-RPC by hand.
Run your own stdio server
A server you wrote runs the same way. Upload it and name its command; the gateway turns its stdio into a URL like the catalog's:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.files.upload("./my-mcp", "/workspace/my-mcp");await sbx.exec("pip install -r my-mcp/requirements.txt", { check: true, timeoutMs: 300_000 });await sbx.mcp.start([{ name: "mine", command: ["python3", "my-mcp/server.py"] }]);console.log((await sbx.mcp.ready()).servers);Pythonfrom withruntime import Sandboxwith Sandbox.create() as sbx: sbx.files.upload("./my-mcp", "/workspace/my-mcp") sbx.exec("pip install -r my-mcp/requirements.txt", check=True, timeout_ms=300_000) sbx.mcp.start([{"name": "mine", "command": ["python3", "my-mcp/server.py"]}]) print(sbx.mcp.ready()["servers"])The settings
| Field | What it does |
|---|---|
id |
A catalog server, such as github, fetch or postgres |
secrets |
Maps a setting the server reads to a Runtime secret; the server sees a stand-in |
env |
Plain settings passed as given |
options |
The catalog entry's own options |
name and command |
Your own stdio server, run as the sandbox's user |
sbx.mcp.ready() |
Waits until no server is still installing; status is ready or failed |
gateway.headers |
The Authorization header every request needs |
sbx.mcp.stop() |
Stops the servers; the sandbox keeps running |
runtime_sandbox_mcp (MCP) |
The same catalog and start, for an agent already connected to Runtime's MCP |
Mistakes and how Runtime handles them
- Putting a token in
env. It then sits in the sandbox, where any code there can read it. Put it insecretsinstead, so a prompt injection that dumps the environment finds a worthless placeholder (secrets). - A database password. It is not an HTTPS header, so it cannot be swapped
in on the way out; the Postgres server's
DATABASE_URIis passed as given. - A host the network rules refuse. The servers run behind the sandbox's own rules. A catalog host the rules block comes back as a warning, and the rules are never changed for you.
- Calling a URL without the header. Every request needs the bearer token as well as the preview link, so a leaked URL alone gets nothing.
- Confusing two things called MCP.
runtime mcpis Runtime's own MCP server, which lets an agent create and drive sandboxes (MCP). The servers on this page run inside one sandbox and serve your agent's tools.
Where this helps
- An agent in Claude Code or Codex gets GitHub, browser or database tools without running them on your laptop.
- Untrusted or third-party servers run in a microVM of their own, away from your machine and network (run untrusted LLM code).
- What is MCP? explains the protocol.
Start
Terminalnpx withruntime sandbox run --trial --keep -- echo readyNew accounts get 50 free sandbox hours, no card. The first run prints a link to approve in your browser.
Sources
- Model Context Protocol, Transports (2025-06-18): https://modelcontextprotocol.io/specification/2025-06-18/basic/transports, read 25 September 2026.
Facts on this page were checked on 25 September 2026.