Claude Code TypeScript SDK - Complete Guide

The @anthropic-ai/claude-code npm package provides both a powerful CLI tool and a programmatic SDK for integrating Claude’s AI coding capabilities into your TypeScript/JavaScript applications.

📦 Installation and Setup

Global CLI Installation

# Install Claude Code CLI globally
npm install -g @anthropic-ai/claude-code
 
# Verify installation
claude --version  # Should show 1.0.56 or later

Local SDK Installation

# Install as a project dependency
npm install @anthropic-ai/claude-code
 
# Install TypeScript types (if using TypeScript)
npm install --save-dev @types/node typescript
 
# Initialize TypeScript config
npx tsc --init

Authentication Setup

# Set your API key
export ANTHROPIC_API_KEY="sk-ant-api03-..."
 
# Or in .env file
echo "ANTHROPIC_API_KEY=sk-ant-api03-..." >> .env

Option 2: OAuth Token (Pro/Max Users)

# Authenticate via browser
claude setup-token
 
# Token stored in ~/.claude/config.json

Option 3: Third-Party Providers

# Amazon Bedrock
export CLAUDE_CODE_USE_BEDROCK=1
export AWS_PROFILE=your-profile
 
# Google Vertex AI
export CLAUDE_CODE_USE_VERTEX=1
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json

🚀 Basic SDK Usage

Simple Query Example

import { query, type SDKMessage } from "@anthropic-ai/claude-code";
 
async function askClaude() {
  const messages: SDKMessage[] = [];
  
  for await (const message of query({
    prompt: "Write a function to calculate fibonacci numbers",
    abortController: new AbortController(),
  })) {
    messages.push(message);
    
    if (message.type === 'assistant') {
      console.log("Claude:", message.data.message.content);
    }
  }
  
  return messages;
}
 
askClaude().then(console.log);

Understanding Message Types

// The SDK returns 4 types of messages:
 
interface SystemMessage {
  type: 'system';
  data: {
    tools: Tool[];           // Available tools
    api_platform?: string;   // 'anthropic', 'bedrock', 'vertex'
  };
}
 
interface UserMessage {
  type: 'user';
  data: {
    message: {
      content: string;       // Your prompt
    };
  };
}
 
interface AssistantMessage {
  type: 'assistant';
  data: {
    message: {
      content: string;       // Claude's response
      tool_calls?: ToolCall[]; // Tool invocations
    };
  };
}
 
interface ResultMessage {
  type: 'result';
  data: {
    status: 'success' | 'error' | 'aborted';
    error?: string;
    total_turns: number;
    total_time_ms: number;
    total_cost_usd: number;
    total_tokens_in: number;
    total_tokens_out: number;
  };
}

🛠️ Advanced SDK Features

Configuration Options

import { query, type QueryOptions } from "@anthropic-ai/claude-code";
 
const options: QueryOptions = {
  // Core settings
  maxTurns: 5,                    // Max conversation rounds (default: 3)
  model: "claude-sonnet-4-20250514", // Model selection
  cwd: "/path/to/project",        // Working directory
  
  // System prompt customization
  systemPrompt: `You are an expert TypeScript developer.
    Follow these conventions:
    - Use functional programming patterns
    - Prefer const over let
    - Always add proper types`,
  
  // Tool configuration
  allowedTools: ["Read", "Write", "Edit", "Bash", "WebSearch"],
  permissionMode: "acceptEdits",  // auto | acceptEdits | confirmAll
  
  // Advanced options
  apiUrl: "https://api.anthropic.com", // Custom endpoint
  apiKey: process.env.CUSTOM_API_KEY,  // Override env var
  temperature: 0.7,                     // Response randomness
  maxTokens: 4096,                      // Max response length
};
 
const messages = [];
for await (const msg of query({ prompt: "...", options })) {
  messages.push(msg);
}

Working with Tools

// Claude can use various tools to complete tasks
 
interface Tool {
  name: string;
  description: string;
  parameters: object;
}
 
// Example: File operations with error handling
async function fileOperationExample() {
  const controller = new AbortController();
  
  try {
    for await (const message of query({
      prompt: `Read package.json and update the version to 2.0.0`,
      abortController: controller,
      options: {
        allowedTools: ["Read", "Edit"],
        permissionMode: "acceptEdits",
      }
    })) {
      if (message.type === 'assistant') {
        const toolCalls = message.data.message.tool_calls;
        
        toolCalls?.forEach(call => {
          console.log(`Tool: ${call.tool_name}`);
          console.log(`Input:`, call.tool_input);
        });
      }
    }
  } catch (error) {
    console.error("Operation failed:", error);
  }
}

Multi-Turn Conversations

// Claude can iteratively work on complex tasks
 
