Tool Calling in AI Agents: How It Actually Works
Learning Journal

Tool Calling in AI Agents: How It Actually Works

Z
Zahid Hasan Tonmoy
September 28, 2026
9 min read
🇧🇩 বাংলায় পড়ুন
|
0 views
Audio Overview & Podcast
~1 min quick overview
0%

Last time I opened up an agent and pointed at its four moving parts — brain, memory, tools and the loop that ties them together. Tools got one paragraph in that post. This one is about what actually happens inside that box: how a model that can only produce text ends up checking today's weather or converting currency for you.

Ask a plain chatbot, with no tools attached, “What's the weather in Dhaka right now, and how much is 500 dollars in taka today?” It will answer both parts without blinking. The temperature will be a guess. The exchange rate will be whatever was true whenever the model was trained, presented as if it were today's. Nothing it just told you was checked against anything real.

What Is Tool Calling?

Tool calling, also called function calling, is the mechanism that lets a language model request a specific, predefined function with structured arguments instead of only producing text. Your code runs that function against real data and sends the result back, and the model uses it to keep responding. It's the one feature that turns a model which can only write into a model that can also check, convert and act.

The Anatomy of a Tool

A tool definition doesn't teach the model how to write code. It tells the model what it may ask for, in three parts:

  • name — a short identifier, like get_weather.
  • description — plain language explaining exactly when to use it. The model never reads your implementation, only this text, so it functions as API documentation written for a reader who can't ask a follow-up question.
  • input_schema — a JSON Schema listing the arguments, their types, and which ones are required.

Get any of the three vague and the model either avoids a tool it should have used or reaches for the wrong one. This is the part of tool calling that has nothing to do with code and everything to do with writing clearly.

One Turn, Two Tools: How Parallel Calls Work

A single response from the model isn't limited to one tool request. If a question needs two independent facts, the model can ask for both tools in the same turn instead of asking for one, waiting for the answer, then asking for the next.

📊 Architecture DiagramArchitecture Flow
Interactive diagram rendering...
flowchart TD
    U[User asks two things at once] --> B[Brain reads the message]
    B -->|needs weather| W[get_weather]
    B -->|needs currency| C[convert_currency]
    W --> R[Both results collected]
    C --> R
    R --> B2[Brain reads both results]
    B2 --> F[One final answer]
System architecture specification and node flow: flowchart TD U[User asks two things at once] --> B[Brain reads the message] B -->|needs weather| W[get_weather] B -->|needs currency| C[convert_currency] W --> R[Both results collected] C --> R R --> B2[Brain reads both results] B2 --> F[One final answer]

Flow summary: The brain can ask for more than one tool in a single turn. Your code runs each one, waits until every result is ready, and sends all of them back together before the brain continues.

That last part matters more than it looks. Every tool the model asks for in one turn has to be answered in the very next message, with one result matched to each request by its ID. Skip one, and the conversation can't continue — more on that in a moment.

Telling the Model Which Tool to Use

By default the model decides on its own whether a tool is needed and which one fits, and that default, called auto, is right for almost everything. Three other settings exist for when you want more control:

  • { type: 'any' } — the model must call some tool, though it picks which one.
  • { type: 'tool', name: 'get_weather' } — the model must call this exact tool.
  • { type: 'none' } — the model must not call a tool this turn, even though the list is still attached. Useful for a final pass where you just want a clean text answer.

One caveat worth knowing before you rely on it: forcing any or a named tool doesn't combine with extended thinking. If thinking is turned on, only auto and none are supported for that turn.

A Working Example: Two Tools, One Question

This agent answers weather and currency questions with two independent tools instead of one. Install the SDK with npm install @anthropic-ai/sdk, set ANTHROPIC_API_KEY, save this as agent/tool-calling.ts and run npx tsx agent/tool-calling.ts.

ts
// agent/tool-calling.ts
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

const weatherData: Record<string, { condition: string; temp_c: number }> = {
  Dhaka: { condition: 'humid and cloudy', temp_c: 31 },
  Chittagong: { condition: 'light rain', temp_c: 29 },
};

const rates: Record<string, number> = {
  USD_BDT: 122.5,
  BDT_USD: 1 / 122.5,
};

const tools: Anthropic.Tool[] = [
  {
    name: 'get_weather',
    description:
      'Get the current weather condition and temperature in Celsius for a named city. Use this whenever the user asks about weather, temperature, or whether it will rain somewhere.',
    input_schema: {
      type: 'object',
      properties: {
        city: { type: 'string', description: 'City name, e.g. Dhaka' },
      },
      required: ['city'],
    },
  },
  {
    name: 'convert_currency',
    description:
      'Convert an amount from one currency to another using the current exchange rate. Use this whenever the user asks how much money is worth in a different currency.',
    input_schema: {
      type: 'object',
      properties: {
        amount: { type: 'number' },
        from: { type: 'string', description: 'Three-letter currency code, e.g. USD' },
        to: { type: 'string', description: 'Three-letter currency code, e.g. BDT' },
      },
      required: ['amount', 'from', 'to'],
    },
  },
];

function getWeather(city: string) {
  const data = weatherData[city];
  return data ? { city, ...data } : { error: `no weather data for ${city}` };
}

