How to use Claude's tool use (function calling) in Node.js — with real examples
Claude's tool use feature (also called function calling) is one of the most powerful things you can do with the API. Instead of just generating text, Claude can decide when to call external functions — and then use the results to give better answers.
This tutorial covers the complete loop: define tools → Claude picks one → you run it → Claude uses the result.
What tool use actually is
Without tool use, Claude receives a prompt and returns text. That's it. The conversation is:
You → "What's the weather in Lagos?"
Claude → "I don't have access to real-time weather data..."
With tool use, the conversation becomes:
You → "What's the weather in Lagos?"
Claude → [calls get_weather(city="Lagos")]
You → [run get_weather, return {temp: 32, condition: "sunny"}]
Claude → "It's 32°C and sunny in Lagos right now."
Claude decides when to use the tool. You decide what the tool actually does.
The complete Node.js implementation
Here's a working tool use loop under 80 lines:
const Anthropic = require('@anthropic-ai/sdk');
const client = new Anthropic({ apiKey: process.env.CLAUDE_API_KEY });
// Define your tools
const tools = [
{
name: 'get_weather',
description: 'Get current weather for a city. Returns temperature in Celsius and conditions.',
input_schema: {
type: 'object',
properties: {
city: {
type: 'string',
description: 'The city name, e.g. Lagos, Manila, Mumbai'
}
},
required: ['city']
}
},
{
name: 'calculate',
description: 'Perform a mathematical calculation. Returns the result.',
input_schema: {
type: 'object',
properties: {
expression: {
type: 'string',
description: 'Math expression to evaluate, e.g. "15 * 24 + 7"'
}
},
required: ['expression']
}
}
];
// Your actual tool implementations
function runTool(name, input) {
if (name === 'get_weather') {
// In production: call a real weather API here
const mockData = {
'Lagos': { temp: 32, condition: 'sunny' },
'Manila': { temp: 29, condition: 'partly cloudy' },
'Mumbai': { temp: 35, condition: 'humid' },
'Nairobi': { temp: 22, condition: 'cool and clear' }
};
return mockData[input.city] || { temp: 20, condition: 'unknown' };
}
if (name === 'calculate') {
try {
// Simple eval — use mathjs in production for safety
return { result: Function('"use strict"; return (' + input.expression + ')')() };
} catch (e) {
return { error: 'Invalid expression' };
}
}
}
// The tool use loop
async function chat(userMessage) {
const messages = [{ role: 'user', content: userMessage }];
while (true) {
const response = await client.messages.create({
model: 'claude-opus-4-5',
max_tokens: 1024,
tools: tools,
messages: messages
});
// If Claude is done (no more tool calls needed)
if (response.stop_reason === 'end_turn') {
const textBlock = response.content.find(b => b.type === 'text');
return textBlock ? textBlock.text : '';
}
// Claude wants to use a tool
if (response.stop_reason === 'tool_use') {
// Add Claude's response to the conversation
messages.push({ role: 'assistant', content: response.content });
// Find the tool call block
const toolUseBlock = response.content.find(b => b.type === 'tool_use');
// Run the tool
const toolResult = runTool(toolUseBlock.name, toolUseBlock.input);
console.log(`[Tool called: ${toolUseBlock.name}]`, toolUseBlock.input, '→', toolResult);
// Add the tool result back to the conversation
messages.push({
role: 'user',
content: [{
type: 'tool_result',
tool_use_id: toolUseBlock.id,
content: JSON.stringify(toolResult)
}]
});
// Loop again — Claude will now use the result
}
}
}
// Test it
(async () => {
console.log(await chat('What is the weather in Lagos?'));
console.log(await chat('Calculate 15 percent of 8500 for me'));
console.log(await chat('Compare the weather in Manila vs Nairobi'));
})();
Running it
npm install @anthropic-ai/sdk
CLAUDE_API_KEY=your_key node tool-use.js
Output:
[Tool called: get_weather] { city: 'Lagos' } → { temp: 32, condition: 'sunny' }
It's currently 32°C and sunny in Lagos.
[Tool called: calculate] { expression: '8500 * 0.15' } → { result: 1275 }
15% of 8,500 is 1,275.
[Tool called: get_weather] { city: 'Manila' } → { temp: 29, condition: 'partly cloudy' }
[Tool called: get_weather] { city: 'Nairobi' } → { temp: 22, condition: 'cool and clear' }
Manila is warmer at 29°C with partly cloudy skies, while Nairobi is cooler at 22°C and clear.
Notice the last example: Claude made two tool calls because it needed to compare two cities. The loop handles this automatically.
Key concepts to understand
stop_reason: 'tool_use' — Claude wants to call a function. Extract the tool block, run it, return the result.
stop_reason: 'end_turn' — Claude is done. Return the final text.
The messages array grows — each tool call adds 2 messages: Claude's tool_use block, then your tool_result. This is how Claude knows what happened.
You control execution — Claude can only request tool calls. Your code decides what actually runs. This is the safety boundary.
What to build with this
Real tool use patterns that work well:
- Database queries: Claude decides when to look up user data vs answer from context
- API calls: weather, stock prices, exchange rates, flight status
- File operations: read files, search codebases, list directories
- Code execution: run tests, evaluate expressions, lint code
- Multi-step workflows: Claude orchestrates a sequence of tool calls to complete a task
The cost question
Tool use adds tokens — every tool call adds the tool definition, the tool_use block, and the tool_result to the conversation. On per-token billing, a complex multi-tool conversation can cost 3-5x more than a simple message.
If you're building something where tool use is called frequently, flat-rate access makes the math a lot cleaner. I run my own Claude API wrapper at SimplyLouie for $2/month — unlimited calls, so tool use loops don't cost extra per iteration.
The architecture article explains how the wrapper is built if you want to run your own.
The full working code is in this article. Copy, paste, run. Questions in the comments.
Top comments (0)