ClaudeMap

·SDKs & Tooling

How to embed the Claude Code agent loop in your own programs with the Claude Agent SDK — installation, a minimal async example, custom tools via MCP, loop control with tool limits and hooks, parallel subagent fan-out and hooks-driven audit trails, plus six anti-patterns that send agent loops off the rails and a full evaluation method.

Building Autonomous Workflows With the Claude Agent SDK

Claude Code is a great interactive tool, but once you want an agent inside a real product — a CI pipeline that triages failing tests, a support bot that resolves tickets, a research assistant that reads a repo and writes a report — you need the same agent loop available programmatically. That is what the Claude Agent SDK provides: the same tools, agent loop, and context management that power Claude Code, exposed as a library in Python and TypeScript. This guide covers installation, a minimal working example, how to wire in custom tools, how to control the agent loop, and the patterns that hold up in production.

TL;DR

  • Agent SDK = the same agent loop that powers Claude Code, as a programmatic library
  • Python: pip install claude-agent-sdk; TypeScript: npm install @anthropic-ai/claude-agent-sdk
  • Core API: query() is an async generator — iterate the messages (assistant text, tool calls, tool results)
  • ClaudeAgentOptions controls the run: allowed tools, cwd, permission mode, model
  • 2026 status: subagents and MCP servers are first-class citizens of the Agent SDK

What the Agent SDK is

The Agent SDK gives you, as a library, the capabilities Claude Code offers interactively. It bundles a native Claude Code binary for your platform, so behind the scenes it is running the same battle-tested agent loop — not a re-implementation. That matters because you inherit the tool implementations (Read, Write, Bash, Grep, and friends), the context-management behavior, and the permission model without rebuilding any of it.

The SDK is recommended for CI/CD pipelines, custom applications, and production automation. For interactive development and one-off tasks, the Claude Code CLI itself is the better fit. Think of the SDK as the "embed Claude Code in your own program" path.

It ships in two languages with first-class support:

  • Pythonpip install claude-agent-sdk (or uv add claude-agent-sdk)
  • TypeScriptnpm install @anthropic-ai/claude-agent-sdk

For other languages, the documented approach is to run the Claude Code CLI programmatically using the -p flag with --output-format json, parsing the JSON stream from your language of choice.

Installing and authenticating

Install the Python SDK into a project that uses Python 3.10 or newer:

pip install claude-agent-sdk

Authentication follows Claude Code: the SDK uses your ANTHROPIC_API_KEY environment variable (or the same auth Claude Code uses if you are already logged in). Set the key before running your program:

export ANTHROPIC_API_KEY="sk-ant-..."

You also need the Claude Code CLI available on the machine, because the SDK drives it under the hood. On a fresh CI box, installing the SDK and ensuring the CLI is on the PATH is the setup; on a developer machine that already runs Claude Code, you usually have everything.

A minimal agent

The SDK exposes an async query function that streams messages back at you. Here is the smallest useful program — it asks the agent to list the files in the current directory, allowing the Bash and Glob tools so it can actually do the work:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    async for message in query(
        prompt="What files are in this directory? Summarize the project structure.",
        options=ClaudeAgentOptions(allowed_tools=["Bash", "Glob"]),
    ):
        # Each message is an event in the agent's run: assistant text, tool
        # calls, tool results, and a final result. The .result attribute marks
        # the final assistant message of the run.
        if hasattr(message, "result"):
            print(message.result)

asyncio.run(main())

A few things worth noticing:

  • query(...) is an async generator. You iterate over it and react to messages as they arrive. This is the streaming interface — you see tool calls and results in real time, not just the final answer.
  • ClaudeAgentOptions controls the run. allowed_tools is the most important knob: it restricts what the agent may do, the same way permission rules do in interactive Claude Code. Start narrow and widen as you build trust.
  • The agent loop is implicit. You do not write the "call the model, parse for tool calls, execute them, feed back results" loop yourself — the SDK runs it. Your code reacts to the stream.

The TypeScript equivalent has the same shape: an async iteration over a query() call, with options that include the allowed tools and other settings.

Custom tools and MCP

Real workflows need tools beyond the built-ins. The Agent SDK inherits Claude Code's MCP support, so you can connect any MCP server the same way you would in .mcp.json. Define your server in a config the SDK reads, or pass the server definitions through options, and the agent can call your custom tools with the same tool-use loop.

