Back to Blog

Claude Agent SDK Quickstart - Building Your First Bug-Fixing Agent

March 22, 20267 min readMichael Ridland

I've written about the Claude Agent SDK's Python reference and agent loop mechanics before. But sometimes you just want to go from zero to working agent as fast as possible. That's what the SDK's quickstart is designed for, and having run through it with several client teams now, I can say it genuinely delivers on that promise.

The official quickstart guide is well-structured. What I want to add here is the context around why certain design decisions matter and what to watch for when you take this from tutorial to real project.

What Makes the Claude Agent SDK Different

Most agent frameworks make you define the orchestration. You build a graph of nodes, configure state transitions, handle tool routing yourself. The Claude Agent SDK takes a fundamentally different approach - Claude figures out what tools to use based on your prompt. You describe the task, provide the tools, and the model handles the rest.

This isn't just a philosophical difference. It means your first agent is genuinely about 20 lines of code. No graph definitions, no state machines, no routing logic. That's not marketing fluff - it's actually how it works.

The trade-off is control. If you need precise orchestration where tool A must run before tool B regardless of context, you'll need to encode that in your prompt or use subagents. But for the 80% of use cases where "read the code, figure out what's wrong, fix it" is a reasonable instruction, the SDK gets out of your way.

Setup - Actually Simple for Once

mkdir my-agent && cd my-agent
npm install @anthropic-ai/claude-agent-sdk

For Python:

uv init && uv add claude-agent-sdk

Or with pip:

python3 -m venv .venv && source .venv/bin/activate
pip3 install claude-agent-sdk

Set your API key in a .env file:

ANTHROPIC_API_KEY=your-api-key

The SDK also supports third-party providers - Amazon Bedrock, Google Vertex AI, and Azure AI Foundry. For our Australian enterprise clients, we frequently use the Azure path since most are already invested in the Microsoft ecosystem. Set CLAUDE_CODE_USE_FOUNDRY=1 and configure your Azure credentials. The agent works the same way regardless of which provider routes the inference.

One thing worth noting: Anthropic doesn't allow third-party developers to offer claude.ai login or rate limits for products built on the SDK. Use API key authentication. This trips up teams that assume they can proxy through Claude's consumer auth.

The Buggy Code Exercise

The quickstart gives you intentionally broken code to fix:

def calculate_average(numbers):
    total = 0
    for num in numbers:
        total += num
    return total / len(numbers)

def get_user_name(user):
    return user["name"].upper()

Two bugs: calculate_average([]) crashes with division by zero, and get_user_name(None) crashes with a TypeError. Simple, but they represent the kind of edge-case bugs that slip through code reviews constantly.

Save this as utils.py in your project directory.

Building the Agent

Here's the TypeScript version:

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Review utils.py for bugs that would cause crashes. Fix any issues you find.",
  options: {
    allowedTools: ["Read", "Edit", "Glob"],
    permissionMode: "acceptEdits"
  }
})) {
  if (message.type === "assistant" && message.message?.content) {
    for (const block of message.message.content) {
      if ("text" in block) {
        console.log(block.text);
      } else if ("name" in block) {
        console.log(`Tool: ${block.name}`);
      }
    }
  } else if (message.type === "result") {
    console.log(`Done: ${message.subtype}`);
  }
}

And Python:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage

async def main():
    async for message in query(
        prompt="Review utils.py for bugs that would cause crashes. Fix any issues you find.",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Edit", "Glob"],
            permission_mode="acceptEdits",
        ),
    ):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if hasattr(block, "text"):
                    print(block.text)
                elif hasattr(block, "name"):
                    print(f"Tool: {block.name}")
        elif isinstance(message, ResultMessage):
            print(f"Done: {message.subtype}")

asyncio.run(main())

Let me break down what's actually happening here.

The Three Key Pieces

query() creates the agentic loop. It returns an async iterator that streams messages as Claude works. Each iteration of the loop is one step in Claude's reasoning-action-observation cycle. The loop continues until Claude decides the task is complete or hits an error.

