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 laterLocal 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 --initAuthentication Setup
Option 1: Anthropic API Key (Recommended)
# Set your API key
export ANTHROPIC_API_KEY="sk-ant-api03-..."
# Or in .env file
echo "ANTHROPIC_API_KEY=sk-ant-api03-..." >> .envOption 2: OAuth Token (Pro/Max Users)
# Authenticate via browser
claude setup-token
# Token stored in ~/.claude/config.jsonOption 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
-
Authentication Errors
// Check API key is set if (!process.env.ANTHROPIC_API_KEY) { throw new Error("ANTHROPIC_API_KEY not set"); } -
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)); } } } -
Memory Issues
// Use streaming and cleanup for await (const message of query({ prompt })) { // Process immediately processMessage(message); // Don't accumulate in memory }
🔗 Related Documentation
📦 Package Information
- NPM: @anthropic-ai/claude-code
- Version: 1.0.56+ (July 2025)
- License: MIT
- Requirements: Node.js 18+, TypeScript 4.9+
Last updated: July 2025 | SDK version 1.0.56