This is the bridge between the Agent SDK and the broader MCP ecosystem: write a tool once as an MCP server (using @modelcontextprotocol/sdk in TypeScript or the Python MCP SDK), and it becomes available to both interactive Claude Code and any Agent SDK program you build. That portability is the main reason to expose custom capabilities as MCP servers rather than ad-hoc functions.

Controlling the loop

Autonomy is powerful, but in production you usually want guardrails. The Agent SDK gives you several ways to keep the agent on a leash:

  • Restrict the tool set. The single most effective control. An agent that can only read and grep cannot mutate state, by construction. Grant write tools only when a task genuinely needs them.
  • Set a working directory and permission mode. Just like interactive Claude Code, the SDK respects permission modes. For automated runs, pre-approve exactly the tools and command patterns the task needs so no interactive prompt blocks the run.
  • Cap the run. Bound the number of turns or the wall-clock time so a confused agent cannot loop forever. Long-running agents that drift are a real failure mode; a cap turns it into a retryable error instead.
  • Use hooks for policy. The SDK supports hooks at lifecycle events, so you can block dangerous tool calls or validate outputs deterministically — the same PreToolUse/PostToolUse model as interactive Claude Code.
  • Stream and observe. Because query is a generator, you can log every tool call and result to an audit trail as it happens. For any production agent, this observability is not optional — it is the only way to answer "what did the agent do?" after the fact.

A real scenario: PR triage

To make this concrete, here is the shape of a real workflow — triaging a failing CI build. The agent reads the failing test output, looks at the relevant source, and posts a comment summarizing the likely cause.

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def triage(failing_test_log: str, repo_dir: str):
    prompt = f"""
A CI build is failing. Here is the test output:

<failing_log>
{failing_test_log}
</failing_log>

Investigate the likely cause: read the failing test, read the source it
exercises, and write a 3-5 sentence summary of the probable root cause and
the file:line where the fix likely belongs. Do not modify any files.
"""
    async for message in query(
        prompt=prompt,
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Grep", "Glob"],
            cwd=repo_dir,
        ),
    ):
        if hasattr(message, "result"):
            return message.result
    return ""

# In the CI step: read the log, call triage(), post the result as a comment.

The decisions that make this safe in production: the tool set is read-only (Read, Grep, Glob — no Bash, no Write), so the agent literally cannot change anything. The working directory is pinned to the checked-out repo. The output is a bounded summary, not free-form code. This is the pattern: narrow tools, pinned scope, structured output, human in the loop for any actual change.

When to use the SDK vs the CLI vs raw API calls

Three related tools, three different fits:

  • Claude Code CLI — interactive development, one-off tasks in a terminal. Best when a human is driving.
  • Claude Agent SDK — production automation, custom applications, CI/CD. Best when you want the full agent loop (tools, context management, the works) embedded in your own program.
  • Raw Claude API (anthropic SDK) — when you need direct control over messages, tool use, and the model call itself, and you are willing to build your own agent loop. Best for custom designs that do not match Claude Code's loop.

A useful rule: if your first instinct is "I wish Claude Code could do X automatically," reach for the Agent SDK. If your first instinct is "I want to call the model with this exact set of messages and tools," reach for the raw API.

Production considerations

A few things that matter once an agent runs unattended:

  • Idempotency. Agents get retried. Make sure the side effects of your tools are idempotent, or guard them so a retry does not double-apply a change.
  • Cost monitoring. Each agent run costs tokens, and agentic runs cost more than single calls. Instrument the token usage and set budget alerts. A runaway agent is also a runaway bill.
  • Secrets. Be careful what you expose to the agent through tool results or file reads. An agent with broad read access can surface secrets into its output. Apply the same deny rules you would in interactive Claude Code.
  • Determinism boundaries. Agents are non-deterministic. Use them for judgement work (triage, summarization, exploration) and keep deterministic logic in plain code. Do not let an agent decide whether a deploy proceeds — let it gather information for a human or a rule to act on.
  • Evaluation. Before shipping, run the agent against a fixed set of inputs and capture the outputs. When you change the prompt, the tools, or the model, re-run and diff. Without this, agent quality drifts invisibly.

Subagent orchestration and multi-agent workflows

