commit 18c5d51e47f2cda23d48ddda419e2c8e115302e2 Author: Zhongwei Li Date: Sun Nov 30 08:23:58 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..42af21a --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "claude-agent-sdk", + "description": "Build autonomous AI agents with Claude Agent SDK. Create multi-step reasoning workflows, orchestrate subagents, integrate MCP servers. Use when: building coding agents, SRE systems, security auditors, code review bots, or troubleshooting CLI not found, context length exceeded errors.", + "version": "1.0.0", + "author": { + "name": "Jeremy Dawes", + "email": "jeremy@jezweb.net" + }, + "skills": [ + "./" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4018bb3 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# claude-agent-sdk + +Build autonomous AI agents with Claude Agent SDK. Create multi-step reasoning workflows, orchestrate subagents, integrate MCP servers. Use when: building coding agents, SRE systems, security auditors, code review bots, or troubleshooting CLI not found, context length exceeded errors. diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..2d3751f --- /dev/null +++ b/SKILL.md @@ -0,0 +1,595 @@ +--- +name: claude-agent-sdk +description: | + Build autonomous AI agents with Claude Agent SDK. Structured outputs (v0.1.45, Nov 2025) guarantee JSON schema validation, plugins system, hooks for event-driven workflows. Use when: building coding agents with validated JSON responses, SRE systems, security auditors, or troubleshooting CLI not found, structured output validation, session forking errors. +license: MIT +metadata: + version: 2.0.0 + last_verified: 2025-11-22 + package_version: 0.1.50 + token_savings: ~70% + errors_prevented: 12 + breaking_changes: v0.1.45 - Structured outputs (Nov 2025), v0.1.0 - No default system prompt, settingSources required + keywords: + - claude agent sdk + - "@anthropic-ai/claude-agent-sdk" + - structured outputs + - json schema validation + - outputFormat + - query() + - createSdkMcpServer + - AgentDefinition + - tool() + - claude subagents + - mcp servers + - autonomous agents + - plugins system + - hooks system + - session management + - session forking + - permissionMode + - canUseTool + - multi-agent orchestration + - settingSources + - CLI not found + - context length exceeded + - structured output validation + - zod schema +--- + +# Claude Agent SDK - Structured Outputs & Error Prevention Guide + +**Package**: @anthropic-ai/claude-agent-sdk@0.1.50 (Nov 21, 2025) +**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"; + +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}`); + } +} +``` + +### 2. Plugins System (v0.1.27) +- **`plugins` array** - Load local plugin paths +- **Custom plugin support** - Extend agent capabilities + +### 3. Hooks System (v0.1.0+) +- **Event-driven callbacks** - PreToolUse, PostToolUse, Notification, UserPromptSubmit +- **Session event hooks** - Monitor and control agent behavior + +### 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. [Filesystem Settings](#filesystem-settings) +8. [Message Types & Streaming](#message-types--streaming) +9. [Error Handling](#error-handling) +10. [Known Issues](#known-issues-prevention) + +--- + +## Core Query API + +**Key signature:** +```typescript +query(prompt: string | AsyncIterable, options?: Options) + -> AsyncGenerator +``` + +**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' + +--- + +## 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 + +--- + +## 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____` + +**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) +} +``` + +**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`) + +**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" + } +} +``` + +--- + +## 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 + } +} +``` + +--- + +## 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; + +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" }; +} +``` + +--- + +## 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. + +--- + +## 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 **12** 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 +**Error**: `"Prompt too long"` +**Source**: Input exceeds model context window +**Why It Happens**: Large codebase, long conversations +**Prevention**: SDK auto-compacts, but reduce context if needed + +### 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 + +--- + +## 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**: ~12,000 tokens (MCP setup, permission patterns, session forking, structured outputs, error handling) +- **With skill**: ~3,600 tokens (focused on v0.1.45+ features + error prevention + advanced patterns) +- **Savings**: ~70% (~8,400 tokens) + +**Errors prevented**: 12 documented issues with exact solutions +**Key value**: Structured outputs (v0.1.45+), session forking, canUseTool patterns, settingSources priority, MCP naming, error codes + +--- + +**Last verified**: 2025-11-22 | **Skill version**: 2.0.0 | **Changes**: Added v0.1.45 structured outputs, plugins, hooks, new options. Removed tutorial/basic examples (~750 lines). Focused on knowledge gaps + error prevention + advanced patterns. diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..a293b2a --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,117 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:jezweb/claude-skills:skills/claude-agent-sdk", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "315199601dfcaed916a0e54f31a1033dda9083c3", + "treeHash": "9fccde519ca19cf105fbc6fad209544bb0c4fc39bab30eefd28c2eeb34a2428a", + "generatedAt": "2025-11-28T10:19:02.433038Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "claude-agent-sdk", + "description": "Build autonomous AI agents with Claude Agent SDK. Create multi-step reasoning workflows, orchestrate subagents, integrate MCP servers. Use when: building coding agents, SRE systems, security auditors, code review bots, or troubleshooting CLI not found, context length exceeded errors.", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "4bb48906321c8bad9a94c37402fba317253a456069a9676db896af5a4c4ea9f8" + }, + { + "path": "SKILL.md", + "sha256": "7c32c7ba4c1ad33e7cfe376c91583b1d736ac0e178dff83218c7c86fb33a7def" + }, + { + "path": "references/permissions-guide.md", + "sha256": "5589e3eb8735bece774525ad1920d3872817c2220762ffbaaf9a323d301931d7" + }, + { + "path": "references/subagents-patterns.md", + "sha256": "02da9234550f631981909e2215b6b75a2f143ce1a23f7900aca46b05d8105614" + }, + { + "path": "references/mcp-servers-guide.md", + "sha256": "de51d6b2de83f28285c4ce9e026aaa82596d387635605b2d940fd3e01903fd74" + }, + { + "path": "references/top-errors.md", + "sha256": "9851933034a5e6d7aabec4efcfec1376c13f4005779427acf143c7885c1e2ef1" + }, + { + "path": "references/query-api-reference.md", + "sha256": "7d4fc6ecb314c35459b761b9d8dee4ed6400f88ed14a89b6fa07da3e647e8789" + }, + { + "path": "references/session-management.md", + "sha256": "03aab36582a282c0287afa54e5d3b099b641ee7661c89565eb2342461a2d8c8e" + }, + { + "path": "scripts/check-versions.sh", + "sha256": "aef12c3666dcf48dbd2ab9ab48ed0e6b3421f07c9b0b1ba8c8f86cce854b4333" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "2f4f11c881066e9935507f609cdd111d3ea4e4345a8ef1712c0d63ef0b2448f3" + }, + { + "path": "templates/query-with-tools.ts", + "sha256": "f8b66826c6215495424d9bf531df8849d6f212337b8f87cfd426e45385a6a29a" + }, + { + "path": "templates/custom-mcp-server.ts", + "sha256": "d0278f889dc636c29d93c5eee276e8df4b767040d5638fec0a749041f6ba9be4" + }, + { + "path": "templates/filesystem-settings.ts", + "sha256": "978c605143ed6a21634e158db942ef058c77aa74d4888fc29369d97158599dce" + }, + { + "path": "templates/subagents-orchestration.ts", + "sha256": "5fd7ba8aa4b8dc0c55751b42d93ba2683d97c24dfe342a3c979d88e256c168ba" + }, + { + "path": "templates/error-handling.ts", + "sha256": "96e200d822d102291001c81988d7067c9617ad62dca4b69c784df3b0fa00a4e3" + }, + { + "path": "templates/package.json", + "sha256": "f0f65eee92f660595f041990aef6f87399a66f31534cd84b87bcd7e353056f0b" + }, + { + "path": "templates/session-management.ts", + "sha256": "a7694ad86949e55eff94ef0497fd57cade9df0e6c611eae4f05831590c7b73ac" + }, + { + "path": "templates/basic-query.ts", + "sha256": "01bc97a086e10a3e154f8fb0f1b973c6b5ab06abe11267332120d278115a4f67" + }, + { + "path": "templates/tsconfig.json", + "sha256": "ec2fcaa2f83c2321ae55023ef9c3b4deba02f391f77edd51f1427c31500af999" + }, + { + "path": "templates/permission-control.ts", + "sha256": "7980eac42cee3af86354465963913b7ee35bd34cbc50b24d4ef9dba952905813" + }, + { + "path": "templates/multi-agent-workflow.ts", + "sha256": "ca43932014e8cad916aeff24ea1b01a0f18b2ba9757a5a19fe7a7a0b64f8a85f" + } + ], + "dirSha256": "9fccde519ca19cf105fbc6fad209544bb0c4fc39bab30eefd28c2eeb34a2428a" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/references/mcp-servers-guide.md b/references/mcp-servers-guide.md new file mode 100644 index 0000000..cc89e57 --- /dev/null +++ b/references/mcp-servers-guide.md @@ -0,0 +1,387 @@ +# MCP Servers Guide + +Complete guide to creating and using Model Context Protocol (MCP) servers with Claude Agent SDK. + +--- + +## What Are MCP Servers? + +MCP servers extend agent capabilities with custom tools. Think of them as plugins that give your agent new abilities. + +**Use Cases**: +- Database access +- API integrations +- Custom calculations +- External service interactions +- File system operations + +--- + +## Creating In-Process MCP Servers + +### Basic Server + +```typescript +import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk"; +import { z } from "zod"; + +const myServer = createSdkMcpServer({ + name: "my-service", + version: "1.0.0", + tools: [ + tool( + "tool_name", + "Tool description", + { /* Zod schema */ }, + async (args) => { + // Implementation + return { + content: [{ type: "text", text: "Result" }] + }; + } + ) + ] +}); +``` + +### Tool Definition Pattern + +```typescript +tool( + name: string, // Tool identifier + description: string, // What it does + inputSchema: ZodSchema, // Input validation + handler: Handler // Implementation +) +``` + +--- + +## Zod Schemas + +### Common Patterns + +```typescript +import { z } from "zod"; + +// String +z.string() +z.string().email() +z.string().url() +z.string().min(5).max(100) +z.string().describe("Description for AI") + +// Number +z.number() +z.number().int() +z.number().positive() +z.number().min(0).max(100) + +// Boolean +z.boolean() +z.boolean().default(false) + +// Enum +z.enum(["option1", "option2", "option3"]) +z.union([z.literal("a"), z.literal("b")]) + +// Optional +z.string().optional() +z.number().default(10) + +// Object +z.object({ + name: z.string(), + age: z.number(), + email: z.string().email().optional() +}) + +// Array +z.array(z.string()) +z.array(z.number()).min(1).max(10) + +// Complex nested +z.object({ + user: z.object({ + id: z.string().uuid(), + name: z.string(), + roles: z.array(z.enum(["admin", "user", "guest"])) + }), + metadata: z.record(z.any()).optional() +}) +``` + +### Best Practices + +```typescript +// āœ… Good: Clear descriptions +{ + location: z.string().describe("City name or coordinates (e.g., 'San Francisco, CA')"), + radius: z.number().min(1).max(100).describe("Search radius in kilometers") +} + +// āŒ Bad: No descriptions +{ + location: z.string(), + radius: z.number() +} + +// āœ… Good: Validation constraints +{ + email: z.string().email(), + age: z.number().int().min(0).max(120), + role: z.enum(["admin", "user", "guest"]) +} + +// āŒ Bad: No validation +{ + email: z.string(), + age: z.number(), + role: z.string() +} +``` + +--- + +## Handler Implementation + +### Success Response + +```typescript +async (args) => { + const result = await performOperation(args); + return { + content: [{ + type: "text", + text: JSON.stringify(result, null, 2) + }] + }; +} +``` + +### Error Response + +```typescript +async (args) => { + try { + const result = await riskyOperation(args); + return { + content: [{ type: "text", text: result }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error: ${error.message}` + }], + isError: true // Mark as error + }; + } +} +``` + +--- + +## Complete Examples + +### Weather Service + +```typescript +const weatherServer = createSdkMcpServer({ + name: "weather", + version: "1.0.0", + tools: [ + tool( + "get_weather", + "Get current weather for a location", + { + location: z.string().describe("City name"), + units: z.enum(["celsius", "fahrenheit"]).default("celsius") + }, + async (args) => { + const response = await fetch( + `https://api.weather.com/v1/current?location=${args.location}&units=${args.units}` + ); + const data = await response.json(); + return { + content: [{ + type: "text", + text: `Temp: ${data.temp}° ${args.units}\nConditions: ${data.conditions}` + }] + }; + } + ), + tool( + "get_forecast", + "Get 7-day forecast", + { + location: z.string(), + days: z.number().min(1).max(7).default(7) + }, + async (args) => { + const forecast = await fetchForecast(args.location, args.days); + return { + content: [{ type: "text", text: JSON.stringify(forecast, null, 2) }] + }; + } + ) + ] +}); +``` + +### Database Service + +```typescript +const databaseServer = createSdkMcpServer({ + name: "database", + version: "1.0.0", + tools: [ + tool( + "query", + "Execute SQL query", + { + sql: z.string().describe("SQL query to execute"), + params: z.array(z.any()).optional().describe("Query parameters") + }, + async (args) => { + try { + const results = await db.query(args.sql, args.params); + return { + content: [{ type: "text", text: JSON.stringify(results, null, 2) }] + }; + } catch (error) { + return { + content: [{ type: "text", text: `SQL Error: ${error.message}` }], + isError: true + }; + } + } + ) + ] +}); +``` + +--- + +## Using MCP Servers + +### In Query Options + +```typescript +const response = query({ + prompt: "What's the weather in NYC?", + options: { + mcpServers: { + "weather": weatherServer, + "database": databaseServer + }, + allowedTools: [ + "mcp__weather__get_weather", + "mcp__database__query" + ] + } +}); +``` + +### Tool Naming Convention + +**Format**: `mcp____` + +Examples: +- `mcp__weather__get_weather` +- `mcp__database__query` +- `mcp__filesystem__read_file` + +**CRITICAL**: Server and tool names must match exactly. + +--- + +## External MCP Servers + +### Stdio Servers + +```typescript +options: { + mcpServers: { + "filesystem": { + command: "npx", + args: ["@modelcontextprotocol/server-filesystem"], + env: { + ALLOWED_PATHS: "/path/to/allowed/dir" + } + } + } +} +``` + +### HTTP/SSE Servers + +```typescript +options: { + mcpServers: { + "remote": { + url: "https://api.example.com/mcp", + headers: { + "Authorization": "Bearer token", + "Content-Type": "application/json" + } + } + } +} +``` + +--- + +## Best Practices + +### āœ… Do + +- Use clear tool names and descriptions +- Add `.describe()` to all Zod fields +- Implement error handling in handlers +- Validate inputs with Zod constraints +- Return clear, formatted responses +- Test tools independently before integration +- Use unique tool names across servers +- Version your servers + +### āŒ Don't + +- Use generic names like "process" or "run" +- Skip input validation +- Return raw error objects +- Forget `isError: true` on errors +- Use duplicate tool names +- Expose sensitive operations without checks +- Skip testing in isolation + +--- + +## Troubleshooting + +### Tool Not Found + +**Problem**: `"Tool mcp__server__tool not found"` + +**Solution**: +1. Check server name matches +2. Check tool name matches +3. Include in `allowedTools` array +4. Verify server added to `mcpServers` + +### Tool Name Collision + +**Problem**: Two tools with same name + +**Solution**: Use unique names or prefix with server name + +### Validation Errors + +**Problem**: Invalid input to tool + +**Solution**: Add descriptive Zod schemas with constraints + +--- + +**For more details**: See SKILL.md +**Official MCP docs**: https://modelcontextprotocol.io/ diff --git a/references/permissions-guide.md b/references/permissions-guide.md new file mode 100644 index 0000000..774df7b --- /dev/null +++ b/references/permissions-guide.md @@ -0,0 +1,429 @@ +# Permissions Guide + +Complete guide to permission control in Claude Agent SDK. + +--- + +## Permission Modes + +### Overview + +Three built-in modes: + +| Mode | Behavior | Use Case | +|------|----------|----------| +| `default` | Standard checks | General use, production | +| `acceptEdits` | Auto-approve file edits | Trusted refactoring | +| `bypassPermissions` | Skip ALL checks | CI/CD, sandboxed envs | + +--- + +## Default Mode + +Standard permission checks. + +```typescript +options: { + permissionMode: "default" +} +``` + +**Prompts user for**: +- File writes/edits +- Potentially dangerous bash commands +- Sensitive operations + +**Auto-allows**: +- Read operations (Read, Grep, Glob) +- Safe bash commands + +--- + +## Accept Edits Mode + +Automatically approves file modifications. + +```typescript +options: { + permissionMode: "acceptEdits" +} +``` + +**Auto-approves**: +- File edits (Edit) +- File writes (Write) + +**Still prompts for**: +- Dangerous bash commands +- Sensitive operations + +**Use when**: Refactoring, code generation workflows + +--- + +## Bypass Permissions Mode + +āš ļø **DANGER**: Skips ALL permission checks. + +```typescript +options: { + permissionMode: "bypassPermissions" +} +``` + +**Auto-approves EVERYTHING**: +- All file operations +- All bash commands +- All tools + +**ONLY use in**: +- CI/CD pipelines +- Sandboxed containers +- Docker environments +- Trusted, isolated contexts + +**NEVER use in**: +- Production systems +- User-facing environments +- Untrusted inputs + +--- + +## Custom Permission Logic + +### canUseTool Callback + +```typescript +type CanUseTool = ( + toolName: string, + input: any +) => Promise; + +type PermissionDecision = + | { behavior: "allow" } + | { behavior: "deny"; message?: string } + | { behavior: "ask"; message?: string }; +``` + +### Basic Example + +```typescript +options: { + canUseTool: async (toolName, input) => { + // Allow read-only + if (['Read', 'Grep', 'Glob'].includes(toolName)) { + return { behavior: "allow" }; + } + + // Deny dangerous bash + if (toolName === 'Bash' && input.command.includes('rm -rf')) { + return { + behavior: "deny", + message: "Destructive command blocked" + }; + } + + // Ask for confirmation + if (toolName === 'Write') { + return { + behavior: "ask", + message: `Create ${input.file_path}?` + }; + } + + return { behavior: "allow" }; + } +} +``` + +--- + +## Common Patterns + +### Pattern 1: Block Destructive Commands + +```typescript +canUseTool: async (toolName, input) => { + if (toolName === 'Bash') { + const dangerous = [ + 'rm -rf', + 'dd if=', + 'mkfs', + '> /dev/', + 'shutdown', + 'reboot', + 'kill -9', + 'pkill' + ]; + + for (const pattern of dangerous) { + if (input.command.includes(pattern)) { + return { + behavior: "deny", + message: `Blocked dangerous command: ${pattern}` + }; + } + } + } + + return { behavior: "allow" }; +} +``` + +### Pattern 2: Protect Sensitive Files + +```typescript +canUseTool: async (toolName, input) => { + if (toolName === 'Write' || toolName === 'Edit') { + const sensitivePaths = [ + '/etc/', + '/root/', + '.env', + 'credentials', + 'secrets', + 'config/production', + '.ssh/', + 'private_key' + ]; + + for (const path of sensitivePaths) { + if (input.file_path?.includes(path)) { + return { + behavior: "ask", + message: `āš ļø Modify sensitive file: ${input.file_path}?` + }; + } + } + } + + return { behavior: "allow" }; +} +``` + +### Pattern 3: Environment-Based Permissions + +```typescript +const environment = process.env.NODE_ENV; // 'development' | 'staging' | 'production' + +canUseTool: async (toolName, input) => { + // Production: require approval for everything + if (environment === 'production') { + if (toolName === 'Bash' || toolName === 'Write' || toolName === 'Edit') { + return { + behavior: "ask", + message: `PRODUCTION: Approve ${toolName}?` + }; + } + } + + // Staging: auto-approve edits + if (environment === 'staging') { + if (toolName === 'Write' || toolName === 'Edit') { + return { behavior: "allow" }; + } + } + + // Development: allow most things + if (environment === 'development') { + return { behavior: "allow" }; + } + + return { behavior: "allow" }; +} +``` + +### Pattern 4: Deployment Confirmation + +```typescript +canUseTool: async (toolName, input) => { + if (toolName === 'Bash') { + const deploymentPatterns = [ + 'deploy', + 'kubectl apply', + 'terraform apply', + 'helm install', + 'docker push', + 'npm publish' + ]; + + for (const pattern of deploymentPatterns) { + if (input.command.includes(pattern)) { + return { + behavior: "ask", + message: `šŸš€ DEPLOYMENT: ${input.command}\n\nProceed?` + }; + } + } + } + + return { behavior: "allow" }; +} +``` + +### Pattern 5: Audit Logging + +```typescript +const auditLog: Array<{ + tool: string; + input: any; + decision: string; + timestamp: Date; +}> = []; + +canUseTool: async (toolName, input) => { + // Log everything + console.log(`[${new Date().toISOString()}] ${toolName}`); + + const decision = { behavior: "allow" as const }; + + // Store audit log + auditLog.push({ + tool: toolName, + input, + decision: decision.behavior, + timestamp: new Date() + }); + + // Could also send to external service + // await logToDatadog(toolName, input, decision); + + return decision; +} +``` + +--- + +## Combining Modes with Custom Logic + +```typescript +options: { + permissionMode: "acceptEdits", // Auto-approve file edits + canUseTool: async (toolName, input) => { + // But still block dangerous bash + if (toolName === 'Bash' && input.command.includes('rm -rf')) { + return { behavior: "deny", message: "Blocked" }; + } + + // Custom logic runs AFTER permission mode + return { behavior: "allow" }; + } +} +``` + +--- + +## Security Best Practices + +### āœ… Do + +- Start with `"default"` mode +- Use `canUseTool` for fine-grained control +- Block known dangerous patterns +- Require confirmation for sensitive ops +- Log all tool usage for auditing +- Test permission logic thoroughly +- Use environment-based rules +- Implement rate limiting if needed + +### āŒ Don't + +- Use `"bypassPermissions"` in production +- Skip permission checks for "trusted" inputs +- Allow arbitrary bash without filtering +- Trust file paths without validation +- Ignore audit logging +- Assume AI won't make mistakes +- Give blanket approvals + +--- + +## Testing Permissions + +```typescript +async function testPermissions() { + const tests = [ + { tool: "Read", input: { file_path: "/etc/passwd" }, expectAllow: true }, + { tool: "Bash", input: { command: "rm -rf /" }, expectDeny: true }, + { tool: "Write", input: { file_path: ".env" }, expectAsk: true } + ]; + + for (const test of tests) { + const decision = await canUseTool(test.tool, test.input); + + if (test.expectAllow && decision.behavior !== 'allow') { + console.error(`FAIL: Expected allow for ${test.tool}`); + } + if (test.expectDeny && decision.behavior !== 'deny') { + console.error(`FAIL: Expected deny for ${test.tool}`); + } + if (test.expectAsk && decision.behavior !== 'ask') { + console.error(`FAIL: Expected ask for ${test.tool}`); + } + } +} +``` + +--- + +## Common Scenarios + +### Allow Read-Only + +```typescript +canUseTool: async (toolName, input) => { + if (['Read', 'Grep', 'Glob'].includes(toolName)) { + return { behavior: "allow" }; + } + return { + behavior: "deny", + message: "Read-only mode" + }; +} +``` + +### Require Approval for All + +```typescript +canUseTool: async (toolName, input) => { + return { + behavior: "ask", + message: `Approve ${toolName}?` + }; +} +``` + +### Block Bash Entirely + +```typescript +canUseTool: async (toolName, input) => { + if (toolName === 'Bash') { + return { + behavior: "deny", + message: "Bash execution disabled" + }; + } + return { behavior: "allow" }; +} +``` + +--- + +## Error Handling + +Handle permission errors gracefully: + +```typescript +for await (const message of response) { + if (message.type === 'error') { + if (message.error.type === 'permission_denied') { + console.log('Permission denied for:', message.error.tool); + // Continue with fallback or skip + } + } +} +``` + +--- + +**For more details**: See SKILL.md +**Template**: templates/permission-control.ts diff --git a/references/query-api-reference.md b/references/query-api-reference.md new file mode 100644 index 0000000..bdbe5cd --- /dev/null +++ b/references/query-api-reference.md @@ -0,0 +1,437 @@ +# Query API Reference + +Complete reference for the `query()` function - the primary interface for Claude Agent SDK. + +--- + +## Function Signature + +```typescript +function query(config: { + prompt: string | AsyncIterable; + options?: Options; +}): AsyncGenerator; +``` + +--- + +## Parameters + +### prompt + +**Type**: `string | AsyncIterable` +**Required**: Yes + +The task or question for the agent. + +```typescript +// Simple string prompt +query({ prompt: "Analyze the codebase" }) + +// Streaming prompt (advanced) +query({ prompt: streamingUserMessages() }) +``` + +### options + +**Type**: `Options` +**Required**: No + +Configuration options for the query. + +--- + +## Options Reference + +### model + +**Type**: `"sonnet" | "haiku" | "opus" | "claude-sonnet-4-5" | "inherit"` +**Default**: `"sonnet"` + +Model to use for the agent. + +```typescript +options: { + model: "claude-sonnet-4-5" // Specific version + model: "haiku" // Fast + model: "opus" // Maximum capability + model: "inherit" // Use parent model (subagents) +} +``` + +### workingDirectory + +**Type**: `string` +**Default**: Current working directory + +Directory where agent operates. + +```typescript +options: { + workingDirectory: "/path/to/project" +} +``` + +### systemPrompt + +**Type**: `string | { type: 'preset', preset: 'claude_code' }` +**Default**: None + +System prompt that defines agent behavior. + +```typescript +// Custom prompt +options: { + systemPrompt: "You are a security-focused code reviewer." +} + +// Use CLAUDE.md from project +options: { + systemPrompt: { type: 'preset', preset: 'claude_code' }, + settingSources: ["project"] // Required to load CLAUDE.md +} +``` + +### allowedTools + +**Type**: `string[]` +**Default**: All tools + +Whitelist of tools agent can use. + +```typescript +options: { + allowedTools: ["Read", "Grep", "Glob"] // Read-only +} +``` + +### disallowedTools + +**Type**: `string[]` +**Default**: None + +Blacklist of tools agent cannot use. + +```typescript +options: { + disallowedTools: ["Bash", "Write", "Edit"] // No modifications +} +``` + +**Note**: If both specified, `allowedTools` wins. + +### permissionMode + +**Type**: `"default" | "acceptEdits" | "bypassPermissions"` +**Default**: `"default"` + +Permission strategy. + +```typescript +options: { + permissionMode: "default" // Standard checks + permissionMode: "acceptEdits" // Auto-approve edits + permissionMode: "bypassPermissions" // Skip all checks (caution!) +} +``` + +### canUseTool + +**Type**: `(toolName: string, input: any) => Promise` +**Default**: None + +Custom permission logic. + +```typescript +options: { + canUseTool: async (toolName, input) => { + if (toolName === 'Bash' && input.command.includes('rm -rf')) { + return { behavior: "deny", message: "Blocked" }; + } + return { behavior: "allow" }; + } +} +``` + +**PermissionDecision**: +- `{ behavior: "allow" }` - Allow execution +- `{ behavior: "deny", message?: string }` - Block execution +- `{ behavior: "ask", message?: string }` - Prompt user + +### agents + +**Type**: `Record` +**Default**: None + +Subagent definitions. + +```typescript +options: { + agents: { + "test-runner": { + description: "Run test suites", + prompt: "You run tests. Fail if any test fails.", + tools: ["Bash", "Read"], + model: "haiku" + } + } +} +``` + +**AgentDefinition**: +- `description` (string, required) - When to use agent +- `prompt` (string, required) - Agent's system prompt +- `tools` (string[], optional) - Allowed tools +- `model` (string, optional) - Model override + +### mcpServers + +**Type**: `Record` +**Default**: None + +MCP server configurations. + +```typescript +options: { + mcpServers: { + "custom-server": customServer, // In-process + "filesystem": { // External (stdio) + command: "npx", + args: ["@modelcontextprotocol/server-filesystem"] + }, + "remote": { // External (HTTP) + url: "https://api.example.com/mcp", + headers: { "Authorization": "Bearer token" } + } + } +} +``` + +### settingSources + +**Type**: `("user" | "project" | "local")[]` +**Default**: `[]` (no filesystem settings) + +Filesystem settings to load. + +```typescript +options: { + settingSources: ["project"] // Project only (CI/CD) + settingSources: ["user", "project", "local"] // All sources + settingSources: [] // Isolated (no files) +} +``` + +**Files**: +- `user` = `~/.claude/settings.json` +- `project` = `.claude/settings.json` +- `local` = `.claude/settings.local.json` + +**Priority**: Programmatic > Local > Project > User + +### resume + +**Type**: `string` +**Default**: None + +Session ID to resume. + +```typescript +options: { + resume: "session-id-here" +} +``` + +### forkSession + +**Type**: `boolean` +**Default**: `false` + +Create new branch from resumed session. + +```typescript +options: { + resume: "session-id-here", + forkSession: true // New branch, original unchanged +} +``` + +--- + +## Return Value + +**Type**: `AsyncGenerator` + +Asynchronous generator yielding messages. + +```typescript +const response = query({ prompt: "..." }); + +for await (const message of response) { + // Process message +} +``` + +--- + +## Message Types + +See full details in SKILL.md. Summary: + +| Type | When | Data | +|------|------|------| +| `system` | Session events | `session_id`, `model`, `tools` | +| `assistant` | Agent response | `content` (string or blocks) | +| `tool_call` | Tool requested | `tool_name`, `input` | +| `tool_result` | Tool completed | `tool_name`, `result` | +| `error` | Error occurred | `error` object | + +--- + +## Usage Patterns + +### Basic Query + +```typescript +const response = query({ + prompt: "Analyze code", + options: { model: "sonnet" } +}); + +for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } +} +``` + +### With Tools + +```typescript +const response = query({ + prompt: "Review and fix bugs", + options: { + model: "sonnet", + allowedTools: ["Read", "Grep", "Edit"] + } +}); +``` + +### With Subagents + +```typescript +const response = query({ + prompt: "Deploy to production", + options: { + agents: { + "tester": { /* ... */ }, + "deployer": { /* ... */ } + } + } +}); +``` + +### With Custom Tools + +```typescript +const response = query({ + prompt: "Get weather and send notification", + options: { + mcpServers: { "weather": weatherServer }, + allowedTools: ["mcp__weather__get_weather"] + } +}); +``` + +### With Session Management + +```typescript +// Start +let session = await startSession("Build API"); + +// Resume +await resumeSession(session, "Add auth"); + +// Fork +await forkSession(session, "Try GraphQL instead"); +``` + +--- + +## Best Practices + +### āœ… Do + +- Set specific `allowedTools` for security +- Use `canUseTool` for fine-grained control +- Implement error handling for all queries +- Capture `session_id` for resuming +- Use `workingDirectory` for clarity +- Test MCP servers independently +- Monitor tool execution with `tool_call` messages + +### āŒ Don't + +- Use `bypassPermissions` in production (unless sandboxed) +- Ignore error messages +- Skip session ID capture if planning to resume +- Allow unrestricted Bash without `canUseTool` +- Load user settings in CI/CD +- Use duplicate tool names + +--- + +## Error Handling + +```typescript +try { + const response = query({ prompt: "..." }); + for await (const message of response) { + if (message.type === 'error') { + console.error('Agent error:', message.error); + } + } +} catch (error) { + if (error.code === 'CLI_NOT_FOUND') { + console.error('Install Claude Code CLI'); + } +} +``` + +--- + +## TypeScript Types + +```typescript +type Options = { + model?: "sonnet" | "haiku" | "opus" | string; + workingDirectory?: string; + systemPrompt?: string | { type: 'preset', preset: 'claude_code' }; + allowedTools?: string[]; + disallowedTools?: string[]; + permissionMode?: "default" | "acceptEdits" | "bypassPermissions"; + canUseTool?: (toolName: string, input: any) => Promise; + agents?: Record; + mcpServers?: Record; + settingSources?: ("user" | "project" | "local")[]; + resume?: string; + forkSession?: boolean; +}; + +type AgentDefinition = { + description: string; + prompt: string; + tools?: string[]; + model?: "sonnet" | "opus" | "haiku" | "inherit"; +}; + +type PermissionDecision = + | { behavior: "allow" } + | { behavior: "deny"; message?: string } + | { behavior: "ask"; message?: string }; +``` + +--- + +**For more details**: See SKILL.md +**Official docs**: https://docs.claude.com/en/api/agent-sdk/typescript diff --git a/references/session-management.md b/references/session-management.md new file mode 100644 index 0000000..f2f5167 --- /dev/null +++ b/references/session-management.md @@ -0,0 +1,419 @@ +# Session Management Guide + +Complete guide to sessions, resuming, and forking in Claude Agent SDK. + +--- + +## What Are Sessions? + +Sessions enable: +- **Persistent conversations** - Resume where you left off +- **Context preservation** - Agent remembers everything +- **Alternative paths** - Fork to explore different approaches + +--- + +## Session Lifecycle + +``` +Start → Capture Session ID → Resume → Resume → ... → End + ↓ + Fork (alternative path) +``` + +--- + +## Starting a Session + +Every `query()` call creates a session. + +```typescript +let sessionId: string | undefined; + +const response = query({ + prompt: "Build a REST API", + options: { model: "sonnet" } +}); + +for await (const message of response) { + if (message.type === 'system' && message.subtype === 'init') { + sessionId = message.session_id; + console.log(`Session started: ${sessionId}`); + } +} +``` + +**CRITICAL**: Capture `session_id` from `system` init message. + +--- + +## Resuming a Session + +Continue a previous conversation. + +```typescript +const resumed = query({ + prompt: "Now add authentication", + options: { + resume: sessionId, // Resume previous session + model: "sonnet" + } +}); +``` + +**What's preserved**: +- All previous messages +- Agent's understanding of context +- Files created/modified +- Decisions made + +**What's NOT preserved**: +- Environment variables +- Tool availability (specify again) +- Permission settings (specify again) + +--- + +## Forking a Session + +Create alternative path without modifying original. + +```typescript +const forked = query({ + prompt: "Actually, make it GraphQL instead", + options: { + resume: sessionId, + forkSession: true, // Creates new branch + model: "sonnet" + } +}); +``` + +**Result**: +- New session created +- Starts from same point as original +- Original session unchanged +- Can compare approaches + +--- + +## Use Case Patterns + +### Pattern 1: Sequential Development + +Step-by-step feature building. + +```typescript +// Step 1: Initial implementation +let session = await startSession("Create user authentication"); + +// Step 2: Add feature +session = await resumeSession(session, "Add OAuth support"); + +// Step 3: Add tests +session = await resumeSession(session, "Write integration tests"); + +// Step 4: Deploy +session = await resumeSession(session, "Deploy to production"); +``` + +### Pattern 2: Exploration & Decision + +Try multiple approaches, choose best. + +```typescript +// Start main conversation +let mainSession = await startSession("Design payment system"); + +// Explore option A +let optionA = await forkSession(mainSession, "Use Stripe"); + +// Explore option B +let optionB = await forkSession(mainSession, "Use PayPal"); + +// Explore option C +let optionC = await forkSession(mainSession, "Use Square"); + +// Choose winner +let chosenSession = optionA; // Decision made +await resumeSession(chosenSession, "Implement chosen approach"); +``` + +### Pattern 3: Multi-User Collaboration + +Multiple developers, independent work. + +```typescript +// Developer A starts work +let sessionA = await startSession("Implement user profile page"); + +// Developer B forks for different feature +let sessionB = await forkSession(sessionA, "Add avatar upload"); + +// Developer C forks for another feature +let sessionC = await forkSession(sessionA, "Implement search"); + +// All can work independently +``` + +### Pattern 4: Error Recovery + +Backup and restore points. + +```typescript +// Save checkpoint before risky operation +let checkpoint = sessionId; + +try { + await resumeSession(checkpoint, "Refactor entire auth system"); +} catch (error) { + console.log("Refactor failed, restoring from checkpoint"); + await forkSession(checkpoint, "Try safer incremental refactor"); +} +``` + +### Pattern 5: A/B Testing + +Test different implementations. + +```typescript +let baseline = await startSession("Implement feature X"); + +// Approach A +let approachA = await forkSession(baseline, "Use algorithm A"); +const metricsA = await measurePerformance(approachA); + +// Approach B +let approachB = await forkSession(baseline, "Use algorithm B"); +const metricsB = await measurePerformance(approachB); + +// Compare and choose +const winner = metricsA.better(metricsB) ? approachA : approachB; +``` + +--- + +## Session Best Practices + +### āœ… Do + +- Always capture `session_id` from init message +- Store session IDs for later use +- Use descriptive prompts when resuming +- Fork for alternative approaches +- Test resuming before deploying +- Consider session lifetime limits + +### āŒ Don't + +- Forget to capture session ID +- Assume sessions last forever +- Resume with completely unrelated prompts +- Fork excessively (creates many branches) +- Rely on sessions for critical state +- Skip testing resume functionality + +--- + +## Helper Functions + +```typescript +async function startSession(prompt: string): Promise { + let sessionId: string | undefined; + + const response = query({ prompt, options: { model: "sonnet" } }); + + for await (const message of response) { + if (message.type === 'system' && message.subtype === 'init') { + sessionId = message.session_id; + } + } + + if (!sessionId) throw new Error('Failed to start session'); + return sessionId; +} + +async function resumeSession( + sessionId: string, + prompt: string +): Promise { + const response = query({ + prompt, + options: { resume: sessionId, model: "sonnet" } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +async function forkSession( + sessionId: string, + prompt: string +): Promise { + let newSessionId: string | undefined; + + const response = query({ + prompt, + options: { + resume: sessionId, + forkSession: true, + model: "sonnet" + } + }); + + for await (const message of response) { + if (message.type === 'system' && message.subtype === 'init') { + newSessionId = message.session_id; + } else if (message.type === 'assistant') { + console.log(message.content); + } + } + + if (!newSessionId) throw new Error('Failed to fork session'); + return newSessionId; +} +``` + +--- + +## Session Storage + +Store sessions for later use: + +```typescript +// In-memory storage +const sessions = new Map(); + +async function saveSession(prompt: string) { + const id = await startSession(prompt); + sessions.set(id, { id, created: new Date() }); + return id; +} + +function getSession(id: string) { + return sessions.get(id); +} + +// Database storage +async function saveSessionToDb(sessionId: string, metadata: any) { + await db.insert('sessions', { + id: sessionId, + created_at: new Date(), + metadata + }); +} +``` + +--- + +## Session Limits + +### Context Window + +Sessions have context window limits (200k tokens for Sonnet). + +**Strategies**: +- SDK auto-compacts context +- Fork to start fresh from a point +- Summarize and start new session + +### Lifetime + +Sessions may expire after inactivity. + +**Strategies**: +- Don't rely on sessions lasting indefinitely +- Store important state separately +- Test resume functionality + +--- + +## Troubleshooting + +### Session Not Found + +**Problem**: `"Invalid session ID"` + +**Causes**: +- Session expired +- Invalid session ID +- Session from different CLI instance + +**Solution**: Start new session + +### Context Preserved Incorrectly + +**Problem**: Agent doesn't remember previous work + +**Causes**: +- Different settings/tools specified +- Context window exceeded +- Fork instead of resume + +**Solution**: Verify using `resume` not `forkSession` + +### Too Many Forks + +**Problem**: Hard to track branches + +**Solution**: Limit forking, clean up unused branches + +--- + +## Complete Example + +```typescript +class SessionManager { + private sessions = new Map(); + + async start(name: string, prompt: string): Promise { + const id = await startSession(prompt); + this.sessions.set(name, id); + console.log(`✨ Started: ${name}`); + return id; + } + + async resume(name: string, prompt: string): Promise { + const id = this.sessions.get(name); + if (!id) throw new Error(`Session ${name} not found`); + console.log(`ā†Ŗļø Resuming: ${name}`); + await resumeSession(id, prompt); + } + + async fork( + fromName: string, + newName: string, + prompt: string + ): Promise { + const fromId = this.sessions.get(fromName); + if (!fromId) throw new Error(`Session ${fromName} not found`); + console.log(`šŸ”€ Forking: ${fromName} → ${newName}`); + const newId = await forkSession(fromId, prompt); + this.sessions.set(newName, newId); + return newId; + } + + list(): string[] { + return Array.from(this.sessions.keys()); + } +} + +// Usage +const manager = new SessionManager(); + +await manager.start("main", "Build a web app"); +await manager.resume("main", "Add authentication"); +await manager.fork("main", "option-a", "Use JWT tokens"); +await manager.fork("main", "option-b", "Use sessions"); + +console.log("Sessions:", manager.list()); +// ["main", "option-a", "option-b"] +``` + +--- + +**For more details**: See SKILL.md +**Template**: templates/session-management.ts diff --git a/references/subagents-patterns.md b/references/subagents-patterns.md new file mode 100644 index 0000000..038a2ce --- /dev/null +++ b/references/subagents-patterns.md @@ -0,0 +1,464 @@ +# Subagents Patterns + +Guide to designing and orchestrating specialized subagents. + +--- + +## What Are Subagents? + +Specialized agents with: +- **Specific expertise** - Focused domain knowledge +- **Custom tools** - Only tools they need +- **Different models** - Match capability to task cost +- **Dedicated prompts** - Tailored instructions + +--- + +## When to Use Subagents + +### āœ… Use Subagents When: + +- Task requires different expertise areas +- Some subtasks need different models (cost optimization) +- Tool access should be restricted per role +- Clear separation of concerns needed +- Multiple steps with specialized knowledge + +### āŒ Don't Use Subagents When: + +- Single straightforward task +- All work can be done by one agent +- Overhead of orchestration > benefit +- Tools/permissions don't vary + +--- + +## AgentDefinition Structure + +```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) +} +``` + +### Field Guidelines + +**description**: +- Clear, action-oriented +- When to invoke this agent +- 1-2 sentences +- Examples: "Run test suites", "Deploy to production" + +**prompt**: +- Agent's role and behavior +- Instructions and constraints +- What to do and what not to do +- Can be detailed (100-500 tokens) + +**tools**: +- If omitted, inherits all tools from main agent +- Use to restrict agent to specific tools +- Examples: `["Read", "Grep"]` for read-only + +**model**: +- `"haiku"` - Fast, cost-effective ($0.25/$1.25 per MTok) +- `"sonnet"` - Balanced ($3/$15 per MTok) +- `"opus"` - Maximum capability ($15/$75 per MTok) +- `"inherit"` - Use main agent's model +- If omitted, inherits main agent's model + +--- + +## Design Patterns + +### Pattern 1: DevOps Pipeline + +```typescript +agents: { + "test-runner": { + description: "Run automated test suites and verify coverage", + prompt: `You run tests. + +Execute: +- Unit tests +- Integration tests +- End-to-end tests + +FAIL if any tests fail. Report clear errors.`, + tools: ["Bash", "Read"], + model: "haiku" // Fast, cost-effective + }, + + "security-checker": { + description: "Security audits and vulnerability scanning", + prompt: `You check security. + +Scan for: +- Exposed secrets +- Dependency vulnerabilities +- Permission issues +- OWASP compliance + +Block deployment if critical issues found.`, + tools: ["Read", "Grep", "Bash"], + model: "sonnet" // Balance for analysis + }, + + "deployer": { + description: "Application deployment and rollbacks", + prompt: `You deploy applications. + +Process: +1. Deploy to staging +2. Verify health checks +3. Deploy to production +4. Create rollback plan + +ALWAYS have rollback ready.`, + tools: ["Bash", "Read"], + model: "sonnet" // Reliable for critical ops + } +} +``` + +### Pattern 2: Code Review Workflow + +```typescript +agents: { + "syntax-checker": { + description: "Check syntax, formatting, and linting", + prompt: `You check syntax and formatting. + +Run: +- ESLint +- Prettier +- TypeScript type checking + +Report all violations clearly.`, + tools: ["Bash", "Read"], + model: "haiku" // Fast checks + }, + + "logic-reviewer": { + description: "Review logic, algorithms, and architecture", + prompt: `You review code logic. + +Check: +- Algorithmic correctness +- Edge cases +- Performance issues +- Design patterns + +Suggest improvements.`, + tools: ["Read", "Grep"], + model: "sonnet" // Complex analysis + }, + + "security-reviewer": { + description: "Review for security vulnerabilities", + prompt: `You review security. + +Check: +- SQL injection +- XSS vulnerabilities +- Authentication bypass +- Data exposure + +FAIL on critical issues.`, + tools: ["Read", "Grep"], + model: "sonnet" // Security expertise + } +} +``` + +### Pattern 3: Content Generation + +```typescript +agents: { + "researcher": { + description: "Research topics and gather information", + prompt: `You research topics. + +Gather: +- Relevant information +- Latest trends +- Best practices +- Examples + +Provide comprehensive research.`, + tools: ["WebSearch", "WebFetch", "Read"], + model: "haiku" // Fast research + }, + + "writer": { + description: "Write content based on research", + prompt: `You write content. + +Write: +- Clear, engaging prose +- Well-structured +- Audience-appropriate +- Fact-based + +Use research provided.`, + tools: ["Write", "Read"], + model: "sonnet" // Quality writing + }, + + "editor": { + description: "Edit and polish content", + prompt: `You edit content. + +Check: +- Grammar and spelling +- Clarity and flow +- Consistency +- Formatting + +Polish to perfection.`, + tools: ["Read", "Edit"], + model: "sonnet" // Quality editing + } +} +``` + +### Pattern 4: Incident Response + +```typescript +agents: { + "incident-detector": { + description: "Detect and triage incidents", + prompt: `You detect incidents. + +Monitor: +- Error rates +- Response times +- System health +- Alerts + +Assess severity and impact.`, + tools: ["Bash", "Read"], + model: "haiku" // Fast detection + }, + + "root-cause-analyzer": { + description: "Analyze root cause of incidents", + prompt: `You analyze root causes. + +Investigate: +- Logs +- Metrics +- Recent changes +- Dependencies + +Identify exact cause.`, + tools: ["Bash", "Read", "Grep"], + model: "sonnet" // Deep analysis + }, + + "fix-implementer": { + description: "Implement fixes for incidents", + prompt: `You implement fixes. + +Fix: +- Apply patches +- Rollback changes +- Update config +- Deploy hotfixes + +Verify fix resolves issue.`, + tools: ["Read", "Edit", "Bash"], + model: "sonnet" // Careful fixes + } +} +``` + +--- + +## Orchestration Strategies + +### Sequential Execution + +Main agent coordinates agents in order: + +``` +test-runner → security-checker → deployer +``` + +**Use when**: Steps must happen in order + +### Parallel Execution + +Main agent delegates to multiple agents at once: + +``` +test-runner +security-checker } in parallel +code-reviewer +``` + +**Use when**: Steps are independent + +### Conditional Execution + +Main agent decides which agents to use based on context: + +``` +if (tests fail) → test-fixer +if (security issue) → security-fixer +if (all pass) → deployer +``` + +**Use when**: Different paths needed + +--- + +## Model Selection Strategy + +### Cost vs Capability + +| Model | Cost (in/out) | Speed | Use For | +|-------|---------------|-------|---------| +| Haiku | $0.25/$1.25 | Fastest | Monitoring, simple checks | +| Sonnet | $3/$15 | Medium | Code review, analysis | +| Opus | $15/$75 | Slowest | Complex reasoning | + +### Optimization Tips + +```typescript +// āœ… Good: Match model to task +agents: { + "monitor": { model: "haiku" }, // Simple checks + "reviewer": { model: "sonnet" }, // Analysis + "architect": { model: "opus" } // Complex design +} + +// āŒ Bad: Opus for everything +agents: { + "monitor": { model: "opus" }, // Wasteful + "reviewer": { model: "opus" }, // Wasteful + "simple-task": { model: "opus" } // Wasteful +} +``` + +--- + +## Tool Restriction Patterns + +### Read-Only Agent + +```typescript +{ + description: "Analyze code without modifications", + tools: ["Read", "Grep", "Glob"], + model: "haiku" +} +``` + +### Write-Only Agent + +```typescript +{ + description: "Generate new files", + tools: ["Write"], + model: "sonnet" +} +``` + +### Modify Agent + +```typescript +{ + description: "Edit existing files", + tools: ["Read", "Edit"], + model: "sonnet" +} +``` + +### Full Access Agent + +```typescript +{ + description: "Complete control", + tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"], + model: "sonnet" +} +``` + +--- + +## Communication Between Agents + +Agents communicate through the main agent: + +``` +Main Agent: "Run tests" + ↓ +Test Runner: "Tests passed" + ↓ +Main Agent: "Deploy to staging" + ↓ +Deployer: "Deployed to staging" + ↓ +Main Agent: "Verify health" + ↓ +Monitor: "Health checks passing" + ↓ +Main Agent: "Deploy to production" +``` + +**No direct agent-to-agent communication**. + +--- + +## Best Practices + +### āœ… Do + +- Give agents clear, specific roles +- Match model to task complexity +- Restrict tools per agent's needs +- Write detailed prompts with constraints +- Use descriptive agent names +- Test agents independently +- Monitor which agents are invoked + +### āŒ Don't + +- Create overlapping responsibilities +- Use Opus for simple tasks +- Give all agents all tools +- Write vague prompts +- Use generic names like "agent1" +- Skip testing in isolation +- Assume agents will coordinate perfectly + +--- + +## Troubleshooting + +### Agent Not Invoked + +**Problem**: Main agent doesn't call subagent + +**Solution**: Improve `description` to be more specific + +### Wrong Agent Invoked + +**Problem**: Main agent calls incorrect subagent + +**Solution**: Make descriptions more distinct + +### Agent Lacks Capability + +**Problem**: Agent can't complete task + +**Solution**: Add required tools or upgrade model + +--- + +**For more details**: See SKILL.md +**Examples**: templates/subagents-orchestration.ts diff --git a/references/top-errors.md b/references/top-errors.md new file mode 100644 index 0000000..eed1455 --- /dev/null +++ b/references/top-errors.md @@ -0,0 +1,503 @@ +# Top Errors & Solutions + +Complete reference for common Claude Agent SDK errors and how to fix them. + +--- + +## Error #1: CLI Not Found + +### Error Message +``` +"Claude Code CLI not installed" +``` + +### Why It Happens +The SDK requires Claude Code CLI to be installed globally, but it's not found in PATH. + +### Solution +```bash +npm install -g @anthropic-ai/claude-code +``` + +Verify installation: +```bash +which claude-code +# Should output: /usr/local/bin/claude-code or similar +``` + +### Prevention +- Install CLI before using SDK +- Add to project setup documentation +- Check CLI availability in CI/CD + +--- + +## Error #2: Authentication Failed + +### Error Message +``` +"Invalid API key" +"Authentication failed" +``` + +### Why It Happens +- ANTHROPIC_API_KEY environment variable not set +- API key is invalid or expired +- API key has wrong format + +### Solution +```bash +# Set API key +export ANTHROPIC_API_KEY="sk-ant-..." + +# Verify it's set +echo $ANTHROPIC_API_KEY +``` + +Get API key: +1. Visit https://console.anthropic.com/ +2. Navigate to API Keys section +3. Create new key +4. Copy and save securely + +### Prevention +- Use environment variables (never hardcode) +- Check key before running +- Add to .env.example template +- Document setup process + +--- + +## Error #3: Permission Denied + +### Error Message +``` +"Tool use blocked" +"Permission denied for tool: Bash" +``` + +### Why It Happens +- Tool not in `allowedTools` array +- `permissionMode` is too restrictive +- Custom `canUseTool` callback denied execution + +### Solution +```typescript +// Add tool to allowedTools +options: { + allowedTools: ["Read", "Write", "Edit", "Bash"] // Add needed tools +} + +// Or use less restrictive permission mode +options: { + permissionMode: "acceptEdits" // Auto-approve edits +} + +// Or check canUseTool logic +options: { + canUseTool: async (toolName, input) => { + console.log("Tool requested:", toolName); // Debug + return { behavior: "allow" }; + } +} +``` + +### Prevention +- Set appropriate `allowedTools` from start +- Test permission logic thoroughly +- Use `permissionMode: "bypassPermissions"` in CI/CD + +--- + +## Error #4: Context Length Exceeded + +### Error Message +``` +"Prompt too long" +"Context length exceeded" +"Too many tokens" +``` + +### Why It Happens +- Input prompt exceeds model's context window (200k tokens) +- Long conversation without pruning +- Large files in context + +### Solution +SDK auto-compacts context, but you can: + +```typescript +// Fork session to start fresh from a point +const forked = query({ + prompt: "Continue with fresh context", + options: { + resume: sessionId, + forkSession: true // Start fresh + } +}); + +// Or reduce context +options: { + allowedTools: ["Read", "Grep"], // Limit tools + systemPrompt: "Keep responses concise." +} +``` + +### Prevention +- Use session forking for long tasks +- Keep prompts focused +- Don't load unnecessary files +- Monitor context usage + +--- + +## Error #5: Tool Execution Timeout + +### Error Message +``` +"Tool did not respond" +"Tool execution timeout" +``` + +### Why It Happens +- Tool takes too long (>5 minutes default) +- Infinite loop in tool implementation +- Network timeout for external tools + +### Solution + +For custom tools: +```typescript +tool("long_task", "Description", schema, async (args) => { + // Add timeout + const timeout = 60000; // 1 minute + const promise = performLongTask(args); + const result = await Promise.race([ + promise, + new Promise((_, reject) => + setTimeout(() => reject(new Error('Timeout')), timeout) + ) + ]); + return { content: [{ type: "text", text: result }] }; +}) +``` + +For bash commands: +```typescript +// Add timeout to bash command +command: "timeout 60s long-running-command" +``` + +### Prevention +- Set reasonable timeouts +- Optimize tool implementations +- Use background jobs for long tasks +- Test tools independently + +--- + +## Error #6: Session Not Found + +### Error Message +``` +"Invalid session ID" +"Session not found" +``` + +### Why It Happens +- Session ID is incorrect +- Session expired (old) +- Session from different CLI instance + +### Solution +```typescript +// Ensure session ID captured correctly +let sessionId: string | undefined; +for await (const message of response) { + if (message.type === 'system' && message.subtype === 'init') { + sessionId = message.session_id; // Capture here + console.log("Session:", sessionId); // Verify + } +} + +// Use correct session ID +query({ + prompt: "...", + options: { resume: sessionId } // Must match exactly +}); +``` + +### Prevention +- Always capture session_id from system init +- Store session IDs reliably +- Don't rely on sessions lasting indefinitely +- Handle session errors gracefully + +--- + +## Error #7: MCP Server Connection Failed + +### Error Message +``` +"Server connection error" +"MCP server not responding" +"Failed to connect to MCP server" +``` + +### Why It Happens +- Server command/URL incorrect +- Server crashed or not running +- Network issues (HTTP/SSE servers) +- Missing dependencies + +### Solution + +For stdio servers: +```typescript +// Verify command works independently +// Test: npx @modelcontextprotocol/server-filesystem +options: { + mcpServers: { + "filesystem": { + command: "npx", // Verify npx is available + args: ["@modelcontextprotocol/server-filesystem"], + env: { + ALLOWED_PATHS: "/path" // Verify path exists + } + } + } +} +``` + +For HTTP servers: +```typescript +// Test URL separately +const testResponse = await fetch("https://api.example.com/mcp"); +console.log(testResponse.status); // Should be 200 +``` + +### Prevention +- Test MCP servers independently before integration +- Verify command/URL works +- Add error handling for server failures +- Use health checks + +--- + +## Error #8: Subagent Definition Error + +### Error Message +``` +"Invalid AgentDefinition" +"Agent configuration error" +``` + +### Why It Happens +- Missing required fields (`description` or `prompt`) +- Invalid `model` value +- Invalid `tools` array + +### Solution +```typescript +agents: { + "my-agent": { + description: "Clear description of when to use", // Required + prompt: "Detailed system prompt", // Required + tools: ["Read", "Write"], // Optional + model: "sonnet" // Optional + } +} +``` + +### Prevention +- Always include `description` and `prompt` +- Use TypeScript types +- Test agent definitions +- Follow examples in templates + +--- + +## Error #9: Settings File Not Found + +### Error Message +``` +"Cannot read settings" +"Settings file not found" +``` + +### Why It Happens +- `settingSources` includes non-existent file +- File path incorrect +- File permissions deny read + +### Solution +```typescript +// Check file exists before loading +import fs from 'fs'; + +const projectSettingsPath = '.claude/settings.json'; +const settingSources = []; + +if (fs.existsSync(projectSettingsPath)) { + settingSources.push('project'); +} + +options: { + settingSources // Only existing files +} +``` + +### Prevention +- Check file exists before including in sources +- Use empty array for isolated execution +- Handle missing files gracefully + +--- + +## Error #10: Tool Name Collision + +### Error Message +``` +"Duplicate tool name" +"Tool already defined" +``` + +### Why It Happens +- Two MCP servers define same tool name +- Tool name conflicts with built-in tool + +### Solution +```typescript +// Use unique tool names +const server1 = createSdkMcpServer({ + name: "service-a", + tools: [ + tool("service_a_process", ...) // Prefix with server name + ] +}); + +const server2 = createSdkMcpServer({ + name: "service-b", + tools: [ + tool("service_b_process", ...) // Different name + ] +}); +``` + +### Prevention +- Use unique tool names +- Prefix tools with server name +- Test integration before deployment + +--- + +## Error #11: Zod Schema Validation Error + +### Error Message +``` +"Invalid tool input" +"Schema validation failed" +``` + +### Why It Happens +- Agent provided data that doesn't match Zod schema +- Schema too restrictive +- Missing `.describe()` on fields + +### Solution +```typescript +// Add descriptive schemas +{ + email: z.string().email().describe("User email address"), + age: z.number().int().min(0).max(120).describe("Age in years"), + role: z.enum(["admin", "user"]).describe("User role") +} + +// Make fields optional if appropriate +{ + email: z.string().email(), + phoneOptional: z.string().optional() // Not required +} +``` + +### Prevention +- Use `.describe()` on all fields +- Add validation constraints +- Test with various inputs +- Make optional fields explicit + +--- + +## Error #12: Filesystem Permission Denied + +### Error Message +``` +"Access denied" +"Cannot access path" +"EACCES: permission denied" +``` + +### Why It Happens +- Path outside `workingDirectory` +- No read/write permissions +- Protected system directory + +### Solution +```typescript +// Set correct working directory +options: { + workingDirectory: "/path/to/accessible/dir" +} + +// Or fix permissions +// chmod +r file.txt (add read) +// chmod +w file.txt (add write) +``` + +### Prevention +- Set appropriate `workingDirectory` +- Verify file permissions +- Don't access system directories +- Use dedicated project directories + +--- + +## General Error Handling Pattern + +```typescript +try { + const response = query({ prompt: "...", options: { ... } }); + + for await (const message of response) { + if (message.type === 'error') { + console.error('Agent error:', message.error); + // Handle non-fatal errors + } + } +} catch (error) { + console.error('Fatal error:', error); + + // Handle specific errors + switch (error.code) { + case 'CLI_NOT_FOUND': + console.error('Install: npm install -g @anthropic-ai/claude-code'); + break; + case 'AUTHENTICATION_FAILED': + console.error('Check ANTHROPIC_API_KEY'); + break; + case 'RATE_LIMIT_EXCEEDED': + console.error('Rate limited. Retry with backoff.'); + break; + case 'CONTEXT_LENGTH_EXCEEDED': + console.error('Reduce context or fork session'); + break; + default: + console.error('Unexpected error:', error); + } +} +``` + +--- + +**For more details**: See SKILL.md +**Template**: templates/error-handling.ts diff --git a/scripts/check-versions.sh b/scripts/check-versions.sh new file mode 100755 index 0000000..a83fbfa --- /dev/null +++ b/scripts/check-versions.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Check package versions for Claude Agent SDK skill +# Usage: ./scripts/check-versions.sh + +set -e + +echo "šŸ” Checking Claude Agent SDK package versions..." +echo "" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Check if npm is available +if ! command -v npm &> /dev/null; then + echo -e "${RED}āŒ npm not found. Please install Node.js.${NC}" + exit 1 +fi + +# Function to check package version +check_package() { + local package=$1 + local current_version=$2 + + echo -n "Checking $package... " + + # Get latest version from npm + latest_version=$(npm view $package version 2>/dev/null) + + if [ $? -ne 0 ]; then + echo -e "${RED}āŒ Not found in npm registry${NC}" + return 1 + fi + + if [ "$current_version" = "$latest_version" ]; then + echo -e "${GREEN}āœ… Up to date ($current_version)${NC}" + else + echo -e "${YELLOW}āš ļø Update available: $current_version → $latest_version${NC}" + fi +} + +echo "šŸ“¦ Dependencies:" +check_package "@anthropic-ai/claude-agent-sdk" "0.1.0" +check_package "zod" "3.23.0" + +echo "" +echo "šŸ› ļø Dev Dependencies:" +check_package "@types/node" "20.0.0" +check_package "typescript" "5.3.0" + +echo "" +echo "✨ Check complete!" diff --git a/templates/basic-query.ts b/templates/basic-query.ts new file mode 100644 index 0000000..6cfea16 --- /dev/null +++ b/templates/basic-query.ts @@ -0,0 +1,55 @@ +import { query } from "@anthropic-ai/claude-agent-sdk"; + +/** + * Basic Query Template + * + * Demonstrates: + * - Simple query execution + * - Model selection + * - Working directory + * - Basic message handling + */ + +async function basicQuery() { + const response = query({ + prompt: "Analyze the codebase and suggest improvements", + options: { + model: "claude-sonnet-4-5", // or "haiku", "opus" + workingDirectory: process.cwd(), + allowedTools: ["Read", "Grep", "Glob"] + } + }); + + // Process streaming messages + for await (const message of response) { + switch (message.type) { + case 'system': + if (message.subtype === 'init') { + console.log(`Session ID: ${message.session_id}`); + console.log(`Model: ${message.model}`); + } + break; + + case 'assistant': + if (typeof message.content === 'string') { + console.log('Assistant:', message.content); + } + break; + + case 'tool_call': + console.log(`Executing tool: ${message.tool_name}`); + break; + + case 'tool_result': + console.log(`Tool ${message.tool_name} completed`); + break; + + case 'error': + console.error('Error:', message.error.message); + break; + } + } +} + +// Run +basicQuery().catch(console.error); diff --git a/templates/custom-mcp-server.ts b/templates/custom-mcp-server.ts new file mode 100644 index 0000000..00c0db6 --- /dev/null +++ b/templates/custom-mcp-server.ts @@ -0,0 +1,161 @@ +import { query, createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk"; +import { z } from "zod"; + +/** + * Custom MCP Server Template + * + * Demonstrates: + * - Creating in-process MCP server + * - Defining tools with Zod schemas + * - Multiple tools in one server + * - Error handling in tools + */ + +// Define a custom MCP server with multiple tools +const weatherServer = createSdkMcpServer({ + name: "weather-service", + version: "1.0.0", + tools: [ + tool( + "get_weather", + "Get current weather for a location", + { + location: z.string().describe("City name or coordinates"), + units: z.enum(["celsius", "fahrenheit"]).default("celsius") + }, + async (args) => { + try { + // Simulate API call + const response = await fetch( + `https://api.weather.com/v1/current?location=${args.location}&units=${args.units}` + ); + const data = await response.json(); + + return { + content: [{ + type: "text", + text: `Temperature: ${data.temp}° ${args.units}\nConditions: ${data.conditions}\nHumidity: ${data.humidity}%` + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Error fetching weather: ${error.message}` + }], + isError: true + }; + } + } + ), + tool( + "get_forecast", + "Get weather forecast for next 7 days", + { + location: z.string(), + days: z.number().min(1).max(7).default(7) + }, + async (args) => { + // Simulated forecast data + return { + content: [{ + type: "text", + text: `7-day forecast for ${args.location}: Mostly sunny with temperatures ranging from 15-25°C` + }] + }; + } + ) + ] +}); + +// Database tools server +const databaseServer = createSdkMcpServer({ + name: "database", + version: "1.0.0", + tools: [ + tool( + "query_users", + "Query user records from the database", + { + email: z.string().email().optional(), + limit: z.number().min(1).max(100).default(10), + offset: z.number().min(0).default(0) + }, + async (args) => { + // Simulated database query + const results = [ + { id: 1, email: "user1@example.com", name: "User 1" }, + { id: 2, email: "user2@example.com", name: "User 2" } + ]; + + return { + content: [{ + type: "text", + text: JSON.stringify(results, null, 2) + }] + }; + } + ), + tool( + "calculate", + "Perform mathematical calculations", + { + expression: z.string().describe("Mathematical expression to evaluate"), + precision: z.number().min(0).max(10).default(2) + }, + async (args) => { + try { + // In production, use a proper math parser (e.g., mathjs) + const result = eval(args.expression); + const rounded = Number(result.toFixed(args.precision)); + + return { + content: [{ + type: "text", + text: `Result: ${rounded}` + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Invalid expression: ${error.message}` + }], + isError: true + }; + } + } + ) + ] +}); + +// Use custom tools in query +async function useCustomTools() { + const response = query({ + prompt: "What's the weather in San Francisco? Also query users with gmail addresses and calculate 15% tip on $85.50", + options: { + model: "claude-sonnet-4-5", + mcpServers: { + "weather-service": weatherServer, + "database": databaseServer + }, + allowedTools: [ + "mcp__weather-service__get_weather", + "mcp__weather-service__get_forecast", + "mcp__database__query_users", + "mcp__database__calculate" + ] + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log('Assistant:', message.content); + } else if (message.type === 'tool_call') { + console.log(`\nšŸ”§ ${message.tool_name}:`, message.input); + } + } +} + +// Run +useCustomTools().catch(console.error); diff --git a/templates/error-handling.ts b/templates/error-handling.ts new file mode 100644 index 0000000..4f06129 --- /dev/null +++ b/templates/error-handling.ts @@ -0,0 +1,283 @@ +import { query } from "@anthropic-ai/claude-agent-sdk"; + +/** + * Error Handling Template + * + * Demonstrates: + * - SDK error handling + * - Message-level error handling + * - Retry strategies + * - Graceful degradation + */ + +// Example 1: Basic Error Handling +async function basicErrorHandling() { + try { + const response = query({ + prompt: "Analyze and refactor code", + options: { + model: "claude-sonnet-4-5", + workingDirectory: "/path/to/project" + } + }); + + for await (const message of response) { + switch (message.type) { + case 'assistant': + console.log('Assistant:', message.content); + break; + + case 'error': + console.error('Agent error:', message.error.message); + if (message.error.type === 'permission_denied') { + console.log('Permission denied for:', message.error.tool); + // Handle permission errors gracefully + } + break; + } + } + } catch (error) { + console.error('Fatal error:', error); + + // Handle specific error codes + if (error.code === 'CLI_NOT_FOUND') { + console.error('Claude Code CLI not installed'); + console.error('Install: npm install -g @anthropic-ai/claude-code'); + } else if (error.code === 'AUTHENTICATION_FAILED') { + console.error('Invalid API key. Check ANTHROPIC_API_KEY'); + } else if (error.code === 'RATE_LIMIT_EXCEEDED') { + console.error('Rate limit exceeded. Retry after delay.'); + } else if (error.code === 'CONTEXT_LENGTH_EXCEEDED') { + console.error('Context too large. Use session compaction.'); + } + } +} + +// Example 2: Retry with Exponential Backoff +async function retryWithBackoff( + prompt: string, + maxRetries = 3, + baseDelay = 1000 +): Promise { + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + const response = query({ + prompt, + options: { + model: "claude-sonnet-4-5" + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } + + return; // Success, exit + } catch (error) { + if (error.code === 'RATE_LIMIT_EXCEEDED' && attempt < maxRetries - 1) { + const delay = baseDelay * Math.pow(2, attempt); + console.log(`Rate limited. Retrying in ${delay}ms...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } else { + throw error; // Re-throw if not rate limit or final attempt + } + } + } +} + +// Example 3: Graceful Degradation +async function gracefulDegradation(prompt: string) { + // Try with full capabilities first + try { + console.log('Attempting with Sonnet model...'); + const response = query({ + prompt, + options: { + model: "claude-sonnet-4-5", + allowedTools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } + } catch (error) { + console.warn('Sonnet failed, falling back to Haiku...'); + + // Fallback to faster/cheaper model with limited tools + try { + const response = query({ + prompt, + options: { + model: "haiku", + allowedTools: ["Read", "Grep", "Glob"] // Read-only + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } + } catch (fallbackError) { + console.error('All attempts failed:', fallbackError); + throw fallbackError; + } + } +} + +// Example 4: Comprehensive Error Handler +async function comprehensiveErrorHandling() { + const errors: Array<{ type: string; message: string; timestamp: Date }> = []; + + try { + const response = query({ + prompt: "Complex multi-step task", + options: { + model: "claude-sonnet-4-5", + permissionMode: "default" + } + }); + + for await (const message of response) { + switch (message.type) { + case 'assistant': + console.log('āœ… Assistant:', message.content); + break; + + case 'tool_call': + console.log(`šŸ”§ Executing: ${message.tool_name}`); + break; + + case 'tool_result': + console.log(`āœ… ${message.tool_name} completed`); + break; + + case 'error': + console.error('āŒ Error:', message.error.message); + errors.push({ + type: message.error.type, + message: message.error.message, + timestamp: new Date() + }); + + // Handle different error types + if (message.error.type === 'permission_denied') { + console.log('→ Permission was denied, continuing with limited access'); + } else if (message.error.type === 'tool_execution_failed') { + console.log('→ Tool failed, attempting alternative approach'); + } + break; + } + } + } catch (error) { + console.error('šŸ’„ Fatal error:', error); + + // Log error details + errors.push({ + type: error.code || 'UNKNOWN', + message: error.message, + timestamp: new Date() + }); + + // Specific handlers + if (error.code === 'AUTHENTICATION_FAILED') { + console.error('→ Check your ANTHROPIC_API_KEY environment variable'); + console.error('→ Visit https://console.anthropic.com/ for API keys'); + } else if (error.code === 'RATE_LIMIT_EXCEEDED') { + console.error('→ Rate limit exceeded'); + console.error('→ Implement exponential backoff or reduce request frequency'); + } else if (error.code === 'CONTEXT_LENGTH_EXCEEDED') { + console.error('→ Context too large'); + console.error('→ Consider using session management or reducing prompt size'); + } else if (error.code === 'CLI_NOT_FOUND') { + console.error('→ Claude Code CLI not found'); + console.error('→ Install: npm install -g @anthropic-ai/claude-code'); + } + + throw error; + } finally { + // Always log error summary + if (errors.length > 0) { + console.log('\n\n=== Error Summary ==='); + errors.forEach(err => { + console.log(`${err.timestamp.toISOString()} - ${err.type}: ${err.message}`); + }); + } + } +} + +// Example 5: Circuit Breaker Pattern +class CircuitBreaker { + private failures = 0; + private lastFailureTime?: Date; + private readonly threshold = 3; + private readonly resetTimeout = 60000; // 1 minute + + async execute(fn: () => Promise): Promise { + // Check if circuit is open + if (this.isOpen()) { + throw new Error('Circuit breaker is OPEN. Too many failures.'); + } + + try { + await fn(); + this.onSuccess(); + } catch (error) { + this.onFailure(); + throw error; + } + } + + private isOpen(): boolean { + if (this.failures >= this.threshold) { + const now = new Date(); + if (this.lastFailureTime && + now.getTime() - this.lastFailureTime.getTime() < this.resetTimeout) { + return true; + } + // Reset after timeout + this.failures = 0; + } + return false; + } + + private onSuccess() { + this.failures = 0; + this.lastFailureTime = undefined; + } + + private onFailure() { + this.failures++; + this.lastFailureTime = new Date(); + console.warn(`Circuit breaker: ${this.failures}/${this.threshold} failures`); + } +} + +async function useCircuitBreaker() { + const breaker = new CircuitBreaker(); + + try { + await breaker.execute(async () => { + const response = query({ + prompt: "Perform task", + options: { model: "sonnet" } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } + }); + } catch (error) { + console.error('Circuit breaker prevented execution or task failed:', error); + } +} + +// Run +comprehensiveErrorHandling().catch(console.error); diff --git a/templates/filesystem-settings.ts b/templates/filesystem-settings.ts new file mode 100644 index 0000000..d1a27b7 --- /dev/null +++ b/templates/filesystem-settings.ts @@ -0,0 +1,211 @@ +import { query } from "@anthropic-ai/claude-agent-sdk"; + +/** + * Filesystem Settings Template + * + * Demonstrates: + * - Loading settings from user, project, local + * - Settings priority and merging + * - Isolated vs configured execution + * - Loading CLAUDE.md project instructions + */ + +// Example 1: Load All Settings (Legacy Behavior) +async function loadAllSettings() { + const response = query({ + prompt: "Build a new feature following project conventions", + options: { + model: "claude-sonnet-4-5", + settingSources: ["user", "project", "local"] + // Loads: + // 1. ~/.claude/settings.json (user) + // 2. .claude/settings.json (project) + // 3. .claude/settings.local.json (local overrides) + // + // Priority (highest first): + // 1. Programmatic options (this config) + // 2. Local settings + // 3. Project settings + // 4. User settings + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 2: Project Settings Only (CI/CD Pattern) +async function projectSettingsOnly() { + const response = query({ + prompt: "Run automated code review", + options: { + model: "claude-sonnet-4-5", + settingSources: ["project"] + // Only .claude/settings.json + // Ignores user and local settings + // Ensures consistent behavior in CI/CD + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 3: No Filesystem Settings (Fully Isolated) +async function isolatedExecution() { + const response = query({ + prompt: "Analyze this code snippet", + options: { + model: "claude-sonnet-4-5", + settingSources: [], // Empty = no filesystem settings + workingDirectory: "/tmp/sandbox", + allowedTools: ["Read", "Grep", "Glob"], + systemPrompt: "You are a code analyzer." + // Fully isolated, no filesystem dependencies + // Perfect for sandboxed/containerized environments + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 4: Hybrid Approach (Project + Programmatic) +async function hybridConfiguration() { + const response = query({ + prompt: "Implement user authentication system", + options: { + model: "claude-sonnet-4-5", + settingSources: ["project"], // Load CLAUDE.md and project settings + systemPrompt: "Follow security best practices and company coding standards.", + agents: { + "security-checker": { + description: "Security validation specialist", + prompt: "Validate all security implementations against OWASP guidelines.", + tools: ["Read", "Grep"], + model: "sonnet" + } + }, + allowedTools: ["Read", "Write", "Edit", "Grep", "Glob"] + // Project settings + programmatic overrides + // Programmatic settings always win + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 5: Loading CLAUDE.md Project Instructions +async function loadProjectInstructions() { + const response = query({ + prompt: "Implement new feature according to project guidelines", + options: { + model: "claude-sonnet-4-5", + systemPrompt: { + type: 'preset', + preset: 'claude_code' // Required to use CLAUDE.md + }, + settingSources: ["project"], // Reads CLAUDE.md from project directory + workingDirectory: process.cwd() + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 6: Environment-Specific Settings +async function environmentSpecificSettings( + environment: 'development' | 'staging' | 'production' +) { + let settingSources: Array<'user' | 'project' | 'local'>; + let permissionMode: 'default' | 'acceptEdits' | 'bypassPermissions'; + + switch (environment) { + case 'development': + settingSources = ["user", "project", "local"]; + permissionMode = "acceptEdits"; + break; + + case 'staging': + settingSources = ["project"]; + permissionMode = "default"; + break; + + case 'production': + settingSources = ["project"]; + permissionMode = "default"; + break; + } + + const response = query({ + prompt: "Deploy application", + options: { + model: "claude-sonnet-4-5", + settingSources, + permissionMode, + workingDirectory: process.cwd() + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Settings File Examples: + +/** + * ~/.claude/settings.json (User Settings) + * { + * "model": "claude-sonnet-4-5", + * "allowedTools": ["Read", "Write", "Edit", "Bash", "Grep", "Glob"], + * "permissionMode": "default" + * } + */ + +/** + * .claude/settings.json (Project Settings - version controlled) + * { + * "model": "claude-sonnet-4-5", + * "allowedTools": ["Read", "Write", "Edit", "Grep", "Glob"], + * "disallowedTools": ["Bash"], + * "agents": { + * "code-reviewer": { + * "description": "Review code for quality", + * "prompt": "You review code for best practices.", + * "tools": ["Read", "Grep"], + * "model": "haiku" + * } + * } + * } + */ + +/** + * .claude/settings.local.json (Local Overrides - gitignored) + * { + * "permissionMode": "acceptEdits", + * "allowedTools": ["Read", "Write", "Edit", "Bash", "Grep", "Glob"] + * } + */ + +// Run +hybridConfiguration().catch(console.error); diff --git a/templates/multi-agent-workflow.ts b/templates/multi-agent-workflow.ts new file mode 100644 index 0000000..bcc731a --- /dev/null +++ b/templates/multi-agent-workflow.ts @@ -0,0 +1,318 @@ +import { query, createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk"; +import { z } from "zod"; + +/** + * Complete Multi-Agent Workflow Template + * + * Demonstrates: + * - Complex multi-agent orchestration + * - Custom tools for agents + * - Environment-based configuration + * - Production-ready error handling + * - Comprehensive monitoring + */ + +// Custom tools for notification and logging +const appTools = createSdkMcpServer({ + name: "app-services", + version: "1.0.0", + tools: [ + tool( + "send_notification", + "Send notification to users or teams", + { + userId: z.string().optional(), + teamId: z.string().optional(), + message: z.string(), + priority: z.enum(["low", "medium", "high", "critical"]).default("medium") + }, + async (args) => { + // Simulate notification service + console.log(`šŸ“§ Notification (${args.priority}): ${args.message}`); + return { + content: [{ type: "text", text: "Notification sent successfully" }] + }; + } + ), + tool( + "log_event", + "Log application events for monitoring", + { + event: z.string(), + data: z.record(z.any()).optional(), + severity: z.enum(["debug", "info", "warning", "error", "critical"]).default("info") + }, + async (args) => { + console.log(`šŸ“ [${args.severity.toUpperCase()}] ${args.event}:`, args.data || ''); + return { + content: [{ type: "text", text: "Event logged" }] + }; + } + ), + tool( + "check_health", + "Check system health metrics", + { + service: z.string(), + metrics: z.array(z.enum(["cpu", "memory", "disk", "network"])).optional() + }, + async (args) => { + // Simulate health check + const health = { + service: args.service, + status: "healthy", + uptime: "99.9%", + responseTime: "50ms" + }; + return { + content: [{ type: "text", text: JSON.stringify(health, null, 2) }] + }; + } + ) + ] +}); + +// Main DevOps agent orchestrator +async function runDevOpsAgent(task: string) { + console.log(`šŸš€ Starting DevOps Agent for: ${task}\n`); + + const response = query({ + prompt: task, + options: { + model: "claude-sonnet-4-5", + workingDirectory: process.cwd(), + systemPrompt: `You are a DevOps automation expert and orchestrator. + +Your responsibilities: +- Monitor system health +- Deploy applications safely +- Handle incidents and alerts +- Maintain infrastructure +- Coordinate specialized agents + +Always log your actions and notify relevant stakeholders. +Follow the principle of least privilege for tool access.`, + + // Custom tools + mcpServers: { + "app-services": appTools + }, + + // Specialized agents for different tasks + agents: { + "deployment-agent": { + description: "Handles application deployments, rollbacks, and release management", + prompt: `You manage deployments. + +Deployment checklist: +1. Verify all tests pass +2. Deploy to staging first +3. Run smoke tests +4. Deploy to production +5. Create rollback plan +6. Monitor for issues + +Always notify stakeholders of deployment status.`, + tools: [ + "Bash", + "Read", + "mcp__app-services__log_event", + "mcp__app-services__send_notification", + "mcp__app-services__check_health" + ], + model: "sonnet" + }, + + "incident-responder": { + description: "Responds to production incidents, outages, and performance issues", + prompt: `You handle incidents. + +Incident response process: +1. Assess impact (users affected, services down) +2. Identify root cause +3. Implement immediate fixes +4. Communicate status updates +5. Document incident for post-mortem + +Work quickly but carefully. User experience is critical.`, + tools: [ + "Bash", + "Read", + "Grep", + "mcp__app-services__log_event", + "mcp__app-services__send_notification", + "mcp__app-services__check_health" + ], + model: "sonnet" + }, + + "monitoring-agent": { + description: "Monitors system metrics, health checks, and alerts on issues", + prompt: `You monitor systems. + +Monitoring tasks: +- Check application metrics +- Analyze error rates +- Monitor response times +- Verify system health +- Alert on anomalies + +Report issues immediately.`, + tools: [ + "Bash", + "Read", + "mcp__app-services__log_event", + "mcp__app-services__send_notification", + "mcp__app-services__check_health" + ], + model: "haiku" // Fast, cost-effective for monitoring + }, + + "security-agent": { + description: "Performs security audits, vulnerability scanning, and compliance checks", + prompt: `You ensure security. + +Security checks: +- Scan for exposed secrets +- Check dependency vulnerabilities +- Verify access controls +- Validate compliance (OWASP, SOC2) +- Audit infrastructure config + +Block deployments if critical issues found.`, + tools: [ + "Bash", + "Read", + "Grep", + "mcp__app-services__log_event", + "mcp__app-services__send_notification" + ], + model: "sonnet" + }, + + "performance-agent": { + description: "Analyzes performance metrics, identifies bottlenecks, and suggests optimizations", + prompt: `You optimize performance. + +Performance analysis: +- Database query times +- API response times +- Memory/CPU usage +- Network latency +- Caching effectiveness + +Identify bottlenecks and optimization opportunities.`, + tools: [ + "Bash", + "Read", + "Grep", + "mcp__app-services__log_event", + "mcp__app-services__check_health" + ], + model: "sonnet" + } + }, + + // Permission control + permissionMode: "default", + canUseTool: async (toolName, input) => { + // Log all tool usage + console.log(`šŸ”§ [${new Date().toISOString()}] ${toolName}`); + + // Prevent destructive operations + if (toolName === 'Bash') { + const dangerous = ['rm -rf', 'dd if=', 'mkfs', '> /dev/', 'shutdown']; + if (dangerous.some(pattern => input.command.includes(pattern))) { + return { + behavior: "deny", + message: `Destructive command blocked: ${input.command}` + }; + } + } + + // Require confirmation for production deployments + if (input.command?.includes('deploy --production') || + input.command?.includes('kubectl apply -n production')) { + return { + behavior: "ask", + message: `āš ļø PRODUCTION DEPLOYMENT: ${input.command}\n\nConfirm?` + }; + } + + return { behavior: "allow" }; + } + } + }); + + // Track execution + const agentsUsed = new Set(); + const toolsExecuted: string[] = []; + let sessionId: string | undefined; + + try { + for await (const message of response) { + switch (message.type) { + case 'system': + if (message.subtype === 'init') { + sessionId = message.session_id; + console.log(`✨ Session: ${sessionId}\n`); + } + break; + + case 'assistant': + console.log('šŸ“‹ Orchestrator:', message.content); + break; + + case 'tool_call': + console.log(`\nšŸ”§ Executing: ${message.tool_name}`); + toolsExecuted.push(message.tool_name); + break; + + case 'tool_result': + console.log(`āœ… ${message.tool_name} completed`); + break; + + case 'error': + console.error('āŒ Error:', message.error.message); + break; + } + } + + console.log(`\n\nāœ… Task completed successfully`); + console.log(`Session ID: ${sessionId}`); + console.log(`Tools executed: ${toolsExecuted.length}`); + } catch (error) { + console.error('šŸ’„ Fatal error:', error); + throw error; + } +} + +// Usage examples +async function main() { + try { + // Example 1: Deployment + await runDevOpsAgent( + "Deploy version 2.5.0 to production with full validation and monitoring" + ); + + // Example 2: Incident Response + // await runDevOpsAgent( + // "API response time increased by 300% in last hour. Investigate, identify root cause, and fix" + // ); + + // Example 3: Security Audit + // await runDevOpsAgent( + // "Perform comprehensive security audit of the application and infrastructure" + // ); + + // Example 4: Performance Optimization + // await runDevOpsAgent( + // "Analyze application performance and implement optimizations for the checkout flow" + // ); + } catch (error) { + console.error('Workflow failed:', error); + process.exit(1); + } +} + +main(); diff --git a/templates/package.json b/templates/package.json new file mode 100644 index 0000000..9945c62 --- /dev/null +++ b/templates/package.json @@ -0,0 +1,29 @@ +{ + "name": "claude-agent-sdk-examples", + "version": "1.0.0", + "description": "Claude Agent SDK usage examples", + "type": "module", + "scripts": { + "basic-query": "tsx templates/basic-query.ts", + "query-with-tools": "tsx templates/query-with-tools.ts", + "custom-mcp-server": "tsx templates/custom-mcp-server.ts", + "subagents": "tsx templates/subagents-orchestration.ts", + "sessions": "tsx templates/session-management.ts", + "permissions": "tsx templates/permission-control.ts", + "settings": "tsx templates/filesystem-settings.ts", + "errors": "tsx templates/error-handling.ts", + "workflow": "tsx templates/multi-agent-workflow.ts" + }, + "dependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.1.0", + "zod": "^3.23.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsx": "^4.0.0", + "typescript": "^5.3.0" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/templates/permission-control.ts b/templates/permission-control.ts new file mode 100644 index 0000000..a0fe84d --- /dev/null +++ b/templates/permission-control.ts @@ -0,0 +1,211 @@ +import { query } from "@anthropic-ai/claude-agent-sdk"; + +/** + * Permission Control Template + * + * Demonstrates: + * - Permission modes (default, acceptEdits, bypassPermissions) + * - Custom canUseTool callback + * - Safety controls for dangerous operations + * - Conditional tool approval + */ + +// Example 1: Accept Edits Mode (auto-approve file edits) +async function autoApproveEdits() { + const response = query({ + prompt: "Refactor the user service to use async/await throughout", + options: { + model: "claude-sonnet-4-5", + workingDirectory: "/path/to/project", + permissionMode: "acceptEdits" // Auto-approve file edits + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 2: Bypass Permissions (use with caution!) +async function bypassAllPermissions() { + const response = query({ + prompt: "Run comprehensive test suite and fix all failures", + options: { + model: "claude-sonnet-4-5", + permissionMode: "bypassPermissions" + // āš ļø CAUTION: Skips ALL permission checks + // Use only in trusted, sandboxed environments + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 3: Custom Permission Logic +async function customPermissions() { + const response = query({ + prompt: "Deploy the application to production", + options: { + model: "claude-sonnet-4-5", + 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/', + 'shutdown', + 'reboot' + ]; + + if (dangerous.some(pattern => input.command.includes(pattern))) { + return { + behavior: "deny", + message: `Destructive command blocked: ${input.command}` + }; + } + } + + // Require confirmation for deployments + if (input.command?.includes('deploy') || + input.command?.includes('kubectl apply') || + input.command?.includes('terraform apply')) { + return { + behavior: "ask", + message: `Confirm deployment: ${input.command}?` + }; + } + + // Require confirmation for file writes to sensitive paths + if (toolName === 'Write' || toolName === 'Edit') { + const sensitivePaths = [ + '/etc/', + '/root/', + '.env', + 'credentials', + 'secrets', + 'config/production' + ]; + + if (sensitivePaths.some(path => input.file_path?.includes(path))) { + return { + behavior: "ask", + message: `Modify sensitive file ${input.file_path}?` + }; + } + } + + // Allow by default + return { behavior: "allow" }; + } + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 4: Environment-Based Permissions +async function environmentBasedPermissions(environment: 'development' | 'staging' | 'production') { + const response = query({ + prompt: "Deploy the latest changes", + options: { + model: "claude-sonnet-4-5", + permissionMode: "default", + canUseTool: async (toolName, input) => { + // Production requires approval for everything + if (environment === 'production') { + if (toolName === 'Bash' || toolName === 'Write' || toolName === 'Edit') { + return { + behavior: "ask", + message: `PRODUCTION: Approve ${toolName}?` + }; + } + } + + // Staging auto-approves edits + if (environment === 'staging') { + if (toolName === 'Write' || toolName === 'Edit') { + return { behavior: "allow" }; + } + } + + // Development bypasses most checks + if (environment === 'development') { + return { behavior: "allow" }; + } + + return { behavior: "allow" }; + } + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Example 5: Logging & Auditing +async function loggingPermissions() { + const toolLog: Array<{ tool: string; input: any; decision: string; timestamp: Date }> = []; + + const response = query({ + prompt: "Implement new feature X", + options: { + model: "claude-sonnet-4-5", + permissionMode: "default", + canUseTool: async (toolName, input) => { + // Log all tool usage + console.log(`[${new Date().toISOString()}] Tool requested: ${toolName}`); + + const decision = { behavior: "allow" as const }; + + // Audit log + toolLog.push({ + tool: toolName, + input, + decision: decision.behavior, + timestamp: new Date() + }); + + // Could also send to external logging service + // await logToDatabase(toolName, input, decision); + + return decision; + } + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } + + // Print audit log + console.log('\n\n=== Audit Log ==='); + toolLog.forEach(entry => { + console.log(`${entry.timestamp.toISOString()} - ${entry.tool} - ${entry.decision}`); + }); +} + +// Run +customPermissions().catch(console.error); diff --git a/templates/query-with-tools.ts b/templates/query-with-tools.ts new file mode 100644 index 0000000..0c0f074 --- /dev/null +++ b/templates/query-with-tools.ts @@ -0,0 +1,54 @@ +import { query } from "@anthropic-ai/claude-agent-sdk"; + +/** + * Query with Tool Control Template + * + * Demonstrates: + * - Allowing/disallowing tools + * - System prompts + * - Tool execution monitoring + */ + +async function queryWithTools() { + const response = query({ + prompt: "Review the authentication module for security issues and fix any vulnerabilities", + options: { + model: "claude-sonnet-4-5", + workingDirectory: "/path/to/project", + systemPrompt: `You are a security-focused code reviewer. + +Analyze code for: +- SQL injection vulnerabilities +- XSS vulnerabilities +- Authentication bypass issues +- Insecure direct object references +- Security misconfiguration + +Provide detailed recommendations and fix critical issues.`, + + // Allow reading and modification, but not bash execution + allowedTools: ["Read", "Grep", "Glob", "Write", "Edit"], + disallowedTools: ["Bash"] + } + }); + + // Track tool usage + const toolsUsed = new Set(); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log('\nAssistant:', message.content); + } else if (message.type === 'tool_call') { + console.log(`\nšŸ”§ Executing: ${message.tool_name}`); + console.log(`Input:`, JSON.stringify(message.input, null, 2)); + toolsUsed.add(message.tool_name); + } else if (message.type === 'tool_result') { + console.log(`āœ… ${message.tool_name} completed`); + } + } + + console.log(`\n\nTools used: ${Array.from(toolsUsed).join(', ')}`); +} + +// Run +queryWithTools().catch(console.error); diff --git a/templates/session-management.ts b/templates/session-management.ts new file mode 100644 index 0000000..becd7ba --- /dev/null +++ b/templates/session-management.ts @@ -0,0 +1,151 @@ +import { query } from "@anthropic-ai/claude-agent-sdk"; + +/** + * Session Management Template + * + * Demonstrates: + * - Starting sessions + * - Resuming sessions + * - Forking sessions (alternative paths) + * - Session lifecycle management + */ + +// Start a new session +async function startSession(prompt: string): Promise { + let sessionId: string | undefined; + + const response = query({ + prompt, + options: { + model: "claude-sonnet-4-5" + } + }); + + for await (const message of response) { + if (message.type === 'system' && message.subtype === 'init') { + sessionId = message.session_id; + console.log(`✨ Session started: ${sessionId}`); + } else if (message.type === 'assistant') { + console.log('Assistant:', message.content); + } + } + + if (!sessionId) { + throw new Error('Failed to start session'); + } + + return sessionId; +} + +// Resume an existing session +async function resumeSession(sessionId: string, prompt: string): Promise { + const response = query({ + prompt, + options: { + resume: sessionId, + model: "claude-sonnet-4-5" + } + }); + + console.log(`\nā†Ŗļø Resuming session: ${sessionId}`); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log('Assistant:', message.content); + } + } +} + +// Fork a session (explore alternative path) +async function forkSession(sessionId: string, prompt: string): Promise { + const response = query({ + prompt, + options: { + resume: sessionId, + forkSession: true, // Creates new branch + model: "claude-sonnet-4-5" + } + }); + + console.log(`\nšŸ”€ Forking session: ${sessionId}`); + + for await (const message of response) { + if (message.type === 'system' && message.subtype === 'init') { + console.log(`New session ID: ${message.session_id}`); + } else if (message.type === 'assistant') { + console.log('Assistant:', message.content); + } + } +} + +// Pattern 1: Sequential Development +async function sequentialDevelopment() { + console.log("šŸš€ Sequential Development Pattern\n"); + + // Step 1: Initial implementation + let session = await startSession("Create a user authentication system with JWT"); + + // Step 2: Add feature + await resumeSession(session, "Add OAuth 2.0 support (Google and GitHub)"); + + // Step 3: Add tests + await resumeSession(session, "Write comprehensive integration tests"); + + // Step 4: Deploy + await resumeSession(session, "Deploy to production with monitoring"); + + console.log("\nāœ… Sequential development complete"); +} + +// Pattern 2: Exploration & Decision +async function explorationAndDecision() { + console.log("šŸ” Exploration & Decision Pattern\n"); + + // Start main conversation + let mainSession = await startSession("Design a payment processing system"); + + // Explore option A + console.log("\n--- Option A: Stripe ---"); + await forkSession(mainSession, "Implement using Stripe API"); + + // Explore option B + console.log("\n--- Option B: PayPal ---"); + await forkSession(mainSession, "Implement using PayPal API"); + + // After decision, continue with chosen approach + console.log("\n--- Chosen: Stripe ---"); + await resumeSession(mainSession, "Implement the Stripe integration with webhooks"); + + console.log("\nāœ… Exploration complete"); +} + +// Pattern 3: Multi-User Collaboration +async function multiUserCollaboration() { + console.log("šŸ‘„ Multi-User Collaboration Pattern\n"); + + // Developer A starts work + let sessionA = await startSession("Implement user profile page with avatar, bio, and settings"); + + // Developer B forks for different feature + await forkSession(sessionA, "Add real-time notifications system with WebSockets"); + + // Developer C forks for another feature + await forkSession(sessionA, "Implement search functionality with filters and sorting"); + + // All developers can work independently without interfering + console.log("\nāœ… Multi-user collaboration setup complete"); +} + +// Run examples +async function main() { + try { + // Choose one pattern to run + await sequentialDevelopment(); + // await explorationAndDecision(); + // await multiUserCollaboration(); + } catch (error) { + console.error('Error:', error); + } +} + +main(); diff --git a/templates/subagents-orchestration.ts b/templates/subagents-orchestration.ts new file mode 100644 index 0000000..0b05736 --- /dev/null +++ b/templates/subagents-orchestration.ts @@ -0,0 +1,166 @@ +import { query } from "@anthropic-ai/claude-agent-sdk"; + +/** + * Subagent Orchestration Template + * + * Demonstrates: + * - Defining specialized subagents + * - Different models for different agents + * - Tool restrictions per agent + * - Multi-agent workflows + */ + +async function deployWithAgents(version: string) { + const response = query({ + prompt: `Deploy version ${version} to production with full validation`, + options: { + model: "claude-sonnet-4-5", + workingDirectory: process.cwd(), + systemPrompt: `You are a DevOps orchestrator. + +Coordinate specialized agents to: +1. Run tests (test-runner agent) +2. Check security (security-checker agent) +3. Deploy application (deployer agent) +4. Monitor systems (monitoring-agent agent) + +Ensure all validation passes before deployment.`, + + agents: { + "test-runner": { + description: "Run automated test suites and verify coverage", + prompt: `You run tests. + +Execute test commands, parse results, report coverage. +FAIL the deployment if any tests fail. +Report clear error messages for failures.`, + tools: ["Bash", "Read", "Grep"], + model: "haiku" // Fast, cost-effective for testing + }, + + "security-checker": { + description: "Security audits and vulnerability scanning", + prompt: `You check security. + +Scan for: +- Exposed secrets or API keys +- Outdated dependencies +- Incorrect file permissions +- OWASP compliance issues + +Verify all security checks pass before deployment.`, + tools: ["Read", "Grep", "Bash"], + model: "sonnet" // Balance for security analysis + }, + + "deployer": { + description: "Application deployment and rollbacks", + prompt: `You deploy applications. + +Deployment process: +1. Deploy to staging environment +2. Verify health checks pass +3. Deploy to production +4. Create rollback plan + +ALWAYS have a rollback ready.`, + tools: ["Bash", "Read"], + model: "sonnet" // Reliable for critical operations + }, + + "monitoring-agent": { + description: "System monitoring and alerting", + prompt: `You monitor systems. + +Check: +- Application metrics +- Error rates +- Response times +- System health + +Alert on issues immediately.`, + tools: ["Bash", "Read"], + model: "haiku" // Fast monitoring checks + } + } + } + }); + + // Track which agents were used + const agentsUsed = new Set(); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log('\nšŸ“‹ Orchestrator:', message.content); + } else if (message.type === 'tool_call') { + console.log(`\nšŸ”§ Tool: ${message.tool_name}`); + } + } +} + +// Example: Complex DevOps workflow +async function complexWorkflow() { + const response = query({ + prompt: "API response time increased by 300% in last hour. Investigate and fix", + options: { + model: "claude-sonnet-4-5", + systemPrompt: "You coordinate incident response across specialized teams.", + + agents: { + "incident-responder": { + description: "Diagnose and respond to production incidents", + prompt: `You handle incidents. + +Steps: +1. Assess impact (users affected, services down) +2. Identify root cause +3. Implement immediate fixes +4. Communicate status updates + +Work with monitoring and deployment agents.`, + tools: ["Bash", "Read", "Grep"], + model: "sonnet" + }, + + "performance-analyst": { + description: "Analyze performance metrics and bottlenecks", + prompt: `You analyze performance. + +Investigate: +- Database query times +- API response times +- Memory/CPU usage +- Network latency + +Identify bottlenecks and optimization opportunities.`, + tools: ["Bash", "Read", "Grep"], + model: "sonnet" + }, + + "fix-implementer": { + description: "Implement performance fixes and optimizations", + prompt: `You implement fixes. + +Apply optimizations: +- Database query optimization +- Caching improvements +- Code refactoring +- Infrastructure scaling + +Test fixes before deploying.`, + tools: ["Read", "Edit", "Bash"], + model: "sonnet" + } + } + } + }); + + for await (const message of response) { + if (message.type === 'assistant') { + console.log(message.content); + } + } +} + +// Run +deployWithAgents("2.5.0").catch(console.error); diff --git a/templates/tsconfig.json b/templates/tsconfig.json new file mode 100644 index 0000000..35f3a52 --- /dev/null +++ b/templates/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "lib": ["ES2022"], + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "strict": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./", + "types": ["node"] + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] +}