# Claude Agent SDK Inside the Sandbox, or the Sandbox as a Tool?

> Run the agent inside the sandbox when its built-in Bash and file tools should only ever touch a disposable VM and it works for long stretches alone. Keep the agent on your server and give it Sandboxer as an MCP tool when your keys and app state must stay with you and only code execution needs isolating.

Source: https://sandboxer.com/guides/claude-agent-sdk-inside-the-sandbox-or-as-a-tool. Updated 2026-10-27.

**Short answer:** Run the agent inside the sandbox when its built-in Bash and file tools should only ever touch a disposable VM and it works for long stretches alone. Keep the agent on your server and give it Sandboxer as an MCP tool when your keys and app state must stay with you and only code execution needs isolating.

## Why the choice matters

The [Claude Agent SDK](https://code.claude.com/docs/en/agent-sdk/overview) is a library that runs the Claude Code binary inside your program. Anthropic's [hosting guide](https://code.claude.com/docs/en/agent-sdk/hosting) explains that each `query()` starts a `claude` subprocess that "owns the shell, the working directory, and the JSONL session transcripts on local disk." Its built-in tools (Bash, Read, Edit, Write) act on whatever machine that subprocess runs on.

So the real question is which machine that is:

- **Setup A, agent inside the sandbox.** Your program and its `claude` subprocess run in a Sandboxer VM. Every built-in tool touches only that VM.
- **Setup B, sandbox as a tool.** Your program runs on your server. It reaches a Sandboxer VM through the [MCP server](/docs/mcp), and you turn off the built-in Bash so commands don't run on your server.

## The decision

| | A: agent inside the sandbox | B: agent on your server, sandbox as a tool |
| - | - | - |
| Where the agent loop runs | In the VM | On your server |
| Where `ANTHROPIC_API_KEY` lives | In the VM's environment | On your server only |
| Where the Sandboxer key lives | On the machine that runs `sbx` (the VM gets none with `--no-env`) | On your server, in the MCP config's header or `env` |
| What a bad command can reach | The VM: its files, its environment, its network | The VM, through MCP tools. Built-in tools you leave on still act on your server |
| Tool-call latency | Built-in tools run locally in the VM. Only model calls leave it | Each tool call is a network round trip to Sandboxer: about 90 ms per hosted MCP call in our test (below) |
| Startup | `sbx new` took 3.2 s in our test (October 27, 2026), plus a one-time SDK install (about 340 MB with its bundled binary) | SDK init with both Sandboxer servers connected took 437–518 ms. The agent creates a VM (`create_sandbox`) when it needs one |
| Sandbox cost | The VM bills for the whole session, model time included | The VM bills from `create_sandbox` until `stop_sandbox` or its TTL |
| Session files and memory | In the VM. Stopping it keeps them; deleting it loses them | On your server, next to your app's own state |
| Best for | Long autonomous tasks, coding agents, anything you'd run with permission prompts off | Chat apps and multi-tenant services where the agent mostly talks and sometimes runs code |

Sandbox cost is small next to the model in both setups. `small` (2 vCPU, 4 GB) is $0.018 an hour and `default` (4 vCPU, 8 GB) is $0.036 an hour, billed per second. Anthropic's hosting guide notes that token cost "typically dominates container infrastructure cost by an order of magnitude or more."

## Setup A: the agent runs inside the sandbox

Create a VM. `--no-env` keeps your account's secrets out of it, so the agent can't act on your Sandboxer account:

```bash
sbx new --type small --no-env
```

In our single run on October 27, 2026, it was ready in 3.2 s. Every sandbox has Node, Python and Claude Code preinstalled:

```bash
sbx exec sb_lw7iz00qux "node --version; npm --version; python3 --version; claude --version"
```

```text
v22.23.3
10.9.9
Python 3.12.3
2.1.301 (Claude Code)
```

That's above Anthropic's [stated minimums](https://code.claude.com/docs/en/agent-sdk/quickstart) (Node.js 18+ or Python 3.10+). Install the SDK in the VM and check that it imports:

```bash
sbx exec sb_lw7iz00qux --timeout 300 'mkdir -p agent && cd agent && npm init -y >/dev/null && npm pkg set type=module && npm i @anthropic-ai/claude-agent-sdk && node -e "import(\"@anthropic-ai/claude-agent-sdk\").then(m => console.log(typeof m.query, typeof m.tool, typeof m.createSdkMcpServer))"'
```

```text
added 105 packages, and audited 106 packages in 10s
…
found 0 vulnerabilities
function function function
```

`query`, `tool` and `createSdkMcpServer` all imported (package version 0.3.311). For Python, use a virtual environment:

```bash
sbx exec sb_lw7iz00qux --timeout 300 'cd agent && python3 -m venv .venv && .venv/bin/pip install -q claude-agent-sdk && .venv/bin/python -c "import claude_agent_sdk as s; print(s.__version__, s.query.__name__, s.ClaudeAgentOptions.__name__)"'
```

```text
0.2.174 query ClaudeAgentOptions
```

Both SDKs bundle their own Claude Code binary, per Anthropic's quickstart. That's why the installs are large: `node_modules` was 342 MB and the venv 338 MB. Install once, then save the VM as a template so later VMs start with it. See [Turn a sandbox into a reusable template](/guides/turn-a-sandbox-into-a-reusable-template).

Here is a small agent that uses the built-in tools, which now act only on the VM:

```javascript
// agent.mjs
import { query } from "@anthropic-ai/claude-agent-sdk";

const prompt = process.argv[2] ?? "Run the tests in this repo and fix what fails.";

for await (const message of query({
  prompt,
  options: {
    cwd: "/home/user/agent",
    allowedTools: ["Bash", "Read", "Edit", "Write", "Glob", "Grep"],
    permissionMode: "acceptEdits",
    maxTurns: 30,
  },
})) {
  if (message.type === "system" && message.subtype === "init") {
    console.log("init: model", message.model, "tools", message.tools.length);
  } else if (message.type === "result") {
    console.log("result:", message.subtype, message.result ?? "");
  }
}
```

Copy it in and start it in the background, so it keeps running after your terminal closes:

```bash
sbx scp ./agent.mjs sb_lw7iz00qux:/home/user/agent/
sbx exec sb_lw7iz00qux --detach --cwd agent 'node agent.mjs "Say hello"'
```

```text
{"ok":true,"type":"command.started","command":"node agent.mjs \"Say hello\"","commandId":"cmd_3xaqnt6wzzqb",…,"processId":630,…}
```

Read its output any time, even after the sandbox stops:

```bash
sbx history sb_lw7iz00qux cmd_3xaqnt6wzzqb
```

```text
init: model claude-opus-5-5 tools 25
result: success Not logged in · Please run /login
```

We ran this with no Anthropic key in the VM, so the agent started, loaded its 25 built-in tools and stopped at sign-in. No model call ran. To run it for real, give the VM your key. Keys saved on the dashboard's **Agents** page never reach a `--no-env` sandbox, so pass it to that one sandbox with `sbx new --no-env -e ANTHROPIC_API_KEY=…`. Anthropic's quickstart says the SDK reads the key from the environment and doesn't load `.env` files.

If you don't need your own program at all, the [integrated agents](/docs/agents) run Claude Code in the VM from one `sbx prompt` call or API request.

## Setup B: the agent runs on your server and calls the sandbox

Point the SDK's `mcpServers` option at Sandboxer. Anthropic's [MCP guide](https://code.claude.com/docs/en/agent-sdk/mcp) uses `type: "http"` with `headers` for remote servers and `command`/`args`/`env` for stdio servers. This config sets up both Sandboxer servers, the hosted one and the local `sbx mcp`. In practice you'd pick one:

```javascript
// agent.mjs, on your server
import { query } from "@anthropic-ai/claude-agent-sdk";

const q = query({
  prompt: "List my sandboxes.",
  options: {
    mcpServers: {
      sandboxer: {
        type: "http",
        url: "https://sandboxer.com/mcp",
        headers: { Authorization: `Bearer ${process.env.SANDBOXER_API_KEY}` },
      },
      "sandboxer-local": {
        type: "stdio",
        command: "sbx",
        args: ["mcp", "--type", "small", "--ttl", "1800"],
        env: { SANDBOXER_API_KEY: process.env.SANDBOXER_API_KEY },
      },
    },
    allowedTools: ["mcp__sandboxer__*", "mcp__sandboxer-local__*"],
    disallowedTools: ["Bash"],
    maxTurns: 1,
  },
});

try {
  for await (const message of q) {
    if (message.type === "system" && message.subtype === "init") {
      console.log("servers:", message.mcp_servers);
      const tools = message.tools.filter((t) => t.startsWith("mcp__"));
      console.log(`${tools.length} MCP tools, e.g.`, tools.slice(0, 3));
    } else if (message.type === "result") {
      console.log("result:", message.subtype, message.result ?? "");
    }
  }
} catch (err) {
  console.log("ended:", String(err).split("\n")[0]);
}
```

```bash
node agent.mjs
```

```text
servers: [
  { name: 'sandboxer', status: 'connected', source: 'dynamic' },
  { name: 'sandboxer-local', status: 'connected', source: 'dynamic' }
]
37 MCP tools, e.g. [
  'mcp__sandboxer__command_status',
  'mcp__sandboxer__create_sandbox',
  'mcp__sandboxer__delete_sandbox'
]
result: success Not logged in · Please run /login
ended: Error: Claude Code returned an error result: Not logged in · Please run /login
```

Both servers connected: 18 tools from the hosted server and 19 from `sbx mcp`, which adds `handoff_session`. As in Setup A, there was no Anthropic key, so the run stopped at sign-in, before any model call. Over three runs, the init message arrived 437–518 ms after `query()` started.

Notes on this config:

- **Use a key, not browser sign-in.** Anthropic's MCP guide says the SDK "doesn't open a browser or run an interactive OAuth flow," so send a Sandboxer API key in `headers`. A [scoped key](/docs/api-keys) limits what the agent can do.
- **Turn off the built-in tools you don't want on your server.** `disallowedTools: ["Bash"]` stops shell commands from running on your server. Add `Write` and `Edit` if the agent shouldn't change files there either.
- **`allowedTools` with `mcp__sandboxer__*` lets the agent call every Sandboxer tool without a prompt.** Tool names follow Anthropic's `mcp__<server>__<tool>` pattern. List single tools instead, such as `mcp__sandboxer__exec`, to allow less.
- **Cap what the agent creates.** `sbx mcp --type small --ttl 1800` makes every sandbox the agent creates `small`, stopping after 30 minutes unless the agent passes its own values.

### Check the Sandboxer side yourself

You can call the hosted server without the SDK. The key goes in through stdin, so it never shows up in your shell history or process list:

```bash
printf 'Authorization: Bearer %s\n' "$SANDBOXER_API_KEY" | curl -sS -D headers.txt -H @- \
  -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}' \
  https://sandboxer.com/mcp
```

```text
{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":false}},"instructions":"Tools for Sandboxer cloud sandboxes: Ubuntu microVMs with root, Docker and a public HTTPS URL per port.\n…","protocolVersion":"2025-06-18","serverInfo":{"name":"sandboxer","title":"Sandboxer",…}}}
```

The response carries an `mcp-session-id` header. Send it back as `Mcp-Session-Id` with `notifications/initialized` (HTTP 202), then `tools/list`. That returned 18 tools, from `create_sandbox` to `delete_snapshot`, in 116 ms. Each `initialize` took 89–91 ms and each `list_sandboxes` call took 91 ms.

The stdio server answers the same handshake on stdin:

```bash
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | sbx mcp --type small --ttl 1800
```

It returned `serverInfo` `sandboxer` and 19 tools.

## Mixing the two

The setups combine well. A server-side agent (B) can create a sandbox, copy a second Agent SDK program into it and start that with `exec` and `detached: true`. That inner agent (A) then works on its own with the Anthropic key you gave that VM. Your orchestrator holds the Sandboxer key, and each worker VM holds only the Anthropic key it needs. To run several workers side by side, see [Run coding agents in parallel](/guides/run-coding-agents-in-parallel).

## FAQ

### Does the Claude Agent SDK need Claude Code installed in the sandbox?

No. Anthropic's quickstart says both SDKs bundle a native Claude Code binary for most installs. Sandboxer VMs also have `claude` preinstalled (2.1.301 in our test), which is handy for `claude -p` or an interactive session over SSH.

### Can I use my Claude subscription with an Agent SDK agent?

Anthropic's docs say third-party developers can't offer claude.ai login or rate limits in products built on the Agent SDK unless Anthropic has approved it. Use an API key, or Bedrock, Vertex or Foundry credentials as Anthropic's quickstart describes.

### Which setup is faster?

For tool-heavy tasks, Setup A. Each Bash or file call runs inside the VM, while in Setup B every tool call is a network round trip (about 90 ms per hosted MCP call in our test). Model calls cost the same in both, and they usually take far longer than either.

### Is it safe to run the agent with permission prompts off?

In Setup A, the agent can only reach what's in that VM, so start it with `--no-env` and only the keys the task needs. See [Claude Code without permission prompts](/guides/claude-code-skip-permissions-safely).

### Do I need the CLI on my server for Setup B?

No. The hosted server at `https://sandboxer.com/mcp` needs only an API key in `headers`. Use `sbx mcp` if you want its `--type` and `--ttl` defaults or its `handoff_session` tool. See [Local or hosted MCP](/guides/local-or-hosted-mcp-for-cursor-claude-code-codex).

## Related

- [Claude Agent SDK docs](/docs/claude-agent-sdk)
- [MCP server](/docs/mcp)
- [Run Claude Code in a cloud VM](/guides/run-claude-code-in-a-cloud-vm)
- [Local or hosted MCP for Cursor, Claude Code and Codex](/guides/local-or-hosted-mcp-for-cursor-claude-code-codex)
