Type safety - Full TypeScript inference with Zod schemas
Example:
import { query } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
const schema = z.object({
summary: z.string(),
sentiment: z.enum(['positive', 'neutral', 'negative']),
confidence: z.number().min(0).max(1)
});
const response = query({
prompt: "Analyze this code review feedback",
options: {
model: "claude-sonnet-4-5",
outputFormat: {
type: "json_schema",
json_schema: {
name: "AnalysisResult",
strict: true,
schema: zodToJsonSchema(schema)
}
}
}
});
for await (const message of response) {
if (message.type === 'result' && message.structured_output) {
// Guaranteed to match schema
const validated = schema.parse(message.structured_output);
console.log(`Sentiment: ${validated.sentiment}`);
}
}
Zod Compatibility (v0.1.71+): SDK supports both Zod v3.24.1+ and Zod v4.0.0+ as peer dependencies. Import remains import { z } from "zod" for either version.
const response = query({
prompt: "Review and refactor the codebase",
options: {
allowedTools: ["Read", "Write", "Edit", "AskUserQuestion"]
}
});
// Agent can now ask clarifying questions
// Questions appear in message stream as tool_call with name "AskUserQuestion"
type AgentDefinition = {
description: string; // When to use this agent
prompt: string; // System prompt for agent
tools?: string[]; // Allowed tools (optional)
model?: 'sonnet' | 'opus' | 'haiku' | 'inherit'; // Model (optional)
skills?: string[]; // Skills to load (v0.2.10+)
maxTurns?: number; // Maximum turns before stopping (v0.2.10+)
}
Field Details:
description: When to use agent (used by main agent for delegation)
prompt: System prompt (defines role, inherits main context)
tools: Allowed tools (if omitted, inherits from main agent)
model: Model override (haiku/sonnet/opus/inherit)
skills: Skills to load for agent (v0.2.10+)
maxTurns: Limit agent to N turns before returning control (v0.2.10+)
forkSession: true - Create new branch from session
continue: prompt - Resume with new prompt (differs from resume)
Session Forking Pattern (Unique Capability):
// Explore alternative without modifying original
const forked = query({
prompt: "Try GraphQL instead of REST",
options: {
resume: sessionId,
forkSession: true // Creates new branch, original session unchanged
}
});
Capture Session ID:
for await (const message of response) {
if (message.type === 'system' && message.subtype === 'init') {
sessionId = message.session_id; // Save for later resume/fork
}
}
V2 Session APIs (Preview - v0.1.54+)
Simpler multi-turn conversation pattern:
import {
unstable_v2_createSession,
unstable_v2_resumeSession,
unstable_v2_prompt
} from "@anthropic-ai/claude-agent-sdk";
// Create a new session
const session = await unstable_v2_createSession({
model: "claude-sonnet-4-5",
workingDirectory: process.cwd(),
allowedTools: ["Read", "Grep", "Glob"]
});
// Send prompts and stream responses
const stream = unstable_v2_prompt(session, "Analyze the codebase structure");
for await (const message of stream) {
console.log(message);
}
// Continue conversation in same session
const stream2 = unstable_v2_prompt(session, "Now suggest improvements");
for await (const message of stream2) {
console.log(message);
}
// Resume a previous session
const resumedSession = await unstable_v2_resumeSession(session.sessionId);
Note: V2 APIs are in preview (unstable_ prefix). The .receive() method was renamed to .stream() in v0.1.72.
Permission Control
Permission Modes:
type PermissionMode = "default" | "acceptEdits" | "bypassPermissions" | "plan";
default - Standard permission checks
acceptEdits - Auto-approve file edits
bypassPermissions - Skip ALL checks (use in CI/CD only)
autoAllowBashIfSandboxed - Skip permission prompts for safe bash commands
excludedCommands - Commands that always require permission
allowUnsandboxedCommands - Allow commands that can't be sandboxed (risky)
network.proxyUrl - Route network through proxy for monitoring
Best Practice: Always use sandbox in production agents handling untrusted input.
File Checkpointing
Enable file state snapshots for rollback capability:
const response = query({
prompt: "Refactor the authentication module",
options: {
enableFileCheckpointing: true // Enable file snapshots
}
});
// Later: rewind file changes to a specific point
for await (const message of response) {
if (message.type === 'user' && message.uuid) {
// Can rewind to this point later
const userMessageUuid = message.uuid;
// To rewind (call on Query object)
await response.rewindFiles(userMessageUuid);
}
}
Use cases:
Undo failed refactoring attempts
A/B test code changes
Safe exploration of alternatives
Filesystem Settings
Setting Sources:
type SettingSource = 'user' | 'project' | 'local';
user - ~/.claude/settings.json (global)
project - .claude/settings.json (team-shared)
local - .claude/settings.local.json (gitignored overrides)
Default: NO settings loaded (settingSources: [])
Settings Priority
When multiple sources loaded, settings merge in this order (highest priority first):
Programmatic options (passed to query()) - Always win
Best Practice: Use settingSources: ["project"] in CI/CD for consistent behavior.
Query Object Methods
The query() function returns a Query object with these methods:
const q = query({ prompt: "..." });
// Async iteration (primary usage)
for await (const message of q) { ... }
// Runtime model control
await q.setModel("claude-opus-4-5"); // Change model mid-session
await q.setMaxThinkingTokens(4096); // Set thinking budget
// Introspection
const models = await q.supportedModels(); // List available models
const commands = await q.supportedCommands(); // List available commands
const account = await q.accountInfo(); // Get account details
// MCP status
const status = await q.mcpServerStatus(); // Check MCP server status
// Returns: { [serverName]: { status: 'connected' | 'failed', error?: string } }
// File operations (requires enableFileCheckpointing)
await q.rewindFiles(userMessageUuid); // Rewind to checkpoint
Use cases:
Dynamic model switching based on task complexity
Monitoring MCP server health
Adjusting thinking budget for reasoning tasks
Message Types & Streaming
Message Types:
system - Session init/completion (includes session_id)
assistant - Agent responses
tool_call - Tool execution requests
tool_result - Tool execution results
error - Error messages
result - Final result (includes structured_output for v0.1.45+)
Streaming Pattern:
for await (const message of response) {
if (message.type === 'system' && message.subtype === 'init') {
sessionId = message.session_id; // Capture for resume/fork
}
if (message.type === 'result' && message.structured_output) {
// Structured output available (v0.1.45+)
const validated = schema.parse(message.structured_output);
}
}
Error Handling
Error Codes:
Error Code
Cause
Solution
CLI_NOT_FOUND
Claude Code not installed
Install: npm install -g @anthropic-ai/claude-code
AUTHENTICATION_FAILED
Invalid API key
Check ANTHROPIC_API_KEY env var
RATE_LIMIT_EXCEEDED
Too many requests
Implement retry with backoff
CONTEXT_LENGTH_EXCEEDED
Prompt too long
Use session compaction, reduce context
PERMISSION_DENIED
Tool blocked
Check permissionMode, canUseTool
TOOL_EXECUTION_FAILED
Tool error
Check tool implementation
SESSION_NOT_FOUND
Invalid session ID
Verify session ID
MCP_SERVER_FAILED
Server error
Check server configuration
Known Issues Prevention
This skill prevents 14 documented issues:
Issue #1: CLI Not Found Error
Error: "Claude Code CLI not installed"Source: SDK requires Claude Code CLI
Why It Happens: CLI not installed globally
Prevention: Install before using SDK: npm install -g @anthropic-ai/claude-code
Issue #2: Authentication Failed
Error: "Invalid API key"Source: Missing or incorrect ANTHROPIC_API_KEY
Why It Happens: Environment variable not set
Prevention: Always set export ANTHROPIC_API_KEY="sk-ant-..."
Issue #3: Permission Denied Errors
Error: Tool execution blocked
Source: permissionMode restrictions
Why It Happens: Tool not allowed by permissions
Prevention: Use allowedTools or custom canUseTool callback
Error: "Prompt too long"Source: Input exceeds model context window (Issue #138)
Why It Happens: Large codebase, long conversations
⚠️ Critical Behavior: Once a session hits context limit:
All subsequent requests to that session return "Prompt too long"
/compact command fails with same error
Session is permanently broken and must be abandoned
Prevention Strategies:
// 1. Proactive session forking (create checkpoints before hitting limit)
const checkpoint = query({
prompt: "Checkpoint current state",
options: {
resume: sessionId,
forkSession: true // Create branch before hitting limit
}
});
// 2. Monitor time and rotate sessions proactively
const MAX_SESSION_TIME = 80 * 60 * 1000; // 80 minutes (before 90-min crash)
let sessionStartTime = Date.now();
function shouldRotateSession() {
return Date.now() - sessionStartTime > MAX_SESSION_TIME;
}
// 3. Start new sessions before hitting context limits
if (shouldRotateSession()) {
const summary = await getSummary(currentSession);
const newSession = query({
prompt: `Continue with context: ${summary}`
});
sessionStartTime = Date.now();
}
Note: SDK auto-compacts, but if limit is reached, session becomes unrecoverable
Issue #5: Tool Execution Timeout
Error: Tool doesn't respond
Source: Long-running tool execution
Why It Happens: Tool takes too long (>5 minutes default)
Prevention: Implement timeout handling in tool implementations
Issue #6: Session Not Found
Error: "Invalid session ID"Source: Session expired or invalid
Why It Happens: Session ID incorrect or too old
Prevention: Capture session_id from system init message
Issue #7: MCP Server Connection Failed
Error: Server not responding
Source: Server not running or misconfigured
Why It Happens: Command/URL incorrect, server crashed
Prevention: Test MCP server independently, verify command/URL
Issue #8: Subagent Definition Errors
Error: Invalid AgentDefinition
Source: Missing required fields
Why It Happens: description or prompt missing
Prevention: Always include description and prompt fields
Issue #9: Settings File Not Found
Error: "Cannot read settings"Source: Settings file doesn't exist
Why It Happens: settingSources includes non-existent file
Prevention: Check file exists before including in sources
Issue #10: Tool Name Collision
Error: Duplicate tool name
Source: Multiple tools with same name
Why It Happens: Two MCP servers define same tool name
Prevention: Use unique tool names, prefix with server name
Issue #11: Zod Schema Validation Error
Error: Invalid tool input
Source: Input doesn't match Zod schema
Why It Happens: Agent provided wrong data type
Prevention: Use descriptive Zod schemas with .describe()
Issue #12: Filesystem Permission Denied
Error: Cannot access path
Source: Restricted filesystem access
Why It Happens: Path outside workingDirectory or no permissions
Prevention: Set correct workingDirectory, check file permissions
Issue #13: MCP Server Config Missing type Field
Error: "Claude Code process exited with code 1" (cryptic, no context)
Source: GitHub Issue #131Why It Happens: URL-based MCP servers require explicit type: "http" or type: "sse" field
Prevention: Always specify transport type for URL-based MCP servers
// ❌ Wrong - missing type field (causes cryptic exit code 1)
mcpServers: {
"my-server": {
url: "https://api.example.com/mcp"
}
}
// ✅ Correct - type field required for URL-based servers
mcpServers: {
"my-server": {
url: "https://api.example.com/mcp",
type: "http" // or "sse" for Server-Sent Events
}
}
Diagnostic Clue: If you see "process exited with code 1" with no other context, check your MCP server configuration for missing type fields.
Issue #14: MCP Tool Result with Unicode Line Separators
Error: JSON parse error, agent hangs
Source: GitHub Issue #137Why It Happens: Unicode U+2028 (line separator) and U+2029 (paragraph separator) are valid in JSON but break JavaScript parsing
Prevention: Escape these characters in MCP tool results