- Blog
- Build Production-Grade AI Agents with the Claude Agent SDK
Build Production-Grade AI Agents with the Claude Agent SDK
Table of contents
Build Production-Grade AI Agents with the Claude Agent SDK
If you've been working with AI, you know that the future isn't just about chatbots—it's about building truly autonomous systems that can handle complex, multi-step tasks, interact with files, and execute code.
That’s where Anthropic’s official Python and TypeScript toolkit, the Claude Agent SDK, comes in. Formerly known as the Claude Code SDK, this framework provides all the necessary building blocks for creating production-grade AI agents. It brings the powerful agentic capabilities of Claude Code—including context management, rich tooling, error handling, and advanced permissions—directly into your applications.
Ready to give your AI agents the digital equivalent of a computer on a desk? Let's walk through how to start building with the Claude Agent SDK!
I. Getting Started: Installation and Prerequisites
To leverage the full power of the Claude Agent SDK, follow these simple setup steps.
1. Prerequisites and Installation
The Python Claude Agent SDK requires a few key components:
- Python 3.10+.
- Node.js.
- Claude Code CLI (2.0.0+): You need to install the underlying engine via NPM (
npm install -g @anthropic-ai/claude-code
). - Install the SDK: The Python package name has been updated to
claude-agent-sdk
(fromclaude-code-sdk
).
# Install the Claude Agent SDK Python package
pip install claude-agent-sdk
# Set your API Key
export ANTHROPIC_API_KEY="your-key-here"
You can obtain your Claude API key from the Claude Console.
II. Core Usage: Choosing Your Interaction Mode
The Claude Agent SDK offers two primary ways to interact with Claude Agent, depending on whether you need session memory:
query()
)
Mode 1: Single-Call Queries (The query()
function is ideal for one-off tasks or questions that do not require remembering previous context. It creates a new session for every interaction, returns an async iterator suitable for processing streaming responses, and automatically manages the connection lifecycle.
Code Detail: Basic Streaming Query
This example shows the minimal usage of the Claude Agent SDK to receive streaming output:
import anyio
from claude_agent_sdk import query, AssistantMessage, TextBlock
async def basic_query_example():
# query() returns an AsyncIterator[Message]
async for message in query(prompt="Hello Claude, what is the capital of France?"):
# Check if the message is from the Assistant
if isinstance(message, AssistantMessage):
for block in message.content:
# Extract and print the text response
if isinstance(block, TextBlock):
print(block.text, end="")
print()
if __name__ == "__main__":
anyio.run(basic_query_example)
ClaudeSDKClient
)
Mode 2: Continuous Conversation (For applications requiring memory, complex interactive dialogues, or advanced features like custom tools and hooks, the Claude Agent SDK recommends using ClaudeSDKClient
. This class maintains session continuity across multiple query()
calls.
Key Features of ClaudeSDKClient
:
- Session Continuity: Claude remembers previous messages and context.
- Supports Advanced Features: Enables custom tools and hooks.
- Interruption: Allows stopping a running agent mid-execution (
client.interrupt()
). - Explicit Lifecycle: You manually control the connection and disconnection, often using an asynchronous context manager.
Code Detail: Persistent Conversation with ClaudeSDKClient
import asyncio
from claude_agent_sdk import ClaudeSDKClient, AssistantMessage, TextBlock
async def continuous_conversation_example():
# Use context manager for automatic connection management
async with ClaudeSDKClient() as client:
# First query: sets the context
await client.query("What is the largest mammal on Earth?")
async for message in client.receive_response():
# Process response...
pass
# Second query: Claude remembers the previous context
await client.query("Tell me one interesting fact about that animal.")
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}")
if __name__ == "__main__":
asyncio.run(continuous_conversation_example())
III. Advanced Development Details: Customizing Your Claude Agent SDK
The real power of the Claude Agent SDK lies in its configuration capabilities using the ClaudeAgentOptions
data class, enabling fine-grained control over tools, permissions, and behavior.
ClaudeAgentOptions
)
1. Configuring Autonomy and Permissions (ClaudeAgentOptions
allows you to set the agent's system prompt, define allowed tools, and specify the working directory (cwd
).
Code Detail: Enabling Built-in Tools and Setting Permissions
The Claude Agent SDK is built on the agent framework that powers Claude Code, which includes powerful built-in tools like Read
, Write
, and Bash
for file operations and command execution.
from claude_agent_sdk import query, ClaudeAgentOptions
import asyncio
async def options_with_tools():
options = ClaudeAgentOptions(
# Set the agent's persona
system_prompt="You are an expert Python developer",
# Allow the agent to use core capabilities
allowed_tools=["Read", "Write", "Bash"],
# Use 'acceptEdits' mode to automatically approve file modifications, useful for automation
permission_mode='acceptEdits',
# Set the current working directory
cwd="/home/user/project"
)
async for message in query(
prompt="Create a Python script named 'hello.py' that prints 'Hello World'",
options=options
):
# The agent will use the 'Write' tool, automatically accepted by the permission_mode
pass
if __name__ == "__main__":
asyncio.run(options_with_tools())
2. Defining Custom Tools (In-Process SDK MCP Servers)
One of the most powerful features of the Claude Agent SDK is the ability to expose your own Python functions as callable tools, known as in-process SDK MCP servers. This eliminates the overhead of inter-process communication (IPC) required by external Model Context Protocol (MCP) servers, simplifying deployment and improving performance.
Code Detail: Creating and Integrating a Custom Tool
- Define the Tool: Use the
@tool
decorator to specify the tool's name, description, and input schema. - Create the Server: Use
create_sdk_mcp_server()
to wrap the tool function. - Configure Options: Include the resulting
McpSdkServerConfig
inClaudeAgentOptions.mcp_servers
. Note the required naming convention forallowed_tools
:mcp__<server_name>__<tool_name>
.
from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions, ClaudeSDKClient
from typing import Any
import asyncio
# 1. Define the tool with type-safe input schema
@tool("calculate", "Execute a mathematical expression", {"expression": str})
async def calculate(args: dict[str, Any]) -> dict[str, Any]:
try:
result = eval(args["expression"], {"__builtins__": {}}) # Example logic
return {"content": [{"type": "text", "text": f"Result: {result}"}]}
except Exception as e:
return {"content": [{"type": "text", "text": f"Error: {str(e)}"}], "is_error": True}
# 2. Create an in-process SDK MCP server
my_server = create_sdk_mcp_server(
name="utilities",
version="1.0.0",
tools=[calculate]
)
async def custom_tool_session():
# 3. Configure ClaudeAgentOptions
options = ClaudeAgentOptions(
mcp_servers={"utils": my_server},
# Allow Claude to use the tool using the MCP naming convention
allowed_tools=["mcp__utils__calculate"]
)
async with ClaudeSDKClient(options=options) as client:
await client.query("What is 123 multiplied by 456?")
async for message in client.receive_response():
# Claude will decide to use the 'calculate' tool
# ... print response ...
pass
if __name__ == "__main__":
asyncio.run(custom_tool_session())
3. Enhancing Safety and Observability with Hooks
Hooks are deterministic callback functions that are executed at specific points during the agent's operation cycle (e.g., before or after a tool is used, or when a user prompt is submitted). They are critical for implementing safety checks, logging, or modifying inputs.
Code Detail: Blocking Dangerous Operations with Hooks (PreToolUse
)
This example demonstrates how to use the PreToolUse
hook to deny a potentially destructive Bash
command:
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, HookMatcher
from typing import Any
import asyncio
async def check_bash_command(input_data: dict[str, Any], tool_use_id: str | None, context: Any) -> dict[str, Any]:
tool_name = input_data.get('tool_name', '')
if tool_name == "Bash":
command = input_data['tool_input'].get('command', '')
# Check for a dangerous pattern
if 'rm -rf /' in command:
print("[SECURITY HOOK] Dangerous command blocked!")
return {
'hookSpecificOutput': {
'hookEventName': 'PreToolUse',
# Deny the operation
'permissionDecision': 'deny',
'permissionDecisionReason': 'Dangerous command blocked'
}
}
return {}
async def hooks_example():
options = ClaudeAgentOptions(
allowed_tools=["Bash"],
hooks={
# Apply hook before any tool use
'PreToolUse': [
# Use HookMatcher to specifically target the 'Bash' tool
HookMatcher(matcher='Bash', hooks=[check_bash_command])
]
}
)
async with ClaudeSDKClient(options=options) as client:
# Agent attempts to run the dangerous command
await client.query("Run a bash command that removes all files recursively.")
# The hook will intercept and block the Bash tool use
async for message in client.receive_response():
print(message)
if __name__ == "__main__":
asyncio.run(hooks_example())
IV. Conclusion: Why the Claude Agent SDK is Production-Ready
The Claude Agent SDK isn't just another agent framework; it’s an infrastructure for building robust, professional autonomous systems.
It directly addresses the challenges faced when deploying agents to production environments:
- Autonomy and Persistence: It enables agents to operate independently for long durations (Claude Sonnet 4.5 can run autonomously for over 30 hours). The SDK handles persistent state management and session continuity.
- Context Management: It includes automatic context compaction and management to prevent token limits from being hit, a necessary feature for long-running workflows.
- Security and Control: Features like
allowed_tools
,permission_mode
, and powerful Hooks ensure you have fine-grained control over what your agent can and cannot do, preventing the agent from performing dangerous actions. - Flexible Tooling: Through the use of MCP and in-process SDK MCP servers, you can seamlessly integrate custom Python functions as high-performance tools, extending the agent's capabilities without complex subprocess management.
If your goal is to transition from simple prototypes to complex, reliable, SRE/DevOps, or Financial Services agents that integrate deeply with your system, the Claude Agent SDK provides the foundation to get work done. Happy building!
Latest from the blog
New research, comparisons, and workflow tips from the Vibe Coding Tools team.
Explore Claude Sonnet 4.5's record-breaking coding performance, new agent tools, usage limits, and expert tips for getting started today.
See how Chrome DevTools MCP ends blind programming by giving AI coding agents real-time debugging insight across the browser.
Step-by-step tutorial for installing Serena MCP, configuring Claude Desktop, and building practical workflows with uv.