Claude Agent SDK Python
SkillAI & modelsBuild and test Python applications using the Claude Agent SDK. Use when creating agent applications, custom MCP tools, permission handlers, lifecycle hooks, or multi-turn conversations. Covers query(), ClaudeSDKClient, @tool decorator, create_sdk_mcp_server(), hooks, permissions, and independent app testing patterns.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Claude Agent SDK Python skill
What this skill tells your AI
The instructions your AI receives, as published by kivo360/omoios in .claude/skills/claude-agent-sdk-python/SKILL.md and read by ahel’s review.
Build production-ready agent applications with the Claude Agent SDK for Python.
Quick Reference
Installation
pip install claude-agent-sdk
Requires Python 3.10+ and bundled Claude CLI v2.0.60+.
Core APIs
| API | Use Case |
|---|---|
query() | One-shot or simple multi-turn queries |
ClaudeSDKClient | Interactive streaming, bidirectional control |
@tool + create_sdk_mcp_server() | Custom in-process tools |
can_use_tool callback | Fine-grained permission control |
hooks | Lifecycle event interception |
Basic Patterns
One-Shot Query
import anyio
from claude_agent_sdk import query, AssistantMessage, TextBlock, ResultMessage
async def main():
async for msg in query(prompt="Explain Python decorators"):
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(msg, ResultMessage):
print(f"Cost: ${msg.total_cost_usd:.4f}")
anyio.run(main)
Interactive Client
import anyio
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
TextBlock,
ResultMessage,
)
async def main():
options = ClaudeAgentOptions(
system_prompt="You are a helpful assistant",
allowed_tools=["Read", "Write", "Bash"],
permission_mode="acceptEdits",
max_turns=10,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Analyze main.py")
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(msg, ResultMessage):
print(f"Done. Cost: ${msg.total_cost_usd:.4f}")
anyio.run(main)
Custom Tools
from typing import Any
from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions
@tool("add", "Add two numbers", {"a": float, "b": float})
async def add(args: dict[str, Any]) -> dict[str, Any]:
result = args["a"] + args["b"]
return {"content": [{"type": "text", "text": f"Result: {result}"}]}
@tool("divide", "Divide two numbers", {"a": float, "b": float})
async def divide(args: dict[str, Any]) -> dict[str, Any]:
if args["b"] == 0:
return {
"content": [{"type": "text", "text": "Error: Division by zero"}],
"is_error": True
}
return {"content": [{"type": "text", "text": f"Result: {args['a'] / args['b']}"}]}
calculator = create_sdk_mcp_server(
name="calculator",
version="1.0.0",
tools=[add, divide]
)
options = ClaudeAgentOptions(
mcp_servers={"calc": calculator},
allowed_tools=["mcp__calc__add", "mcp__calc__divide"],
permission_mode="acceptEdits",
)
Configuration Reference
ClaudeAgentOptions
ClaudeAgentOptions(
# Model
model="claude-sonnet-4-20250514",
fallback_model=None,
# Prompts
system_prompt="You are a helpful assistant",
# Limits
max_turns=10,
max_budget_usd=1.00,
max_thinking_tokens=8000,
# Environment
cwd="/path/to/project",
env={"CUSTOM_VAR": "value"},
# Tools
tools=["Read", "Write", "Bash"], # or {"type": "preset", "preset": "claude_code"}
allowed_tools=["Read", "Write"], # whitelist
disallowed_tools=["WebFetch"], # blacklist
mcp_servers={"name": config}, # custom tools
# Permissions
permission_mode="acceptEdits", # default, acceptEdits, plan, bypassPermissions
can_use_tool=my_permission_callback,
# Sessions
continue_conversation=False,
resume="session-id",
fork_session=False,
# Hooks
hooks={"PreToolUse": [HookMatcher(matcher="*", hooks=[my_hook])]},
)
Permission Modes
| Mode | Behavior |
|---|---|
"default" | Prompt for all actions |
"acceptEdits" | Auto-approve file edits |
"plan" | Planning mode (no execution) |
"bypassPermissions" | Auto-approve all (use with caution) |
Advanced Patterns
For detailed examples of:
- Permission callbacks: See references/permissions.md
- Lifecycle hooks: See references/hooks.md
- Testing patterns: See references/testing.md
- Production patterns: See references/production.md
Independent App Testing
Use scripts/run_agent_app.py to test agent applications in isolation:
# Run a simple agent app
python scripts/run_agent_app.py path/to/app.py
# Run with custom options
python scripts/run_agent_app.py path/to/app.py --cwd /project --max-turns 5
Use scripts/create_agent_project.py to scaffold new agent projects:
# Create a basic agent project
python scripts/create_agent_project.py my_agent --template basic
# Create with custom tools
python scripts/create_agent_project.py my_agent --template custom-tools
# Create interactive chat app
python scripts/create_agent_project.py my_agent --template chat
Message Types
| Type | Description |
|---|---|
AssistantMessage | Claude's responses (text, thinking, tool use) |
UserMessage | User input or tool results |
ResultMessage | Session summary (always last) |
SystemMessage | System events |
Content Blocks
from claude_agent_sdk import TextBlock, ThinkingBlock, ToolUseBlock, ToolResultBlock
# In AssistantMessage.content
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(block, ThinkingBlock):
print(f"Thinking: {block.thinking}")
elif isinstance(block, ToolUseBlock):
print(f"Tool: {block.name}, Input: {block.input}")
Error Handling
from claude_agent_sdk import (
ClaudeSDKError,
CLINotFoundError,
ProcessError,
SDKJSONDecodeError,
)
try:
async with ClaudeSDKClient(options) as client:
await client.query("Do something")
async for msg in client.receive_response():
process(msg)
except CLINotFoundError:
print("Claude CLI not found. Install from https://claude.ai")
except ProcessError as e:
print(f"CLI failed with exit code: {e.exit_code}")
except SDKJSONDecodeError:
print("Failed to parse CLI response")
Best Practices
- Always use context managers for
ClaudeSDKClientto ensure cleanup - Set
max_budget_usdto prevent runaway costs - Use
permission_mode="acceptEdits"for file operations without prompts - Return
is_error: Truefrom custom tools on failure - Use structured outputs for predictable JSON responses
- Test tools independently before integrating with full agent
Signals
- GitHub stars
- 77
- Forks
- 7
- Last commit
- Jun 2026
Advanced
- Catalog kind
- skill
- Gateway key
claude-agent-sdk-python- Source
- github.com/kivo360/omoios