Installation
Python
pip install claude-agent-sdkSet your API key:
export ANTHROPICAPIKEY=sk-...Or in code:
import os
os.environ["ANTHROPICAPIKEY"] = "sk-..."
TypeScript
npm install @anthropic-ai/claude-agent-sdkSet your API key:
export ANTHROPICAPIKEY=sk-...---
Your First Agent
Python Example
import asyncio
from claudeagentsdk import query, ClaudeAgentOptions
async def main():
# Simple agent: read a file
async for message in query(
prompt="Read package.json and tell me the main entry point",
options=ClaudeAgentOptions(
allowedtools=["Read"]
)
):
print(f"Message: {message}")
asyncio.run(main())
Run it:
python agent.pyOutput:
Message: SystemMessage(data={'sessionid': '...'})
Message: AssistantMessage(...)
Message: UserMessage(...)
[... tool execution ...]
Message: ResultMessage(...)
TypeScript Example
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
for await (const message of query({
prompt: "Read package.json and tell me the main entry point",
options: {
allowedTools: ["Read"]
}
})) {
console.log(Message:, message);
}
}
main();
---
Message Types
The SDK sends you messages as the agent loop runs. Each message tells you what's happening.
1. SystemMessage (type: "init")
Fired when the agent session starts.
SystemMessage(
subtype="init",
data={
"sessionid": "sessabc123...",
"timestamp": "2024-...",
"model": "claude-3-5-sonnet"
}
)
Use case: Log session start, capture session ID for resuming
for message in query(prompt="..."):
if isinstance(message, SystemMessage) and message.subtype == "init":
sessionid = message.data["sessionid"]
print(f"Session started: {sessionid}")
2. AssistantMessage
Claude has made a decision to use tools.
AssistantMessage(
content=[
TextBlock(text="I'll read your file"),
ToolUseBlock(
id="toolxyz",
name="Read",
input={"filepath": "/path/to/file"}
)
]
)
Use case: See what Claude decided to do before tools execute
3. UserMessage
Tool results being sent back to Claude.
UserMessage(
content=[
ToolResultBlock(
tooluseid="toolxyz",
content="<file contents here>"
)
]
)
Use case: Monitor what tools returned
4. ResultMessage
Final completion message.
ResultMessage(
subtype="success",
result="The main entry point is 'dist/index.js'",
usage={"inputtokens": 234, "outputtokens": 45},
cost={"input": 0.00001, "output": 0.00002}
)
Subtypes:
"success"— Agent completed successfully
"errormaxturns" — Hit max iteration limit"errormaxbudget" — Exceeded cost budget"errortoolerror" — A tool threw an exception"cancelled" — Agent was cancelledUse case: Check if task succeeded, log costs
async for message in query(prompt="..."):
if isinstance(message, ResultMessage):
if message.subtype == "success":
print(f"Success! Result: {message.result}")
else:
print(f"Failed: {message.subtype}")
print(f"Cost: ${message.cost['input'] + message.cost['output']}")
---
Processing Messages
Pattern: Collect Final Result
finalresult = None
async for message in query(prompt="..."):
if isinstance(message, ResultMessage):
if message.subtype == "success":
final
result = message.result
print(finalresult)
break # Done!
Pattern: Monitor Progress
async for message in query(prompt="..."):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock):
print(f"Using tool: {block.name}")
elif isinstance(message, ResultMessage):
print(f"Agent finished: {message.result}")
Pattern: Handle Errors
async for message in query(prompt="..."):
if isinstance(message, ResultMessage):
if message.subtype == "errormaxturns":
print("Agent ran out of iterations. Partial result:")
print(message.result)
elif message.subtype == "errortoolerror":
print("A tool failed:", message.result)
else:
print("Success:", message.result)
break
---
Session Management
What Is a Session?
A session is a stateful conversation with an agent. The agent remembers previous context.
Session 1: Ask agent to read auth.ts
async for message in query(prompt="Read auth.ts"):
if isinstance(message, ResultMessage):
sessionid = ... # Save this
result = message.result
Later: Session 2: Resume with new question
async for message in query(
prompt="Now find where this is used",
options={"resume": sessionid} # Resume same session
):
# Agent remembers auth.ts content!
pass
Resuming Sessions
The agent remembers everything from the previous session:
Session 1
async for msg in query(prompt="Read api.ts"):
if isinstance(msg, ResultMessage):
sessionid = msg.data.get("sessionid")
Session 2 (minutes later)
async for msg in query(
prompt="What patterns does it use?",
options={"resume": sessionid}
):
# Agent still has api.ts context!
Use case: Long-running tasks split across sessions
Session Context Window
Each session has its own context window. As you add messages, context fills up.
Session starts: 200K tokens available (depends on model)
Turn 1: You ask question (1K tokens used, 199K left)
Turn 2: Agent reads file (10K tokens used, 189K left)
Turn 3: You ask followup (2K tokens used, 187K left)
...continues until context is full or task completes
---
Agent Options
Control agent behavior with ClaudeAgentOptions:
options = ClaudeAgentOptions(
model="claude-3-5-sonnet",
allowedtools=["Read", "Bash", "Grep"],
maxturns=20,
effort="high"
)
Common Options
| Option | Default | Use Case |
|---|---|---|
| -------- | --------- | ---------- |
allowedtools | None (ask) | Pre-approve tools, no permission prompts |
maxturns | Unlimited | Prevent infinite loops |
maxbudgetusd | None | Stop if cost exceeds budget |
effort | High | Control reasoning depth (low/medium/high/xhigh/max) |
model | claude-3-5-sonnet | Choose model |
permissionmode | "default" | How to handle tools not in allowedtools |
---
Key Gotchas
Gotcha #1: Sessions have token limits
Gotcha #2: Effort vs extended thinking
effort controls reasoning depth per turnGotcha #3: Async/await required
async for or asyncio.run()Gotcha #4: Results are summaries
---
Next Steps
You now know how to run agents with the SDK. Next module: Tools & Tool Use — what tools are available and how they work!