# Build a browser agent with the Claude Agent SDK
URL: /cookbook/claude-agent-sdk

---
title: Build a browser agent with the Claude Agent SDK
description: "Use Steel with the Claude Agent SDK (TypeScript) to build a tool-using browser agent on Anthropic's first-party agent loop."
---

<RecipeJsonLd slug="claude-agent-sdk" title={"Build a browser agent with the Claude Agent SDK"} description={"Use Steel with the Claude Agent SDK (TypeScript) to build a tool-using browser agent on Anthropic's first-party agent loop."} authors={[{"handle":"junhsss","name":"Jun Ryu"}]} datePublished="2026-04-29" dateModified="2026-04-29" sourceUrl="https://github.com/steel-dev/steel-cookbook/tree/92f29742253e2b6c6801d109e18232768e5291a0/examples/claude-agent-sdk-ts" />

<Tabs items={['TypeScript', 'Python']} groupId="lang" persist updateAnchor className="cookbook-concept-tabs">

<Tab id="typescript" className="cookbook-concept-tab">

<RecipeMeta href="https://github.com/steel-dev/steel-cookbook/tree/92f29742253e2b6c6801d109e18232768e5291a0/examples/claude-agent-sdk-ts" path="examples/claude-agent-sdk-ts" authors={[{"handle":"junhsss","name":"Jun Ryu","avatar":"https://github.com/junhsss.png?size=40"}]} updated="2026-04-29" />

<RecipeQuickstart slug="claude-agent-sdk-ts" />

`@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, 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. 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)

</Tab>

<Tab id="python" className="cookbook-concept-tab">

<RecipeMeta href="https://github.com/steel-dev/steel-cookbook/tree/92f29742253e2b6c6801d109e18232768e5291a0/examples/claude-agent-sdk-py" path="examples/claude-agent-sdk-py" authors={[{"handle":"junhsss","name":"Jun Ryu","avatar":"https://github.com/junhsss.png?size=40"}]} updated="2026-04-29" />

<RecipeQuickstart slug="claude-agent-sdk-py" />

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)

</Tab>

</Tabs>

## Related recipes

<RecipeGrid>
<RecipeCard slug="microsoft-agent-framework" title={"Build a browser agent with Microsoft Agent Framework"} description={"Use Steel with Microsoft Agent Framework 1.0 (the successor to AutoGen and Semantic Kernel) to build a tool-using browser agent."} topics={['Agents']} date="2026-05-11" />
<RecipeCard slug="convex-chat-with-page" title={"Chat with any webpage on Convex"} description={"Convex app that streams an AI agent's answer about any URL. The agent runs server-side with one Steel-backed scrape tool and pages through long articles via a chunked cache."} topics={['Agents', 'Convex']} date="2026-05-04" />
<RecipeCard slug="deep-research" title={"Deep research with Claude Agent SDK subagents"} description={"Lead orchestrator dispatches parallel researcher subagents, each driving its own Steel browser, and synthesizes findings into a cited Markdown report."} topics={['Agents', 'Subagents']} date="2026-05-01" />
</RecipeGrid>
