# Tool Calling Explained with Real Examples

Tool calling is how an AI model reaches outside its own context window to do
real work. You have probably heard it called "function calling" too. Same
thing. Here is the part people miss: the model does not run your tools. It asks
to. Your system runs them and hands the results back. Get that one distinction
and the rest follows.

## The key insight: the model only asks

When Claude decides it needs to search the web or read a file, it does not
reach out and do it. It emits a structured request, something like:

```json
{
  "tool": "web_search",
  "input": { "query": "current Node.js LTS version" }
}
```

Your application receives that request, calls the real tool, and returns the
result. The model never had network access. It asked, you fetched, you told it
what you found. From there it carries on.

Why does this matter? A few things fall out of it:

- You control exactly what tools exist and what they can do.
- Every tool call is auditable. You see the request before it runs.
- You can block, log, or rewrite any call before it executes.
- The model can plan whole sequences of calls without ever running one itself.

## How does tool calling work?

Every tool interaction follows the same four-step cycle, regardless of what
the tool does.

One round is rarely enough. A model working on a hard task might call four
tools in sequence, each result feeding the next call, before it produces a
final answer. In Claude Code you watch every step scroll past as it happens.

## Concrete examples

The same loop drives every kind of tool. Here are four categories, with what
the model asks for and what comes back.

### Web search

The model wants current information it was not trained on. It asks:

```json
{ "tool": "web_search", "input": { "query": "Next.js 15 breaking changes" } }
```

Your app calls the search API, grabs the top results, and returns the titles,
snippets, and URLs as plain text. The model reads that text. Then it decides:
fetch a specific page, answer directly, or search again with a sharper query.

### Reading a file

The model needs to understand code before editing it. It asks:

```json
{ "tool": "read_file", "input": { "path": "src/auth/session.ts" } }
```

Your app opens the file and returns its contents as a string. The model reads
it, sees the structure, and makes a targeted edit instead of guessing.

### Querying a database

An agent doing data analysis might ask:

```json
{
  "tool": "run_sql",
  "input": { "query": "SELECT user_id, COUNT(*) FROM events GROUP BY user_id LIMIT 20" }
}
```

Your app executes that query against your actual database (with whatever
access controls you have wired in) and returns the rows as JSON. The model
interprets the data and continues.Because the model only asks and your app runs, it is tempting to hand a model-written SQL string straight to your database. That string carries the model's full DB privileges, so a confused or prompt-injected model can issue a `DELETE` or read a table it should never see. Give the runner a read-only role scoped to the rows the task needs, and validate the call before it executes, rather than trusting the request just because the model emitted it.

### Hitting an API

A support agent might ask:

```json
{
  "tool": "get_order",
  "input": { "order_id": "ORD-88421" }
}
```

Your app calls your internal order service and returns the order object. Now
the model answers the customer from real data, with no invented order details.

## What the model sees at each stage

It helps to watch the context grow over a multi-step task. Picture the model's
view as a conversation that keeps stacking up results.

| Turn | Added to context |
|---|---|
| 1 | User prompt: "What changed in Next.js 15?" |
| 2 | Model tool call: `web_search("Next.js 15 changelog")` |
| 3 | Tool result: search snippets injected |
| 4 | Model tool call: `fetch_url("https://nextjs.org/blog/next-15")` |
| 5 | Tool result: full article text injected |
| 6 | Model final answer synthesised from both results |

The model reasons over real information it asked for
and received.Each result stays in context for the rest of the task, so a tool that returns a whole file or thousands of rows can fill the window and bury the signal the model needs. Trim results to the fields that matter and paginate large lists rather than returning everything at once.

## What makes a good tool?

The quality of your tools sets the ceiling on the model's work. Poorly defined
tools lead to wrong calls, missed steps, and invented parameters. Good tools
share these properties:

| Property | What it means in practice |
|---|---|
| Single purpose | Does exactly one thing. A `get_user` tool should not also update the user. |
| Clear name | Reads like a verb phrase. `search_products`, not `do_stuff`. |
| Typed inputs | Every parameter has a name, a type, and a description the model can read. |
| Predictable output | Returns consistent structure. The model learns your schema from one call. |
| Tight scope | Returns only what is needed. A paginated `list` beats dumping 10,000 rows. |
| Safe defaults | Destructive operations (delete, send, pay) should require explicit confirmation or an extra parameter. |

