Overview

Claude Code in a sandbox

Interactively, from the CLI#

e2b sbx create claude
# inside the sandbox
claude

Headless#

-p runs non-interactively; --dangerously-skip-permissions auto-approves tool calls.

Auto-approved tool calls are contained by the sandbox, but the sandbox can reach the internet by default. If the agent reads untrusted input or holds secrets, restrict outbound traffic with network rules.

import { Sandbox } from 'e2b'
 
const sandbox = await Sandbox.create('claude', {
  envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
 
const result = await sandbox.commands.run(
  `claude --dangerously-skip-permissions -p "Create a hello world HTTP server in Go"`
)
 
console.log(result.stdout)
await sandbox.kill()
import os
from e2b import Sandbox
 
sandbox = Sandbox.create("claude", envs={
    "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
 
result = sandbox.commands.run(
    'claude --dangerously-skip-permissions -p "Create a hello world HTTP server in Go"',
)
 
print(result.stdout)
sandbox.kill()

Work on a real repository#

Clone the repo with git through commands.run (the sandbox git module is deprecated), let Claude Code change it, read the diff.

const sandbox = await Sandbox.create('claude', {
  envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
  timeoutMs: 600_000,
})
 
await sandbox.commands.run(
  'git clone --depth 1 "https://x-access-token:${GITHUB_TOKEN}@github.com/your-org/your-repo.git" /home/user/repo',
  { envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
 
await sandbox.commands.run(
  `cd /home/user/repo && claude --dangerously-skip-permissions -p "Add error handling to all API endpoints"`,
  { onStdout: (data) => process.stdout.write(data) }
)
 
const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)
sandbox = Sandbox.create("claude", envs={
    "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
 
sandbox.commands.run(
    'git clone --depth 1 "https://x-access-token:${GITHUB_TOKEN}@github.com/your-org/your-repo.git" /home/user/repo',
    envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
 
sandbox.commands.run(
    'cd /home/user/repo && claude --dangerously-skip-permissions -p "Add error handling to all API endpoints"',
    on_stdout=lambda data: print(data, end=""),
)
 
diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)

Output formats and sessions#

Structured JSON

Add --output-format json and parse result.stdout with JSON.parse / json.loads. Useful for pipelines that extract one field.

Real-time event stream

--output-format stream-json emits JSONL — tool calls, token usage, result metadata. Split onStdout data on newlines and parse each line; events have type assistant or result.

Resume a session

The JSON response carries session_id. Pass it back with --resume <session_id> to give the same session a follow-up task, e.g. plan first, then "Now implement step 1 of the plan".

Project context

Write a CLAUDE.md into the repo with sandbox.files.write(), or pass task-specific instructions with --system-prompt.

Give it MCP tools#

The MCP gateway exposes 200+ tools from the Docker MCP Catalog. Register it with Claude Code inside the sandbox:

const sandbox = await Sandbox.create('claude', {
  envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
  mcp: {
    browserbase: {
      apiKey: process.env.BROWSERBASE_API_KEY,
      projectId: process.env.BROWSERBASE_PROJECT_ID,
    },
  },
})
 
const mcpUrl = sandbox.getMcpUrl()
const mcpToken = await sandbox.getMcpToken()
 
await sandbox.commands.run(
  `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`
)
sandbox = Sandbox.create("claude", envs={
    "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, mcp={
    "browserbase": {
        "apiKey": os.environ["BROWSERBASE_API_KEY"],
        "projectId": os.environ["BROWSERBASE_PROJECT_ID"],
    },
})
 
mcp_url = sandbox.get_mcp_url()
mcp_token = sandbox.get_mcp_token()
 
sandbox.commands.run(
    f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"',
)

Your own image on top of claude#

Pre-install dependencies by building a template from the claude template: Template().fromTemplate('claude'), then Template.build(template, 'my-claude', { cpuCount: 2, memoryMB: 2048 }). See Custom templates.

Next#

Updated

Was this page helpful?