# Claude Code (/docs/agents/claude-code)

<!-- agent-signals: reading_time_min: 9 · est_tokens: 5479 · updated: 2026-07-30 -->
Related: [Amp](/docs/agents/amp.md), [Claude Managed Agents](/docs/agents/claude-managed-agents.md), [Codex](/docs/agents/codex.md), [Crabbox with E2B](/docs/agents/crabbox.md), [Devin](/docs/agents/devin.md), [Devin Outposts](/docs/agents/devin-outposts.md)

[Claude Code](https://docs.anthropic.com/en/docs/claude-code) is Anthropic's agentic coding tool. E2B provides a pre-built `claude` template with Claude Code already installed.

## CLI [#cli]

Create a sandbox with the [E2B CLI](/docs/cli).

```bash
e2b sbx create claude
```

Once inside the sandbox, start Claude Code.

```bash
claude
```

## Run headless [#run-headless]

Use `-p` for non-interactive mode and `--dangerously-skip-permissions` to auto-approve all tool calls. The sandbox isolates the agent from your host machine, but sandboxes can reach the open internet by default — restrict outbound traffic with [network rules](/docs/network/internet-access) if the agent processes untrusted input or handles secrets.

<Note>
  Auto-approving tool calls is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests — internet access is enabled by default. To limit where an auto-approved agent can connect, configure [outbound network rules](/docs/network/internet-access) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
</Note>

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      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()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      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()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

### Example: work on a cloned repository [#example-work-on-a-cloned-repository]

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      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()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      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()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Structured output [#structured-output]

Use `--output-format json` to get machine-readable responses — useful for building pipelines or extracting specific results.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      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()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      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()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Streaming output [#streaming-output]

Use `--output-format stream-json` to get a real-time JSONL event stream — including tool calls, token usage, and result metadata.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      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()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      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()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Resume a session [#resume-a-session]

Claude Code persists conversations that can be resumed with follow-up tasks using `--resume`.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      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()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      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()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Custom system prompt [#custom-system-prompt]

Write a `CLAUDE.md` file into the sandbox for project context or use `--system-prompt` to provide task-specific instructions.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      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()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      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()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Connect MCP tools [#connect-mcp-tools]

Claude Code has built-in support for [MCP](https://modelcontextprotocol.io/). E2B provides an [MCP gateway](/docs/mcp) that gives Claude access to 200+ tools from the [Docker MCP Catalog](https://hub.docker.com/mcp).

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      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()
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      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()
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Build a custom template [#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-built `claude` template.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      // template.ts
      import { Template } from 'e2b'

      export const template = Template()
        .fromTemplate('claude')
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      # template.py
      from e2b import Template

      template = (
          Template()
          .from_template("claude")
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      // 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(),
      })
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      # 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(),
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

Run the build script to create the template.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```bash  
      npx tsx build.ts
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```bash  
      python build.py
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Related guides [#related-guides]

<CardGroup cols="3">
  <Card title="MCP tools" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M15.5 2V6M8.5 6V2&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M6.00446 7.61331C5.93719 6.74273 6.63957 6 7.53014 6H16.4699C17.3604 6 18.0628 6.74273 17.9955 7.61331L17.8117 9.99197C17.6796 11.7019 17.1011 13.3498 16.132 14.7773L15.5312 15.6622C14.9638 16.4979 14.0077 17 12.9838 17H11.0162C9.99228 17 9.03617 16.4979 8.46881 15.6622L7.86803 14.7773C6.89885 13.3498 6.32041 11.7019 6.18827 9.99197L6.00446 7.61331Z&#x22; stroke=&#x22;currentColor&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M12 17V22&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M11 9H13&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/docs/mcp">
    Connect Claude Code to 200+ MCP tools
  </Card>

  <Card title="Sandbox persistence" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><circle cx=&#x22;12&#x22; cy=&#x22;12&#x22; r=&#x22;10&#x22; stroke=&#x22;currentColor&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M12 8V12L14 14&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/docs/sandbox/persistence">
    Auto-pause, resume, and manage sandbox lifecycle
  </Card>

  <Card title="Git integration" icon="<svg xmlns=&#x22;http://www.w3.org/2000/svg&#x22; viewBox=&#x22;0 0 24 24&#x22; fill=&#x22;none&#x22;><path d=&#x22;M16 6.99998L19.0664 9.64296C20.3554 10.7541 21 11.3096 21 12C21 12.6903 20.3555 13.2459 19.0664 14.357L16 17&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/><path d=&#x22;M8 6.99998L4.93365 9.64296C3.64455 10.7541 3 11.3096 3 12C3 12.6903 3.64455 13.2459 4.93365 14.357L8 17&#x22; stroke=&#x22;currentColor&#x22; stroke-linecap=&#x22;round&#x22; stroke-linejoin=&#x22;round&#x22; stroke-width=&#x22;1.5&#x22;/></svg>" href="/docs/sandbox/git-integration">
    Clone repos, manage branches, and push changes
  </Card>
</CardGroup>
