Files

Agent SDK — Python

The Claude Agent SDK provides a higher-level interface for building AI agents with built-in tools, safety features, and agentic capabilities.

Installation

pip install claude-agent-sdk

Quick Start

import anyio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

async def main():
    async for message in query(
        prompt="Explain this codebase",
        options=ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"])
    ):
        if isinstance(message, ResultMessage):
            print(message.result)

anyio.run(main)

Built-in Tools

Tool Description
Read Read files in the workspace
Write Create new files
Edit Make precise edits to existing files
Bash Execute shell commands
Glob Find files by pattern
Grep Search files by content
WebSearch Search the web for information
WebFetch Fetch and analyze web pages
AskUserQuestion Ask user clarifying questions
Agent Spawn subagents

Primary Interfaces

query() — Simple One-Shot Usage

The query() function is the simplest way to run an agent. It returns an async iterator of messages.

from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

async for message in query(
    prompt="Explain this codebase",
    options=ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"])
):
    if isinstance(message, ResultMessage):
        print(message.result)

ClaudeSDKClient — Full Control

ClaudeSDKClient provides full control over the agent lifecycle. Use it when you need custom tools, hooks, streaming, or the ability to interrupt execution.

import anyio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock

async def main():
    options = ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"])
    async with ClaudeSDKClient(options=options) as client:
        await client.query("Explain this codebase")
        async for message in client.receive_response():
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, TextBlock):
                        print(block.text)

anyio.run(main)

ClaudeSDKClient supports:

  • Context manager (async with) for automatic resource cleanup
  • client.query(prompt) to send a prompt to the agent
  • receive_response() for streaming messages until completion
  • interrupt() to stop agent execution mid-task
  • Required for custom tools (via SDK MCP servers)

Permission System

from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

async for message in query(
    prompt="Refactor the authentication module",
    options=ClaudeAgentOptions(
        allowed_tools=["Read", "Edit", "Write"],
        permission_mode="acceptEdits"  # Auto-accept file edits
    )
):
    if isinstance(message, ResultMessage):
        print(message.result)

Permission modes:

  • "default": Prompt for dangerous operations
  • "plan": Planning only, no execution
  • "acceptEdits": Auto-accept file edits
  • "dontAsk": Don't prompt (useful for CI/CD)
  • "bypassPermissions": Skip all prompts (requires allow_dangerously_skip_permissions=True in options)

MCP (Model Context Protocol) Support

from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

async for message in query(
    prompt="Open example.com and describe what you see",
    options=ClaudeAgentOptions(
        mcp_servers={
            "playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]}
        }
    )
):
    if isinstance(message, ResultMessage):
        print(message.result)

Hooks

Customize agent behavior with hooks using callback functions:

from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, ResultMessage

async def log_file_change(input_data, tool_use_id, context):
    file_path = input_data.get('tool_input', {}).get('file_path', 'unknown')
    print(f"Modified: {file_path}")
    return {}

async for message in query(
    prompt="Refactor utils.py",
    options=ClaudeAgentOptions(
        permission_mode="acceptEdits",
        hooks={
            "PostToolUse": [HookMatcher(matcher="Edit|Write", hooks=[log_file_change])]
        }
    )
):
    if isinstance(message, ResultMessage):
        print(message.result)

Available hook events: PreToolUse, PostToolUse, PostToolUseFailure, Notification, UserPromptSubmit, SessionStart, SessionEnd, Stop, SubagentStart, SubagentStop, PreCompact, PermissionRequest, Setup, TeammateIdle, TaskCompleted, ConfigChange


Common Options

query() takes a top-level prompt (string) and an options object (ClaudeAgentOptions):

async for message in query(prompt="...", options=ClaudeAgentOptions(...)):
Option Type Description
cwd string Working directory for file operations
allowed_tools list Tools the agent can use (e.g., ["Read", "Edit", "Bash"])
tools list Built-in tools to make available (restricts the default set)
disallowed_tools list Tools to explicitly disallow
permission_mode string How to handle permission prompts
allow_dangerously_skip_permissions bool Must be True to use permission_mode="bypassPermissions"
mcp_servers dict MCP servers to connect to
hooks dict Hooks for customizing behavior
system_prompt string Custom system prompt
max_turns int Maximum agent turns before stopping
max_budget_usd float Maximum budget in USD for the query
model string Model ID (default: determined by CLI)
agents dict Subagent definitions (dict[str, AgentDefinition])
output_format dict Structured output schema
thinking dict Thinking/reasoning control
betas list Beta features to enable (e.g., ["context-1m-2025-08-07"])
setting_sources list Settings to load (e.g., ["project"]). Default: none (no CLAUDE.md files)
env dict Environment variables to set for the session

Message Types

from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage, SystemMessage

async for message in query(
    prompt="Find TODO comments",
    options=ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"])
):
    if isinstance(message, ResultMessage):
        print(message.result)
    elif isinstance(message, SystemMessage) and message.subtype == "init":
        session_id = message.session_id  # Capture for resuming later

Subagents

from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition, ResultMessage

async for message in query(
    prompt="Use the code-reviewer agent to review this codebase",
    options=ClaudeAgentOptions(
        allowed_tools=["Read", "Glob", "Grep", "Agent"],
        agents={
            "code-reviewer": AgentDefinition(
                description="Expert code reviewer for quality and security reviews.",
                prompt="Analyze code quality and suggest improvements.",
                tools=["Read", "Glob", "Grep"]
            )
        }
    )
):
    if isinstance(message, ResultMessage):
        print(message.result)

Error Handling

from claude_agent_sdk import query, ClaudeAgentOptions, CLINotFoundError, CLIConnectionError, ResultMessage

try:
    async for message in query(
        prompt="...",
        options=ClaudeAgentOptions(allowed_tools=["Read"])
    ):
        if isinstance(message, ResultMessage):
            print(message.result)
except CLINotFoundError:
    print("Claude Code CLI not found. Install with: pip install claude-agent-sdk")
except CLIConnectionError as e:
    print(f"Connection error: {e}")

Best Practices

  1. Always specify allowed_tools — Explicitly list which tools the agent can use
  2. Set working directory — Always specify cwd for file operations
  3. Use appropriate permission modes — Start with "default" and only escalate when needed
  4. Handle all message types — Check for ResultMessage to get agent output
  5. Limit max_turns — Prevent runaway agents with reasonable limits