claude-agent-sdk

|

jezweb/claude-skills424 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: claude-agent-sdk
description: |
license: MIT
---

# Claude Agent SDK - Structured Outputs & Error Prevention Guide

**Package**: @anthropic-ai/claude-agent-sdk@0.2.12
**Breaking Changes**: v0.1.45 - Structured outputs (Nov 2025), v0.1.0 - No default system prompt, settingSources required

---

## What's New in v0.1.45+ (Nov 2025)

**Major Features:**

### 1. Structured Outputs (v0.1.45, Nov 14, 2025)
- **JSON schema validation** - Guarantees responses match exact schemas
- **`outputFormat` parameter** - Define output structure with JSON schema or Zod
- **Access validated results** - Via `message.structured_output`
- **Beta header required**: `structured-outputs-2025-11-13`
- **Type safety** - Full TypeScript inference with Zod schemas

**Example:**
```typescript
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.

### 2. Plugins System (v0.1.27)
- **`plugins` array** - Load local plugin paths
- **Custom plugin support** - Extend agent capabilities

### 3. Hooks System (v0.1.0+)

**All 12 Hook Events:**

| Hook | When Fired | Use Case |
|------|------------|----------|
| `PreToolUse` | Before tool execution | Validate, modify, or block tool calls |
| `PostToolUse` | After tool execution | Log results, trigger side effects |
| `Notification` | Agent notifications | Display status updates |
| `UserPromptSubmit` | User prompt received | Pre-process or validate input |
| `SubagentStart` | Subagent spawned | Track delegation, log context |
| `SubagentStop` | Subagent completed | Aggregate results, cleanup |
| `PreCompact` | Before context compaction | Save state before truncation |
| `PermissionRequest` | Permission needed | Custom approval workflows |
| `Stop` | Agent stopping | Cleanup, final logging |
| `SessionStart` | Session begins | Initialize state |
| `SessionEnd` | Session ends | Persist state, cleanup |
| `Error` | Error occurred | Custom error handling |

**Hook Configuration:**
```typescript
const response = query({
  prompt: "...",
  options: {
    hooks: {
      PreToolUse: async (input) => {
        console.log(`Tool: ${input.toolName}`);
        return { allow: true };  // or { allow: false, message: "..." }
      },
      PostToolUse: async (input) => {
        await logToolUsage(input.toolName, input.result);
      }
    }
  }
});
```

### 4. Additional Options
- **`fallbackModel`** - Automatic model fallback on failures
- **`maxThinkingTokens`** - Control extended thinking budget
- **`strictMcpConfig`** - Strict MCP configuration validation
- **`continue`** - Resume with new prompt (differs from `resume`)
- **`permissionMode: 'plan'`** - New permission mode for planning workflows

📚 **Docs**: https://platform.claude.com/docs/en/agent-sdk/structured-outputs

---

## The Complete Claude Agent SDK Reference

## Table of Contents

1. [Core Query API](#core-query-api)
2. [Tool Integration](#tool-integration-built-in--custom)
3. [MCP Servers](#mcp-servers-model-context-protocol)
4. [Subagent Orchestration](#subagent-orchestration)
5. [Session Management](#session-management)
6. [Permission Control](#permission-control)
7. [Sandbox Settings](#sandbox-settings-security-critical)
8. [File Checkpointing](#file-checkpointing)
9. [Filesystem Settings](#filesystem-settings)
10. [Query Object Methods](#query-object-methods)
11. [Message Types & Streaming](#message-types--streaming)
12. [Error Handling](#error-handling)
13. [Known Issues](#known-issues-prevention)

---

## Core Query API

**Key signature:**
```typescript
query(prompt: string | AsyncIterable<SDKUserMessage>, options?: Options)
  -> AsyncGenerator<SDKMessage>
```

**Critical Options:**
- `outputFormat` - Structured JSON schema validation (v0.1.45+)
- `settingSources` - Filesystem settings loading ('user'|'project'|'local')
- `canUseTool` - Custom permission logic callback
- `agents` - Programmatic subagent definitions
- `mcpServers` - MCP server configuration
- `permissionMode` - 'default'|'acceptEdits'|'bypassPermissions'|'plan'
- `betas` - Enable beta features (e.g., 1M context window)
- `sandbox` - Sandbox settings for secure execution
- `enableFileCheckpointing` - Enable file state snapshots
- `systemPrompt` - System prompt (string or preset object)

### Extended Context (1M Tokens)

Enable 1 million token context window:

```typescript
const response = query({
  prompt: "Analyze this large codebase",
  options: {
    betas: ['context-1m-2025-08-07'],  // Enable 1M context
    model: "claude-sonnet-4-5"
  }
});
```

### System Prompt Configuration

Two forms of systemPrompt:

```typescript
// 1. Simple string
systemPrompt: "You are a helpful coding assistant."

