Prerequisites
Before beginning, ensure you have the following installed on your developer machine:
- Node.js (v18.0.0 or higher)
- An Anthropic API key (console.anthropic.com)
- TypeScript & ts-node (optional for immediate execution)
Initialize Project & Install the Official SDK
Create an isolated directory and install the official Anthropic Node.js SDK and `dotenv` for secure environment variable handling:
mkdir claude-agent && cd claude-agent
npm init -y
npm install @anthropic-ai/sdk dotenv
npm install -D typescript @types/node ts-node
Create a .env file in your root folder:
ANTHROPIC_API_KEY="sk-ant-api03-..."
Define the Client and Tool Schema
Create index.ts. We will define a real-world tool that queries stock inventory by SKU:
import Anthropic from "@anthropic-ai/sdk";
import * as dotenv from "dotenv";
dotenv.config();
const anthropic = new Anthropic();
// 1. Define the tool specification with strict schema
const tools: Anthropic.Tool[] = [
{
name: "lookup_inventory",
description: "Check the available warehouse stock and restock date for a product SKU.",
input_schema: {
type: "object",
properties: {
sku: {
type: "string",
description: "Product SKU identifier (e.g., 'KB-PRO-99')"
}
},
required: ["sku"]
}
}
];
Execute the Tool Call Cycle
When Claude determines that answering the query requires external data, it returns a tool_use block
with stop_reason: "tool_use". Your application executes the tool and delivers the result back to the model:
async function runAgent(userPrompt: string) {
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: userPrompt }
];
// First call: Claude decides whether to use the tool
const response = await anthropic.messages.create({
model: "claude-3-7-sonnet-20250219",
max_tokens: 1024,
tools: tools,
messages: messages
});
console.log("Claude Stop Reason:", response.stop_reason);
// Check if tool execution was requested
if (response.stop_reason === "tool_use") {
const toolBlock = response.content.find(block => block.type === "tool_use");
if (toolBlock && toolBlock.type === "tool_use") {
console.log(`Executing tool "${toolBlock.name}" with args:`, toolBlock.input);
// Mock database lookup
const mockResult = {
sku: (toolBlock.input as any).sku,
in_stock: 42,
warehouse: "Austin-TX",
next_delivery: "2026-11-15"
};
// Append assistant message and tool_result message
messages.push({ role: "assistant", content: response.content });
messages.push({
role: "user",
content: [
{
type: "tool_result",
tool_use_id: toolBlock.id,
content: JSON.stringify(mockResult)
}
]
});
// Second call: Claude generates the final human answer
const finalResponse = await anthropic.messages.create({
model: "claude-3-7-sonnet-20250219",
max_tokens: 1024,
tools: tools,
messages: messages
});
console.log("\nFinal Answer:\n", (finalResponse.content[0] as any).text);
}
}
}
runAgent("How many units of SKU 'KB-PRO-99' do we have in stock?");
Execute and Verify Expected Output
Run the script using ts-node:
$ npx ts-node index.ts
Claude Stop Reason: tool_use
Executing tool "lookup_inventory" with args: { sku: 'KB-PRO-99' }
Final Answer:
There are currently 42 units of SKU 'KB-PRO-99' in stock at the Austin, TX warehouse.
The next scheduled delivery is expected on November 15, 2026.
Always validate the tool input arguments with a runtime validator such as Zod before passing them to internal databases or payment gateways. This guarantees total immunity against prompt-injected argument tampering.