async function complexRefactoring() {
  const messages: SDKMessage[] = [];
  let turnCount = 0;
  
  for await (const message of query({
    prompt: `Refactor the user authentication module to:
      1. Add proper TypeScript types
      2. Implement error handling
      3. Add unit tests
      4. Update documentation`,
    options: {
      maxTurns: 10,  // Allow up to 10 rounds
      allowedTools: ["Read", "Write", "Edit", "Bash"],
    }
  })) {
    messages.push(message);
    
    // Track progress
    if (message.type === 'assistant') {
      turnCount++;
      console.log(`Turn ${turnCount}: Working on refactoring...`);
    }
    
    // Monitor costs
    if (message.type === 'result') {
      console.log(`Total cost: $${message.data.total_cost_usd}`);
      console.log(`Tokens used: ${message.data.total_tokens_in + message.data.total_tokens_out}`);
    }
  }
  
  return messages;
}

🎯 Real-World Examples

1. Code Review Automation

import { query } from "@anthropic-ai/claude-code";
import { readFileSync } from "fs";
 
async function reviewPullRequest(files: string[]) {
  const systemPrompt = `You are a senior code reviewer. 
    Focus on: security issues, performance problems, 
    code style violations, and potential bugs.`;
  
  const fileContents = files.map(file => ({
    path: file,
    content: readFileSync(file, 'utf-8')
  }));
  
  const prompt = `Review these files for a pull request:
    ${fileContents.map(f => `\n${f.path}:\n${f.content}`).join('\n')}
    
    Provide specific, actionable feedback.`;
  
  const feedback = [];
  
  for await (const message of query({
    prompt,
    options: {
      systemPrompt,
      maxTurns: 1,  // Single response
      temperature: 0.3,  // More focused
    }
  })) {
    if (message.type === 'assistant') {
      feedback.push(message.data.message.content);
    }
  }
  
  return feedback.join('\n');
}

2. Test Generation

async function generateTests(sourceFile: string) {
  const controller = new AbortController();
  
  // Add timeout
  setTimeout(() => controller.abort(), 60000); // 1 minute
  
  for await (const message of query({
    prompt: `Generate comprehensive unit tests for ${sourceFile}
      Include: edge cases, error scenarios, async operations`,
    abortController: controller,
    options: {
      allowedTools: ["Read", "Write"],
      maxTurns: 5,
      cwd: process.cwd(),
    }
  })) {
    // Process messages...
    if (message.type === 'result' && message.data.status === 'success') {
      console.log("Tests generated successfully!");
    }
  }
}

3. Documentation Generator

interface DocGenOptions {
  inputDir: string;
  outputDir: string;
  format: 'markdown' | 'jsdoc' | 'typedoc';
}
 
async function generateDocs(options: DocGenOptions) {
  const messages = [];
  
  for await (const message of query({
    prompt: `Generate ${options.format} documentation for all TypeScript 
      files in ${options.inputDir}. Output to ${options.outputDir}.
      Include: function signatures, parameters, return types, examples.`,
    options: {
      allowedTools: ["Read", "Write", "Bash"],
      maxTurns: 20,  // May need many turns
      systemPrompt: "You are a technical documentation expert.",
    }
  })) {
    messages.push(message);
    
    // Stream progress updates
    if (message.type === 'assistant') {
      process.stdout.write('.');
    }
  }
  
  console.log('\nDocumentation complete!');
  return messages;
}

🔧 Error Handling and Recovery

import { query } from "@anthropic-ai/claude-code";
 
async function robustQuery(prompt: string, maxRetries = 3) {
  let attempt = 0;
  let lastError: Error | null = null;
  
  while (attempt < maxRetries) {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 300000); // 5 min
      
      const messages = [];
      for await (const message of query({
        prompt,
        abortController: controller,
        options: {
          maxTurns: 5,
          permissionMode: "acceptEdits",
        }
      })) {
        messages.push(message);
        
        // Check for errors in result
        if (message.type === 'result' && message.data.status === 'error') {
          throw new Error(message.data.error || 'Unknown error');
        }
      }
      
      clearTimeout(timeout);
      return messages;
      
    } catch (error) {
      lastError = error as Error;
      attempt++;
      
      // Handle specific errors
      if (error.message.includes('rate limit')) {
        console.log(`Rate limited. Waiting ${30 * attempt}s...`);
        await new Promise(r => setTimeout(r, 30000 * attempt));
      } else if (error.message.includes('abort')) {
        console.log('Request timed out. Retrying...');
      } else {
        throw error; // Unrecoverable
      }
    }
  }
  
  throw lastError;
}

🚀 Performance Optimization

Streaming Responses

// Process Claude's responses as they arrive
 
