Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:23:58 +08:00
commit 18c5d51e47
22 changed files with 5082 additions and 0 deletions

View File

@@ -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": [
"./"
]
}

3
README.md Normal file
View File

@@ -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.

595
SKILL.md Normal file
View File

@@ -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<SDKUserMessage>, options?: Options)
-> AsyncGenerator<SDKMessage>
```
**Critical Options:**
- `outputFormat` - Structured JSON schema validation (v0.1.45+)
- `settingSources` - Filesystem settings loading ('user'|'project'|'local')
- `canUseTool` - Custom permission logic callback
- `agents` - Programmatic subagent definitions
- `mcpServers` - MCP server configuration
- `permissionMode` - 'default'|'acceptEdits'|'bypassPermissions'|'plan'
---
## 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__<server-name>__<tool-name>`
**CRITICAL:**
- Server name and tool name MUST match configuration
- Use double underscores (`__`) as separators
- Include in `allowedTools` array
**Examples:** `mcp__weather-service__get_weather`, `mcp__filesystem__read_file`
---
## Subagent Orchestration
### AgentDefinition Type
```typescript
type AgentDefinition = {
description: string; // When to use this agent
prompt: string; // System prompt for agent
tools?: string[]; // Allowed tools (optional)
model?: 'sonnet' | 'opus' | 'haiku' | 'inherit'; // Model (optional)
}
```
**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<PermissionDecision>;
type PermissionDecision =
| { behavior: "allow" }
| { behavior: "deny"; message?: string }
| { behavior: "ask"; message?: string };
```
**Examples:**
```typescript
// Block all file writes
canUseTool: async (toolName, input) => {
if (toolName === 'Write' || toolName === 'Edit') {
return { behavior: "deny", message: "No file modifications allowed" };
}
return { behavior: "allow" };
}
// Require confirmation for specific files
canUseTool: async (toolName, input) => {
const sensitivePaths = ['/etc/', '/root/', '.env', 'credentials.json'];
if ((toolName === 'Write' || toolName === 'Edit') &&
sensitivePaths.some(path => input.file_path?.includes(path))) {
return {
behavior: "ask",
message: `Modify sensitive file ${input.file_path}?`
};
}
return { behavior: "allow" };
}
// Log all tool usage
canUseTool: async (toolName, input) => {
console.log(`Tool requested: ${toolName}`, input);
await logToDatabase(toolName, input);
return { behavior: "allow" };
}
```
---
## 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.

117
plugin.lock.json Normal file
View File

@@ -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": []
}
}

View File

@@ -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__<server-name>__<tool-name>`
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/

View File

@@ -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<PermissionDecision>;
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

View File

@@ -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<SDKUserMessage>;
options?: Options;
}): AsyncGenerator<SDKMessage, void>;
```
---
## Parameters
### prompt
**Type**: `string | AsyncIterable<SDKUserMessage>`
**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<PermissionDecision>`
**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<string, AgentDefinition>`
**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<string, McpServerConfig>`
**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<SDKMessage, void>`
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<PermissionDecision>;
agents?: Record<string, AgentDefinition>;
mcpServers?: Record<string, McpServerConfig>;
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

View File

@@ -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<string> {
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<void> {
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<string> {
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<string, { id: string; created: Date }>();
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<string, string>();
async start(name: string, prompt: string): Promise<string> {
const id = await startSession(prompt);
this.sessions.set(name, id);
console.log(`✨ Started: ${name}`);
return id;
}
async resume(name: string, prompt: string): Promise<void> {
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<string> {
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

View File

@@ -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

503
references/top-errors.md Normal file
View File

@@ -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

55
scripts/check-versions.sh Executable file
View File

@@ -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!"

55
templates/basic-query.ts Normal file
View File

@@ -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);

View File

@@ -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);

283
templates/error-handling.ts Normal file
View File

@@ -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<void> {
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<void>): Promise<void> {
// 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);

View File

@@ -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);

View File

@@ -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<string>();
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();

29
templates/package.json Normal file
View File

@@ -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"
}
}

View File

@@ -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);

View File

@@ -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<string>();
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);

View File

@@ -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<string> {
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<void> {
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<void> {
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();

View File

@@ -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<string>();
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);

22
templates/tsconfig.json Normal file
View File

@@ -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"]
}