Overview
Claude API tool use for TypeScript applications — from the simplest tool runner to full agentic loops. Uses claude-sonnet-4-6 as the capable default; swap to claude-opus-4-6 for complex reasoning tasks.
Model Defaults
| Task | Model | Why |
|------|-------|-----|
| Production default | claude-sonnet-4-6 | Cost-efficient, highly capable |
| Complex reasoning | claude-opus-4-6 | Maximum reasoning depth |
| Simple tasks | claude-haiku-4-5 | Cheapest, fast |
Use exact model ID strings — never append date suffixes.
Basic API Call
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
system: "You are a helpful assistant. Be concise.",
messages: [
{ role: "user", content: "Summarize the key points of async/await in JavaScript." }
],
});
const text = response.content[0].type === "text" ? response.content[0].text : "";
Tool Use — Tool Runner (Recommended)
The tool runner handles the agentic loop automatically:
import Anthropic from "@anthropic-ai/sdk";
import { z } from "zod";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [
{
name: "get_weather",
description: "Get current weather for a city",
input_schema: {
type: "object" as const,
properties: {
city: { type: "string", description: "City name" },
units: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["city"],
},
},
];
async function getWeather(city: string, units = "celsius") {
// Real implementation would call a weather API
return { temperature: 22, condition: "Sunny", city, units };
}
async function askClaude(userMessage: string) {
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: userMessage }
];
while (true) {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 4096,
tools,
messages,
});
if (response.stop_reason === "end_turn") {
const textBlock = response.content.find(b => b.type === "text");
return textBlock?.text ?? "";
}
if (response.stop_reason === "tool_use") {
messages.push({ role: "assistant", content: response.content });
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type !== "tool_use") continue;
const input = block.input as { city: string; units?: string };
const result = await getWeather(input.city, input.units);
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(result),
});
}
messages.push({ role: "user", content: toolResults });
}
}
}
Structured Outputs
// Prefer output_config over deprecated output_format
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
output_config: {
format: {
type: "json_schema",
json_schema: {
name: "article_analysis",
schema: {
type: "object",
properties: {
title: { type: "string" },
sentiment: { type: "string", enum: ["positive", "negative", "neutral"] },
keyPoints: { type: "array", items: { type: "string" } },
score: { type: "number", minimum: 0, maximum: 10 },
},
required: ["title", "sentiment", "keyPoints", "score"],
},
},
},
},
messages: [{ role: "user", content: `Analyze: ${articleText}` }],
});
Streaming (Use for Long Outputs)
// Stream to avoid HTTP timeout on long responses
const stream = client.messages.stream({
model: "claude-sonnet-4-6",
max_tokens: 8192,
messages: [{ role: "user", content: prompt }],
});
stream.on("text", (text) => process.stdout.write(text));
const finalMessage = await stream.finalMessage();
// finalMessage.usage has token counts
Adaptive Thinking (Opus 4.6)
const response = await client.messages.create({
model: "claude-opus-4-6",
max_tokens: 16000,
thinking: { type: "adaptive" }, // Claude decides when and how much to think
messages: [{ role: "user", content: complexProblem }],
});
Never use budget_tokens with Opus 4.6 or Sonnet 4.6 — it is deprecated. Use adaptive instead.
Prompt Caching (Cost Reduction)
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
system: [
{
type: "text",
text: longSystemPrompt, // This gets cached
cache_control: { type: "ephemeral" },
},
],
messages: [{ role: "user", content: "Question about the document." }],
});
// Subsequent calls reuse cached system prompt — 90% cost reduction on input tokens
Common Pitfalls
| Mistake | Fix |
|---------|-----|
| Date-suffixed model IDs | Use exact IDs: claude-sonnet-4-6 |
| budget_tokens on Opus/Sonnet 4.6 | Use thinking: {type: "adaptive"} |
| Deprecated output_format | Use output_config: {format: {...}} |
| Prefill on Opus 4.6 | Removed — use output_config instead |
| Blocking fetch for large outputs | Use streaming with .stream() |
| String-matching error messages | Use typed exceptions: Anthropic.RateLimitError |