The model reads your tool's name and description every time it weighs up calling
it. Write those descriptions for a reader who has never seen your system. A
vague description means the model guesses, and guesses compound across
multi-step tasks.

## The tool-calling loop in Claude Code

In Claude Code you never write the loop. The CLI ships with a built-in toolbox
(Read, Edit, Bash, WebSearch, WebFetch, and more), and you extend it with
MCP
servers. The agent runs the request-decide-run-return cycle on its own, for
every tool call a task needs.

You interact with it through the CLI:

```bash
# See every MCP server and the tools it exposes
claude mcp list
claude mcp get 

# Add a new server (and its tools) to the agent's toolbox
claude mcp add github -- npx -y @modelcontextprotocol/server-github

# Ask the agent something that needs a tool
claude "What are the open PRs on this repo assigned to me?"
```

That last command will trigger at least one tool call to the GitHub MCP
server, inject the result, and give you a real answer from your actual repo.

## How does tool calling power agents?

One tool call is useful. A chain of them is an agent. Let the model loop,
calling tools until it has what a task needs, and you get autonomous behaviour
without hard-coding a workflow.

Every arrow in that diagram is a tool call the model chose to make off the back
of the previous result. No script decides the order; the model makes
the decisions. Your tools feed it real information at each step, which is what keeps
those decisions grounded.

This is also why tool quality compounds. A poorly described tool causes one
bad decision. That decision changes the context for the next call, which skews
the one after it. Errors stack. Good tools keep the model on track across the
whole chain.

## How does MCP extend tool calling?

MCP
is the standard that lets you add new tools without touching the model. An MCP
server is a program that registers named tools the model can call. Connect one,
and its tools show up next to the built-ins. The model treats them the same.

The pattern is:

1. Write or install an MCP server that wraps your API, database, or service.
2. Connect it with `claude mcp add`.
3. The model discovers the tools at startup and can call them in any task.

You can commit a `.mcp.json` file to a repo so every contributor gets the
same tools without manual setup. The full configuration format is covered in
[MCP Configuration](/guides/mcp-configuration).

```json
{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "${DATABASE_URL}"],
      "type": "stdio"
    }
  }
}
```

With that in place, Claude Code can query your database mid-task, and you wrote
no query code at all. You wrote the configuration. MCP and the tool-calling loop
do the rest.

## Common mistakes

**Assuming the model runs the tool itself.** It does not. The model asks. You
run. If your tool runner is not connected, the call fails silently or the
model reports it cannot reach the tool. Always verify a new MCP server is
registered with `claude mcp list` before expecting it to work.

**Vague tool descriptions.** The model reads your description every time it
decides whether to call a tool. "Does things with orders" tells it nothing.
"Returns full order details including line items and status for a given order
ID" tells it exactly when to reach for this tool.

**Tools with too wide a scope.** A tool that can read, update, and delete
records is harder for the model to use correctly than three separate ones. It
will sometimes call the wrong operation. Narrow tools produce reliable
behaviour.

**No pagination on list tools.** If a `list_products` tool returns every
product in your catalogue, the model's context fills up fast. Cap results and
let the model ask for the next page when it needs more.

**Confusing tool calling with tool use in Claude Code.** In Claude Code you
are configuring tools and writing prompts, not wiring the request loop. The
loop is already built in. The guides [Tool Use and Function Calling](/guides/tool-use-and-function-calling) and
[MCP Fundamentals](/guides/mcp-fundamentals) cover the Claude Code-specific configuration in detail.

## What to learn next

Once the request-decide-run-return loop is clear, the natural next step is
understanding how multiple agents share tools and hand tasks to each other.
See [Multi-Agent Architectures](/guides/multi-agent-architectures) for that. For the mechanics of connecting
real MCP servers to Claude Code, see [MCP Fundamentals](/guides/mcp-fundamentals) and [MCP
Configuration](/guides/mcp-configuration).