async function streamingExample() {
  const chunks: string[] = [];
  
  for await (const message of query({
    prompt: "Write a detailed implementation plan",
    options: { maxTurns: 1 }
  })) {
    if (message.type === 'assistant') {
      const content = message.data.message.content;
      
      // Process in chunks
      const lines = content.split('\n');
      for (const line of lines) {
        chunks.push(line);
        console.log(line); // Real-time output
      }
    }
  }
  
  return chunks.join('\n');
}

Batch Processing

// Process multiple tasks efficiently
 
async function batchProcess(tasks: string[]) {
  const results = new Map<string, SDKMessage[]>();
  
  // Process in parallel with concurrency limit
  const concurrency = 3;
  const queue = [...tasks];
  const active = new Set<Promise<void>>();
  
  while (queue.length > 0 || active.size > 0) {
    while (active.size < concurrency && queue.length > 0) {
      const task = queue.shift()!;
      
      const promise = (async () => {
        const messages = [];
        for await (const msg of query({ prompt: task })) {
          messages.push(msg);
        }
        results.set(task, messages);
      })();
      
      active.add(promise);
      promise.finally(() => active.delete(promise));
    }
    
    if (active.size > 0) {
      await Promise.race(active);
    }
  }
  
  return results;
}

📊 Cost Management

// Track and control API costs
 
class CostTracker {
  private totalCost = 0;
  private budget: number;
  
  constructor(budget: number) {
    this.budget = budget;
  }
  
  async trackQuery(prompt: string, options?: QueryOptions) {
    if (this.totalCost >= this.budget) {
      throw new Error(`Budget exceeded: $${this.totalCost} >= $${this.budget}`);
    }
    
    const messages = [];
    for await (const message of query({ prompt, options })) {
      messages.push(message);
      
      if (message.type === 'result') {
        this.totalCost += message.data.total_cost_usd;
        
        console.log(`Query cost: $${message.data.total_cost_usd}`);
        console.log(`Total spent: $${this.totalCost}/$${this.budget}`);
      }
    }
    
    return messages;
  }
  
  getSpent() { return this.totalCost; }
  getRemaining() { return this.budget - this.totalCost; }
}
 
// Usage
const tracker = new CostTracker(10.00); // $10 budget
await tracker.trackQuery("Generate tests for my module");

🔗 Integration Examples

Express.js Integration

import express from 'express';
import { query } from '@anthropic-ai/claude-code';
 
const app = express();
app.use(express.json());
 
app.post('/api/claude', async (req, res) => {
  const { prompt, options } = req.body;
  const messages = [];
  
  try {
    for await (const message of query({ prompt, options })) {
      messages.push(message);
      
      // Stream responses
      if (message.type === 'assistant') {
        res.write(JSON.stringify(message) + '\n');
      }
    }
    
    res.end();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
 
app.listen(3000);

GitHub Actions Integration

# .github/workflows/claude-review.yml
name: Claude Code Review
 
on:
  pull_request:
    types: [opened, synchronize]
 
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      
      - name: Install Claude Code
        run: npm install -g @anthropic-ai/claude-code
      
      - name: Run Review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          claude review "Review the changes in this PR" \
            --model claude-sonnet-4 \
            --max-turns 1

📚 TypeScript Types Reference

// Complete type definitions
 
import type {
  SDKMessage,
  QueryOptions,
  Tool,
  ToolCall,
  Model,
  PermissionMode,
} from "@anthropic-ai/claude-code";
 
// Available models
type Model = 
  | "claude-opus-4-20250514"
  | "claude-sonnet-4-20250514"
  | "claude-haiku-3-20240307";
 
// Permission modes
type PermissionMode = 
  | "auto"         // Automatic decisions
  | "acceptEdits"  // Accept file edits automatically
  | "confirmAll";  // Confirm everything
 
// Tool names
type ToolName = 
  | "Read"
  | "Write"
  | "Edit"
  | "MultiEdit"
  | "Bash"
  | "WebSearch"
  | "WebFetch";
 
// Query function signature
declare function query(params: {
  prompt: string;
  abortController?: AbortController;
  options?: QueryOptions;
}): AsyncIterable<SDKMessage>;

🆘 Troubleshooting

Common Issues

  1. Authentication Errors

    // Check API key is set
    if (!process.env.ANTHROPIC_API_KEY) {
      throw new Error("ANTHROPIC_API_KEY not set");
    }
  2. Rate Limiting

    // Implement exponential backoff
    async function withRetry(fn: () => Promise<any>, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          return await fn();
        } catch (error) {
          if (i === maxRetries - 1) throw error;
          await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
        }
      }
    }
  3. Memory Issues

    // Use streaming and cleanup
    for await (const message of query({ prompt })) {
      // Process immediately
      processMessage(message);
      // Don't accumulate in memory
    }

📦 Package Information


Last updated: July 2025 | SDK version 1.0.56