MCP server
Give an MCP-capable agent its own disposable machine. The MCP server exposes the same create / execute / destroy operations as the REST API, as tools the model can call directly.
What MCP is
The Model Context Protocol is a standard way for an AI application to connect a model to external tools. The client (Claude Code, Claude Desktop, or any other MCP client) starts a server process, discovers the tools it offers, and lets the model call them. Our server is a small stdio process that turns those tool calls into API requests with your key — so the agent gets a sandbox without you writing any integration glue.
Two ways to connect
The same eleven tools are reachable two ways, and they are the same code either way. The remote server is a URL: nothing to install, and the key travels on the request. The local server is a small stdio process your MCP client starts, which reads the key from its own environment. Use the remote one unless your client cannot speak HTTP to an MCP server.
Remote server (no install)
Point any MCP client that speaks Streamable HTTP at this URL and send your API key as a bearer token:
https://sandbox-as-a-service.com/v1/mcp
Authorization: Bearer aas_sk_your_key_here
Some gateways cannot set Authorization, so the header x-api-key is
accepted as an exact alias for the same value. Send one or the other — a request carrying two
different keys is refused rather than guessed at. A key is never read from the query string, where
it would end up in every access log between you and us.
Claude Code
claude mcp add --transport http sandbox https://sandbox-as-a-service.com/v1/mcp \
--header "Authorization: Bearer aas_sk_your_key_here"
Clients that take a JSON config
{
"mcpServers": {
"sandbox": {
"type": "http",
"url": "https://sandbox-as-a-service.com/v1/mcp",
"headers": { "Authorization": "Bearer aas_sk_your_key_here" }
}
}
}
What works without a key
Connecting, listing the tools and calling get_service_info need no key at all, so a
directory or a registry can inspect the server and a model can find out what a sandbox costs before
anyone signs up. get_service_info is free — it provisions nothing and is never charged
for. Everything else acts on an account and requires your own key; there is no shared credential and
no service-wide key. Each call is authenticated against the account the key belongs to, and its
sandboxes, its quota and its balance are the only ones it can reach.
A registry that publishes this server can read the machine-readable description at
/.well-known/mcp/server-card.json. The
configuration it needs is one field:
{
"type": "object",
"properties": {
"apiKey": {
"type": "string",
"title": "API key",
"x-from": { "header": "x-api-key" },
"x-to": { "header": "x-api-key" }
}
},
"required": ["apiKey"]
}
Local server (stdio)
The server runs through npx; there is nothing to install ahead of time. It needs
Node.js 20 or newer — the version its own package.json requires — and an API key from
Dashboard → API keys.
Claude Code
claude mcp add sandbox \
--env AAS_API_KEY=aas_sk_your_key_here \
-- npx -y https://sandbox-as-a-service.com/mcp.tgz
Then check it is connected:
claude mcp list
Claude Desktop
Edit the MCP config file
(~/Library/Application Support/Claude/claude_desktop_config.json on macOS,
%APPDATA%\Claude\claude_desktop_config.json on Windows) and restart the app:
{
"mcpServers": {
"sandbox": {
"command": "npx",
"args": ["-y", "https://sandbox-as-a-service.com/mcp.tgz"],
"env": {
"AAS_API_KEY": "aas_sk_your_key_here"
}
}
}
}
Other MCP clients
Any client that can launch a stdio server works. The contract is: run
npx -y https://sandbox-as-a-service.com/mcp.tgz with AAS_API_KEY in the environment. The server reads
the key at startup and exits with an error if it is missing.
AAS_API_KEY=aas_sk_your_key_here npx -y https://sandbox-as-a-service.com/mcp.tgz
Tools
| Tool | Arguments | What it does |
|---|---|---|
create_sandbox | size (small | medium | large, default small), name, timeout_minutes (1–1440, default 15) | Creates a sandbox and returns its id once it is ready. Blocks until then. |
run_command | sandbox_id (required), command (required), timeout_ms, cwd, env | Runs a shell command as the unprivileged sandbox user and returns exit code, stdout and stderr. |
write_file | sandbox_id (required), path (required), content (required), encoding | Writes a file into the sandbox. Content is transferred out of band, so quotes, backticks and binary data survive intact. |
read_file | sandbox_id (required), path (required), encoding | Reads a file back out — how the agent gets at whatever its code produced. |
list_files | sandbox_id (required), path | Lists a directory recursively, with types and sizes. Lets the agent discover what a run produced rather than guessing. |
expose_port | sandbox_id (required), port (required) | Gives a server listening inside the sandbox a public https preview URL, so the agent can hand a person a link to what it built. |
get_sandbox | sandbox_id (required) | Returns status, size, and the expiry timestamp. |
list_sandboxes | limit, include_deleted | Lists your sandboxes, newest first — useful for finding strays. |
extend_sandbox | sandbox_id (required), additional_minutes (default 15) | Pushes the expiry out, up to the 1440-minute ceiling, when a job outgrows its timeout. |
destroy_sandbox | sandbox_id (required) | Destroys the sandbox and stops billing for it. |
get_usage | none | Reports the remaining credit balance and recent usage, so the agent can see what it is spending. |
The filesystem tools are what make the difference between an agent that can run a command and an agent that can do a piece of work: it writes a script, runs it, lists the directory to see what appeared, and reads the result back. Before those existed it had to build heredocs by hand, and generated code containing a quote would break the command that was supposed to write it.
The tools enforce exactly the same limits as the REST API — 20 concurrent sandboxes,
the 24 hours lifetime ceiling, 1 MiB of captured output per stream — and surface the same
errors, so an agent that hits quota_exceeded gets a message telling it to destroy a
sandbox first.
Agent workflows
1. Run generated code before trusting it
The most useful thing an agent can do with a sandbox is check its own work. Instead of asking you to run a script it just wrote, it runs it somewhere disposable and reads the traceback.
You: Write a script that de-duplicates this CSV by email, keeping the newest row.
Verify it works on a sample before showing it to me.
Agent: create_sandbox { "name": "csv-dedupe", "timeout_minutes": 15 }
-> sbx_9f2c4a1b8d3e6f0a12
write_file { "sandbox_id": "sbx_...", "path": "sample.csv",
"content": "email,updated_at\n..." }
write_file { "sandbox_id": "sbx_...", "path": "dedupe.py",
"content": "import csv, sys\n..." }
run_command { "sandbox_id": "sbx_...",
"command": "python3 /workspace/dedupe.py sample.csv" }
-> exit_code 1, stderr: KeyError: 'Email'
(fixes the case-sensitivity bug, re-runs)
-> exit_code 0, stdout: 412 rows -> 388 rows
destroy_sandbox { "sandbox_id": "sbx_..." }
2. Reproduce a bug in a clean checkout
"Works on my machine" stops being a question when the agent can build a fresh machine. Nothing from your laptop — no stale cache, no half-installed dependency, no local env var — is in the picture.
You: Issue #412 says the test suite fails on a fresh clone. Confirm it, and find out why.
Agent: create_sandbox { "size": "medium", "timeout_minutes": 60 }
run_command { "command": "git clone --depth 1 https://github.com/acme/widget.git /workspace/w",
"timeout_ms": 180000 }
run_command { "cwd": "/workspace/w",
"command": "python3 -m venv .venv && .venv/bin/pip install -q -e '.[test]'",
"timeout_ms": 300000 }
run_command { "cwd": "/workspace/w",
"command": ".venv/bin/pytest -x -q 2>&1 | tail -c 20000",
"timeout_ms": 600000 }
-> reproduces: the suite assumes a TZ env var that CI sets and a clean box does not
destroy_sandbox { "sandbox_id": "sbx_..." }
3. Try a risky change without touching anything you own
Dependency bumps, migration scripts and "let's see what this does" commands belong on a machine whose worst case is a wasted few cents.
You: Does upgrading to pandas 3 break our ETL? Don't touch my working copy.
Agent: create_sandbox { "size": "medium", "name": "pandas-3-trial", "timeout_minutes": 45 }
run_command { "command": "git clone --depth 1 https://github.com/acme/etl.git /workspace/etl",
"timeout_ms": 180000 }
run_command { "cwd": "/workspace/etl",
"command": "python3 -m venv .venv && .venv/bin/pip install -q -r requirements.txt",
"timeout_ms": 300000 }
run_command { "cwd": "/workspace/etl",
"command": ".venv/bin/pip install -q 'pandas>=3' && .venv/bin/pytest -q 2>&1 | tail -c 20000",
"timeout_ms": 600000 }
-> 3 failures, all in the groupby-apply path; reports the diff needed
destroy_sandbox { "sandbox_id": "sbx_..." }
Operating notes
- Agents forget to clean up. Sandboxes expire on their own, but until then they bill. Ask the agent to destroy sandboxes when it is done, keep
timeout_minutesshort, and runlist_sandboxesoccasionally to catch strays. - Keep the key out of the sandbox. The key belongs in the MCP client's configuration — the request header for the remote server,
AAS_API_KEYfor the local one — never in anenvpassed torun_command. Code inside a sandbox that has your key can spend your balance. - Only running machines cost anything. A machine is charged per hour, pro rata, from the moment it is ready until it is destroyed. Discovery,
get_service_info,get_usageandlist_sandboxesare free. Current rates are on the pricing page and inget_service_info. - Nothing survives. When the sandbox is destroyed its disk is destroyed. Have the agent copy results out — into its reply, into a git remote, or into your own storage — before it destroys the machine.
- The concurrency limit is shared. 20 sandboxes per account, counting whatever your other integrations are running.