参考数据:工具使用参考 — TypeScript
Data: Tool use reference — TypeScript
v2.1.63TypeScript tool use reference including tool runner, manual agentic loop, code execution, and structured outputs
工具使用 — TypeScript
关于概念性概述(工具定义、工具选择、技巧),请参阅 shared/tool-use-concepts.md。
工具运行器(推荐)
Beta 版: 工具运行器在 TypeScript SDK 中处于 Beta 阶段。
使用 betaZodTool 和 Zod 模式来定义带有 run 函数的工具,然后将它们传递给 client.beta.messages.toolRunner():
import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";
const client = new Anthropic();
const getWeather = betaZodTool({
name: "get_weather",
description: "Get current weather for a location",
inputSchema: z.object({
location: z.string().describe("City and state, e.g., San Francisco, CA"),
unit: z.enum(["celsius", "fahrenheit"]).optional(),
}),
run: async (input) => {
// Your implementation here
return `72°F and sunny in ${input.location}`;
},
});
// The tool runner handles the agentic loop and returns the final message
const finalMessage = await client.beta.messages.toolRunner({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
tools: [getWeather],
messages: [{ role: "user", content: "What's the weather in Paris?" }],
});
console.log(finalMessage.content);工具运行器的主要优势:
- 无需手动循环 — SDK 处理调用工具和反馈结果
- 通过 Zod 模式实现类型安全的工具输入
- 工具模式从 Zod 定义自动生成
- 当 Claude 没有更多工具调用时,迭代自动停止
手动 Agentic 循环
当你需要细粒度控制时使用此方法(自定义日志记录、条件性工具执行、流式传输单个迭代、人工介入审批):
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [...]; // Your tool definitions
let messages: Anthropic.MessageParam[] = [{ role: "user", content: userInput }];
while (true) {
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
tools: tools,
messages: messages,
});
if (response.stop_reason === "end_turn") break;
// Server-side tool hit iteration limit; re-send to continue
if (response.stop_reason === "pause_turn") {
messages = [
{ role: "user", content: userInput },
{ role: "assistant", content: response.content },
];
continue;
}
const toolUseBlocks = response.content.filter(
(b): b is Anthropic.ToolUseBlock => b.type === "tool_use",
);
messages.push({ role: "assistant", content: response.content });
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const tool of toolUseBlocks) {
const result = await executeTool(tool.name, tool.input);
toolResults.push({
type: "tool_result",
tool_use_id: tool.id,
content: result,
});
}
messages.push({ role: "user", content: toolResults });
}流式手动循环
当你在手动循环中需要流式传输时,使用 client.messages.stream() + finalMessage() 而不是 .create()。文本增量会在每次迭代时流式传输;finalMessage() 收集完整的 Message,以便你可以检查 stop_reason 并提取工具使用块:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [...];
let messages: Anthropic.MessageParam[] = [{ role: "user", content: userInput }];
while (true) {
const stream = client.messages.stream({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
tools,
messages,
});
// Stream text deltas on each iteration
stream.on("text", (delta) => {
process.stdout.write(delta);
});
// finalMessage() resolves with the complete Message — no need to
// manually wire up .on("message") / .on("error") / .on("abort")
const message = await stream.finalMessage();
if (message.stop_reason === "end_turn") break;
// Server-side tool hit iteration limit; re-send to continue
if (message.stop_reason === "pause_turn") {
messages = [
{ role: "user", content: userInput },
{ role: "assistant", content: message.content },
];
continue;
}
const toolUseBlocks = message.content.filter(
(b): b is Anthropic.ToolUseBlock => b.type === "tool_use",
);
messages.push({ role: "assistant", content: message.content });
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const tool of toolUseBlocks) {
const result = await executeTool(tool.name, tool.input);
toolResults.push({
type: "tool_result",
tool_use_id: tool.id,
content: result,
});
}
messages.push({ role: "user", content: toolResults });
}重要: 不要将
.on()事件包装在new Promise()中来收集最终消息 — 请改用stream.finalMessage()。SDK 在内部处理所有错误/中止/完成状态。
循环中的错误处理: 使用 SDK 的类型化异常(例如
Anthropic.RateLimitError、Anthropic.APIError)— 有关示例,请参阅 错误处理。不要通过字符串匹配来检查错误消息。
SDK 类型: 对所有 API 相关的数据结构使用
Anthropic.MessageParam、Anthropic.Tool、Anthropic.ToolUseBlock、Anthropic.ToolResultBlockParam、Anthropic.Message等。不要重新定义等效的接口。
处理工具结果
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
tools: tools,
messages: [{ role: "user", content: "What's the weather in Paris?" }],
});
for (const block of response.content) {
if (block.type === "tool_use") {
const result = await executeTool(block.name, block.input);
const followup = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
tools: tools,
messages: [
{ role: "user", content: "What's the weather in Paris?" },
{ role: "assistant", content: response.content },
{
role: "user",
content: [
{ type: "tool_result", tool_use_id: block.id, content: result },
],
},
],
});
}
}工具选择
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
tools: tools,
tool_choice: { type: "tool", name: "get_weather" },
messages: [{ role: "user", content: "What's the weather in Paris?" }],
});代码执行
基本用法
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
messages: [
{
role: "user",
content:
"Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]",
},
],
tools: [{ type: "code_execution_20260120", name: "code_execution" }],
});上传文件进行分析
import Anthropic, { toFile } from "@anthropic-ai/sdk";
import { createReadStream } from "fs";
const client = new Anthropic();
// 1. Upload a file
const uploaded = await client.beta.files.upload({
file: await toFile(createReadStream("sales_data.csv"), undefined, {
type: "text/csv",
}),
betas: ["files-api-2025-04-14"],
});
// 2. Pass to code execution
// Code execution is GA; Files API is still beta (pass via RequestOptions)
const response = await client.messages.create(
{
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
messages: [
{
role: "user",
content: [
{
type: "text",
text: "Analyze this sales data. Show trends and create a visualization.",
},
{ type: "container_upload", file_id: uploaded.id },
],
},
],
tools: [{ type: "code_execution_20260120", name: "code_execution" }],
},
{ headers: { "anthropic-beta": "files-api-2025-04-14" } },
);检索生成的文件
import path from "path";
import fs from "fs";
const OUTPUT_DIR = "./claude_outputs";
await fs.promises.mkdir(OUTPUT_DIR, { recursive: true });
for (const block of response.content) {
if (block.type === "bash_code_execution_tool_result") {
const result = block.content;
if (result.type === "bash_code_execution_result" && result.content) {
for (const fileRef of result.content) {
if (fileRef.type === "bash_code_execution_output") {
const metadata = await client.beta.files.retrieveMetadata(
fileRef.file_id,
);
const response = await client.beta.files.download(fileRef.file_id);
const fileBytes = Buffer.from(await response.arrayBuffer());
const safeName = path.basename(metadata.filename);
if (!safeName || safeName === "." || safeName === "..") {
console.warn(`Skipping invalid filename: ${metadata.filename}`);
continue;
}
const outputPath = path.join(OUTPUT_DIR, safeName);
await fs.promises.writeFile(outputPath, fileBytes);
console.log(`Saved: ${outputPath}`);
}
}
}
}
}容器复用
// First request: set up environment
const response1 = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
messages: [
{
role: "user",
content: "Install tabulate and create data.json with sample user data",
},
],
tools: [{ type: "code_execution_20260120", name: "code_execution" }],
});
// Reuse container
const containerId = response1.container.id;
const response2 = await client.messages.create({
container: containerId,
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
messages: [
{
role: "user",
content: "Read data.json and display as a formatted table",
},
],
tools: [{ type: "code_execution_20260120", name: "code_execution" }],
});记忆工具
基本用法
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 2048,
messages: [
{
role: "user",
content: "Remember that my preferred language is TypeScript.",
},
],
tools: [{ type: "memory_20250818", name: "memory" }],
});SDK 记忆助手
使用 betaMemoryTool 和 MemoryToolHandlers 实现:
import {
betaMemoryTool,
type MemoryToolHandlers,
} from "@anthropic-ai/sdk/helpers/beta/memory";
const handlers: MemoryToolHandlers = {
async view(command) { ... },
async create(command) { ... },
async str_replace(command) { ... },
async insert(command) { ... },
async delete(command) { ... },
async rename(command) { ... },
};
const memory = betaMemoryTool(handlers);
const runner = client.beta.messages.toolRunner({
model: "{\{OPUS_ID}\}",
max_tokens: 2048,
tools: [memory],
messages: [{ role: "user", content: "Remember my preferences" }],
});
for await (const message of runner) {
console.log(message);
}有关完整的实现示例,请使用 WebFetch:
https://github.com/anthropics/anthropic-sdk-typescript/blob/main/examples/tools-helpers-memory.ts
结构化输出
JSON 输出(Zod — 推荐)
import Anthropic from "@anthropic-ai/sdk";
import { z } from "zod";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
const ContactInfoSchema = z.object({
name: z.string(),
email: z.string(),
plan: z.string(),
interests: z.array(z.string()),
demo_requested: z.boolean(),
});
const client = new Anthropic();
const response = await client.messages.parse({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
messages: [
{
role: "user",
content:
"Extract: Jane Doe (jane@co.com) wants Enterprise, interested in API and SDKs, wants a demo.",
},
],
output_config: {
format: zodOutputFormat(ContactInfoSchema),
},
});
console.log(response.parsed_output.name); // "Jane Doe"严格工具使用
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
messages: [
{
role: "user",
content: "Book a flight to Tokyo for 2 passengers on March 15",
},
],
tools: [
{
name: "book_flight",
description: "Book a flight to a destination",
strict: true,
input_schema: {
type: "object",
properties: {
destination: { type: "string" },
date: { type: "string", format: "date" },
passengers: {
type: "integer",
enum: [1, 2, 3, 4, 5, 6, 7, 8],
},
},
required: ["destination", "date", "passengers"],
additionalProperties: false,
},
},
],
});英文原文 / English Original
Tool Use — TypeScript
For conceptual overview (tool definitions, tool choice, tips), see shared/tool-use-concepts.md.
Tool Runner (Recommended)
Beta: The tool runner is in beta in the TypeScript SDK.
Use betaZodTool with Zod schemas to define tools with a run function, then pass them to client.beta.messages.toolRunner():
import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";
const client = new Anthropic();
const getWeather = betaZodTool({
name: "get_weather",
description: "Get current weather for a location",
inputSchema: z.object({
location: z.string().describe("City and state, e.g., San Francisco, CA"),
unit: z.enum(["celsius", "fahrenheit"]).optional(),
}),
run: async (input) => {
// Your implementation here
return `72°F and sunny in \${input.location}`;
},
});
// The tool runner handles the agentic loop and returns the final message
const finalMessage = await client.beta.messages.toolRunner({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
tools: [getWeather],
messages: [{ role: "user", content: "What's the weather in Paris?" }],
});
console.log(finalMessage.content);Key benefits of the tool runner:
- No manual loop — the SDK handles calling tools and feeding results back
- Type-safe tool inputs via Zod schemas
- Tool schemas are generated automatically from Zod definitions
- Iteration stops automatically when Claude has no more tool calls
Manual Agentic Loop
Use this when you need fine-grained control (custom logging, conditional tool execution, streaming individual iterations, human-in-the-loop approval):
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [...]; // Your tool definitions
let messages: Anthropic.MessageParam[] = [{ role: "user", content: userInput }];
while (true) {
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
tools: tools,
messages: messages,
});
if (response.stop_reason === "end_turn") break;
// Server-side tool hit iteration limit; re-send to continue
if (response.stop_reason === "pause_turn") {
messages = [
{ role: "user", content: userInput },
{ role: "assistant", content: response.content },
];
continue;
}
const toolUseBlocks = response.content.filter(
(b): b is Anthropic.ToolUseBlock => b.type === "tool_use",
);
messages.push({ role: "assistant", content: response.content });
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const tool of toolUseBlocks) {
const result = await executeTool(tool.name, tool.input);
toolResults.push({
type: "tool_result",
tool_use_id: tool.id,
content: result,
});
}
messages.push({ role: "user", content: toolResults });
}Streaming Manual Loop
Use client.messages.stream() + finalMessage() instead of .create() when you need streaming within a manual loop. Text deltas are streamed on each iteration; finalMessage() collects the complete Message so you can inspect stop_reason and extract tool-use blocks:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [...];
let messages: Anthropic.MessageParam[] = [{ role: "user", content: userInput }];
while (true) {
const stream = client.messages.stream({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
tools,
messages,
});
// Stream text deltas on each iteration
stream.on("text", (delta) => {
process.stdout.write(delta);
});
// finalMessage() resolves with the complete Message — no need to
// manually wire up .on("message") / .on("error") / .on("abort")
const message = await stream.finalMessage();
if (message.stop_reason === "end_turn") break;
// Server-side tool hit iteration limit; re-send to continue
if (message.stop_reason === "pause_turn") {
messages = [
{ role: "user", content: userInput },
{ role: "assistant", content: message.content },
];
continue;
}
const toolUseBlocks = message.content.filter(
(b): b is Anthropic.ToolUseBlock => b.type === "tool_use",
);
messages.push({ role: "assistant", content: message.content });
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const tool of toolUseBlocks) {
const result = await executeTool(tool.name, tool.input);
toolResults.push({
type: "tool_result",
tool_use_id: tool.id,
content: result,
});
}
messages.push({ role: "user", content: toolResults });
}Important: Don't wrap
.on()events innew Promise()to collect the final message — usestream.finalMessage()instead. The SDK handles all error/abort/completion states internally.
Error handling in the loop: Use the SDK's typed exceptions (e.g.,
Anthropic.RateLimitError,Anthropic.APIError) — see Error Handling for examples. Don't check error messages with string matching.
SDK types: Use
Anthropic.MessageParam,Anthropic.Tool,Anthropic.ToolUseBlock,Anthropic.ToolResultBlockParam,Anthropic.Message, etc. for all API-related data structures. Don't redefine equivalent interfaces.
Handling Tool Results
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
tools: tools,
messages: [{ role: "user", content: "What's the weather in Paris?" }],
});
for (const block of response.content) {
if (block.type === "tool_use") {
const result = await executeTool(block.name, block.input);
const followup = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
tools: tools,
messages: [
{ role: "user", content: "What's the weather in Paris?" },
{ role: "assistant", content: response.content },
{
role: "user",
content: [
{ type: "tool_result", tool_use_id: block.id, content: result },
],
},
],
});
}
}Tool Choice
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
tools: tools,
tool_choice: { type: "tool", name: "get_weather" },
messages: [{ role: "user", content: "What's the weather in Paris?" }],
});Code Execution
Basic Usage
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
messages: [
{
role: "user",
content:
"Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]",
},
],
tools: [{ type: "code_execution_20260120", name: "code_execution" }],
});Upload Files for Analysis
import Anthropic, { toFile } from "@anthropic-ai/sdk";
import { createReadStream } from "fs";
const client = new Anthropic();
// 1. Upload a file
const uploaded = await client.beta.files.upload({
file: await toFile(createReadStream("sales_data.csv"), undefined, {
type: "text/csv",
}),
betas: ["files-api-2025-04-14"],
});
// 2. Pass to code execution
// Code execution is GA; Files API is still beta (pass via RequestOptions)
const response = await client.messages.create(
{
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
messages: [
{
role: "user",
content: [
{
type: "text",
text: "Analyze this sales data. Show trends and create a visualization.",
},
{ type: "container_upload", file_id: uploaded.id },
],
},
],
tools: [{ type: "code_execution_20260120", name: "code_execution" }],
},
{ headers: { "anthropic-beta": "files-api-2025-04-14" } },
);Retrieve Generated Files
import path from "path";
import fs from "fs";
const OUTPUT_DIR = "./claude_outputs";
await fs.promises.mkdir(OUTPUT_DIR, { recursive: true });
for (const block of response.content) {
if (block.type === "bash_code_execution_tool_result") {
const result = block.content;
if (result.type === "bash_code_execution_result" && result.content) {
for (const fileRef of result.content) {
if (fileRef.type === "bash_code_execution_output") {
const metadata = await client.beta.files.retrieveMetadata(
fileRef.file_id,
);
const response = await client.beta.files.download(fileRef.file_id);
const fileBytes = Buffer.from(await response.arrayBuffer());
const safeName = path.basename(metadata.filename);
if (!safeName || safeName === "." || safeName === "..") {
console.warn(`Skipping invalid filename: \${metadata.filename}`);
continue;
}
const outputPath = path.join(OUTPUT_DIR, safeName);
await fs.promises.writeFile(outputPath, fileBytes);
console.log(`Saved: \${outputPath}`);
}
}
}
}
}Container Reuse
// First request: set up environment
const response1 = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
messages: [
{
role: "user",
content: "Install tabulate and create data.json with sample user data",
},
],
tools: [{ type: "code_execution_20260120", name: "code_execution" }],
});
// Reuse container
const containerId = response1.container.id;
const response2 = await client.messages.create({
container: containerId,
model: "{\{OPUS_ID}\}",
max_tokens: 4096,
messages: [
{
role: "user",
content: "Read data.json and display as a formatted table",
},
],
tools: [{ type: "code_execution_20260120", name: "code_execution" }],
});Memory Tool
Basic Usage
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 2048,
messages: [
{
role: "user",
content: "Remember that my preferred language is TypeScript.",
},
],
tools: [{ type: "memory_20250818", name: "memory" }],
});SDK Memory Helper
Use betaMemoryTool with a MemoryToolHandlers implementation:
import {
betaMemoryTool,
type MemoryToolHandlers,
} from "@anthropic-ai/sdk/helpers/beta/memory";
const handlers: MemoryToolHandlers = {
async view(command) { ... },
async create(command) { ... },
async str_replace(command) { ... },
async insert(command) { ... },
async delete(command) { ... },
async rename(command) { ... },
};
const memory = betaMemoryTool(handlers);
const runner = client.beta.messages.toolRunner({
model: "{\{OPUS_ID}\}",
max_tokens: 2048,
tools: [memory],
messages: [{ role: "user", content: "Remember my preferences" }],
});
for await (const message of runner) {
console.log(message);
}For full implementation examples, use WebFetch:
https://github.com/anthropics/anthropic-sdk-typescript/blob/main/examples/tools-helpers-memory.ts
Structured Outputs
JSON Outputs (Zod — Recommended)
import Anthropic from "@anthropic-ai/sdk";
import { z } from "zod";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
const ContactInfoSchema = z.object({
name: z.string(),
email: z.string(),
plan: z.string(),
interests: z.array(z.string()),
demo_requested: z.boolean(),
});
const client = new Anthropic();
const response = await client.messages.parse({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
messages: [
{
role: "user",
content:
"Extract: Jane Doe (jane@co.com) wants Enterprise, interested in API and SDKs, wants a demo.",
},
],
output_config: {
format: zodOutputFormat(ContactInfoSchema),
},
});
console.log(response.parsed_output.name); // "Jane Doe"Strict Tool Use
const response = await client.messages.create({
model: "{\{OPUS_ID}\}",
max_tokens: 1024,
messages: [
{
role: "user",
content: "Book a flight to Tokyo for 2 passengers on March 15",
},
],
tools: [
{
name: "book_flight",
description: "Book a flight to a destination",
strict: true,
input_schema: {
type: "object",
properties: {
destination: { type: "string" },
date: { type: "string", format: "date" },
passengers: {
type: "integer",
enum: [1, 2, 3, 4, 5, 6, 7, 8],
},
},
required: ["destination", "date", "passengers"],
additionalProperties: false,
},
},
],
});