Claude
Skills
Sign in
Back

claude-agent-sdk

Included with Lifetime
$97 forever

Builds production-ready applications with the Claude Agent SDK for Python, covering orchestrators with subagents, programmatic agent configuration, hooks, and permissions.

Backend & APIsassets

What this skill does


# Claude Agent SDK

Build production-ready applications using the Claude Agent SDK for Python.

**SDK Version:** This skill targets `claude-agent-sdk>=0.1.6` (Python)

## Overview

This skill provides patterns, examples, and best practices for building SDK applications that orchestrate Claude agents.

## Quick Start

Copy the template and customize:

```bash
cp assets/sdk-template.py my-app.py
# Edit my-app.py - customize agents and workflow
chmod +x my-app.py
./my-app.py
```

The template includes proper uv script headers, agent definitions, and async patterns.

## Choosing Between query() and ClaudeSDKClient

The SDK provides two ways to interact with Claude: the `query()` function for simple one-shot tasks, and `ClaudeSDKClient` for continuous conversations.

### Quick Comparison

| Feature | `query()` | `ClaudeSDKClient` |
|---------|-----------|-------------------|
| **Conversation memory** | No - each call is independent | Yes - maintains context across queries |
| **Use case** | One-off tasks, single questions | Multi-turn conversations, complex workflows |
| **Complexity** | Simple - one function call | More setup - context manager pattern |
| **Hooks support** | No | Yes |
| **Custom tools** | No | Yes |
| **Interrupts** | No | Yes - can interrupt ongoing operations |
| **Session control** | New session each time | Single persistent session |

> **Important:** Hooks and custom tools (SDK MCP servers) are **only supported with `ClaudeSDKClient`**, not with `query()`. If you need hooks or custom tools, you must use `ClaudeSDKClient`.
>
> **Note on Async Runtimes:** The SDK works with both `asyncio` and `anyio`. The official SDK examples prefer `anyio.run()` for better async library compatibility, but `asyncio.run()` works equally well. Use whichever fits your project's async runtime.

### When to Use query()

Use `query()` for simple, independent tasks where you don't need conversation history:

```python
import anyio  # or: import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def analyze_file():
    """One-shot file analysis - no conversation needed."""
    options = ClaudeAgentOptions(
        system_prompt="You are a code analyzer",
        allowed_tools=["Read", "Grep", "Glob"],
        permission_mode="acceptEdits"
    )

    async for message in query(
        prompt="Analyze /path/to/file.py for bugs",
        options=options
    ):
        print(message)

anyio.run(analyze_file)  # or: asyncio.run(analyze_file())
```

**Best for:**

- Single analysis tasks
- Independent file operations
- Quick questions without follow-up
- Scripts that run once and exit

**Key limitation:** Each `query()` call creates a new session with no memory of previous calls.

### When to Use ClaudeSDKClient

Use `ClaudeSDKClient` when you need conversation context across multiple interactions:

```python
import anyio  # or: import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock

async def interactive_debugging():
    """Multi-turn debugging conversation with context."""
    options = ClaudeAgentOptions(
        system_prompt="You are a debugging assistant",
        allowed_tools=["Read", "Grep", "Bash"],
        permission_mode="acceptEdits"
    )

    async with ClaudeSDKClient(options=options) as client:
        # First query
        await client.query("Find all TODO comments in /path/to/project")
        async for message in client.receive_response():
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, TextBlock):
                        print(f"Claude: {block.text}")

        # Follow-up - Claude remembers the TODOs found above
        await client.query("Now prioritize them by complexity")
        async for message in client.receive_response():
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, TextBlock):
                        print(f"Claude: {block.text}")

        # Another follow-up - still in same conversation
        await client.query("Create a plan to address the top 3")
        async for message in client.receive_response():
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, TextBlock):
                        print(f"Claude: {block.text}")

anyio.run(interactive_debugging)  # or: asyncio.run(interactive_debugging())
```

**Best for:**

- Multi-turn conversations
- Interactive workflows
- Tasks requiring context from previous responses
- Applications with interrupt capability
- Orchestrators managing complex workflows

**Key advantage:** Claude remembers all previous queries and responses in the session.

**See:** `examples/streaming_mode.py` - Comprehensive ClaudeSDKClient examples with all patterns

### Advanced: Interrupts with ClaudeSDKClient

Only `ClaudeSDKClient` supports interrupting ongoing operations:

```python
import anyio  # or: import asyncio
from claude_agent_sdk import ClaudeSDKClient

async def interruptible_task():
    async with ClaudeSDKClient() as client:
        await client.query("Run a long analysis on /large/codebase")

        # Start processing in background
        async with anyio.create_task_group() as tg:
            tg.start_soon(process_messages, client)

            # Simulate user interrupt after 5 seconds
            await anyio.sleep(5)
            await client.interrupt()

async def process_messages(client):
    async for message in client.receive_response():
        print(message)

anyio.run(interruptible_task)  # or: asyncio.run(interruptible_task())
```

### Quick Decision Guide

**Use `query()` if:**

- Task is self-contained
- No follow-up questions needed
- Each execution is independent
- Simpler code is preferred

**Use `ClaudeSDKClient` if:**

- Need conversation memory
- Building interactive workflows
- Require interrupt capability
- Managing complex multi-step processes
- Working with orchestrators and subagents

## Core Patterns

### 1. Orchestrator with Subagents

Define a main orchestrator that delegates work to specialized subagents.

**Critical requirements:**

- Orchestrator must use `system_prompt={"type": "preset", "preset": "claude_code"}` (provides Task tool knowledge)
- Register agents programmatically via `agents={}` parameter (SDK best practice)
- Orchestrator must include `"Task"` in `allowed_tools`
- Match agent names exactly between definition and usage

**Example:**

```python
from claude_agent_sdk import AgentDefinition, ClaudeAgentOptions

options = ClaudeAgentOptions(
    system_prompt={"type": "preset", "preset": "claude_code"},  # REQUIRED for orchestrators
    allowed_tools=["Bash", "Task", "Read", "Write"],
    agents={
        "analyzer": AgentDefinition(
            description="Analyzes code structure and patterns",
            prompt="You are a code analyzer...",
            tools=["Read", "Grep", "Glob"],
            model="sonnet"
        ),
        "fixer": AgentDefinition(
            description="Fixes identified issues",
            prompt="You are a code fixer...",
            tools=["Read", "Edit", "Bash"],
            model="sonnet"
        )
    },
    permission_mode="acceptEdits",
    model="claude-sonnet-4-5"
)
```

**See:**

- `references/agent-patterns.md` - Complete agent definition patterns
- `examples/agents.py` - Official SDK agent examples with different agent types

### 2. System Prompt Configuration

Choose the appropriate system prompt pattern:

```python
# Orchestrator (use claude_code preset) - dict format (official examples prefer this)
system_prompt={"type": "preset", "preset": "claude_code"}

# Shorthand format (equivalent, but less explicit)
system_prompt="claude_code"

# Custom behavior
system_prompt="You are a Python expert..."

# Extend preset with additional instructions
system_prompt={
    "type

Related in Backend & APIs