function convertCurrency(amount: number, from: string, to: string) {
  const rate = rates[`${from}_${to}`];
  if (!rate) return { error: `no exchange rate for ${from} to ${to}` };
  return { amount, from, to, converted: Math.round(amount * rate * 100) / 100, rate };
}

function execute(name: string, input: Record<string, unknown>): unknown {
  try {
    if (name === 'get_weather') return getWeather(String(input.city));
    if (name === 'convert_currency') {
      return convertCurrency(Number(input.amount), String(input.from), String(input.to));
    }
    return { error: 'unknown tool' };
  } catch (err) {
    return { error: String(err) };
  }
}

async function runAgent(userText: string): Promise<string> {
  const messages: Anthropic.MessageParam[] = [{ role: 'user', content: userText }];

  for (let step = 0; step < 5; step++) {
    const response = await client.messages.create({
      model: 'claude-sonnet-5',
      max_tokens: 1024,
      tools,
      messages,
    });

    messages.push({ role: 'assistant', content: response.content });

    if (response.stop_reason !== 'tool_use') {
      return response.content.flatMap((b) => (b.type === 'text' ? [b.text] : [])).join('');
    }

    const results: Anthropic.ToolResultBlockParam[] = [];
    for (const block of response.content) {
      if (block.type === 'tool_use') {
        const output = execute(block.name, block.input as Record<string, unknown>);
        results.push({ type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(output) });
      }
    }
    messages.push({ role: 'user', content: results });
  }

  return 'Stopped: step limit reached';
}

runAgent("What's the weather in Dhaka, and how much is 500 USD in BDT?").then(console.log);

Ask it about Dhaka's weather and a currency conversion in the same sentence, and the loop above runs once with two tool_use blocks in the response, executes get_weather and convert_currency, and sends both results back in a single message before the model writes its answer.

The Mistake That Cost Me a Debugging Session

The first version of this agent only had one tool. My loop grabbed the tool request with response.content.find(b => b.type === 'tool_use'), since there was only ever going to be one, and moved on. It worked fine through every test I ran.

Then I added convert_currency and asked a question that needed both tools at once. The model, correctly, asked for both in the same response. My code found the first one, answered it, and quietly dropped the second. The next call didn't come back with a wrong answer. It refused outright, pointing at the exact tool ID that never got a result and stopping until every open tool call was answered. That's not a lenient API being generous with a small mistake; it enforces a strict rule: every tool_use block must be answered by exactly one tool_result in the next message. My loop only handled the first request instead of looping over all of them, so the fix was to stop assuming there would ever be exactly one tool call and iterate over every block the model actually sent.

Common Mistakes With Tool Calling

  • Reading only the first tool call. As above — always loop over every tool_use block, never assume the count.
  • Overlapping descriptions. Two tools whose descriptions both sound like a fit for the same request force the model to guess. If you find yourself explaining the difference between two tools in your own head, rewrite the descriptions until a stranger wouldn't need to ask.
  • Vague units in the schema. “amount: number” with no mention of what currency or scale it's in invites the model to guess. Say it in the description: amount in the currency named by from, not a formatted string.
  • One tool with too many optional parameters. A tool with fifteen optional fields is harder to fill in correctly than three small tools with two or three required fields each. Split by shape, not by convenience.

What's Next

Tool calling gives an agent hands: something to reach out and act with. What it still needs is a way to know things it wasn't told directly in the prompt — that's the next piece worth opening up.

Frequently Asked Questions

What's the difference between tool calling and function calling?

They're the same mechanism under two names. “Function calling” is the older term, from when a tool was always a plain function you wrote. “Tool calling” is the term used now because a tool doesn't have to be your code — it can be a hosted capability like web search. In the code you write, both work identically: a name, a description, a schema, and a result you send back.

Can a tool call fail without breaking the conversation?

Yes, as long as your code catches the failure and returns it as the tool result instead of throwing an unhandled error. Describe what went wrong in plain text, optionally mark the result as an error, and let the model decide what to do next: try different arguments, ask the user for missing information, or say plainly that it can't complete the request.

Do I need a separate tool for every action?

Not if the actions share the same shape. Closely related actions can live under one tool with a parameter like action: 'add' | 'remove'. Split them into separate tools once their required arguments genuinely differ. The model chooses by reading the whole tool list, so too many near-identical tools makes the choice harder, not easier.

img2
img2

img1
img1

thumbnail
thumbnail

img2
img2

img1
img1

thumbnail
thumbnail

🟢 Available for Freelance & Contract Work

Need a High-Performance Web App or Custom AI Solution?

I help founders, businesses, and engineering teams build lightning-fast web applications, resilient backend architectures, and intelligent AI workflows. Have an idea in mind? Let’s bring it to life.

Fast MVP Launch (2–4 weeks)
AI Agent & LLM API Integration
100/100 Core Web Vitals & SEO
Production-Ready Clean Architecture

Found this article helpful?

Give some claps to support more in-depth engineering logs!

0 views
Z

Zahid Hasan Tonmoy

Author & Developer

MERN Full Stack Developer & AI Agent Developer based in Dhaka, Bangladesh. Writing about web development, React, PostgreSQL and my learning journey.

Related Articles

Stay Updated

Subscribe to get insights on full-stack architecture, AI agent engineering, and web development.