Claude Code: npm install -g @anthropic-ai/claude-code
Quick Start
1
2
3
4
5
6
7
8
importanyiofromclaude_agent_sdkimportqueryasyncdefmain():asyncformessageinquery(prompt="What is 2 + 2?"):print(message)anyio.run(main)
Basic Usage: query()
query() is an async function for querying Claude Code. It returns an AsyncIterator of response messages. See src/claude_agent_sdk/query.py.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fromclaude_agent_sdkimportquery,ClaudeAgentOptions,AssistantMessage,TextBlock# Simple queryasyncformessageinquery(prompt="Hello Claude"):ifisinstance(message,AssistantMessage):forblockinmessage.content:ifisinstance(block,TextBlock):print(block.text)# With optionsoptions=ClaudeAgentOptions(system_prompt="You are a helpful assistant",max_turns=1)asyncformessageinquery(prompt="Tell me a joke",options=options):print(message)
Using Tools
1
2
3
4
5
6
7
8
9
10
11
options=ClaudeAgentOptions(allowed_tools=["Read","Write","Bash"],permission_mode='acceptEdits'# auto-accept file edits)asyncformessageinquery(prompt="Create a hello.py file",options=options):# Process tool use and resultspass
Working Directory
1
2
3
4
5
frompathlibimportPathoptions=ClaudeAgentOptions(cwd="/path/to/project"# or Path("/path/to/project"))
Unlike query(), ClaudeSDKClient additionally enables custom tools and hooks, both of which can be defined as Python functions.
Custom Tools (as In-Process SDK MCP Servers)
A custom tool is a Python function that you can offer to Claude, for Claude to invoke as needed.
Custom tools are implemented in-process MCP servers that run directly within your Python application, eliminating the need for separate processes that regular MCP servers require.
fromclaude_agent_sdkimporttool,create_sdk_mcp_server,ClaudeAgentOptions,ClaudeSDKClient# Define a tool using the @tool decorator@tool("greet","Greet a user",{"name":str})asyncdefgreet_user(args):return{"content":[{"type":"text","text":f"Hello, {args['name']}!"}]}# Create an SDK MCP serverserver=create_sdk_mcp_server(name="my-tools",version="1.0.0",tools=[greet_user])# Use it with Claudeoptions=ClaudeAgentOptions(mcp_servers={"tools":server},allowed_tools=["mcp__tools__greet"])asyncwithClaudeSDKClient(options=options)asclient:awaitclient.query("Greet Alice")# Extract and print responseasyncformsginclient.receive_response():print(msg)
Benefits Over External MCP Servers
No subprocess management - Runs in the same process as your application
Better performance - No IPC overhead for tool calls
Simpler deployment - Single Python process instead of multiple
Easier debugging - All code runs in the same process
Type safety - Direct Python function calls with type hints
# BEFORE: External MCP server (separate process)options=ClaudeAgentOptions(mcp_servers={"calculator":{"type":"stdio","command":"python","args":["-m","calculator_server"]}})# AFTER: SDK MCP server (in-process)frommy_toolsimportadd,subtract# Your tool functionscalculator=create_sdk_mcp_server(name="calculator",tools=[add,subtract])options=ClaudeAgentOptions(mcp_servers={"calculator":calculator})
Mixed Server Support
You can use both SDK and external MCP servers together:
A hook is a Python function that the Claude Code application (not Claude) invokes at specific points of the Claude agent loop. Hooks can provide deterministic processing and automated feedback for Claude. Read more in Claude Code Hooks Reference.
fromclaude_agent_sdkimportClaudeAgentOptions,ClaudeSDKClient,HookMatcherasyncdefcheck_bash_command(input_data,tool_use_id,context):tool_name=input_data["tool_name"]tool_input=input_data["tool_input"]iftool_name!="Bash":return{}command=tool_input.get("command","")block_patterns=["foo.sh"]forpatterninblock_patterns:ifpatternincommand:return{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":f"Command contains invalid pattern: {pattern}",}}return{}options=ClaudeAgentOptions(allowed_tools=["Bash"],hooks={"PreToolUse":[HookMatcher(matcher="Bash",hooks=[check_bash_command]),],})asyncwithClaudeSDKClient(options=options)asclient:# Test 1: Command with forbidden pattern (will be blocked)awaitclient.query("Run the bash command: ./foo.sh --help")asyncformsginclient.receive_response():print(msg)print("\n"+"="*50+"\n")# Test 2: Safe command that should workawaitclient.query("Run the bash command: echo 'Hello from hooks example!'")asyncformsginclient.receive_response():print(msg)
fromclaude_agent_sdkimport(ClaudeSDKError,# Base errorCLINotFoundError,# Claude Code not installedCLIConnectionError,# Connection issuesProcessError,# Process failedCLIJSONDecodeError,# JSON parsing issues)try:asyncformessageinquery(prompt="Hello"):passexceptCLINotFoundError:print("Please install Claude Code")exceptProcessErrorase:print(f"Process failed with exit code: {e.exit_code}")exceptCLIJSONDecodeErrorase:print(f"Failed to parse response: {e}")
If you’re upgrading from the Claude Code SDK (versions < 0.1.0), please see the CHANGELOG.md for details on breaking changes and new features, including:
ClaudeCodeOptions → ClaudeAgentOptions rename
Merged system prompt configuration
Settings isolation and explicit control
New programmatic subagents and session forking features