# How to run an MCP server in a sandbox Call `sbx.mcp.start([{ id: "github" }])`. Wait for `sbx.mcp.ready()`. Then 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 has servers for GitHub, Postgres, a Playwright browser, filesystem, fetch, git, time, memory, sequential thinking, 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](/docs/pricing)). ## Start servers from the catalog Store the token once as a secret. Then start the servers by ID: ```ts check import { 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 installed for (const server of gateway.servers) console.log(server.name, server.status, server.url); console.log(gateway.headers); // { Authorization: "Bearer ..." } ``` ```python check import os from withruntime import Runtime runtime = 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"]) ``` ```bash no-run runtime secrets set GITHUB_TOKEN --host api.github.com < token.txt runtime sandboxes mcp "${id}" start github fetch --secret github.GITHUB_PERSONAL_ACCESS_TOKEN=GITHUB_TOKEN runtime sandboxes mcp "${id}" # state, URLs and the header to send runtime sandboxes mcp "${id}" stop ``` The CLI's `start` command also prints a `claude mcp add` line for each server, so Claude Code can use them immediately. `runtime sandboxes mcp catalog` (or `runtime.mcp.catalog()`) lists every server, with its license, pinned version, settings and the hosts it calls. ## Connect an agent to the URLs Each server responds at its own Streamable HTTP URL. That URL is reached through a private preview link. Every request also carries the gateway's bearer token. Any MCP client that supports Streamable HTTP connects with those two values. Under the MCP specification, a client sends each message as an HTTP POST to one endpoint. It accepts either a JSON response or an event stream: ```ts check import { 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, pass the URL and header to your agent framework's MCP client instead of 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, as it does for catalog servers: ```ts check import { 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); ``` ```python check from withruntime import Sandbox with 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 that 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_sandboxes_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 in `secrets` instead. Then a prompt injection that dumps the environment finds only a worthless placeholder ([secrets](/docs/security#secrets-sandboxes-never-see)). - **A database password.** A database password is not an HTTPS header, so the proxy cannot swap it in on the way out. The Postgres server's `DATABASE_URI` is passed as given. - **A host the network rules refuse.** The servers run behind the sandbox's own network rules. A catalog host that the rules block returns a warning. Runtime never changes the rules 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 mcp` is Runtime's own MCP server. It lets an agent create and control sandboxes ([MCP](/docs/mcp)). The servers on this page run inside one sandbox, and they serve your agent's tools. ## Where this helps - An agent in [Claude Code](/integrations/claude-code) or [Codex](/integrations/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](/use-cases/run-untrusted-llm-code)). - [What is MCP?](/glossary/model-context-protocol) explains the protocol. ## Start ```bash no-run npx withruntime sandboxes run --keep -- echo ready ``` A new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card. The first run prints a link to approve in your browser. ## Sources - Model Context Protocol, Transports (2026-07-28): https://modelcontextprotocol.io/specification/2026-07-28/basic/transports, read 26 October 2026. Facts on this page were checked on 26 October 2026.