Initial commit
This commit is contained in:
387
references/mcp-servers-guide.md
Normal file
387
references/mcp-servers-guide.md
Normal 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/
|
||||
429
references/permissions-guide.md
Normal file
429
references/permissions-guide.md
Normal 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
|
||||
437
references/query-api-reference.md
Normal file
437
references/query-api-reference.md
Normal 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
|
||||
419
references/session-management.md
Normal file
419
references/session-management.md
Normal 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
|
||||
464
references/subagents-patterns.md
Normal file
464
references/subagents-patterns.md
Normal 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
503
references/top-errors.md
Normal 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
|
||||
Reference in New Issue
Block a user