Loading...
    • Developer Guide
    • API Reference
    • MCP
    • Resources
    • Release Notes
    Search...
    ⌘K

    First steps

    Intro to ClaudeQuickstart

    Models & pricing

    Models overviewChoosing a modelWhat's new in Claude 4.5Migrating to Claude 4.5Model deprecationsPricing

    Build with Claude

    Features overviewUsing the Messages APIContext windowsPrompting best practices

    Capabilities

    Prompt cachingContext editingExtended thinkingStreaming MessagesBatch processingCitationsMultilingual supportToken countingEmbeddingsVisionPDF supportFiles APISearch resultsGoogle Sheets add-on

    Tools

    OverviewHow to implement tool useToken-efficient tool useFine-grained tool streamingBash toolCode execution toolComputer use toolText editor toolWeb fetch toolWeb search toolMemory tool

    Agent Skills

    OverviewQuickstartBest practicesUsing Skills with the API

    Agent SDK

    OverviewTypeScript SDKPython SDK

    Guides

    Streaming InputHandling PermissionsSession ManagementHosting the Agent SDKModifying system promptsMCP in the SDKCustom ToolsSubagents in the SDKSlash Commands in the SDKAgent Skills in the SDKTracking Costs and UsageTodo ListsPlugins in the SDK

    MCP in the API

    MCP connectorRemote MCP servers

    Claude on 3rd-party platforms

    Amazon BedrockVertex AI

    Prompt engineering

    OverviewPrompt generatorUse prompt templatesPrompt improverBe clear and directUse examples (multishot prompting)Let Claude think (CoT)Use XML tagsGive Claude a role (system prompts)Prefill Claude's responseChain complex promptsLong context tipsExtended thinking tips

    Test & evaluate

    Define success criteriaDevelop test casesUsing the Evaluation ToolReducing latency

    Strengthen guardrails

    Reduce hallucinationsIncrease output consistencyMitigate jailbreaksStreaming refusalsReduce prompt leakKeep Claude in character

    Administration and monitoring

    Admin API overviewUsage and Cost APIClaude Code Analytics API
    Console
    Agent SDK

    Agent SDK reference - Python

    Complete API reference for the Python Agent SDK, including all functions, types, and classes.

    Installation

    pip install claude-agent-sdk

    Choosing Between query() and ClaudeSDKClient

    The Python SDK provides two ways to interact with Claude Code:

    Quick Comparison

    Featurequery()ClaudeSDKClient
    SessionCreates new session each timeReuses same session
    ConversationSingle exchangeMultiple exchanges in same context
    ConnectionManaged automaticallyManual control
    Streaming Input✅ Supported✅ Supported
    Interrupts❌ Not supported✅ Supported
    Hooks❌ Not supported✅ Supported
    Custom Tools❌ Not supported✅ Supported
    Continue Chat❌ New session each time✅ Maintains conversation
    Use CaseOne-off tasksContinuous conversations

    When to Use query() (New Session Each Time)

    Best for:

    • One-off questions where you don't need conversation history
    • Independent tasks that don't require context from previous exchanges
    • Simple automation scripts
    • When you want a fresh start each time

    When to Use ClaudeSDKClient (Continuous Conversation)

    Best for:

    • Continuing conversations - When you need Claude to remember context
    • Follow-up questions - Building on previous responses
    • Interactive applications - Chat interfaces, REPLs
    • Response-driven logic - When next action depends on Claude's response
    • Session control - Managing conversation lifecycle explicitly

    Functions

    query()

    Creates a new session for each interaction with Claude Code. Returns an async iterator that yields messages as they arrive. Each call to query() starts fresh with no memory of previous interactions.

    async def query(
        *,
        prompt: str | AsyncIterable[dict[str, Any]],
        options: ClaudeAgentOptions | None = None
    ) -> AsyncIterator[Message]

    Parameters

    ParameterTypeDescription
    promptstr | AsyncIterable[dict]The input prompt as a string or async iterable for streaming mode
    optionsClaudeAgentOptions | NoneOptional configuration object (defaults to ClaudeAgentOptions() if None)

    Returns

    Returns an AsyncIterator[Message] that yields messages from the conversation.

    Example - With options

    
    import asyncio
    from claude_agent_sdk import query, ClaudeAgentOptions
    
    async def main():
        options = ClaudeAgentOptions(
            system_prompt="You are an expert Python developer",
            permission_mode='acceptEdits',
            cwd="/home/user/project"
        )
    
        async for message in query(
            prompt="Create a Python web server",
            options=options
        ):
            print(message)
    
    
    asyncio.run(main())

    tool()

    Decorator for defining MCP tools with type safety.

    def tool(
        name: str,
        description: str,
        input_schema: type | dict[str, Any]
    ) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]

    Parameters

    ParameterTypeDescription
    namestrUnique identifier for the tool
    descriptionstrHuman-readable description of what the tool does
    input_schematype | dict[str, Any]Schema defining the tool's input parameters (see below)

    Input Schema Options

    1. Simple type mapping (recommended):

      {"text": str, "count": int, "enabled": bool}
    2. JSON Schema format (for complex validation):

      {
          "type": "object",
          "properties": {
              "text": {"type": "string"},
              "count": {"type": "integer", "minimum": 0}
          },
          "required": ["text"]
      }

    Returns

    A decorator function that wraps the tool implementation and returns an SdkMcpTool instance.

    Example

    from claude_agent_sdk import tool
    from typing import Any
    
    @tool("greet", "Greet a user", {"name": str})
    async def greet(args: dict[str, Any]) -> dict[str, Any]:
        return {
            "content": [{
                "type": "text",
                "text": f"Hello, {args['name']}!"
            }]
        }

    create_sdk_mcp_server()

    Create an in-process MCP server that runs within your Python application.

    def create_sdk_mcp_server(
        name: str,
        version: str = "1.0.0",
        tools: list[SdkMcpTool[Any]] | None = None
    ) -> McpSdkServerConfig

    Parameters

    ParameterTypeDefaultDescription
    namestr-Unique identifier for the server
    versionstr"1.0.0"Server version string
    toolslist[SdkMcpTool[Any]] | NoneNoneList of tool functions created with @tool decorator

    Returns

    Returns an McpSdkServerConfig object that can be passed to ClaudeAgentOptions.mcp_servers.

    Example

    from claude_agent_sdk import tool, create_sdk_mcp_server
    
    @tool("add", "Add two numbers", {"a": float, "b": float})
    async def add(args):
        return {
            "content": [{
                "type": "text",
                "text": f"Sum: {args['a'] + args['b']}"
            }]
        }
    
    @tool("multiply", "Multiply two numbers", {"a": float, "b": float})
    async def multiply(args):
        return {
            "content": [{
                "type": "text",
                "text": f"Product: {args['a'] * args['b']}"
            }]
        }
    
    calculator = create_sdk_mcp_server(
        name="calculator",
        version="2.0.0",
        tools=[add, multiply]  # Pass decorated functions
    )
    
    # Use with Claude
    options = ClaudeAgentOptions(
        mcp_servers={"calc": calculator},
        allowed_tools=["mcp__calc__add", "mcp__calc__multiply"]
    )

    Classes

    ClaudeSDKClient

    Maintains a conversation session across multiple exchanges. This is the Python equivalent of how the TypeScript SDK's query() function works internally - it creates a client object that can continue conversations.

    Key Features

    • Session Continuity: Maintains conversation context across multiple query() calls
    • Same Conversation: Claude remembers previous messages in the session
    • Interrupt Support: Can stop Claude mid-execution
    • Explicit Lifecycle: You control when the session starts and ends
    • Response-driven Flow: Can react to responses and send follow-ups
    • Custom Tools & Hooks: Supports custom tools (created with @tool decorator) and hooks
    class ClaudeSDKClient:
        def __init__(self, options: ClaudeAgentOptions | None = None)
        async def connect(self, prompt: str | AsyncIterable[dict] | None = None) -> None
        async def query(self, prompt: str | AsyncIterable[dict], session_id: str = "default") -> None
        async def receive_messages(self) -> AsyncIterator[Message]
        async def receive_response(self) -> AsyncIterator[Message]
        async def interrupt(self) -> None
        async def disconnect(self) -> None

    Methods

    MethodDescription
    __init__(options)Initialize the client with optional configuration
    connect(prompt)Connect to Claude with an optional initial prompt or message stream
    query(prompt, session_id)Send a new request in streaming mode
    receive_messages()Receive all messages from Claude as an async iterator
    receive_response()Receive messages until and including a ResultMessage
    interrupt()Send interrupt signal (only works in streaming mode)
    disconnect()Disconnect from Claude

    Context Manager Support

    The client can be used as an async context manager for automatic connection management:

    async with ClaudeSDKClient() as client:
        await client.query("Hello Claude")
        async for message in client.receive_response():
            print(message)

    Important: When iterating over messages, avoid using break to exit early as this can cause asyncio cleanup issues. Instead, let the iteration complete naturally or use flags to track when you've found what you need.

    Example - Continuing a conversation

    import asyncio
    from claude_agent_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage
    
    async def main():
        async with ClaudeSDKClient() as client:
            # First question
            await client.query("What's the capital of France?")
    
            # Process response
            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 question - Claude remembers the previous context
            await client.query("What's the population of that city?")
    
            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 the same conversation
            await client.query("What are some famous landmarks there?")
    
            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}")
    
    asyncio.run(main())

    Example - Streaming input with ClaudeSDKClient

    import asyncio
    from claude_agent_sdk import ClaudeSDKClient
    
    async def message_stream():
        """Generate messages dynamically."""
        yield {"type": "text", "text": "Analyze the following data:"}
        await asyncio.sleep(0.5)
        yield {"type": "text", "text": "Temperature: 25°C"}
        await asyncio.sleep(0.5)
        yield {"type": "text", "text": "Humidity: 60%"}
        await asyncio.sleep(0.5)
        yield {"type": "text", "text": "What patterns do you see?"}
    
    async def main():
        async with ClaudeSDKClient() as client:
            # Stream input to Claude
            await client.query(message_stream())
    
            # Process response
            async for message in client.receive_response():
                print(message)
    
            # Follow-up in same session
            await client.query("Should we be concerned about these readings?")
    
            async for message in client.receive_response():
                print(message)
    
    asyncio.run(main())

    Example - Using interrupts

    import asyncio
    from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
    
    async def interruptible_task():
        options = ClaudeAgentOptions(
            allowed_tools=["Bash"],
            permission_mode="acceptEdits"
        )
    
        async with ClaudeSDKClient(options=options) as client:
            # Start a long-running task
            await client.query("Count from 1 to 100 slowly")
    
            # Let it run for a bit
            await asyncio.sleep(2)
    
            # Interrupt the task
            await client.interrupt()
            print("Task interrupted!")
    
            # Send a new command
            await client.query("Just say hello instead")
    
            async for message in client.receive_response():
                # Process the new response
                pass
    
    asyncio.run(interruptible_task())

    Example - Advanced permission control

    from claude_agent_sdk import (
        ClaudeSDKClient,
        ClaudeAgentOptions
    )
    
    async def custom_permission_handler(
        tool_name: str,
        input_data: dict,
        context: dict
    ):
        """Custom logic for tool permissions."""
    
        # Block writes to system directories
        if tool_name == "Write" and input_data.get("file_path", "").startswith("/system/"):
            return {
                "behavior": "deny",
                "message": "System directory write not allowed",
                "interrupt": True
            }
    
        # Redirect sensitive file operations
        if tool_name in ["Write", "Edit"] and "config" in input_data.get("file_path", ""):
            safe_path = f"./sandbox/{input_data['file_path']}"
            return {
                "behavior": "allow",
                "updatedInput": {**input_data, "file_path": safe_path}
            }
    
        # Allow everything else
        return {
            "behavior": "allow",
            "updatedInput": input_data
        }
    
    async def main():
        options = ClaudeAgentOptions(
            can_use_tool=custom_permission_handler,
            allowed_tools=["Read", "Write", "Edit"]
        )
    
        async with ClaudeSDKClient(options=options) as client:
            await client.query("Update the system config file")
    
            async for message in client.receive_response():
                # Will use sandbox path instead
                print(message)
    
    asyncio.run(main())

    Types

    SdkMcpTool

    Definition for an SDK MCP tool created with the @tool decorator.

    @dataclass
    class SdkMcpTool(Generic[T]):
        name: str
        description: str
        input_schema: type[T] | dict[str, Any]
        handler: Callable[[T], Awaitable[dict[str, Any]]]
    PropertyTypeDescription
    namestrUnique identifier for the tool
    descriptionstrHuman-readable description
    input_schematype[T] | dict[str, Any]Schema for input validation
    handlerCallable[[T], Awaitable[dict[str, Any]]]Async function that handles tool execution

    ClaudeAgentOptions

    Configuration dataclass for Claude Code queries.

    @dataclass
    class ClaudeAgentOptions:
        allowed_tools: list[str] = field(default_factory=list)
        system_prompt: str | SystemPromptPreset | None = None
        mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)
        permission_mode: PermissionMode | None = None
        continue_conversation: bool = False
        resume: str | None = None
        max_turns: int | None = None
        disallowed_tools: list[str] = field(default_factory=list)
        model: str | None = None
        permission_prompt_tool_name: str | None = None
        cwd: str | Path | None = None
        settings: str | None = None
        add_dirs: list[str | Path] = field(default_factory=list)
        env: dict[str, str] = field(default_factory=dict)
        extra_args: dict[str, str | None] = field(default_factory=dict)
        max_buffer_size: int | None = None
        debug_stderr: Any = sys.stderr  # Deprecated
        stderr: Callable[[str], None] | None = None
        can_use_tool: CanUseTool | None = None
        hooks: dict[HookEvent, list[HookMatcher]] | None = None
        user: str | None = None
        include_partial_messages: bool = False
        fork_session: bool = False
        agents: dict[str, AgentDefinition] | None = None
        setting_sources: list[SettingSource] | None = None
    PropertyTypeDefaultDescription
    allowed_toolslist[str][]List of allowed tool names
    system_promptstr | SystemPromptPreset | NoneNoneSystem prompt configuration. Pass a string for custom prompt, or use {"type": "preset", "preset": "claude_code"} for Claude Code's system prompt. Add "append" to extend the preset
    mcp_serversdict[str, McpServerConfig] | str | Path{}MCP server configurations or path to config file
    permission_modePermissionMode | NoneNonePermission mode for tool usage
    continue_conversationboolFalseContinue the most recent conversation
    resumestr | NoneNoneSession ID to resume
    max_turnsint | NoneNoneMaximum conversation turns
    disallowed_toolslist[str][]List of disallowed tool names
    modelstr | NoneNoneClaude model to use
    permission_prompt_tool_namestr | NoneNoneMCP tool name for permission prompts
    cwdstr | Path | NoneNoneCurrent working directory
    settingsstr | NoneNonePath to settings file
    add_dirslist[str | Path][]Additional directories Claude can access
    envdict[str, str]{}Environment variables
    extra_argsdict[str, str | None]{}Additional CLI arguments to pass directly to the CLI
    max_buffer_sizeint | NoneNoneMaximum bytes when buffering CLI stdout
    debug_stderrAnysys.stderrDeprecated - File-like object for debug output. Use stderr callback instead
    stderrCallable[[str], None] | NoneNoneCallback function for stderr output from CLI
    can_use_toolCanUseTool | NoneNoneTool permission callback function
    hooksdict[HookEvent, list[HookMatcher]] | NoneNoneHook configurations for intercepting events
    userstr | NoneNoneUser identifier
    include_partial_messagesboolFalseInclude partial message streaming events
    fork_sessionboolFalseWhen resuming with resume, fork to a new session ID instead of continuing the original session
    agentsdict[str, AgentDefinition] | NoneNoneProgrammatically defined subagents
    pluginslist[SdkPluginConfig][]Load custom plugins from local paths. See Plugins for details
    setting_sourceslist[SettingSource] | NoneNone (no settings)Control which filesystem settings to load. When omitted, no settings are loaded. Note: Must include "project" to load CLAUDE.md files

    SystemPromptPreset

    Configuration for using Claude Code's preset system prompt with optional additions.

    class SystemPromptPreset(TypedDict):
        type: Literal["preset"]
        preset: Literal["claude_code"]
        append: NotRequired[str]
    FieldRequiredDescription
    typeYesMust be "preset" to use a preset system prompt
    presetYesMust be "claude_code" to use Claude Code's system prompt
    appendNoAdditional instructions to append to the preset system prompt

    SettingSource

    Controls which filesystem-based configuration sources the SDK loads settings from.

    SettingSource = Literal["user", "project", "local"]
    ValueDescriptionLocation
    "user"Global user settings~/.claude/settings.json
    "project"Shared project settings (version controlled).claude/settings.json
    "local"Local project settings (gitignored).claude/settings.local.json

    Default behavior

    When setting_sources is omitted or None, the SDK does not load any filesystem settings. This provides isolation for SDK applications.

    Why use setting_sources?

    Load all filesystem settings (legacy behavior):

    # Load all settings like SDK v0.0.x did
    from claude_agent_sdk import query, ClaudeAgentOptions
    
    async for message in query(
        prompt="Analyze this code",
        options=ClaudeAgentOptions(
            setting_sources=["user", "project", "local"]  # Load all settings
        )
    ):
        print(message)

    Load only specific setting sources:

    # Load only project settings, ignore user and local
    async for message in query(
        prompt="Run CI checks",
        options=ClaudeAgentOptions(
            setting_sources=["project"]  # Only .claude/settings.json
        )
    ):
        print(message)

    Testing and CI environments:

    # Ensure consistent behavior in CI by excluding local settings
    async for message in query(
        prompt="Run tests",
        options=ClaudeAgentOptions(
            setting_sources=["project"],  # Only team-shared settings
            permission_mode="bypassPermissions"
        )
    ):
        print(message)

    SDK-only applications:

    # Define everything programmatically (default behavior)
    # No filesystem dependencies - setting_sources defaults to None
    async for message in query(
        prompt="Review this PR",
        options=ClaudeAgentOptions(
            # setting_sources=None is the default, no need to specify
            agents={ /* ... */ },
            mcp_servers={ /* ... */ },
            allowed_tools=["Read", "Grep", "Glob"]
        )
    ):
        print(message)

    Loading CLAUDE.md project instructions:

    # Load project settings to include CLAUDE.md files
    async for message in query(
        prompt="Add a new feature following project conventions",
        options=ClaudeAgentOptions(
            system_prompt={
                "type": "preset",
                "preset": "claude_code"  # Use Claude Code's system prompt
            },
            setting_sources=["project"],  # Required to load CLAUDE.md from project
            allowed_tools=["Read", "Write", "Edit"]
        )
    ):
        print(message)

    Settings precedence

    When multiple sources are loaded, settings are merged with this precedence (highest to lowest):

    1. Local settings (.claude/settings.local.json)
    2. Project settings (.claude/settings.json)
    3. User settings (~/.claude/settings.json)

    Programmatic options (like agents, allowed_tools) always override filesystem settings.

    AgentDefinition

    Configuration for a subagent defined programmatically.

    @dataclass
    class AgentDefinition:
        description: str
        prompt: str
        tools: list[str] | None = None
        model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None
    FieldRequiredDescription
    descriptionYesNatural language description of when to use this agent
    toolsNoArray of allowed tool names. If omitted, inherits all tools
    promptYesThe agent's system prompt
    modelNoModel override for this agent. If omitted, uses the main model

    PermissionMode

    Permission modes for controlling tool execution.

    PermissionMode = Literal[
        "default",           # Standard permission behavior
        "acceptEdits",       # Auto-accept file edits
        "plan",              # Planning mode - no execution
        "bypassPermissions"  # Bypass all permission checks (use with caution)
    ]

    McpSdkServerConfig

    Configuration for SDK MCP servers created with create_sdk_mcp_server().

    class McpSdkServerConfig(TypedDict):
        type: Literal["sdk"]
        name: str
        instance: Any  # MCP Server instance

    McpServerConfig

    Union type for MCP server configurations.

    McpServerConfig = McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfig

    McpStdioServerConfig

    class McpStdioServerConfig(TypedDict):
        type: NotRequired[Literal["stdio"]]  # Optional for backwards compatibility
        command: str
        args: NotRequired[list[str]]
        env: NotRequired[dict[str, str]]

    McpSSEServerConfig

    class McpSSEServerConfig(TypedDict):
        type: Literal["sse"]
        url: str
        headers: NotRequired[dict[str, str]]

    McpHttpServerConfig

    class McpHttpServerConfig(TypedDict):
        type: Literal["http"]
        url: str
        headers: NotRequired[dict[str, str]]

    SdkPluginConfig

    Configuration for loading plugins in the SDK.

    class SdkPluginConfig(TypedDict):
        type: Literal["local"]
        path: str
    FieldTypeDescription
    typeLiteral["local"]Must be "local" (only local plugins currently supported)
    pathstrAbsolute or relative path to the plugin directory

    Example:

    plugins=[
        {"type": "local", "path": "./my-plugin"},
        {"type": "local", "path": "/absolute/path/to/plugin"}
    ]

    For complete information on creating and using plugins, see Plugins.

    Message Types

    Message

    Union type of all possible messages.

    Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage

    UserMessage

    User input message.

    @dataclass
    class UserMessage:
        content: str | list[ContentBlock]

    AssistantMessage

    Assistant response message with content blocks.

    @dataclass
    class AssistantMessage:
        content: list[ContentBlock]
        model: str

    SystemMessage

    System message with metadata.

    @dataclass
    class SystemMessage:
        subtype: str
        data: dict[str, Any]

    ResultMessage

    Final result message with cost and usage information.

    @dataclass
    class ResultMessage:
        subtype: str
        duration_ms: int
        duration_api_ms: int
        is_error: bool
        num_turns: int
        session_id: str
        total_cost_usd: float | None = None
        usage: dict[str, Any] | None = None
        result: str | None = None

    Content Block Types

    ContentBlock

    Union type of all content blocks.

    ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock

    TextBlock

    Text content block.

    @dataclass
    class TextBlock:
        text: str

    ThinkingBlock

    Thinking content block (for models with thinking capability).

    @dataclass
    class ThinkingBlock:
        thinking: str
        signature: str

    ToolUseBlock

    Tool use request block.

    @dataclass
    class ToolUseBlock:
        id: str
        name: str
        input: dict[str, Any]

    ToolResultBlock

    Tool execution result block.

    @dataclass
    class ToolResultBlock:
        tool_use_id: str
        content: str | list[dict[str, Any]] | None = None
        is_error: bool | None = None

    Error Types

    ClaudeSDKError

    Base exception class for all SDK errors.

    class ClaudeSDKError(Exception):
        """Base error for Claude SDK."""

    CLINotFoundError

    Raised when Claude Code CLI is not installed or not found.

    class CLINotFoundError(CLIConnectionError):
        def __init__(self, message: str = "Claude Code not found", cli_path: str | None = None):
            """
            Args:
                message: Error message (default: "Claude Code not found")
                cli_path: Optional path to the CLI that was not found
            """

    CLIConnectionError

    Raised when connection to Claude Code fails.

    class CLIConnectionError(ClaudeSDKError):
        """Failed to connect to Claude Code."""

    ProcessError

    Raised when the Claude Code process fails.

    class ProcessError(ClaudeSDKError):
        def __init__(self, message: str, exit_code: int | None = None, stderr: str | None = None):
            self.exit_code = exit_code
            self.stderr = stderr

    CLIJSONDecodeError

    Raised when JSON parsing fails.

    class CLIJSONDecodeError(ClaudeSDKError):
        def __init__(self, line: str, original_error: Exception):
            """
            Args:
                line: The line that failed to parse
                original_error: The original JSON decode exception
            """
            self.line = line
            self.original_error = original_error

    Hook Types

    HookEvent

    Supported hook event types. Note that due to setup limitations, the Python SDK does not support SessionStart, SessionEnd, and Notification hooks.

    HookEvent = Literal[
        "PreToolUse",      # Called before tool execution
        "PostToolUse",     # Called after tool execution
        "UserPromptSubmit", # Called when user submits a prompt
        "Stop",            # Called when stopping execution
        "SubagentStop",    # Called when a subagent stops
        "PreCompact"       # Called before message compaction
    ]

    HookCallback

    Type definition for hook callback functions.

    HookCallback = Callable[
        [dict[str, Any], str | None, HookContext],
        Awaitable[dict[str, Any]]
    ]

    Parameters:

    • input_data: Hook-specific input data (see hook documentation)
    • tool_use_id: Optional tool use identifier (for tool-related hooks)
    • context: Hook context with additional information

    Returns a dictionary that may contain:

    • decision: "block" to block the action
    • systemMessage: System message to add to the transcript
    • hookSpecificOutput: Hook-specific output data

    HookContext

    Context information passed to hook callbacks.

    @dataclass
    class HookContext:
        signal: Any | None = None  # Future: abort signal support

    HookMatcher

    Configuration for matching hooks to specific events or tools.

    @dataclass
    class HookMatcher:
        matcher: str | None = None        # Tool name or pattern to match (e.g., "Bash", "Write|Edit")
        hooks: list[HookCallback] = field(default_factory=list)  # List of callbacks to execute

    Hook Usage Example

    from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, HookContext
    from typing import Any
    
    async def validate_bash_command(
        input_data: dict[str, Any],
        tool_use_id: str | None,
        context: HookContext
    ) -> dict[str, Any]:
        """Validate and potentially block dangerous bash commands."""
        if input_data['tool_name'] == 'Bash':
            command = input_data['tool_input'].get('command', '')
            if 'rm -rf /' in command:
                return {
                    'hookSpecificOutput': {
                        'hookEventName': 'PreToolUse',
                        'permissionDecision': 'deny',
                        'permissionDecisionReason': 'Dangerous command blocked'
                    }
                }
        return {}
    
    async def log_tool_use(
        input_data: dict[str, Any],
        tool_use_id: str | None,
        context: HookContext
    ) -> dict[str, Any]:
        """Log all tool usage for auditing."""
        print(f"Tool used: {input_data.get('tool_name')}")
        return {}
    
    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(matcher='Bash', hooks=[validate_bash_command]),
                HookMatcher(hooks=[log_tool_use])  # Applies to all tools
            ],
            'PostToolUse': [
                HookMatcher(hooks=[log_tool_use])
            ]
        }
    )
    
    async for message in query(
        prompt="Analyze this codebase",
        options=options
    ):
        print(message)

    Tool Input/Output Types

    Documentation of input/output schemas for all built-in Claude Code tools. While the Python SDK doesn't export these as types, they represent the structure of tool inputs and outputs in messages.

    Task

    Tool name: Task

    Input:

    {
        "description": str,      # A short (3-5 word) description of the task
        "prompt": str,           # The task for the agent to perform
        "subagent_type": str     # The type of specialized agent to use
    }

    Output:

    {
        "result": str,                    # Final result from the subagent
        "usage": dict | None,             # Token usage statistics
        "total_cost_usd": float | None,  # Total cost in USD
        "duration_ms": int | None         # Execution duration in milliseconds
    }

    Bash

    Tool name: Bash

    Input:

    {
        "command": str,                  # The command to execute
        "timeout": int | None,           # Optional timeout in milliseconds (max 600000)
        "description": str | None,       # Clear, concise description (5-10 words)
        "run_in_background": bool | None # Set to true to run in background
    }

    Output:

    {
        "output": str,              # Combined stdout and stderr output
        "exitCode": int,            # Exit code of the command
        "killed": bool | None,      # Whether command was killed due to timeout
        "shellId": str | None       # Shell ID for background processes
    }

    Edit

    Tool name: Edit

    Input:

    {
        "file_path": str,           # The absolute path to the file to modify
        "old_string": str,          # The text to replace
        "new_string": str,          # The text to replace it with
        "replace_all": bool | None  # Replace all occurrences (default False)
    }

    Output:

    {
        "message": str,      # Confirmation message
        "replacements": int, # Number of replacements made
        "file_path": str     # File path that was edited
    }

    Read

    Tool name: Read

    Input:

    {
        "file_path": str,       # The absolute path to the file to read
        "offset": int | None,   # The line number to start reading from
        "limit": int | None     # The number of lines to read
    }

    Output (Text files):

    {
        "content": str,         # File contents with line numbers
        "total_lines": int,     # Total number of lines in file
        "lines_returned": int   # Lines actually returned
    }

    Output (Images):

    {
        "image": str,       # Base64 encoded image data
        "mime_type": str,   # Image MIME type
        "file_size": int    # File size in bytes
    }

    Write

    Tool name: Write

    Input:

    {
        "file_path": str,  # The absolute path to the file to write
        "content": str     # The content to write to the file
    }

    Output:

    {
        "message": str,        # Success message
        "bytes_written": int,  # Number of bytes written
        "file_path": str       # File path that was written
    }

    Glob

    Tool name: Glob

    Input:

    {
        "pattern": str,       # The glob pattern to match files against
        "path": str | None    # The directory to search in (defaults to cwd)
    }

    Output:

    {
        "matches": list[str],  # Array of matching file paths
        "count": int,          # Number of matches found
        "search_path": str     # Search directory used
    }

    Grep

    Tool name: Grep

    Input:

    {
        "pattern": str,                    # The regular expression pattern
        "path": str | None,                # File or directory to search in
        "glob": str | None,                # Glob pattern to filter files
        "type": str | None,                # File type to search
        "output_mode": str | None,         # "content", "files_with_matches", or "count"
        "-i": bool | None,                 # Case insensitive search
        "-n": bool | None,                 # Show line numbers
        "-B": int | None,                  # Lines to show before each match
        "-A": int | None,                  # Lines to show after each match
        "-C": int | None,                  # Lines to show before and after
        "head_limit": int | None,          # Limit output to first N lines/entries
        "multiline": bool | None           # Enable multiline mode
    }

    Output (content mode):

    {
        "matches": [
            {
                "file": str,
                "line_number": int | None,
                "line": str,
                "before_context": list[str] | None,
                "after_context": list[str] | None
            }
        ],
        "total_matches": int
    }

    Output (files_with_matches mode):

    {
        "files": list[str],  # Files containing matches
        "count": int         # Number of files with matches
    }

    NotebookEdit

    Tool name: NotebookEdit

    Input:

    {
        "notebook_path": str,                     # Absolute path to the Jupyter notebook
        "cell_id": str | None,                    # The ID of the cell to edit
        "new_source": str,                        # The new source for the cell
        "cell_type": "code" | "markdown" | None,  # The type of the cell
        "edit_mode": "replace" | "insert" | "delete" | None  # Edit operation type
    }

    Output:

    {
        "message": str,                              # Success message
        "edit_type": "replaced" | "inserted" | "deleted",  # Type of edit performed
        "cell_id": str | None,                       # Cell ID that was affected
        "total_cells": int                           # Total cells in notebook after edit
    }

    WebFetch

    Tool name: WebFetch

    Input:

    {
        "url": str,     # The URL to fetch content from
        "prompt": str   # The prompt to run on the fetched content
    }

    Output:

    {
        "response": str,           # AI model's response to the prompt
        "url": str,                # URL that was fetched
        "final_url": str | None,   # Final URL after redirects
        "status_code": int | None  # HTTP status code
    }

    WebSearch

    Tool name: WebSearch

    Input:

    {
        "query": str,                        # The search query to use
        "allowed_domains": list[str] | None, # Only include results from these domains
        "blocked_domains": list[str] | None  # Never include results from these domains
    }

    Output:

    {
        "results": [
            {
                "title": str,
                "url": str,
                "snippet": str,
                "metadata": dict | None
            }
        ],
        "total_results": int,
        "query": str
    }

    TodoWrite

    Tool name: TodoWrite

    Input:

    {
        "todos": [
            {
                "content": str,                              # The task description
                "status": "pending" | "in_progress" | "completed",  # Task status
                "activeForm": str                            # Active form of the description
            }
        ]
    }

    Output:

    {
        "message": str,  # Success message
        "stats": {
            "total": int,
            "pending": int,
            "in_progress": int,
            "completed": int
        }
    }

    BashOutput

    Tool name: BashOutput

    Input:

    {
        "bash_id": str,       # The ID of the background shell
        "filter": str | None  # Optional regex to filter output lines
    }

    Output:

    {
        "output": str,                                      # New output since last check
        "status": "running" | "completed" | "failed",       # Current shell status
        "exitCode": int | None                              # Exit code when completed
    }

    KillBash

    Tool name: KillBash

    Input:

    {
        "shell_id": str  # The ID of the background shell to kill
    }

    Output:

    {
        "message": str,  # Success message
        "shell_id": str  # ID of the killed shell
    }

    ExitPlanMode

    Tool name: ExitPlanMode

    Input:

    {
        "plan": str  # The plan to run by the user for approval
    }

    Output:

    {
        "message": str,          # Confirmation message
        "approved": bool | None  # Whether user approved the plan
    }

    ListMcpResources

    Tool name: ListMcpResources

    Input:

    {
        "server": str | None  # Optional server name to filter resources by
    }

    Output:

    {
        "resources": [
            {
                "uri": str,
                "name": str,
                "description": str | None,
                "mimeType": str | None,
                "server": str
            }
        ],
        "total": int
    }

    ReadMcpResource

    Tool name: ReadMcpResource

    Input:

    {
        "server": str,  # The MCP server name
        "uri": str      # The resource URI to read
    }

    Output:

    {
        "contents": [
            {
                "uri": str,
                "mimeType": str | None,
                "text": str | None,
                "blob": str | None
            }
        ],
        "server": str
    }

    Advanced Features with ClaudeSDKClient

    Building a Continuous Conversation Interface

    from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock
    import asyncio
    
    class ConversationSession:
        """Maintains a single conversation session with Claude."""
    
        def __init__(self, options: ClaudeAgentOptions = None):
            self.client = ClaudeSDKClient(options)
            self.turn_count = 0
    
        async def start(self):
            await self.client.connect()
            print("Starting conversation session. Claude will remember context.")
            print("Commands: 'exit' to quit, 'interrupt' to stop current task, 'new' for new session")
    
            while True:
                user_input = input(f"\n[Turn {self.turn_count + 1}] You: ")
    
                if user_input.lower() == 'exit':
                    break
                elif user_input.lower() == 'interrupt':
                    await self.client.interrupt()
                    print("Task interrupted!")
                    continue
                elif user_input.lower() == 'new':
                    # Disconnect and reconnect for a fresh session
                    await self.client.disconnect()
                    await self.client.connect()
                    self.turn_count = 0
                    print("Started new conversation session (previous context cleared)")
                    continue
    
                # Send message - Claude remembers all previous messages in this session
                await self.client.query(user_input)
                self.turn_count += 1
    
                # Process response
                print(f"[Turn {self.turn_count}] Claude: ", end="")
                async for message in self.client.receive_response():
                    if isinstance(message, AssistantMessage):
                        for block in message.content:
                            if isinstance(block, TextBlock):
                                print(block.text, end="")
                print()  # New line after response
    
            await self.client.disconnect()
            print(f"Conversation ended after {self.turn_count} turns.")
    
    async def main():
        options = ClaudeAgentOptions(
            allowed_tools=["Read", "Write", "Bash"],
            permission_mode="acceptEdits"
        )
        session = ConversationSession(options)
        await session.start()
    
    # Example conversation:
    # Turn 1 - You: "Create a file called hello.py"
    # Turn 1 - Claude: "I'll create a hello.py file for you..."
    # Turn 2 - You: "What's in that file?"
    # Turn 2 - Claude: "The hello.py file I just created contains..." (remembers!)
    # Turn 3 - You: "Add a main function to it"
    # Turn 3 - Claude: "I'll add a main function to hello.py..." (knows which file!)
    
    asyncio.run(main())

    Using Hooks for Behavior Modification

    from claude_agent_sdk import (
        ClaudeSDKClient,
        ClaudeAgentOptions,
        HookMatcher,
        HookContext
    )
    import asyncio
    from typing import Any
    
    async def pre_tool_logger(
        input_data: dict[str, Any],
        tool_use_id: str | None,
        context: HookContext
    ) -> dict[str, Any]:
        """Log all tool usage before execution."""
        tool_name = input_data.get('tool_name', 'unknown')
        print(f"[PRE-TOOL] About to use: {tool_name}")
    
        # You can modify or block the tool execution here
        if tool_name == "Bash" and "rm -rf" in str(input_data.get('tool_input', {})):
            return {
                'hookSpecificOutput': {
                    'hookEventName': 'PreToolUse',
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Dangerous command blocked'
                }
            }
        return {}
    
    async def post_tool_logger(
        input_data: dict[str, Any],
        tool_use_id: str | None,
        context: HookContext
    ) -> dict[str, Any]:
        """Log results after tool execution."""
        tool_name = input_data.get('tool_name', 'unknown')
        print(f"[POST-TOOL] Completed: {tool_name}")
        return {}
    
    async def user_prompt_modifier(
        input_data: dict[str, Any],
        tool_use_id: str | None,
        context: HookContext
    ) -> dict[str, Any]:
        """Add context to user prompts."""
        original_prompt = input_data.get('prompt', '')
    
        # Add timestamp to all prompts
        from datetime import datetime
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
        return {
            'hookSpecificOutput': {
                'hookEventName': 'UserPromptSubmit',
                'updatedPrompt': f"[{timestamp}] {original_prompt}"
            }
        }
    
    async def main():
        options = ClaudeAgentOptions(
            hooks={
                'PreToolUse': [
                    HookMatcher(hooks=[pre_tool_logger]),
                    HookMatcher(matcher='Bash', hooks=[pre_tool_logger])
                ],
                'PostToolUse': [
                    HookMatcher(hooks=[post_tool_logger])
                ],
                'UserPromptSubmit': [
                    HookMatcher(hooks=[user_prompt_modifier])
                ]
            },
            allowed_tools=["Read", "Write", "Bash"]
        )
    
        async with ClaudeSDKClient(options=options) as client:
            await client.query("List files in current directory")
    
            async for message in client.receive_response():
                # Hooks will automatically log tool usage
                pass
    
    asyncio.run(main())

    Real-time Progress Monitoring

    from claude_agent_sdk import (
        ClaudeSDKClient,
        ClaudeAgentOptions,
        AssistantMessage,
        ToolUseBlock,
        ToolResultBlock,
        TextBlock
    )
    import asyncio
    
    async def monitor_progress():
        options = ClaudeAgentOptions(
            allowed_tools=["Write", "Bash"],
            permission_mode="acceptEdits"
        )
    
        async with ClaudeSDKClient(options=options) as client:
            await client.query(
                "Create 5 Python files with different sorting algorithms"
            )
    
            # Monitor progress in real-time
            files_created = []
            async for message in client.receive_messages():
                if isinstance(message, AssistantMessage):
                    for block in message.content:
                        if isinstance(block, ToolUseBlock):
                            if block.name == "Write":
                                file_path = block.input.get("file_path", "")
                                print(f"🔨 Creating: {file_path}")
                        elif isinstance(block, ToolResultBlock):
                            print(f"✅ Completed tool execution")
                        elif isinstance(block, TextBlock):
                            print(f"💭 Claude says: {block.text[:100]}...")
    
                # Check if we've received the final result
                if hasattr(message, 'subtype') and message.subtype in ['success', 'error']:
                    print(f"\n🎯 Task completed!")
                    break
    
    asyncio.run(monitor_progress())

    Example Usage

    Basic file operations (using query)

    from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
    import asyncio
    
    async def create_project():
        options = ClaudeAgentOptions(
            allowed_tools=["Read", "Write", "Bash"],
            permission_mode='acceptEdits',
            cwd="/home/user/project"
        )
    
        async for message in query(
            prompt="Create a Python project structure with setup.py",
            options=options
        ):
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, ToolUseBlock):
                        print(f"Using tool: {block.name}")
    
    asyncio.run(create_project())

    Error handling

    from claude_agent_sdk import (
        query,
        CLINotFoundError,
        ProcessError,
        CLIJSONDecodeError
    )
    
    try:
        async for message in query(prompt="Hello"):
            print(message)
    except CLINotFoundError:
        print("Please install Claude Code: npm install -g @anthropic-ai/claude-code")
    except ProcessError as e:
        print(f"Process failed with exit code: {e.exit_code}")
    except CLIJSONDecodeError as e:
        print(f"Failed to parse response: {e}")

    Streaming mode with client

    from claude_agent_sdk import ClaudeSDKClient
    import asyncio
    
    async def interactive_session():
        async with ClaudeSDKClient() as client:
            # Send initial message
            await client.query("What's the weather like?")
    
            # Process responses
            async for msg in client.receive_response():
                print(msg)
    
            # Send follow-up
            await client.query("Tell me more about that")
    
            # Process follow-up response
            async for msg in client.receive_response():
                print(msg)
    
    asyncio.run(interactive_session())

    Using custom tools with ClaudeSDKClient

    from claude_agent_sdk import (
        ClaudeSDKClient,
        ClaudeAgentOptions,
        tool,
        create_sdk_mcp_server,
        AssistantMessage,
        TextBlock
    )
    import asyncio
    from typing import Any
    
    # Define custom tools with @tool decorator
    @tool("calculate", "Perform mathematical calculations", {"expression": str})
    async def calculate(args: dict[str, Any]) -> dict[str, Any]:
        try:
            result = eval(args["expression"], {"__builtins__": {}})
            return {
                "content": [{
                    "type": "text",
                    "text": f"Result: {result}"
                }]
            }
        except Exception as e:
            return {
                "content": [{
                    "type": "text",
                    "text": f"Error: {str(e)}"
                }],
                "is_error": True
            }
    
    @tool("get_time", "Get current time", {})
    async def get_time(args: dict[str, Any]) -> dict[str, Any]:
        from datetime import datetime
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        return {
            "content": [{
                "type": "text",
                "text": f"Current time: {current_time}"
            }]
        }
    
    async def main():
        # Create SDK MCP server with custom tools
        my_server = create_sdk_mcp_server(
            name="utilities",
            version="1.0.0",
            tools=[calculate, get_time]
        )
    
        # Configure options with the server
        options = ClaudeAgentOptions(
            mcp_servers={"utils": my_server},
            allowed_tools=[
                "mcp__utils__calculate",
                "mcp__utils__get_time"
            ]
        )
    
        # Use ClaudeSDKClient for interactive tool usage
        async with ClaudeSDKClient(options=options) as client:
            await client.query("What's 123 * 456?")
    
            # Process calculation response
            async for message in client.receive_response():
                if isinstance(message, AssistantMessage):
                    for block in message.content:
                        if isinstance(block, TextBlock):
                            print(f"Calculation: {block.text}")
    
            # Follow up with time query
            await client.query("What time is it now?")
    
            async for message in client.receive_response():
                if isinstance(message, AssistantMessage):
                    for block in message.content:
                        if isinstance(block, TextBlock):
                            print(f"Time: {block.text}")
    
    asyncio.run(main())

    See also

    • Python SDK guide - Tutorial and examples
    • SDK overview - General SDK concepts
    • TypeScript SDK reference - TypeScript SDK documentation
    • CLI reference - Command-line interface
    • Common workflows - Step-by-step guides
    • Installation
    • Choosing Between and
    • Quick Comparison
    • When to Use (New Session Each Time)
    • When to Use (Continuous Conversation)
    • Functions
    • Classes
    • Types
    • Message Types
    • Content Block Types
    • Error Types
    • Hook Types
    • Hook Usage Example
    • Tool Input/Output Types
    • Task
    • Bash
    • Edit
    • Read
    • Write
    • Glob
    • Grep
    • NotebookEdit
    • WebFetch
    • WebSearch
    • TodoWrite
    • BashOutput
    • KillBash
    • ExitPlanMode
    • ListMcpResources
    • ReadMcpResource
    • Advanced Features with ClaudeSDKClient
    • Building a Continuous Conversation Interface
    • Using Hooks for Behavior Modification
    • Real-time Progress Monitoring
    • Example Usage
    • Basic file operations (using query)
    • Error handling
    • Streaming mode with client
    • Using custom tools with ClaudeSDKClient
    • See also
    © 2025 ANTHROPIC PBC

    Products

    • Claude
    • Claude Code
    • Max plan
    • Team plan
    • Enterprise plan
    • Download app
    • Pricing
    • Log in

    Features

    • Claude and Slack
    • Claude in Excel

    Models

    • Opus
    • Sonnet
    • Haiku

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Claude Developer Platform

    • Overview
    • Developer docs
    • Pricing
    • Amazon Bedrock
    • Google Cloud’s Vertex AI
    • Console login

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Help and security

    • Availability
    • Status
    • Support center

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy

    Products

    • Claude
    • Claude Code
    • Max plan
    • Team plan
    • Enterprise plan
    • Download app
    • Pricing
    • Log in

    Features

    • Claude and Slack
    • Claude in Excel

    Models

    • Opus
    • Sonnet
    • Haiku

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Claude Developer Platform

    • Overview
    • Developer docs
    • Pricing
    • Amazon Bedrock
    • Google Cloud’s Vertex AI
    • Console login

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Help and security

    • Availability
    • Status
    • Support center

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy
    © 2025 ANTHROPIC PBC