claude template with Claude Code already installed.
CLI
Create a sandbox with the E2B CLI.e2b sbx create claude
claude
Run headless
Use-p for non-interactive mode and --dangerously-skip-permissions to auto-approve all tool calls (safe inside E2B sandboxes).
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()
Example: work on a cloned repository
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
timeoutMs: 600_000,
})
await sandbox.git.clone('https://github.com/your-org/your-repo.git', {
path: '/home/user/repo',
username: 'x-access-token',
password: process.env.GITHUB_TOKEN,
depth: 1,
})
const result = 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)
await sandbox.kill()
import os
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
sandbox.git.clone("https://github.com/your-org/your-repo.git",
path="/home/user/repo",
username="x-access-token",
password=os.environ["GITHUB_TOKEN"],
depth=1,
)
result = 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)
sandbox.kill()
Structured output
Use--output-format json to get machine-readable responses — useful for building pipelines or extracting specific results.
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 --output-format json -p "Review this codebase and list all security issues as JSON"`
)
const response = JSON.parse(result.stdout)
console.log(response)
await sandbox.kill()
import os
import json
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 --output-format json -p "Review this codebase and list all security issues as JSON"',
)
response = json.loads(result.stdout)
print(response)
sandbox.kill()
Streaming output
Use--output-format stream-json to get a real-time JSONL event stream — including tool calls, token usage, and result metadata.
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(
`cd /home/user/repo && claude --dangerously-skip-permissions --output-format stream-json -p "Find and fix all TODO comments"`,
{
onStdout: (data) => {
for (const line of data.split('\n').filter(Boolean)) {
const event = JSON.parse(line)
if (event.type === 'assistant') {
console.log(`[assistant] tokens: ${event.message.usage?.output_tokens}`)
} else if (event.type === 'result') {
console.log(`[done] ${event.subtype} in ${event.duration_ms}ms`)
}
}
},
}
)
await sandbox.kill()
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
def handle_event(data):
for line in data.strip().split("\n"):
if line:
event = json.loads(line)
if event["type"] == "assistant":
usage = event.get("message", {}).get("usage", {})
print(f"[assistant] tokens: {usage.get('output_tokens')}")
elif event["type"] == "result":
print(f"[done] {event['subtype']} in {event['duration_ms']}ms")
result = sandbox.commands.run(
'cd /home/user/repo && claude --dangerously-skip-permissions --output-format stream-json -p "Find and fix all TODO comments"',
on_stdout=handle_event,
)
sandbox.kill()
Resume a session
Claude Code persists conversations that can be resumed with follow-up tasks using--resume.
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
timeoutMs: 600_000,
})
// Start a new session
const initial = await sandbox.commands.run(
`cd /home/user/repo && claude --dangerously-skip-permissions --output-format json -p "Analyze the codebase and create a refactoring plan"`
)
// Extract session ID from the JSON response
const response = JSON.parse(initial.stdout)
const sessionId = response.session_id
// Continue with a follow-up task
const followUp = await sandbox.commands.run(
`cd /home/user/repo && claude --dangerously-skip-permissions --resume ${sessionId} -p "Now implement step 1 of the plan"`,
{ onStdout: (data) => process.stdout.write(data) }
)
const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)
await sandbox.kill()
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
# Start a new session
initial = sandbox.commands.run(
'cd /home/user/repo && claude --dangerously-skip-permissions --output-format json -p "Analyze the codebase and create a refactoring plan"',
)
# Extract session ID from the JSON response
response = json.loads(initial.stdout)
session_id = response["session_id"]
# Continue with a follow-up task
follow_up = sandbox.commands.run(
f'cd /home/user/repo && claude --dangerously-skip-permissions --resume {session_id} -p "Now implement step 1 of the plan"',
on_stdout=lambda data: print(data, end=""),
)
diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)
sandbox.kill()
Custom system prompt
Write aCLAUDE.md file into the sandbox for project context or use --system-prompt to provide task-specific instructions.
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
// Write project context
await sandbox.files.write('/home/user/repo/CLAUDE.md', `
You are working on a Go microservice.
Always use structured logging with slog.
Follow the project's error handling conventions in pkg/errors.
`)
const result = await sandbox.commands.run(
`cd /home/user/repo && claude --dangerously-skip-permissions -p "Add a /healthz endpoint"`
)
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"],
})
# Write project context
sandbox.files.write("/home/user/repo/CLAUDE.md", """
You are working on a Go microservice.
Always use structured logging with slog.
Follow the project's error handling conventions in pkg/errors.
""")
result = sandbox.commands.run(
'cd /home/user/repo && claude --dangerously-skip-permissions -p "Add a /healthz endpoint"',
)
print(result.stdout)
sandbox.kill()
Connect MCP tools
Claude Code has built-in support for MCP. E2B provides an MCP gateway that gives Claude access to 200+ tools from the Docker MCP Catalog.import { Sandbox } from 'e2b'
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}"`
)
const result = await sandbox.commands.run(
`claude --dangerously-skip-permissions -p "Use browserbase to research E2B and summarize your findings"`,
{ onStdout: console.log }
)
await sandbox.kill()
import os
from e2b import Sandbox
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}"',
)
result = sandbox.commands.run(
'claude --dangerously-skip-permissions -p "Use browserbase to research E2B and summarize your findings"',
on_stdout=lambda data: print(data, end=""),
)
sandbox.kill()
Build a custom template
If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-builtclaude template.
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('claude')
# template.py
from e2b import Template
template = (
Template()
.from_template("claude")
)
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as claudeCodeTemplate } from './template'
await Template.build(claudeCodeTemplate, 'my-claude', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
})
# build.py
from e2b import Template, default_build_logger
from template import template as claude_code_template
Template.build(claude_code_template, "my-claude",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
npx tsx build.ts
python build.py
Related guides
MCP tools
Connect Claude Code to 200+ MCP tools
Sandbox persistence
Auto-pause, resume, and manage sandbox lifecycle
Git integration
Clone repos, manage branches, and push changes