Claude Code Agent SDK: The Complete Beginner's Guide to Building AI Agents (2026)
Want to build your first AI agent but don't know where to start? Anthropic's Claude Code Agent SDK makes it possible — even without deep programming knowledge.
In this comprehensive guide, we'll walk you through step by step how to create autonomous AI agents capable of handling complex tasks independently.
What You'll Learn in This Guide
- What AI agents are and how they differ from chatbots
- How the Claude Code Agent SDK works
- Practical code examples for your first agent
- Tools, sub-agents, and MCP integration
- Best practices for production-ready agents
What Is an AI Agent? (And Why Is It Different from ChatGPT?)
Before we dive into code, let's clarify a fundamental concept: What makes an AI agent different from a regular chatbot?
Chatbot vs Agent
| Feature | Chatbot (e.g., ChatGPT) | AI Agent |
|---|---|---|
| Interaction | Question → Answer | Goal → Autonomous execution |
| Execution | Single request | Loops until goal is reached |
| Tools | Limited | Infinitely extensible |
| Autonomy | None | Independent decisions |
A chatbot answers your questions. An AI agent works autonomously toward a goal, using various tools and making its own decisions along the way.
The Agent Loop Pattern
Every AI agent follows a fundamental pattern — the famous Agent Loop:
┌─────────────────────────────────────────────────────────────┐
│ AGENT LOOP PATTERN │
├─────────────────────────────────────────────────────────────┤
│ 1. GATHER CONTEXT │
│ └─▶ Understand the task, read relevant data │
│ │
│ 2. EXECUTE AN ACTION │
│ └─▶ Use tools, perform operations │
│ │
│ 3. VERIFY RESULTS │
│ └─▶ Did the action succeed? │
│ │
│ 4. REPEAT │
│ └─▶ Until the goal is reached │
└─────────────────────────────────────────────────────────────┘
Example: You ask an agent to plan a trip. The agent:
- Gathers context: Asks for date, budget, preferences
- Executes actions: Searches for flights, hotels, activities
- Verifies: Does everything fit the budget? Are the schedules compatible?
- Repeats: Until a complete travel plan is ready
What Is the Claude Code Agent SDK?
The Claude Code Agent SDK is the same framework Anthropic uses internally for Claude Code — their powerful coding assistant.
It provides everything you need to build production-ready AI agents:
Key SDK Features
| Feature | Description |
|---|---|
| Automatic Context Compression | Intelligently manages large context windows |
| Tool Ecosystem | File operations, code execution, web search |
| Permission Controls | Granular control over agent actions |
| MCP Extensibility | Integration of external services via Model Context Protocol |
| Sub-agents | Delegation to specialized sub-agents |
| Hook System | Event-driven workflows |
Why Choose the Claude Code Agent SDK?
- Battle-tested in production: The same system powering Claude Code
- Minimal boilerplate: Less code, more functionality
- Built-in tools: File operations, Bash, Web ready out of the box
- MCP native: Seamless integration with Model Context Protocol
Quick Start: Installation and Setup
Prerequisites
- Python 3.10 or higher
- Anthropic API key (from console.anthropic.com)
Step 1: Install the SDK
# Install with pip
pip install claude-agent-sdk
# Or with uv (recommended for faster installation)
uv pip install claude-agent-sdk
Step 2: Configure the API Key
# Set as environment variable
export ANTHROPIC_API_KEY="sk-ant-..."
# Or in a .env file
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
Step 3: Test the First Connection
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
async def test_connection():
options = ClaudeAgentOptions(
model="sonnet", # Claude Sonnet 4
system_prompt="You are a helpful assistant."
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Say hello!")
async for message in client.receive_response():
print(message)
# Run
import asyncio
asyncio.run(test_connection())
Practical Example 1: Simple Q&A Agent
Let's start with the simplest agent — one that answers questions:
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from claude_agent_sdk.types import TextBlock
import asyncio
SYSTEM_PROMPT = """You are a friendly assistant that answers questions.
Be precise and helpful."""
async def simple_qa_agent():
options = ClaudeAgentOptions(
model="sonnet",
system_prompt=SYSTEM_PROMPT,
permission_mode="acceptEdits" # Allows read operations
)
async with ClaudeSDKClient(options=options) as client:
print("🤖 Q&A Agent ready! (Type 'exit' to quit)\n")
while True:
user_input = input("You: ").strip()
if user_input.lower() in ['exit', 'quit']:
print("Goodbye!")
break
await client.query(user_input)
print("Agent: ", end="")
async for message in client.receive_response():
if hasattr(message, 'content'):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text, end="")
print("\n")
if __name__ == "__main__":
asyncio.run(simple_qa_agent())
What this code does:
- Creates a client with a system prompt
- Starts an interactive loop
- Sends user input to Claude
- Returns the response via streaming
Practical Example 2: Agent with Memory
An agent with conversation memory can remember previous messages:
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from claude_agent_sdk.types import TextBlock
import asyncio
SYSTEM_PROMPT = """You are an assistant with memory.
You remember everything that has been said in this conversation.
Reference previous statements when relevant."""
async def memory_agent():
options = ClaudeAgentOptions(
model="sonnet",
system_prompt=SYSTEM_PROMPT,
permission_mode="acceptEdits",
# The SDK automatically manages conversation history!
)
async with ClaudeSDKClient(options=options) as client:
print("🧠 Memory Agent ready!\n")
print("Tip: Ask questions related to previous answers.\n")
while True:
user_input = input("You: ").strip()
if not user_input:
continue
if user_input.lower() == 'exit':
break
await client.query(user_input)
print("Agent: ", end="")
async for message in client.receive_response():
if hasattr(message, 'content'):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text, end="", flush=True)
print("\n")
if __name__ == "__main__":
asyncio.run(memory_agent())
Test the memory:
You: My name is Max.
Agent: Hello Max! Nice to meet you.
You: What's my name?
Agent: Your name is Max — you just introduced yourself!
Practical Example 3: Agent with Tools
This is where it gets interesting! Agents with tools can perform real actions:
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from claude_agent_sdk.types import TextBlock, ToolUseBlock
import asyncio
SYSTEM_PROMPT = """You are a research assistant with access to tools.
Use the available tools to accomplish tasks:
- WebFetch: Retrieves content from web pages
- Read: Reads local files
- Grep: Searches for patterns in files
- Glob: Finds files by patterns
Always explain which tool you're using and why."""
async def tool_agent():
options = ClaudeAgentOptions(
model="sonnet",
system_prompt=SYSTEM_PROMPT,
permission_mode="acceptEdits",
allowed_tools=[
"WebFetch", # Fetch web content
"Read", # Read files
"Grep", # Search text
"Glob", # Find files
]
)
async with ClaudeSDKClient(options=options) as client:
print("🔧 Tool Agent ready!")
print("Commands: 'search [URL]', 'find [file]', etc.\n")
while True:
user_input = input("You: ").strip()
if user_input.lower() == 'exit':
break
await client.query(user_input)
async for message in client.receive_response():
if hasattr(message, 'content'):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Agent: {block.text}")
elif isinstance(block, ToolUseBlock):
print(f"🛠️ Using tool: {block.name}")
print(f" Parameters: {block.input}")
print()
if __name__ == "__main__":
asyncio.run(tool_agent())
Available Built-in Tools
| Tool | Function | Example |
|---|---|---|
Read | Read files | Read("config.json") |
Write | Write files | Write("output.txt", content) |
Edit | Edit files | Edit(file, old, new) |
Glob | Search for files | Glob("**/*.py") |
Grep | Search for text | Grep("TODO", "src/") |
Bash | Execute commands | Bash("npm install") |
WebFetch | Fetch web content | WebFetch(url, prompt) |
Practical Example 4: Autonomous Travel Planner Agent
Now let's build a fully autonomous agent:
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from claude_agent_sdk.types import TextBlock, ToolUseBlock, ToolResultBlock
import asyncio
import json
TRAVEL_SYSTEM_PROMPT = """You are an autonomous travel planning agent.
YOUR GOAL: Create a complete travel plan based on the user's needs.
YOUR WORKFLOW:
1. Collect all necessary information (date, budget, preferences)
2. Research options with WebFetch
3. Create a structured travel plan
4. Present the plan and ask for feedback
IMPORTANT:
- FIRST ask for missing information
- Use WebFetch for current information
- Create a clear, structured plan
- Offer alternatives
ALWAYS start with a friendly greeting and ask about the destination."""
async def travel_planner_agent():
options = ClaudeAgentOptions(
model="sonnet",
system_prompt=TRAVEL_SYSTEM_PROMPT,
permission_mode="acceptEdits",
allowed_tools=["WebFetch", "Read", "Write"],
max_turns=20 # Allows longer agent loops
)
async with ClaudeSDKClient(options=options) as client:
print("✈️ Travel Planner Agent started!")
print("=" * 50)
# Initial greeting from the agent
await client.query("Start travel planning for a new client.")
async for message in client.receive_response():
if hasattr(message, 'content'):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Agent: {block.text}")
print()
# Interactive loop
while True:
user_input = input("You: ").strip()
if user_input.lower() in ['exit', 'done', 'thanks']:
print("Agent: You're welcome! Have a great trip! 🌍✨")
break
await client.query(user_input)
async for message in client.receive_response():
if hasattr(message, 'content'):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Agent: {block.text}")
elif isinstance(block, ToolUseBlock):
print(f"🔍 Searching: {block.name}...")
print()
if __name__ == "__main__":
asyncio.run(travel_planner_agent())
Advanced Features: MCP Integration
The Model Context Protocol (MCP) enables integration with external services:
Including MCP Servers
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
# MCP server configuration
MCP_CONFIG = {
"utils": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://your-mcp-server.com/api"]
}
}
async def mcp_agent():
options = ClaudeAgentOptions(
model="sonnet",
system_prompt="You have access to MCP tools for extended functionality.",
permission_mode="acceptEdits",
allowed_tools=[
"WebFetch",
"mcp__utils__save_note", # MCP tools are prefixed
"mcp__utils__find_note",
],
mcp_servers=MCP_CONFIG
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Create a note with the title 'Important'")
# ...
Creating Your Own MCP Server
# mcp_server.py - Simple MCP server
from mcp import Server
server = Server("my-tools")
@server.tool(name="save_note")
async def save_note(title: str, content: str) -> dict:
"""Saves a note."""
# Your logic here
return {"status": "success", "id": "note_123"}
@server.tool(name="find_note")
async def find_note(pattern: str) -> dict:
"""Finds notes by pattern."""
# Your logic here
return {"notes": [...]}
if __name__ == "__main__":
import asyncio
asyncio.run(server.run())
Sub-agents: Specialized Agents
For complex tasks, you can use sub-agents — specialized agents that handle subtasks:
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
ORCHESTRATOR_PROMPT = """You are a coordinator that delegates tasks to
specialized sub-agents:
- Research Agent: For information gathering
- Writing Agent: For content creation
- Analysis Agent: For data analysis
Delegate tasks according to their type."""
async def orchestrator_agent():
options = ClaudeAgentOptions(
model="sonnet",
system_prompt=ORCHESTRATOR_PROMPT,
permission_mode="acceptEdits",
allowed_tools=["SubAgent", "Read", "Write"],
subagents_enabled=True # Enables sub-agents
)
async with ClaudeSDKClient(options=options) as client:
await client.query(
"Research the latest AI trends and create a report."
)
# The agent automatically delegates to sub-agents
When to Use Sub-agents?
| Situation | Recommendation |
|---|---|
| Simple, linear tasks | ❌ No sub-agent needed |
| Parallel, independent tasks | ✅ Use sub-agents |
| Specialized expertise required | ✅ Use sub-agents |
| Long, complex workflows | ✅ Use sub-agents |
Hooks: Event-Driven Workflows
Hooks enable automated actions on specific events:
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, HookMatcher
# Define hook function
async def log_tool_usage(event):
"""Logs every tool usage."""
print(f"📝 Log: Tool '{event.tool_name}' was called")
return True # Allow execution
async def block_dangerous_commands(event):
"""Blocks dangerous Bash commands."""
dangerous = ['rm -rf', 'sudo', 'chmod 777']
if any(cmd in str(event.arguments) for cmd in dangerous):
print("⚠️ Dangerous command blocked!")
return False # Block execution
return True
async def hook_agent():
options = ClaudeAgentOptions(
model="sonnet",
system_prompt="You are an assistant with security hooks.",
permission_mode="acceptAll",
allowed_tools=["Bash", "Write", "Read"],
hooks={
"PreToolUse": [
HookMatcher(hooks=[log_tool_usage]),
HookMatcher(
tool_name="Bash",
hooks=[block_dangerous_commands]
)
]
}
)
async with ClaudeSDKClient(options=options) as client:
# ...
Hook Types
| Hook | When Triggered | Use Case |
|---|---|---|
PreToolUse | Before tool execution | Validation, logging |
PostToolUse | After tool execution | Post-processing, alerts |
Notification | On important events | Notifications |
Stop | At session end | Cleanup, reporting |
Best Practices for Production-Ready Agents
1. Clear System Prompts
# ❌ Bad: Vague instructions
SYSTEM_PROMPT = "Be helpful."
# ✅ Good: Clear role, boundaries, and behavior
SYSTEM_PROMPT = """You are a customer service agent for TechCorp.
YOUR ROLE:
- Answer questions about our products
- Escalate complex requests to humans
- Document all interactions
BOUNDARIES:
- No price negotiation
- No technical changes without approval
- When in doubt: Ask
BEHAVIOR:
- Friendly and professional
- Respond in the customer's language
- Summarize complex topics"""
2. Define Permissions Granularly
# ❌ Bad: Allow everything
permission_mode = "acceptAll"
# ✅ Good: Only allow necessary tools
permission_mode = "acceptEdits"
allowed_tools = ["Read", "WebFetch"] # Only what's truly needed
3. Implement Error Handling
async def resilient_agent():
max_retries = 3
for attempt in range(max_retries):
try:
async with ClaudeSDKClient(options=options) as client:
await client.query(prompt)
async for message in client.receive_response():
# Processing
pass
break # Success, exit loop
except RateLimitError:
wait_time = 2 ** attempt # Exponential backoff
print(f"Rate limit hit. Waiting {wait_time}s...")
await asyncio.sleep(wait_time)
except Exception as e:
print(f"Error: {e}")
if attempt == max_retries - 1:
raise
4. Context Management
# For long conversations: Compress context
options = ClaudeAgentOptions(
model="sonnet",
system_prompt=SYSTEM_PROMPT,
# The SDK automatically compresses when needed
max_context_tokens=100000, # Maximum context size
)
Costs and Model Selection
Available Models
| Model | Alias | Strengths | Cost |
|---|---|---|---|
| Claude Opus 4.5 | opus | Maximum quality, complex tasks | $$$ |
| Claude Sonnet 4 | sonnet | Best quality/cost ratio | $$ |
| Claude Haiku | haiku | Fast, affordable | $ |
Model Recommendation by Use Case
| Use Case | Recommended Model |
|---|---|
| Simple Q&A | Haiku |
| General agents | Sonnet |
| Code generation | Sonnet |
| Complex analyses | Opus |
| Research agents | Opus |
Summary: Your Path to Your First Agent
Checklist to Get Started
- Python 3.10+ installed
- Anthropic API key created
-
claude-agent-sdkinstalled - Simple Q&A agent tested
- Agent with memory tried
- Tools added
- Autonomous agent built
Next Steps
- Experiment: Start with the simple Q&A agent
- Extend: Add tools as needed
- Specialize: Create domain-specific agents
- Scale: Use sub-agents for complex workflows
- Secure: Implement hooks for production environments
Frequently Asked Questions (FAQ)
What's the difference between Claude Code and the Claude Agent SDK?
Claude Code is Anthropic's complete coding assistant — a finished product you can use in your terminal.
The Claude Agent SDK is the underlying framework that lets you build your own agents for any use case. The SDK gives you the building blocks that also power Claude Code.
Do I need programming knowledge for the SDK?
Basic Python knowledge is helpful but not strictly required. You can follow the simple examples in this guide even without deep programming experience.
However, for production-ready agents, we recommend solid Python fundamentals.
How much does using the Claude Agent SDK cost?
Using the SDK itself is free. You only pay for API calls to Claude.
Sonnet costs approximately $3 per million input tokens and $15 per million output tokens (January 2026). For development and testing, we recommend starting with small contexts.
Can I use the SDK with other AI models?
The Claude Agent SDK is specifically optimized for Claude models.
For other models like GPT-4 or Gemini, alternative SDKs like OpenAI Agents SDK or Google ADK exist. However, the concepts (Agent Loop, Tools, Memory) are transferable.
How do I secure my agent for production use?
Three important steps:
- Set
permission_modeto "manual" or "acceptEdits" instead of "acceptAll" - Explicitly define
allowed_toolswith only the necessary tools - Implement hooks for security controls and logging
For critical applications, add human approvals in the loop.
Additional Resources
- Official Documentation: docs.claude.com/en/api/agent-sdk
- Anthropic Engineering Blog: anthropic.com/engineering
- MCP Specification: modelcontextprotocol.io
- Claude Code: anthropic.com/claude-code
This guide was created for developers and AI enthusiasts taking their first steps with AI agents. The Claude Code Agent SDK is constantly evolving — visit the official documentation for the latest updates.