The Python Agent SDK wraps the full Claude Code agent loop — tool dispatch, session management, and streaming — into a clean Python interface.
pip install anthropic-agent # Requires Python 3.10+ # ANTHROPIC_API_KEY must be set in the environment
import anthropic_agent
agent = anthropic_agent.ClaudeAgent(
model="claude-opus-4-8", # required: model ID string
allowed_tools=["Read", "Write", "Bash"], # optional: whitelist tools
denied_tools=["Bash"], # optional: blacklist tools (overrides allowed)
max_tokens=8192, # optional: max tokens per response turn
)
Synchronous. Runs the full agent loop and returns when the agent stops.
result = agent.run("List all Python files and summarize each one.")
print(result.text) # final text response
print(result.stop_reason) # "end_turn", "tool_use", or "max_tokens"
print(result.usage) # {"input_tokens": N, "output_tokens": N}
Async generator. Yields events as they arrive. Use for real-time output.
import asyncio
async def main():
async for event in agent.run_stream("Explain the codebase structure."):
if event.type == "text_delta":
print(event.text, end="", flush=True)
elif event.type == "tool_use":
print(f"\n[Tool: {event.tool_name}]")
elif event.type == "stop":
print(f"\nStop reason: {event.stop_reason}")
asyncio.run(main())
# Create a new session (persists conversation history)
session = agent.create_session()
result1 = session.run("What files are in src/?")
result2 = session.run("Now summarize the main one.") # has context from result1
# Resume a previous session by ID
session_id = session.id
restored = agent.resume_session(session_id)
result3 = restored.run("Continue where we left off.")
usage = agent.get_usage()
print(usage)
# => {"total_input_tokens": 12400, "total_output_tokens": 3100,
# "total_cost_usd": 0.187, "session_count": 3}
@agent.tool(description="Search the internal knowledge base")
def search_kb(query: str) -> str:
# Your implementation here
return f"Results for: {query}"
@agent.before_tool
def log_tool_call(tool_name: str, tool_input: dict):
print(f"[BEFORE] {tool_name}: {tool_input}")
# Return modified tool_input dict to transform the call
return tool_input
@agent.after_tool
def log_tool_result(tool_name: str, tool_output: dict):
print(f"[AFTER] {tool_name} -> {tool_output}")
# Spawn a subagent with a scoped task and restricted tools
sub_result = agent.spawn_subagent(
prompt="Audit all SQL queries in src/ for injection vulnerabilities.",
allowed_tools=["Read", "Bash"],
model="claude-sonnet-4-6", # optional: use cheaper model for sub-tasks
)
print(sub_result.text)