Skip to content
Claude Code

Claude Code

claude is a pre-built AI Agents runtime environment that includes the Claude Code CLI and official MCP servers. It allows you to run Anthropic's intelligent coding agent in a headless, isolated sandbox with full access to the filesystem, terminal, and git.

The template comes pre-installed with Node.js, git, ripgrep, vim, GitHub CLI, and frontend scaffolding tools like pnpm/tsx/vite. No API keys are baked into the image; authentication is provided via envs at sandbox creation or through secret injection rules.

Template Contents

ComponentDescription
claudeClaude Code CLI
MCP servers@modelcontextprotocol/server-filesystem / server-github / server-memory / server-sequential-thinking
/home/user/.claude.jsonPre-set onboarding state to avoid blocking on the official service connectivity check during first interaction
Base toolsNode.js 24.x, git, ripgrep, vim, GitHub CLI, pnpm/tsx/vite, etc.

Creating a Sandbox

Pass ANTHROPIC_API_KEY via envs:

javascript
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create('claude', {
  envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})

To point to an Anthropic-compatible gateway, additionally inject ANTHROPIC_BASE_URL. Replace the example host with your actual gateway host, and make sure it matches the base_url configured in your injection rule:

javascript
const sandbox = await Sandbox.create('claude', {
  envs: {
    ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
    ANTHROPIC_BASE_URL: 'https://api.example.com',
  },
})

Common CLI Flags

FlagDescription
-p "<prompt>"Headless (non-interactive) mode, passes the prompt directly to the CLI
--dangerously-skip-permissionsSkips tool call approval; safe to use in an isolated sandbox environment
--output-format jsonReturns a single JSON response
--output-format stream-jsonOutputs a real-time JSONL event stream
--resume <sessionId>Resumes a previous session
--system-prompt <text>Injects a task-level system prompt

Headless Execution

javascript
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()

Git Repository Integration

Clone a repository inside the sandbox and let Claude Code make changes and show the diff:

javascript
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()

Structured JSON Output

javascript
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)

Streaming Event Handling

javascript
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`)
        }
      }
    },
  },
)

Session Resumption

The session_id returned by --output-format json can be used with --resume to split multi-step tasks across multiple calls:

javascript
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"`,
)

const sessionId = JSON.parse(initial.stdout).session_id

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) },
)

Custom System Prompt (CLAUDE.md)

Write project-level conventions into CLAUDE.md, and Claude Code will automatically read it:

javascript
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"`,
)

Using Secret Injection

When injecting ANTHROPIC_API_KEY directly via envs, the real key ends up in the sandbox process's environment variables. If you want the real key to stay entirely on the platform side and be unreadable by code inside the sandbox, use secret injection. The CLI still sends requests to api.anthropic.com (or a custom ANTHROPIC_BASE_URL), and the platform rewrites the x-api-key header to the real key before forwarding it upstream.

How It Works

text
Sandbox (claude CLI) ──► Platform MITM ──► api.anthropic.com / Compatible Gateway
   x-api-key=placeholder   Injects real key

For the Claude Code CLI to hit the injection rule, two conditions must be met:

  1. ANTHROPIC_BASE_URL must match the rule's base_url (host part): The platform matches by SNI hostname. If the CLI's default api.anthropic.com doesn't match the rule, the request won't be intercepted and will go directly to Anthropic.
  2. ANTHROPIC_API_KEY must be set to any non-empty placeholder: The CLI needs a key field to initiate a request during its local pre-check. The real key is written into the header by the platform on egress; the local placeholder value is never sent out.

Method 1: Reference a Saved Injection Rule

First, create an anthropic type rule in the Sufy Console or through the Open API. For an Anthropic-compatible gateway, set base_url to the gateway host when creating the rule.

When creating the sandbox, reference the <rule-id> and set the Claude Code CLI's ANTHROPIC_API_KEY to any placeholder:

javascript
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create('claude', {
  envs: {
    ANTHROPIC_API_KEY: 'placeholder',
    // Only needed if the rule specifies a base_url
    ANTHROPIC_BASE_URL: 'https://api.example.com',
  },
  injections: [{ id: '<rule-id>' }],
})

const result = await sandbox.commands.run(
  `claude --dangerously-skip-permissions -p "Create a hello world HTTP server in Go"`,
)
console.log(result.stdout)

Method 2: Inline Injection (No Pre-created Rule Needed)

Suitable for temporary verification; the rule is not retained after the sandbox is destroyed. Pass the inline rule when creating the sandbox through the Open API:

bash
curl -X POST "$SUFY_SANDBOX_API_URL/sandboxes" \
  -H "X-API-Key: $SUFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateID": "claude",
    "envVars": {
      "ANTHROPIC_API_KEY": "placeholder"
    },
    "injections": [
      {
        "type": "anthropic",
        "api_key": "sk-ant-real-xxx"
      }
    ]
  }'

If using a compatible gateway, ensure base_url, ANTHROPIC_BASE_URL, and the rule are consistent:

bash
curl -X POST "$SUFY_SANDBOX_API_URL/sandboxes" \
  -H "X-API-Key: $SUFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateID": "claude",
    "envVars": {
      "ANTHROPIC_API_KEY": "placeholder",
      "ANTHROPIC_BASE_URL": "https://api.example.com"
    },
    "injections": [
      {
        "type": "anthropic",
        "api_key": "sk-real-xxx",
        "base_url": "https://api.example.com"
      }
    ]
  }'

Important Notes

  • First matching rule for the same host takes effect: If multiple rules have the same base_url hostname, the platform applies only the first one in list order. Within a single sandbox, the CLI can only point to one ANTHROPIC_BASE_URL, so only the rule corresponding to that host will be triggered.
  • Placeholder cannot be empty: ANTHROPIC_API_KEY="" will cause the CLI to error out locally. Any non-empty string (e.g., placeholder) works.
  • --dangerously-skip-permissions does not affect injection: Injection happens at the network egress stage and is independent of whether the CLI skips tool approval.
  • Use persistent rules for production: Inline injection is convenient for temporary debugging, but saved rules are easier to audit, rotate, and revoke for team collaboration or long-term use.

References