> Full docs index: https://docs.steel.dev/llms.txt

# Build a browser agent with the Claude Agent SDK
URL: https://docs.steel.dev/cookbook/claude-agent-sdk


**TypeScript**

`@anthropic-ai/claude-agent-sdk` is the engine behind the Claude Code CLI, exposed as a Node library. You get the CLI's [agent loop](/cookbook/topics/agents), hooks, subagents, MCP support, and built-in tool catalog (`Read`, `Edit`, `Bash`, `Grep`, ...) without spawning the CLI yourself.

This recipe disables those built-ins and attaches a Steel cloud browser instead; the [Claude Agent SDK integration](/integrations/claude-agent-sdk) covers install and setup. Four MCP tools (`openSession`, `navigate`, `snapshot`, `extract`) sit in front of Playwright; the agent calls them by name and streams back typed messages.

```typescript
const navigate = tool(
  "navigate",
  "Navigate the open session to a URL and wait for it to load.",
  { url: z.string().describe("Absolute URL to navigate to") },
  async ({ url }) => {
    await page.goto(url, { waitUntil: "domcontentloaded", timeout: 45_000 });
    return {
      content: [
        { type: "text", text: JSON.stringify({ url: page.url(), title: await page.title() }) },
      ],
    };
  },
);

const steelServer = createSdkMcpServer({
  name: "steel",
  version: "1.0.0",
  tools: [openSession, navigate, snapshot, extract],
});

for await (const message of query({
  prompt: PROMPT,
  options: {
    model: "claude-sonnet-4-6",
    systemPrompt: SYSTEM_PROMPT,
    mcpServers: { steel: steelServer },
    allowedTools: ["mcp__steel__*"],
    tools: [],
    settingSources: [],
    maxTurns: 20,
    permissionMode: "bypassPermissions",
  },
})) {
  if (message.type === "assistant") {
    for (const block of message.message.content) {
      if (block.type === "tool_use") {
        const name = block.name.replace(/^mcp__steel__/, "");
        console.log(`  -> ${name}(${JSON.stringify(block.input).slice(0, 120)})`);
      }
    }
  } else if (message.type === "result") {
    if (message.subtype === "success") finalText = message.result ?? "";
  }
}
```

`tools: []` drops the entire Claude Code built-in catalog: no filesystem reads, no `Bash`, no `WebFetch`. `settingSources: []` skips loading `.claude/` from your working directory or home, so the recipe behaves the same on every machine.

## Run it

```bash
cd examples/claude-agent-sdk-ts
cp .env.example .env          # set STEEL_API_KEY and ANTHROPIC_API_KEY
npm install
npx playwright install chromium
npm start
```