The SDK is not just a single-agent loop — it lets you spin up multiple parallel subagents inside the same process for fan-out research. This is the same capability you have interactively in Claude Code, but the SDK lets you programmatically orchestrate them:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def fan_out_research(topics: list[str]) -> list[str]:
    # each subagent runs its own query — independent, isolated context
    subagents = [
        query(
            prompt=f"Research 2026 best practices for {topic}. Return 3-5 points.",
            options=ClaudeAgentOptions(
                allowed_tools=["Read", "Grep", "WebSearch"],
                permission_mode="bypassPermissions",
            ),
        )
        for topic in topics
    ]
    # gather all subagent streams, then iterate each
    streams = await asyncio.gather(*subagents, return_exceptions=True)
    results = []
    for stream in streams:
        if isinstance(stream, Exception):
            continue
        async for message in stream:
            if hasattr(message, "result"):
                results.append(message.result)
    return results

# the main thread only sees the three distilled reports —
# all the source pages and code the subagents read never
# pollute the main context

Key patterns: (1) each subagent has its own context window (isolated by default, never bleeds into the main thread); (2) permission_mode="bypassPermissions" is the safe default inside the SDK — subagents have no way to interact, automated tasks must skip prompts; (3) asyncio.gather(..., return_exceptions=True) fires all subagents concurrently and waits for every one to finish; return_exceptions=True keeps a single failure from killing the others. The real payoff is tokens: the main thread only sees three summaries at the end, while each subagent's thousands of internal context tokens never touch the main thread's budget.

Hooks integration and audit trails

The SDK inherits Claude Code's hooks model — you can attach scripts to PreToolUse, PostToolUse, Stop, and other events to add deterministic guardrails and post-hoc audits to your agents:

import json, logging
from claude_agent_sdk import query, ClaudeAgentOptions

audit = logging.getLogger("agent.audit")

async def logged_query(prompt: str):
    async for message in query(
        prompt=prompt,
        options=ClaudeAgentOptions(
            allowed_tools=["Bash", "Read", "Write"],
            hooks={
                "PreToolUse": [{
                    "matcher": "Bash",
                    "hooks": [{
                        "type": "command",
                        # block any rm -rf call before it runs
                        "command": "python3 .claude/hooks/block_rm.py",
                    }],
                }],
            },
        ),
    ):
        # stream-log every event — this is the audit trail
        audit.info(json.dumps({
            "type": type(message).__name__,
            "data": getattr(message, "data", None),
        }))
        if hasattr(message, "result"):
            yield message.result

Production uses: (1) deterministic safety guardrails — use PreToolUse hooks to block rm -rf, curl | sh, writes to sensitive paths; (2) audit logs — write every tool call and result to structured (JSON) logs, read them straight out when a CI run fails; (3) quality gates — PostToolUse hooks run ruff / tsc / tests and feed errors back to the agent so it self-corrects. The pattern: put "this must never go wrong" in hooks (deterministic), leave "this needs judgement" to the agent (probabilistic) — the two responsibilities never overlap.

Common Pitfalls and Anti-Patterns: Six Ways an Agent Loop Goes Wrong

The production notes above are principles; this section is the field guide — six recurring patterns that all share one trait: fine in the demo, inevitable in production.

Pitfall 1: no max_turns. Symptom: the agent shuttles output between two tools forever (A feeds B, B feeds back into A) until the budget burns. Cause: the loop has no termination condition, and the model never feels the cost of "one more round". Fix: cap max_turns, back it with a max_budget_usd ceiling (what each knob does in the Agent SDK deep dive), and check every turn that the task actually progressed.

Pitfall 2: tool descriptions written as API docs. Symptom: the model passes wrong arguments, or fails to call a tool it should. Cause: a tool description is usage documentation for the model, not a signature comment for programmers — the model has no idea what id: string means. Fix: write when to use it, an example argument set, and what happens on failure; constrain shapes with JSON Schema enums and patterns.

Pitfall 3: tool errors swallowed into an exception log. Symptom: the same failing action retries until max_turns. Cause: the tool raised, you logged it, but nothing went back as a tool_result — the model never learned the previous attempt failed. Fix: catch everything and return a structured error (code + suggestion) as the tool_result, letting the agent pick another path or stop.

Pitfall 4: tool results return whole files. Symptom: after two or three turns the context explodes, cost spikes, and the model starts "forgetting" the task. Cause: a 5,000-line log went into context verbatim. Fix: summarize or truncate at the tool layer — return the first N lines plus "full file at path" and let the agent read on demand.

Pitfall 5: subagents for everything. Symptom: a single-step task gets a three-level orchestration, doubling latency and cost. Cause: subagents buy context isolation, not parallel decoration. Fix: if one tool call can do it, don't spawn a subagent — orchestration complexity must purchase something (isolation, parallelism, focused context); see the subagent orchestration section below.