prompt is your natural language instruction. Claude determines which tools to use based on this. You don't need to specify "first read the file, then analyse it, then edit it." Just describe the outcome you want.

options configures permissions and behaviour. allowedTools pre-approves specific tools so the agent doesn't need to ask permission for each one. permissionMode: "acceptEdits" auto-approves file modifications. Other useful options include systemPrompt for shaping agent behaviour, mcpServers for connecting external tools, and cwd for setting the working directory.

What Happens When You Run It

npx tsx agent.ts

The agent autonomously:

  1. Uses Glob to find utils.py in the project
  2. Uses Read to examine the file contents
  3. Analyses the code and identifies the edge cases
  4. Uses Edit to add proper error handling

Check utils.py after it runs and you'll see defensive code handling empty lists and null values. The agent didn't just identify the bugs - it fixed them. That's the key distinction from a regular API call where you'd get suggestions but still need to apply changes yourself.

Taking This Beyond the Tutorial

The quickstart is intentionally simple. Here's how it translates to real work.

Code Review Pipeline

We've built agents for clients that run as part of their CI/CD pipeline. On every pull request, an agent reviews the changed files for potential bugs, security issues, and style violations. The prompt is more detailed than the quickstart example, but the structure is identical:

options = ClaudeAgentOptions(
    system_prompt="You are a senior developer reviewing code changes. Focus on bugs, security issues, and maintainability. Be specific about what's wrong and why.",
    allowed_tools=["Read", "Glob", "Grep"],
    permission_mode="default",  # Read-only - don't auto-approve edits in CI
)

Notice we switched to default permission mode for CI. You don't want your review agent silently editing code in a pipeline. It should identify issues and report them, not fix them unilaterally.

Document Processing

Another pattern we use: agents that process incoming documents, extract structured data, and write results to a specific format.

async for message in query(
    prompt="Read the invoice PDF in /incoming, extract vendor name, amount, due date, and line items. Write the structured data to /processed/invoice.json",
    options=ClaudeAgentOptions(
        allowed_tools=["Read", "Write", "Glob"],
        permission_mode="acceptEdits",
    ),
):
    # Handle messages

The agent reads the document, understands its structure, and produces JSON output. No template matching, no OCR configuration - just a natural language description of what you want extracted.

Streaming vs Batch

The quickstart uses streaming, which shows progress in real time. For background jobs and CI pipelines, you might not need live output. The SDK supports both modes. Streaming is better for interactive use where a developer is watching. Batch is better for automation where you just need the final result.

What I'd Change About the Quickstart

The tutorial is good. If I were writing it, I'd add two things.

First, I'd show what happens when the agent fails. What does the error look like? How do you debug it? The happy path is clean, but new users need to know what to do when things go sideways. Missing API key errors, permission denials, tool failures - seeing these once in a tutorial prevents confusion later.

Second, I'd include a slightly more complex example. The two-bug file is great for proving the concept, but a 100-line file with subtle logic errors would better represent real usage. The agent handles complexity well - showing that early builds confidence.

When to Use the Agent SDK vs Other Approaches

The Claude Agent SDK is ideal when you want Claude to work autonomously with your files and codebase. It's the right choice for code review, bug fixing, documentation generation, and any task where the agent needs to read context, reason about it, and take action.

It's not the right tool for every AI use case. If you need structured API calls with guaranteed output schemas, the standard Anthropic API with tool use is more appropriate. If you need multi-model orchestration or complex DAG-based workflows, frameworks like LangGraph might be a better fit. But for autonomous coding agents, the Claude Agent SDK is the cleanest option available right now.

If your team is looking to build AI agents - whether for internal automation, client-facing products, or development workflow improvements - we've been doing this work at Team 400 across the full range of agent frameworks. Our AI agent development practice helps organisations pick the right tools and build agents that actually work in production. We also run AI training sessions for development teams who want to build these capabilities in-house.

The quickstart gets you from zero to working agent in under 30 minutes. The real work starts when you apply these patterns to your own problems. But that first working agent - watching it read your code, reason about bugs, and fix them without intervention - is still a pretty satisfying experience.