or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

apis

agents.mdhooks.mdmcp.mdmessages.mdoptions.mdpermissions.mdquery-api.mdsandbox.mdtools.md
index.mdpatterns.mdquick-reference.mdtypes.md
tile.json

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