claude-code-system-prompts/system-prompts/data-agent-sdk-reference-python.md
2026-03-17 17:30:45 -06:00

12 KiB

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

```bash pip install claude-agent-sdk ```


Quick Start

```python 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.

```python 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.

```python 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

```python 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
  • `"bypassPermissions"`: Skip all prompts (use with caution)

MCP (Model Context Protocol) Support

```python 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:

```python 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) ```

Hook callback inputs for tool-lifecycle events (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`) include `agent_id` and `agent_type` fields, allowing hooks to identify which agent (main or subagent) triggered the tool call.

Available hook events: `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `UserPromptSubmit`, `Stop`, `SubagentStop`, `PreCompact`, `Notification`, `SubagentStart`, `PermissionRequest`


Common Options

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

```python 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
`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

```python 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) print(f"Stop reason: {message.stop_reason}") # e.g., "end_turn", "max_turns" elif isinstance(message, SystemMessage) and message.subtype == "init": session_id = message.data.get("session_id") # Capture for resuming later ```

Typed task message subclasses are available for better type safety when handling subagent task events:

  • `TaskStartedMessage` — emitted when a subagent task is registered
  • `TaskProgressMessage` — real-time progress updates with cumulative usage metrics
  • `TaskNotificationMessage` — task completion notifications

`RateLimitEvent` is emitted when the rate limit status transitions (e.g., from `allowed` to `allowed_warning` or `rejected`). Use it to warn users or back off gracefully:

```python from claude_agent_sdk import query, ClaudeAgentOptions, RateLimitEvent

async for message in query(prompt="...", options=ClaudeAgentOptions()): if isinstance(message, RateLimitEvent): print(f"Rate limit status: {message.rate_limit_info.status}") if message.rate_limit_info.resets_at: print(f"Resets at: {message.rate_limit_info.resets_at}") ```


Subagents

```python 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

```python 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}") ```


Session History

Retrieve past session data with top-level functions:

```python from claude_agent_sdk import list_sessions, get_session_messages

List all past sessions (sync function — no await)

sessions = list_sessions() for session in sessions: print(f"{session.session_id}: {session.cwd}")

Get messages from a specific session (sync function — no await)

messages = get_session_messages(session_id="...") for msg in messages: print(msg) ```

Session Mutations

Rename or tag sessions (sync functions — no await):

```python from claude_agent_sdk import rename_session, tag_session

Rename a session

rename_session(session_id="...", title="My refactoring session")

Tag a session (tags are Unicode-sanitized automatically)

tag_session(session_id="...", tag="experiment")

Clear a tag

tag_session(session_id="...", tag=None)

Optionally scope to a specific project directory

rename_session(session_id="...", title="New title", directory="/path/to/project") ```


MCP Server Management

Manage MCP servers at runtime using `ClaudeSDKClient`:

```python async with ClaudeSDKClient(options=options) as client: # Reconnect a disconnected MCP server await client.reconnect_mcp_server("my-server")

# Toggle an MCP server on/off
await client.toggle_mcp_server("my-server", enabled=False)

# Get status of all MCP servers
status = await client.get_mcp_status()  # returns McpStatusResponse

```


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