// 2. Preset with optional append (preserves Claude Code defaults)
systemPrompt: {
  type: 'preset',
  preset: 'claude_code',
  append: "\n\nAdditional context: Focus on security."
}
```

**Use preset form** when you want Claude Code's default behaviors plus custom additions.

---

## Tool Integration (Built-in + Custom)

**Tool Control:**
- `allowedTools` - Whitelist (takes precedence)
- `disallowedTools` - Blacklist
- `canUseTool` - Custom permission callback (see Permission Control section)

**Built-in Tools:** Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, Task, NotebookEdit, BashOutput, KillBash, ListMcpResources, ReadMcpResource, AskUserQuestion

### AskUserQuestion Tool (v0.1.71+)

Enable user interaction during agent execution:

```typescript
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"
```

**Use cases:**
- Clarify ambiguous requirements mid-task
- Get user approval before destructive operations
- Present options and get selection

### Tools Configuration (v0.1.57+)

**Three forms of tool configuration:**

```typescript
// 1. Exact allowlist (string array)
tools: ["Read", "Write", "Grep"]

// 2. Disable all tools (empty array)
tools: []

// 3. Preset with defaults (object form)
tools: { type: 'preset', preset: 'claude_code' }
```

**Note:** `allowedTools` and `disallowedTools` still work but `tools` provides more flexibility.

---

## MCP Servers (Model Context Protocol)

**Server Types:**
- **In-process** - `createSdkMcpServer()` with `tool()` definitions
- **External** - stdio, HTTP, SSE transport

**Tool Definition:**
```typescript
tool(name: string, description: string, zodSchema, handler)
```

**Handler Return:**
```typescript
{ content: [{ type: "text", text: "..." }], isError?: boolean }
```

### External MCP Servers (stdio)

```typescript
const response = query({
  prompt: "List files and analyze Git history",
  options: {
    mcpServers: {
      // Filesystem server
      "filesystem": {
        command: "npx",
        args: ["@modelcontextprotocol/server-filesystem"],
        env: {
          ALLOWED_PATHS: "/Users/developer/projects:/tmp"
        }
      },
      // Git operations server
      "git": {
        command: "npx",
        args: ["@modelcontextprotocol/server-git"],
        env: {
          GIT_REPO_PATH: "/Users/developer/projects/my-repo"
        }
      }
    },
    allowedTools: [
      "mcp__filesystem__list_files",
      "mcp__filesystem__read_file",
      "mcp__git__log",
      "mcp__git__diff"
    ]
  }
});
```

### External MCP Servers (HTTP/SSE)

```typescript
const response = query({
  prompt: "Analyze data from remote service",
  options: {
    mcpServers: {
      "remote-service": {
        url: "https://api.example.com/mcp",
        headers: {
          "Authorization": "Bearer your-token-here",
          "Content-Type": "application/json"
        }
      }
    },
    allowedTools: ["mcp__remote-service__analyze"]
  }
});
```

### MCP Tool Naming Convention

**Format**: `mcp__<server-name>__<tool-name>`

**CRITICAL:**
- Server name and tool name MUST match configuration
- Use double underscores (`__`) as separators
- Include in `allowedTools` array

**Examples:** `mcp__weather-service__get_weather`, `mcp__filesystem__read_file`

---

## Subagent Orchestration

### AgentDefinition Type

```typescript
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+)

**Usage:**
```typescript
agents: {
  "security-checker": {
    description: "Security audits and vulnerability scanning",
    prompt: "You check security. Scan for secrets, verify OWASP compliance.",
    tools: ["Read", "Grep", "Bash"],
    model: "sonnet",
    skills: ["security-best-practices"],  // Load specific skills
    maxTurns: 10  // Limit to 10 turns
  }
}
```

### ⚠️ Subagent Cleanup Warning

**Known Issue**: Subagents don't stop when parent agent stops ([Issue #132](https://github.com/anthropics/claude-agent-sdk-typescript/issues/132))

