| name | agent-sdk-builder |
| description | Build apps with the Claude Agent SDK (formerly Claude Code SDK). Covers programmatic agent loops, tool integration, subagent orchestration, prompt caching, and migration between Claude model versions. TRIGGER WHEN: code references claude-agent-sdk, user says "agent sdk", "build an agent", "programmatic claude", "claude code sdk", "sidecar", "run claude programmatically". DO NOT TRIGGER WHEN: user is using the Claude API client SDK (`anthropic`/`@anthropic-ai/sdk`) for direct chat completions, or doing general programming unrelated to agent orchestration.
|
Claude Agent SDK
The Claude Agent SDK lets you run Claude Code programmatically -- build AI agents that read files, write code, execute commands, search the web, and orchestrate subagents, all from your application code.
Key distinction: The Agent SDK (claude-agent-sdk) runs the full Claude Code agent loop with built-in tools. The Anthropic Client SDK (anthropic) is for raw API calls. Use the Agent SDK when you need autonomous tool-using agents.
Quick Reference
| TypeScript | Python |
|---|
| Package | @anthropic-ai/claude-agent-sdk | claude-agent-sdk |
| Install | npm install @anthropic-ai/claude-agent-sdk | pip install claude-agent-sdk |
| Auth | ANTHROPIC_API_KEY env var | ANTHROPIC_API_KEY env var |
| Core function | query() | query() |
| GitHub | anthropics/claude-agent-sdk-typescript | anthropics/claude-agent-sdk-python |
The CLI package @anthropic-ai/claude-code is bundled inside the SDK -- no separate install needed.
1. Installation & Auth
npm install @anthropic-ai/claude-agent-sdk
pip install claude-agent-sdk
uv add claude-agent-sdk
Authentication via environment variable:
export ANTHROPIC_API_KEY=sk-ant-...
Alternative providers:
- Amazon Bedrock:
CLAUDE_CODE_USE_BEDROCK=1 + AWS credentials
- Google Vertex AI:
CLAUDE_CODE_USE_VERTEX=1 + GCP credentials
- Microsoft Azure:
CLAUDE_CODE_USE_FOUNDRY=1 + Azure credentials
2. Core API -- query()
Both SDKs expose query() as the primary entry point. It returns an async iterator streaming SDKMessage objects. Claude handles the entire tool loop autonomously -- you do NOT implement tool execution.
TypeScript
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Find and fix the bug in auth.py",
options: {
allowedTools: ["Read", "Edit", "Bash"],
maxTurns: 10,
},
})) {
if (message.type === "assistant" && message.content) {
for (const block of message.content) {
if (block.type === "text") process.stdout.write(block.text);
}
}
if ("result" in message) {
console.log("\nFinal:", message.result);
console.log("Cost:", message.total_cost_usd);
}
}
Python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Bash"],
max_turns=10,
)
async for message in query(prompt="Find and fix the bug in auth.py", options=options):
if hasattr(message, "result"):
print(f"Final: {message.result}")
print(f"Cost: ${message.total_cost_usd:.4f}")
asyncio.run(main())
3. Configuration Options
Full Options Reference
| Option (TS / Py) | Type | Description |
|---|
allowedTools / allowed_tools | string[] | Tools to auto-approve without user confirmation |
disallowedTools / disallowed_tools | string[] | Tools to always deny |
permissionMode / permission_mode | string | Permission strategy (see Permissions section) |
systemPrompt / system_prompt | string | Custom system prompt or "claude_code" for default |
model | string | Model ID (e.g., "claude-fable-5", "claude-opus-4-8", "claude-sonnet-4-6") -- short aliases resolve to the latest date-slugged release (e.g., "claude-haiku-4-5" resolves to "claude-haiku-4-5-20251001"); pin a full slug for reproducibility |
maxTurns / max_turns | number | Maximum agentic loop iterations |
maxBudgetUsd / max_budget_usd | number | Spending cap in USD |
effort | string | "low", "medium", "high", "max" |
cwd | string | Working directory for file operations |
mcpServers / mcp_servers | object | MCP server configurations |
hooks | object | Lifecycle hook callbacks |
agents | object | Subagent definitions |
resume | string | Session ID to resume |
continue / continue_conversation | boolean | Continue most recent session |
forkSession / fork_session | string | Fork from an existing session |
settingSources / setting_sources | string[] | Load settings from ["user", "project", "local"] |
plugins | string[] | Local plugin directory paths |
sandbox | object | Sandbox/isolation settings |
thinking | object | Extended thinking: "adaptive", {type: "enabled", budget: N}, "disabled" |
outputFormat / output_format | object | JSON schema for structured output |
env | object | Environment variables passed to agent |
canUseTool / can_use_tool | function | Runtime permission callback |
includePartialMessages / include_partial_messages | boolean | Enable token-level streaming |
spawnClaudeCodeProcess | function | Custom process spawner (VMs, containers, remote) |
agentProgressSummaries | boolean | Enable periodic AI-generated progress summaries for running subagents |
debug / debug | boolean | Enable programmatic debug logging |
debugFile / debug_file | string | File path for debug log output |
Example -- Full Configuration
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const msg of query({
prompt: "Refactor the auth module to use JWT tokens",
options: {
model: "claude-sonnet-4-6",
allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
disallowedTools: ["WebSearch", "WebFetch"],
permissionMode: "bypassPermissions",
maxTurns: 25,
maxBudgetUsd: 1.0,
effort: "high",
cwd: "/home/user/project",
systemPrompt: "You are a senior backend engineer. Follow the project's coding standards.",
thinking: "adaptive",
env: { NODE_ENV: "development" },
},
})) {
}
4. Built-in Tools
The agent has access to these tools by default:
| Tool | Purpose |
|---|
Read | Read files from filesystem |
Write | Create new files |
Edit | Precise string replacements in existing files |
Bash | Execute shell commands |
Glob | Find files by pattern |
Grep | Search file contents with regex |
WebSearch | Search the web |
WebFetch | Fetch and parse web pages |
Agent | Spawn subagents (required for multi-agent) |
Skill | Invoke skills from plugins |
AskUserQuestion | Request user input |
TodoWrite | Manage task lists |
ToolSearch | Discover deferred tools |
Control which tools the agent can use:
options: {
allowedTools: ["Read", "Glob", "Grep"],
disallowedTools: ["Bash", "Write", "Edit"],
}
5. Custom Tools via MCP
Create custom tools using the SDK's MCP server helpers. Tools are defined with schemas and handlers, then exposed as in-process MCP servers.
TypeScript
import { tool, createSdkMcpServer, query } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
const getWeather = tool(
"get_weather",
"Get current weather for a city",
{ city: z.string(), units: z.enum(["celsius", "fahrenheit"]).default("celsius") },
async ({ city, units }) => ({
content: [{ type: "text", text: JSON.stringify({ city, temp: 22, units }) }],
})
);
const searchDatabase = tool(
"search_db",
"Search the application database",
{ query: z.string(), limit: z.number().default(10) },
async ({ query: q, limit }) => {
const results = await db.search(q, limit);
return { content: [{ type: "text", text: JSON.stringify(results) }] };
}
);
const server = createSdkMcpServer({
name: "app-tools",
tools: [getWeather, searchDatabase],
});
for await (const msg of query({
prompt: "What's the weather in Rome and find related travel posts?",
options: {
mcpServers: { app: server },
allowedTools: ["mcp__app__get_weather", "mcp__app__search_db"],
},
})) {
}
Python
from claude_agent_sdk import tool, create_sdk_mcp_server, query, ClaudeAgentOptions
@tool("get_weather", "Get current weather for a city", {"city": str, "units": str})
async def get_weather(args):
return {"content": [{"type": "text", "text": f"Weather in {args['city']}: 22C"}]}
@tool("search_db", "Search the database", {"query": str, "limit": int})
async def search_db(args):
results = await db.search(args["query"], args["limit"])
return {"content": [{"type": "text", "text": str(results)}]}
server = create_sdk_mcp_server(name="app-tools", tools=[get_weather, search_db])
options = ClaudeAgentOptions(
mcp_servers={"app": server},
allowed_tools=["mcp__app__get_weather", "mcp__app__search_db"],
)
async for msg in query(prompt="Weather in Rome?", options=options):
pass
MCP Tool Naming Convention
Custom tools follow the pattern: mcp__<server-name>__<tool-name>
Example: server named "mytools" with tool "search" becomes mcp__mytools__search
External MCP Servers
Connect to external MCP servers via stdio or HTTP:
options: {
mcpServers: {
localServer: {
command: "node",
args: ["./mcp-server.js"],
},
remoteServer: {
url: "https://mcp.example.com/sse",
headers: { Authorization: "Bearer token" },
},
},
}
6. Subagents -- Multi-Agent Orchestration
Subagents let you define specialized agents that the main agent can spawn for parallel or delegated work.
Defining Subagents
for await (const msg of query({
prompt: "Review the codebase for quality and security issues",
options: {
allowedTools: ["Read", "Grep", "Glob", "Agent"],
agents: {
"security-reviewer": {
description: "Security expert. Use for vulnerability scanning, auth review, injection detection.",
prompt: "You are a security auditor. Find vulnerabilities, auth issues, and injection vectors.",
tools: ["Read", "Glob", "Grep"],
model: "opus",
},
"code-quality": {
description: "Code quality reviewer. Use for style, patterns, complexity, dead code.",
prompt: "Review code quality: naming, complexity, patterns, duplication.",
tools: ["Read", "Glob", "Grep"],
model: "sonnet",
},
"test-runner": {
description: "Runs tests and reports results.",
prompt: "Execute test suites and analyze failures.",
tools: ["Bash", "Read", "Grep"],
model: "haiku",
},
},
},
})) {
}
Subagent Properties
| Property | Type | Description |
|---|
description | string | Required. When to use this agent (Claude decides based on this) |
prompt | string | Required. System prompt for the subagent |
tools | string[] | Restricted tool set |
model | string | "sonnet", "opus", "haiku", or "inherit" |
disallowedTools | string[] | Tools to block (TS only) |
mcpServers | object | MCP servers available to subagent |
skills | string[] | Skills the subagent can invoke |
memory | object | Memory configuration for the subagent |
maxTurns | number | Turn limit for this subagent (TS only) |
Subagent Behavior
- Context isolation -- each subagent gets a fresh conversation; only its final message returns to the parent
- Parallel execution -- multiple subagents run concurrently when spawned together
- No nesting -- subagents cannot spawn their own subagents
- Resumable -- subagents can be resumed by ID from tool results
- Cost isolated -- each subagent's token usage is tracked separately
- Progress summaries -- enable
agentProgressSummaries: true to receive periodic AI-generated progress updates from running subagents
7. Session Management
Sessions persist conversation history to disk, enabling multi-turn workflows.
Resume a Session (TypeScript)
let sessionId: string | undefined;
for await (const msg of query({
prompt: "Read the authentication module",
options: { allowedTools: ["Read", "Glob"] },
})) {
if (msg.type === "system" && msg.subtype === "init") {
sessionId = msg.session_id;
}
}
for await (const msg of query({
prompt: "Now refactor it to use JWT",
options: { resume: sessionId },
})) {
if ("result" in msg) console.log(msg.result);
}
Continue Most Recent Session
for await (const msg of query({
prompt: "Continue where we left off",
options: { continue: true },
})) { }
options = ClaudeAgentOptions(continue_conversation=True)
async for msg in query(prompt="Continue where we left off", options=options):
pass
Session Client (Python)
Python provides ClaudeSDKClient for managed multi-turn conversations:
from claude_agent_sdk import ClaudeSDKClient
async with ClaudeSDKClient() as client:
await client.query("What's the project structure?")
async for msg in client.receive_response():
pass
await client.query("Now find all API endpoints")
async for msg in client.receive_response():
pass
await client.interrupt()
List, Inspect, and Manage Sessions
import {
listSessions, getSessionInfo, getSessionMessages,
forkSession, tagSession, renameSession,
} from "@anthropic-ai/claude-agent-sdk";
const sessions = await listSessions({ dir: "/path/to/project", limit: 10 });
for (const session of sessions) {
console.log(session.sessionId, session.createdAt, session.tag);
}
const info = await getSessionInfo(sessionId);
console.log(info.tag, info.createdAt);
const messages = await getSessionMessages(sessionId);
const forked = await forkSession(sessionId);
await tagSession(sessionId, "auth-refactor");
await renameSession(sessionId, "auth-refactor-v2");
from claude_agent_sdk import (
list_sessions, get_session_info, get_session_messages,
fork_session, tag_session, rename_session,
)
sessions = await list_sessions(dir="/path/to/project", limit=10)
for session in sessions:
messages = await get_session_messages(session.session_id)
info = await get_session_info(session_id)
await tag_session(session_id, "auth-refactor")
await rename_session(session_id, "auth-refactor-v2")
Session State Events
Session state change events are opt-in as of v0.2.83. Enable with environment variable:
export CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS=1
Exit Reasons
The ExitReason type includes: "end_turn", "max_turns", "budget", "interrupt", "resume".
8. Introspection Utilities
import { supportedAgents, getSettings } from "@anthropic-ai/claude-agent-sdk";
const agents = await supportedAgents();
const settings = await getSettings();
console.log(settings.applied.model, settings.applied.effort);
9. Permissions
Control what the agent can do at runtime.
Permission Modes
| Mode | Behavior |
|---|
"default" | Unmatched tools trigger canUseTool callback or user prompt |
"dontAsk" | Deny anything not pre-approved (TypeScript only) |
"acceptEdits" | Auto-accept file mutations (Edit, Write, mkdir, touch, rm, mv, cp) |
"bypassPermissions" | All tools run without prompts (use with caution) |
"plan" | No execution -- planning/analysis only |
Permission Evaluation Order
- Hooks (
PreToolUse)
- Deny rules (
disallowedTools) -- overrides everything, including bypassPermissions
- Permission mode
- Allow rules (
allowedTools) -- does NOT constrain bypassPermissions
canUseTool callback
Important: disallowedTools is the only hard block. Even bypassPermissions cannot override it. Use it for safety-critical restrictions.
Runtime Permission Callback
for await (const msg of query({
prompt: "Deploy the application",
options: {
allowedTools: ["Read", "Bash"],
canUseTool: async (toolName, input) => {
if (toolName === "Bash" && input.command?.includes("rm -rf")) {
return { behavior: "deny", message: "Destructive commands not allowed" };
}
return { behavior: "allow" };
},
},
})) { }
Callback return values:
{ behavior: "allow" } -- approve the tool call
{ behavior: "deny", message: "reason" } -- reject with explanation
{ behavior: "ask" } -- fall through to user prompt (default mode)
10. Hooks -- Lifecycle Events
Hooks intercept agent lifecycle events for logging, validation, or control flow.
Available Hook Events
| Hook | When | Can modify? |
|---|
PreToolUse | Before a tool executes | Yes -- allow, deny, or modify input |
PostToolUse | After a tool completes | Yes -- modify output |
PostToolUseFailure | After a tool fails | Log errors |
UserPromptSubmit | When user sends a prompt | Modify prompt text |
Stop | Agent is about to stop | Force continue or modify |
SubagentStart | Subagent is starting | Modify subagent config |
SubagentStop | Subagent completed | Process results |
PreCompact | Before context compaction | Log or modify |
Notification | Agent sends a notification | Display or forward |
PermissionRequest | Tool needs permission | Auto-approve or deny |
TaskCompleted | A task has been completed | Process results |
TeammateIdle | A teammate agent is idle | Reassign or notify |
ConfigChange | Configuration changed at runtime | Security auditing |
SessionStart (TS) | Session initialized | Setup actions |
SessionEnd (TS) | Session completed | Cleanup actions |
Hook Matchers
Hooks can use regex matchers to target specific tools, and support async (fire-and-forget) mode:
hooks: {
PreToolUse: [
{
matcher: /^(Write|Edit)$/,
hooks: [async (event) => {
if (event.input.file_path?.includes(".env")) {
return { behavior: "deny", message: "Cannot modify .env files" };
}
return { behavior: "allow" };
}],
timeout: 30,
},
{
matcher: /.*/,
hooks: [{ async: true, asyncTimeout: 5, handler: async (event) => {
await fetch("https://logs.example.com/webhook", {
method: "POST",
body: JSON.stringify({ tool: event.toolName, time: Date.now() }),
});
}}],
},
],
}
Hook Return Values
Hook outputs can include:
behavior: "allow", "deny", or "ask" (deny > ask > allow when multiple hooks conflict)
systemMessage: inject a system message into conversation
continue: force the agent to continue (for Stop hooks)
hookSpecificOutput: event-specific modifications
Hook Example
for await (const msg of query({
prompt: "Analyze the codebase",
options: {
hooks: {
PreToolUse: async (event) => {
console.log(`Tool: ${event.toolName}, Input: ${JSON.stringify(event.input)}`);
if (event.toolName === "Write" && event.input.file_path?.includes("production")) {
return { behavior: "deny", message: "Cannot modify production files" };
}
return { behavior: "allow" };
},
PostToolUse: async (event) => {
console.log(`Tool ${event.toolName} completed in ${event.duration_ms}ms`);
},
Stop: async (event) => {
console.log(`Agent stopping. Result: ${event.result}`);
},
},
},
})) { }
11. Streaming
Message Types
Messages streamed from query() include:
| Type | Description |
|---|
system (subtype: init) | Session initialized -- contains session_id |
system (subtype: api_retry) | API retry info -- attempt count, max retries, delay, error status |
assistant | Claude's response with content blocks (text, tool_use) |
result | Final result with result text, total_cost_usd, usage |
task_progress | Real-time usage metrics for running agents |
rate_limit | Rate limit event with retry timing (Python: RateLimitEvent) |
stream_event | Partial token (when includePartialMessages: true) |
Token-Level Streaming
for await (const msg of query({
prompt: "Explain the auth flow",
options: {
includePartialMessages: true,
allowedTools: ["Read"],
},
})) {
if (msg.type === "stream_event") {
process.stdout.write(msg.delta?.text ?? "");
}
}
12. Structured Output
Force the agent to return JSON conforming to a schema:
for await (const msg of query({
prompt: "Analyze this codebase and list all API endpoints",
options: {
outputFormat: {
type: "json_schema",
json_schema: {
name: "api_analysis",
schema: {
type: "object",
properties: {
endpoints: {
type: "array",
items: {
type: "object",
properties: {
method: { type: "string" },
path: { type: "string" },
handler: { type: "string" },
},
required: ["method", "path", "handler"],
},
},
},
required: ["endpoints"],
},
},
},
},
})) {
if ("result" in msg) {
const analysis = JSON.parse(msg.result);
console.log(analysis.endpoints);
}
}
13. Plugins and Skills
Load local plugins to give the agent access to custom skills, agents, and commands:
for await (const msg of query({
prompt: "Review the frontend code",
options: {
plugins: ["/path/to/my-plugin"],
allowedTools: ["Read", "Glob", "Grep", "Skill"],
},
})) { }
Load filesystem settings (CLAUDE.md, .claude/ configs):
options: {
settingSources: ["user", "project", "local"],
}
14. Cost Tracking
for await (const msg of query({ prompt: "Analyze auth.py", options: {} })) {
if ("result" in msg) {
console.log(`Total cost: $${msg.total_cost_usd}`);
console.log(`Input tokens: ${msg.usage?.input_tokens}`);
console.log(`Output tokens: ${msg.usage?.output_tokens}`);
if (msg.modelUsage) {
for (const [model, usage] of Object.entries(msg.modelUsage)) {
console.log(`${model}: $${usage.cost_usd}`);
}
}
}
}
Use maxBudgetUsd to set a hard spending cap:
options: { maxBudgetUsd: 0.50 }
15. Hosting & Deployment Patterns
Ephemeral Sessions
New container per task, destroy on completion. Best for CI/CD, one-shot tasks.
const result = [];
for await (const msg of query({
prompt: taskDescription,
options: {
maxTurns: 15,
maxBudgetUsd: 0.25,
permissionMode: "bypassPermissions",
cwd: "/workspace",
},
})) {
if ("result" in msg) result.push(msg);
}
Long-Running Sessions
Persistent containers with multiple agent interactions. Best for interactive applications.
const sessionId = await loadSessionId();
for await (const msg of query({
prompt: userInput,
options: { resume: sessionId },
})) { }
Custom Process Spawning
Run agents in VMs, containers, or remote environments:
for await (const msg of query({
prompt: "Analyze the repo",
options: {
spawnClaudeCodeProcess: async (options) => {
const container = await docker.createContainer({
Image: "node:20",
Cmd: ["npx", "@anthropic-ai/claude-code", ...options.args],
});
await container.start();
return container.stream;
},
},
})) { }
Sandbox Isolation
Use Anthropic's sandbox runtime for secure execution:
import { createSandbox } from "@anthropic-ai/sandbox-runtime";
const sandbox = await createSandbox({ image: "node:20" });
for await (const msg of query({
prompt: "Run the test suite",
options: { sandbox },
})) { }
16. Security Best Practices
- Always set
allowedTools -- restrict to minimum necessary tools
- Use
maxBudgetUsd -- prevent runaway costs
- Use
maxTurns -- prevent infinite loops
- Use
canUseTool callbacks -- validate dangerous operations at runtime
- Sandbox untrusted code -- use container isolation for user-submitted tasks
- Never pass secrets in prompts -- use
env option or MCP tools for credential access
- Use
disallowedTools -- explicitly block tools you never want used
- Proxy credentials -- use a proxy pattern for API keys the agent needs
options: {
allowedTools: ["Read", "Glob", "Grep"],
disallowedTools: ["Bash", "Write", "Edit"],
maxTurns: 10,
maxBudgetUsd: 0.25,
permissionMode: "dontAsk",
canUseTool: async (tool, input) => {
if (input.file_path?.includes("..")) {
return { behavior: "deny", message: "Path traversal blocked" };
}
return { behavior: "allow" };
},
}
17. Common Patterns
CI/CD Code Review Agent
import { query } from "@anthropic-ai/claude-agent-sdk";
async function reviewPR(diff: string): Promise<string> {
let review = "";
for await (const msg of query({
prompt: `Review this PR diff for bugs, security issues, and style:\n\n${diff}`,
options: {
allowedTools: ["Read", "Glob", "Grep"],
maxTurns: 15,
maxBudgetUsd: 0.50,
outputFormat: {
type: "json_schema",
json_schema: {
name: "pr_review",
schema: {
type: "object",
properties: {
issues: { type: "array", items: { type: "object", properties: {
severity: { type: "string" }, file: { type: "string" },
line: { type: "number" }, description: { type: "string" },
}}},
summary: { type: "string" },
approved: { type: "boolean" },
},
required: ["issues", "summary", "approved"],
},
},
},
},
})) {
if ("result" in msg) review = msg.result;
}
return review;
}
Multi-Agent Research Pipeline
for await (const msg of query({
prompt: "Research best practices for rate limiting in Node.js APIs",
options: {
allowedTools: ["Read", "Grep", "Glob", "WebSearch", "WebFetch", "Agent"],
agents: {
"codebase-analyst": {
description: "Analyzes the local codebase for existing patterns.",
prompt: "Search the codebase for rate limiting implementations and patterns.",
tools: ["Read", "Glob", "Grep"],
model: "sonnet",
},
"web-researcher": {
description: "Researches best practices and documentation online.",
prompt: "Search for current best practices, libraries, and patterns.",
tools: ["WebSearch", "WebFetch"],
model: "sonnet",
},
},
},
})) { }
Interactive Chat Application
from claude_agent_sdk import ClaudeSDKClient
async def chat_loop():
async with ClaudeSDKClient() as client:
while True:
user_input = input("You: ")
if user_input.lower() == "exit":
break
await client.query(user_input)
async for msg in client.receive_response():
if hasattr(msg, "content"):
for block in msg.content:
if block.get("type") == "text":
print(f"Agent: {block['text']}")
18. Migration from claude-code-sdk
The old claude-code-sdk / @anthropic-ai/claude-code-sdk packages are deprecated. Migration:
npm uninstall @anthropic-ai/claude-code-sdk
npm install @anthropic-ai/claude-agent-sdk
pip uninstall claude-code-sdk
pip install claude-agent-sdk
Update imports:
import { query } from "@anthropic-ai/claude-code-sdk";
import { query } from "@anthropic-ai/claude-agent-sdk";
from claude_code_sdk import query, ClaudeCodeOptions
from claude_agent_sdk import query, ClaudeAgentOptions
Breaking Changes
The API surface is mostly identical, but two critical defaults changed:
-
System prompt no longer defaults to Claude Code's prompt -- the new SDK uses a minimal system prompt. To restore Claude Code behavior:
systemPrompt: { type: "preset", preset: "claude_code" }
-
Settings sources no longer loaded by default -- CLAUDE.md, .claude/ configs, and user settings are NOT loaded unless you opt in:
settingSources: ["user", "project", "local"]
To fully restore old claude-code-sdk behavior, use both options together.
19. TypeScript V2 Preview
A simplified session-based API is available as a preview:
import {
unstable_v2_createSession,
unstable_v2_resumeSession,
unstable_v2_prompt,
} from "@anthropic-ai/claude-agent-sdk";
await using session = await unstable_v2_createSession({
model: "claude-sonnet-4-6",
allowedTools: ["Read", "Edit", "Bash"],
});
const response = session.send("Find bugs in auth.py");
for await (const event of response) {
}
const stream = session.stream("Now fix them");
for await (const msg of stream) {
}
const resumed = await unstable_v2_resumeSession(sessionId);
for await (const msg of unstable_v2_prompt("Quick question", { model: "claude-haiku-4-5" })) {
}
await using (TypeScript 5.2+) automatically closes the session when the scope exits. The V2 API does not yet support session forking.
20. ClaudeSDKClient Methods (Python)
The Python ClaudeSDKClient provides additional runtime control methods:
async with ClaudeSDKClient() as client:
await client.query("Analyze the codebase")
async for msg in client.receive_response():
pass
await client.set_permission_mode("bypassPermissions")
await client.set_model("claude-sonnet-4-6")
await client.rewind_files()
status = await client.get_mcp_status()
await client.add_mcp_server("new-server", {"command": "node", "args": ["server.js"]})
await client.remove_mcp_server("old-server")
info = await client.get_server_info()
await client.interrupt()
await client.disconnect()
Official Resources