Pitfall 6: retries without backoff or caps. Symptom: an upstream 5xx turns into hard retry loops that amplify one failure into an avalanche. Fix: exponential backoff, a retry ceiling, and a degradation path (fallback model / skip / page a human).

Evaluating and Iterating: How to Know Your Agent Got Better

Evaluation gets one line in the production notes; it deserves a whole system — agent regressions are stealthier than prompt regressions. A tool changes one parameter, the model bumps a version, and success rate slides without a single error message.

A golden task set. Collect 10-20 end-to-end tasks (input + expected outcome + pass rule) and rerun the full set on every change. Unlike prompt evaluation, agent verdicts are rarely string comparisons — write assertion scripts over the result JSON, the final file diff, or the database state, or grade with a model against an explicit rubric.

Three metrics, kept separate. Task success rate (is the outcome right), tool efficiency (how many calls, any detours), and unit cost (tokens and dollars per task). A change often trades success rate for cost — read all three before calling it a win.

Trace replay. Review failed tasks trace by trace: where did the model go wrong, what misleading payload did a tool return? Logging every tool call and result (see the hooks integration above) is what makes replay possible.

One variable at a time. The same discipline as prompt iteration: don't change the prompt while changing tools, don't swap the model while swapping the toolset. Multi-variable changes make attribution guesswork.

The regression set grows with every incident. Each production bad case becomes a new golden task. Six months in, that task set is your agent system's balance sheet — it survives maintainer turnover.

Frequently asked questions

What is the Claude Agent SDK and how does it differ from Claude Code?

The Claude Agent SDK exposes the same agent loop, tools, and context management that power Claude Code, as a library in Python and TypeScript. Claude Code is the interactive CLI you run in a terminal; the Agent SDK is what you embed inside your own programs for CI/CD, custom applications, and production automation. Behind the scenes the SDK drives a bundled Claude Code binary, so you inherit the same battle-tested behavior.

How do I install the Claude Agent SDK?

Install the Python SDK with pip install claude-agent-sdk (Python 3.10 or newer) or uv add claude-agent-sdk, and install the TypeScript SDK with npm install @anthropic-ai/claude-agent-sdk. You also need the Claude Code CLI available on the machine and an ANTHROPIC_API_KEY environment variable set. For other languages, run the Claude Code CLI programmatically with the -p flag and --output-format json.

How do I control what tools the agent can use?

Pass an allowed_tools list through ClaudeAgentOptions (or its TypeScript equivalent). Restricting the tool set is the single most effective way to keep an agent safe — an agent that can only Read and Grep cannot mutate state by construction. Grant write or shell tools only when a task genuinely needs them, and combine this with permission modes and hooks for policy.

Should I use the Agent SDK or call the Claude API directly?

Use the Agent SDK when you want the full Claude Code agent loop (tools, context management, the works) embedded in your program without rebuilding it. Use the raw Claude API (the anthropic SDK) when you need direct control over messages and tool use and are willing to build your own agent loop. A rule of thumb: if you wish Claude Code could do something automatically, reach for the Agent SDK; if you want to call the model with an exact set of messages, use the raw API.

Can the Agent SDK run multiple subagents in parallel?

Yes. The SDK lets you start multiple query() calls inside the same process with asyncio.gather(*queries, return_exceptions=True), each running its own subagent with isolated contexts. The typical pattern is fan-out research: three subagents investigate different directions in parallel, and the main thread only sees three distilled reports at the end — none of the intermediate context touches the main budget. Pairing: permission_mode="bypassPermissions" is the safe default inside the SDK (subagents have no way to interact), and allowed_tools should be tightened to the minimum each subagent actually needs.

How do I add audit logs and deterministic guardrails to the Agent SDK?

Use ClaudeAgentOptions.hooks to wire into Claude Code's hooks system — PreToolUse blocks dangerous tool calls (rm -rf, writes to sensitive paths); PostToolUse runs a linter and feeds errors back to the agent; Stop triggers a notification. For auditing: async for message in query(...) exposes every event (assistant text, tool calls, tool results), so you write them to a structured (JSON) log. Production pattern: put "must never go wrong" in hooks (deterministic logic), leave "needs judgement" to the agent (probabilistic logic) — clean separation, zero overlap.

Official references

This article reflects publicly available information as of July 2026; relevant APIs may evolve.