When a parent agent is stopped (via cancellation or error), spawned subagents continue running as orphaned processes. This can lead to:
- Resource leaks
- Continued tool execution after parent stopped
- RAM out-of-memory in recursive scenarios ([Claude Code Issue #4850](https://github.com/anthropics/claude-code/issues/4850))

**Workaround**: Implement cleanup in Stop hooks:

```typescript
const response = query({
  prompt: "Deploy to production",
  options: {
    agents: {
      "deployer": {
        description: "Handle deployments",
        prompt: "Deploy the application",
        tools: ["Bash"]
      }
    },
    hooks: {
      Stop: async (input) => {
        // Manual cleanup of spawned processes
        console.log("Parent stopped - cleaning up subagents");
        // Implement process tracking and termination
      }
    }
  }
});
```

**Enhancement Tracking**: [Issue #142](https://github.com/anthropics/claude-agent-sdk-typescript/issues/142) proposes auto-termination

---

## Session Management

**Options:**
- `resume: sessionId` - Continue previous session
- `forkSession: true` - Create new branch from session
- `continue: prompt` - Resume with new prompt (differs from `resume`)

**Session Forking Pattern (Unique Capability):**

```typescript
// 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:**
```typescript
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:**

```typescript
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:**
```typescript
type PermissionMode = "default" | "acceptEdits" | "bypassPermissions" | "plan";
```

- `default` - Standard permission checks
- `acceptEdits` - Auto-approve file edits
- `bypassPermissions` - Skip ALL checks (use in CI/CD only)
- `plan` - Planning mode (v0.1.45+)

### Custom Permission Logic

```typescript
const response = query({
  prompt: "Deploy application to production",
  options: {
    permissionMode: "default",
    canUseTool: async (toolName, input) => {
      // Allow read-only operations
      if (['Read', 'Grep', 'Glob'].includes(toolName)) {
        return { behavior: "allow" };
      }

      // Deny destructive bash commands
      if (toolName === 'Bash') {
        const dangerous = ['rm -rf', 'dd if=', 'mkfs', '> /dev/'];
        if (dangerous.some(pattern => input.command.includes(pattern))) {
          return {
            behavior: "deny",
            message: "Destructive command blocked for safety"
          };
        }
      }

      // Require confirmation for deployments
      if (input.command?.includes('deploy') || input.command?.includes('kubectl apply')) {
        return {
          behavior: "ask",
          message: "Confirm deployment to production?"
        };
      }

      // Allow by default
      return { behavior: "allow" };
    }
  }
});
```

### canUseTool Callback

```typescript
type CanUseToolCallback = (
  toolName: string,
  input: any
) => Promise<PermissionDecision>;

type PermissionDecision =
  | { behavior: "allow" }
  | { behavior: "deny"; message?: string }
  | { behavior: "ask"; message?: string };
```

**Examples:**

```typescript
// Block all file writes
canUseTool: async (toolName, input) => {
  if (toolName === 'Write' || toolName === 'Edit') {
    return { behavior: "deny", message: "No file modifications allowed" };
  }
  return { behavior: "allow" };
}

// Require confirmation for specific files
canUseTool: async (toolName, input) => {
  const sensitivePaths = ['/etc/', '/root/', '.env', 'credentials.json'];
  if ((toolName === 'Write' || toolName === 'Edit') &&
      sensitivePaths.some(path => input.file_path?.includes(path))) {
    return {
      behavior: "ask",
      message: `Modify sensitive file ${input.file_path}?`
    };
  }
  return { behavior: "allow" };
}

// Log all tool usage
canUseTool: async (toolName, input) => {
  console.log(`Tool requested: ${toolName}`, input);
  await logToDatabase(toolName, input);
  return { behavior: "allow" };
}
```

---

## Sandbox Settings (Security-Critical)

**Enable sandboxed execution for Bash commands:**

```typescript
const response = query({
  prompt: "Run system diagnostics",
  options: {
    sandbox: {
      enabled: true,
      autoAllowBashIfSandboxed: true,  // Auto-approve bash in sandbox
      excludedCommands: ["rm", "dd", "mkfs"],  // Never auto-approve these
      allowUnsandboxedCommands: false  // Deny unsandboxable commands
    }
  }
});
```

### SandboxSettings Type

```typescript
type SandboxSettings = {
  enabled: boolean;
  autoAllowBashIfSandboxed?: boolean;  // Default: false
  excludedCommands?: string[];
  allowUnsandboxedCommands?: boolean;  // Default: false
  network?: NetworkSandboxSettings;
  ignoreViolations?: SandboxIgnoreViolations;
};

type NetworkSandboxSettings = {
  enabled: boolean;
  proxyUrl?: string;  // HTTP proxy for network requests
};
```

**Key Options:**
- `enabled` - Activate sandbox isolation
- `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:**

```typescript
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:**
```typescript
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):

1. **Programmatic options** (passed to `query()`) - Always win
2. **Local settings** (`.claude/settings.local.json`)
3. **Project settings** (`.claude/settings.json`)
4. **User settings** (`~/.claude/settings.json`)

**Example:**

```typescript
// .claude/settings.json
{
  "allowedTools": ["Read", "Write", "Edit"]
}

// .claude/settings.local.json
{
  "allowedTools": ["Read"]  // Overrides project settings
}

// Programmatic
const response = query({
  options: {
    settingSources: ["project", "local"],
    allowedTools: ["Read", "Grep"]  // ← This wins
  }
});

// Actual allowedTools: ["Read", "Grep"]
```

**Best Practice:** Use `settingSources: ["project"]` in CI/CD for consistent behavior.

---

## Query Object Methods

The `query()` function returns a `Query` object with these methods:

```typescript
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:**
```typescript
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

### Issue #4: Context Length Exceeded (Session-Breaking)
**Error**: `"Prompt too long"`
**Source**: Input exceeds model context window ([Issue #138](https://github.com/anthropics/claude-agent-sdk-typescript/issues/138))
**Why It Happens**: Large codebase, long conversations

**⚠️ Critical Behavior**: Once a session hits context limit:
1. All subsequent requests to that session return "Prompt too long"
2. `/compact` command fails with same error
3. **Session is permanently broken and must be abandoned**

**Prevention Strategies**:

```typescript
// 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 #131](https://github.com/anthropics/claude-agent-sdk-typescript/issues/131)
**Why It Happens**: URL-based MCP servers require explicit `type: "http"` or `type: "sse"` field
**Prevention**: Always specify transport type for URL-based MCP servers

```typescript
// ❌ 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 #137](https://github.com/anthropics/claude-agent-sdk-typescript/issues/137)
**Why 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

```typescript
// MCP tool handler - sanitize external data
tool("fetch_content", "Fetch text content", {}, async (args) => {
  const content = await fetchExternalData();

  // ✅ Sanitize Unicode line/paragraph separators
  const sanitized = content
    .replace(/\u2028/g, '\\u2028')
    .replace(/\u2029/g, '\\u2029');

  return {
    content: [{ type: "text", text: sanitized }]
  };
});
```

**When This Matters**: External data sources (APIs, web scraping, user input) that may contain these characters

**Related**: [MCP Python SDK Issue #1356](https://github.com/modelcontextprotocol/python-sdk/issues/1356)

---

## Official Documentation

- **Agent SDK Overview**: https://platform.claude.com/docs/en/api/agent-sdk/overview
- **TypeScript API**: https://platform.claude.com/docs/en/api/agent-sdk/typescript
- **Structured Outputs**: https://platform.claude.com/docs/en/agent-sdk/structured-outputs
- **GitHub (TypeScript)**: https://github.com/anthropics/claude-agent-sdk-typescript
- **CHANGELOG**: https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md

---

**Token Efficiency**:
- **Without skill**: ~15,000 tokens (MCP setup, permission patterns, session APIs, sandbox config, hooks, structured outputs, error handling)
- **With skill**: ~4,500 tokens (comprehensive v0.2.12 coverage + error prevention + advanced patterns)
- **Savings**: ~70% (~10,500 tokens)

**Errors prevented**: 14 documented issues with exact solutions (including 2 community-sourced gotchas)
**Key value**: V2 Session APIs, Sandbox Settings, File Checkpointing, Query methods, AskUserQuestion tool, structured outputs (v0.1.45+), session forking, canUseTool patterns, complete hooks system (12 events), Zod v4 support, subagent cleanup patterns

---

**Last verified**: 2026-01-20 | **Skill version**: 3.1.0 | **Changes**: Added Issue #13 (MCP type field), Issue #14 (Unicode U+2028/U+2029), expanded Issue #4 (session-breaking), added subagent cleanup warning with Stop hook pattern

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY