CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-anthropic-ai--claude-agent-sdk

SDK for building AI agents with Claude Code's capabilities to programmatically interact with Claude and build autonomous agents that can understand codebases, edit files, and execute workflows.

Overview
Eval results
Files

patterns.mddocs/

Common Patterns

Practical code patterns for the Claude Agent SDK.

Basic Query

import { query } from '@anthropic-ai/claude-agent-sdk';

const result = query({
  prompt: 'Task description',
  options: {model: 'claude-sonnet-4-5-20250929', cwd: '/path'}
});

for await (const msg of result) {
  if (msg.type === 'result') {
    console.log(msg.subtype === 'success' ? msg.result : msg.errors);
  }
}

Multi-turn Session (V2 API)

import { unstable_v2_createSession } from '@anthropic-ai/claude-agent-sdk';

const session = unstable_v2_createSession({model: 'claude-sonnet-4-5-20250929'});

await session.send('First message');
for await (const msg of session.receive()) {
  if (msg.type === 'result') break;
}

await session.send('Second message');
for await (const msg of session.receive()) {
  if (msg.type === 'result') break;
}

session.close();

Custom Permission Handler

const result = query({
  prompt: 'Task',
  options: {
    canUseTool: async (toolName, input, {decisionReason}) => {
      // Block dangerous commands
      if (toolName === 'Bash' && (input.command as string).includes('rm -rf')) {
        return {behavior: 'deny', message: 'Command not allowed', interrupt: true};
      }

      // Block sensitive files
      if ((toolName === 'Write' || toolName === 'Edit') &&
          (input.file_path as string).includes('.env')) {
        return {behavior: 'deny', message: 'Cannot modify .env'};
      }

      return {behavior: 'allow', updatedInput: input};
    }
  }
});

Hook-based Logging

const result = query({
  prompt: 'Task',
  options: {
    hooks: {
      PreToolUse: [{
        hooks: [async (input) => {
          if (input.hook_event_name === 'PreToolUse') {
            console.log(`[${input.tool_name}]`, input.tool_input);
          }
          return {continue: true};
        }]
      }],
      PostToolUse: [{
        hooks: [async (input) => {
          if (input.hook_event_name === 'PostToolUse') {
            console.log(`[${input.tool_name} ✓]`);
          }
          return {continue: true};
        }]
      }]
    }
  }
});

Custom MCP Tools

import { query, createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
import { z } from 'zod';

const myServer = createSdkMcpServer({
  name: 'my-tools',
  tools: [
    tool(
      'calculate',
      'Perform calculations',
      {
        op: z.enum(['add', 'sub', 'mul', 'div']),
        a: z.number(),
        b: z.number()
      },
      async (args) => {
        const ops = {add: '+', sub: '-', mul: '*', div: '/'};
        const result = eval(`${args.a} ${ops[args.op]} ${args.b}`);
        return {content: [{type: 'text', text: `Result: ${result}`}]};
      }
    )
  ]
});

const result = query({
  prompt: 'Calculate 15 + 27',
  options: {mcpServers: {'my-tools': myServer}}
});

Custom Subagents

const result = query({
  prompt: 'Review and test the code',
  options: {
    agents: {
      'reviewer': {
        description: 'Reviews code for issues',
        tools: ['Read', 'Grep', 'Glob'],
        prompt: 'You are a code reviewer. Find bugs, style issues, security vulnerabilities.',
        model: 'opus'
      },
      'tester': {
        description: 'Writes and runs tests',
        tools: ['Read', 'Write', 'Bash'],
        prompt: 'You are a test engineer. Write comprehensive tests.',
        model: 'sonnet'
      }
    }
  }
});

Sandbox Configuration

const result = query({
  prompt: 'Build Docker container',
  options: {
    sandbox: {
      enabled: true,
      autoAllowBashIfSandboxed: true,
      network: {
        allowUnixSockets: ['/var/run/docker.sock'],
        allowLocalBinding: true
      },
      excludedCommands: ['docker', 'npm'],
      allowUnsandboxedCommands: true
    }
  }
});

Tool Access Control

// Read-only access
const result1 = query({
  prompt: 'Analyze code',
  options: {allowedTools: ['Read', 'Grep', 'Glob']}
});

// No shell access
const result2 = query({
  prompt: 'Modify files',
  options: {
    tools: {type: 'preset', preset: 'claude_code'},
    disallowedTools: ['Bash', 'WebFetch', 'WebSearch']
  }
});

Structured Output

const result = query({
  prompt: 'Analyze file complexity',
  options: {
    outputFormat: {
      type: 'json_schema',
      schema: {
        type: 'object',
        properties: {
          complexity: {type: 'number'},
          issues: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                severity: {type: 'string'},
                message: {type: 'string'}
              }
            }
          }
        },
        required: ['complexity', 'issues']
      }
    }
  }
});

for await (const msg of result) {
  if (msg.type === 'result' && msg.subtype === 'success') {
    console.log(msg.structured_output);
  }
}

Budget and Limits

const result = query({
  prompt: 'Complex task',
  options: {
    maxTurns: 50,
    maxBudgetUsd: 2.00,
    maxThinkingTokens: 1000
  }
});

Session Management

// Continue previous conversation
const result1 = query({prompt: 'Continue', options: {continue: true}});

// Resume specific session
const result2 = query({prompt: 'Resume', options: {resume: 'session-id'}});

// Fork session
const result3 = query({
  prompt: 'Branch',
  options: {resume: 'session-id', forkSession: true}
});

Error Handling

const controller = new AbortController();
setTimeout(() => controller.abort(), 30000); // 30s timeout

try {
  const result = query({
    prompt: 'Task',
    options: {
      abortController: controller,
      stderr: (data) => console.error('[STDERR]', data)
    }
  });

  for await (const msg of result) {
    if (msg.type === 'result') {
      if (msg.subtype === 'success') {
        console.log('Success:', msg.result);
        console.log('Cost: $', msg.total_cost_usd);
        console.log('Turns:', msg.num_turns);
      } else {
        console.error('Errors:', msg.errors);
        console.log('Partial cost: $', msg.total_cost_usd);
      }

      if (msg.permission_denials.length > 0) {
        console.warn('Denied:', msg.permission_denials.map(d => d.tool_name));
      }
    }
  }
} catch (error) {
  if (error instanceof AbortError) {
    console.log('Aborted');
  } else {
    throw error;
  }
}

File Operations Workflow

const result = query({
  prompt: 'Find all TODO comments, update them, and create a report',
  options: {
    tools: ['Read', 'Grep', 'Glob', 'Write'],
    permissionMode: 'acceptEdits'
  }
});

// Agent will:
// 1. Glob: Find files matching pattern
// 2. Grep: Search for TODO comments
// 3. Read: Read files with TODOs
// 4. Write: Update files and create report

Query Control Methods

const result = query({prompt: 'Task', options: {model: 'claude-sonnet-4-5-20250929'}});

// Interrupt execution
setTimeout(() => result.interrupt(), 10000);

// Change model mid-execution (streaming input mode only)
setTimeout(() => result.setModel('claude-opus-4-20250514'), 5000);

// Query session info
const models = await result.supportedModels();
const commands = await result.supportedCommands();
const mcpStatus = await result.mcpServerStatus();
const account = await result.accountInfo();

External MCP Servers

const result = query({
  prompt: 'Use external tools',
  options: {
    mcpServers: {
      'database': {
        command: 'node',
        args: ['./servers/db-server.js'],
        env: {DB_HOST: 'localhost'}
      },
      'api': {
        type: 'http',
        url: 'http://localhost:3000/mcp',
        headers: {'Authorization': 'Bearer token'}
      },
      'realtime': {
        type: 'sse',
        url: 'http://localhost:4000/stream'
      }
    },
    strictMcpConfig: true
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-anthropic-ai--claude-agent-sdk

docs

index.md

patterns.md

quick-reference.md

types.md

tile.json