Get keys at [app.steel.dev/settings/api-keys](https://app.steel.dev/settings/api-keys) and [console.anthropic.com](https://console.anthropic.com/). A Steel session viewer URL prints when `openSession` runs; open it in another tab to watch the browser live.

Your output varies. Structure looks like this:

```text
Steel + Claude Agent SDK (TypeScript) Starter
============================================================
Sure, let me open a browser session and pull that page.
  -> open_session({})
    open_session: 1747ms
  -> navigate({"url":"https://github.com/trending/python?since=daily"})
    navigate: 2007ms
  -> snapshot({})
    snapshot: 272ms (4000 chars, 49 links)
I have everything I need. Top three trending repos ...

--- Final answer ---
Top 3 AI/ML-related repos:
1. owner/repo - description (X stars)
...

Releasing Steel session...
Session released. Replay: https://app.steel.dev/sessions/ab12cd34...
```

A run takes ~25 to 45 seconds and 3 to 6 turns. Cost is Steel session-minutes plus Anthropic tokens. The `finally` block calls `steel.sessions.release()`.

## Make it yours

- **Swap the task.** Change `PROMPT` and (optionally) `SYSTEM_PROMPT`. The four tools are task-agnostic.
- **Reach for Opus 4.7.** Set `model: "claude-opus-4-7"` for harder reasoning.
- **Add a tool.** Define another `tool()`, append it to the `tools` array in `createSdkMcpServer`. A `click(selector)` tool that calls `page.click` is the most common fifth one.
- **Hook the lifecycle.** Pass a `hooks` option with callbacks for `PreToolUse`, `PostToolUse`, `Stop`, `SessionStart` to audit, log, or block individual tool calls.
- **Resume sessions.** Capture `session_id` from the first `system`/`init` message, pass `resume: sessionId` on the next `query()` call to keep agent memory across runs.
- **Persist a login.** Pair with [credentials](/cookbook/credentials) or [auth-context](/cookbook/auth-context) so Steel sessions start already authenticated.

## Related

[Anthropic Agent SDK docs](https://platform.claude.com/docs/en/agent-sdk/overview) · [Python version](/cookbook/claude-agent-sdk) · [Claude Computer Use (TypeScript)](/cookbook/claude-computer-use)

**Python**

The Claude Agent SDK is the agent loop that powers Claude Code, packaged as a library. Tools are async functions decorated with `@tool`, bundled into an in-process MCP server with `create_sdk_mcp_server`, and registered through the `mcp_servers` option.

This recipe wires four browser tools (`open_session`, `navigate`, `snapshot`, `extract`) into one Steel session and points the agent at GitHub Trending.

```python
@tool(
    "navigate",
    "Navigate the open session to a URL and wait for the page to load.",
    {"url": str},
)
async def navigate(args: dict[str, Any]) -> dict[str, Any]:
    await _page.goto(args["url"], wait_until="domcontentloaded", timeout=45_000)
    return {
        "content": [
            {"type": "text", "text": json.dumps({"url": _page.url, "title": await _page.title()})}
        ]
    }

steel_server = create_sdk_mcp_server(
    name="steel",
    version="1.0.0",
    tools=[open_session, navigate, snapshot, extract],
)

options = ClaudeAgentOptions(
    model="claude-sonnet-4-6",
    system_prompt=SYSTEM_PROMPT,
    mcp_servers={"steel": steel_server},
    allowed_tools=["mcp__steel__*"],
    tools=[],
    setting_sources=[],
    max_turns=20,
    permission_mode="bypassPermissions",
)
```

`tools=[]` drops the SDK's built-ins (`Read`, `Write`, `Edit`, `Bash`, `Grep`, `WebFetch`). `setting_sources=[]` skips loading `.claude/` from your working directory or home, so the recipe runs identically everywhere.

`query()` returns an async iterator over typed messages:

```python
async for message in query(prompt=PROMPT, options=options):
    if isinstance(message, AssistantMessage):
        for block in message.content:
            if isinstance(block, ToolUseBlock):
                name = block.name.removeprefix("mcp__steel__")
                print(f"  -> {name}({json.dumps(block.input)[:120]})")
    elif isinstance(message, ResultMessage):
        if message.subtype == "success":
            final_text = message.result or ""
```

## Run it

```bash
cd examples/claude-agent-sdk-py
cp .env.example .env          # set STEEL_API_KEY and ANTHROPIC_API_KEY
uv run playwright install chromium
uv run main.py
```

Get keys from [app.steel.dev](https://app.steel.dev/settings/api-keys) and [console.anthropic.com](https://console.anthropic.com/).

Your output varies. Structure looks like this:

```text
Steel + Claude Agent SDK (Python) Starter
============================================================
Sure, let me open a browser session and pull that page.
  -> open_session({})
    open_session: 1840ms
  -> navigate({"url": "https://github.com/trending/python?since=daily"})
    navigate: 2484ms
  -> snapshot({})
    snapshot: 487ms (4000 chars, 49 links)
I have everything I need. Here are the top 3 ...

--- Final answer ---
Top 3 AI/ML-related Python repos on today's trending list:
1. owner/repo - <description> (X stars)
...

Releasing Steel session...
Session released. Replay: https://app.steel.dev/sessions/ab12cd34...
```

A run takes ~30 to 50 seconds and 3 to 6 turns. Cost is Steel session-minutes plus Anthropic tokens. The `finally` block closes Playwright and calls `steel.sessions.release()`.

## Make it yours

- **Swap the task.** Change `PROMPT` and, if useful, `SYSTEM_PROMPT`. The four tools are task-agnostic.
- **Use Opus 4.7 for harder pages.** Set `model="claude-opus-4-7"` in `ClaudeAgentOptions`.
- **Add a tool.** Decorate a new async function with `@tool`, append it to the `tools` list passed to `create_sdk_mcp_server`. A `click(selector)` tool that calls `page.click` is a useful fifth one.
- **Hook the lifecycle.** Pass `hooks={"PostToolUse": [...]}` on `ClaudeAgentOptions` to log every tool call, validate arguments, or veto destructive actions. Hook events: `PreToolUse`, `PostToolUse`, `Stop`, `SessionStart`.
- **Resume sessions.** Capture `SystemMessage.data["session_id"]` from the first run, pass `resume=session_id` on the next `ClaudeAgentOptions` to continue with full context.
- **Hand off auth.** Pair with [credentials](/cookbook/credentials) or [auth-context](/cookbook/auth-context) so the Steel session starts already logged in.

## Related

[Anthropic Agent SDK docs](https://platform.claude.com/docs/en/agent-sdk/overview) · [TypeScript version](/cookbook/claude-agent-sdk) · [Claude Computer Use (Python)](/cookbook/claude-computer-use)

## Related recipes

- [Run a durable browser agent with Restate](/cookbook/restate-agent): Build a Restate Virtual Object in TypeScript that uses durable OpenAI planning steps and Steel scraping to answer browser research questions.
- [Expose a Steel browser to any MCP client](/cookbook/mcp): Build a Model Context Protocol server in Go with the official SDK and chromedp that hands any MCP client a Steel cloud browser through explicit session-handle tools.
- [Build a browser agent with Genkit](/cookbook/genkit): Use Steel with Genkit Go to build a tool-calling agent that navigates and extracts from a chromedp-backed browser and completes a web task.
