Handbooks / RAG & AI Agents / Chapter 3
Tool Calling
7 pages · ~5 min✓ Reviewed
Builds on Chunking and Retrieval. Next up: Agent Loops and Memory.
Tool Calling Mental Model
1. What a Tool Is
- A tool = a function the model asks for by name with JSON arguments
- The model never runs it; your code does
- Definition has 3 parts:
name,description, input JSON schema - The
descriptionis the prompt: it tells the model when to use the tool
2. The Round Trip
- Sendmessages + tools
- Tool callname, id, arguments
- Runyour code
- Returnresult by id
| API | Wants tools | Final answer |
|---|---|---|
| Anthropic | stop_reason: tool_use | end_turn |
| OpenAI | finish_reason: tool_calls | stop |
Remember
A tool call comes instead of text. Send the result back, then repeat until a plain answer.
3. Interview Point
- Who executes a call? Your app, never the model
- What does the model output? A tool call: name, id, JSON arguments
- How do you know it wants a tool?
tool_use/tool_calls;end_turn/stopmeans final
Defining Tools and JSON Schemas
1. Schema Basics
| Keyword | Constrains |
|---|---|
type: object | top level; fields go in properties |
required | array of mandatory names |
type / enum | value kind / fixed choices |
minimum / maximum | number range |
items | element schema of an array |
description | meaning and format |
2. Steering the Model
- Description: what it does, when to use it, when not, each arg's format
- Fixed choices →
enum; keep params flat additionalProperties: falseasks for no extra keys; enforced only with strict tool use
json
{"name":"get_weather","input_schema":{"type":"object",
"properties":{"location":{"type":"string"},
"unit":{"enum":["celsius","fahrenheit"]}},
"required":["location"],
"additionalProperties":false}}the model's call is below
output
{"location":"Pune","unit":"celsius"}3. Mistakes and Interview Point
- Vague names like
do_action - Many overlapping tools
- Optional fields marked required: model invents values
Schema is a hint
Always validate arguments server side.
- Why validate? The schema only guides the model; check values server side
- Wrong tool? Sharp descriptions, few distinct tools, narrow schemas
- In a description? What, when, when not, argument formats
The Tool Call Loop
1. The Loop
- Call modelmessages + tools
- Tool calls?none: final answer
- Run eachyour code
- Appendassistant + results, repeat
- Match each result to its call by id (
tool_use_id/tool_call_id) - Results must follow the assistant message that made the calls
- Parallel calls: run them concurrently
- Return all results together, each matched by id
2. Skeleton and tool_choice
python
for _ in range(max_turns): resp = model(messages, tools) messages.append(resp.message) if not resp.tool_calls: break for c in resp.tool_calls: messages.append(result(c.id, run(c))) else: raise RuntimeError('turn cap hit')
for/else: the cap fails loudly, not silently
| Goal | Anthropic | OpenAI |
|---|---|---|
| Model decides | auto | auto |
| Must call some tool | any | required |
| Must call this tool | tool + name | named function |
| No tools | none | none |
3. Common Mistakes
- No turn cap: runaway loop and cost
- Dropped assistant tool-call message
- Result with a mismatched id
Always cap turns
A max_turns cap ends a runaway loop; handle the cap, never hang.
Structured Outputs and Error Handling
1. Structured Output
| Method | Guarantee |
|---|---|
| Prompt for JSON | None; may add prose or break syntax |
| Forced tool | always a tool call; args usually match, guaranteed only with strict mode |
| Schema-constrained output | Valid JSON in your schema |
- Forced tool: input schema = your shape, set
tool_choice - Still validate values; handle refusal and
max_tokenstruncation
2. Tool Errors
- Tool fails
- Flagged resultis_error true
- Model fixes call
- Retry cap, then give up
json
{"type":"tool_result","tool_use_id":"t1","is_error":true,
"content":"{\"error\":\"unit must be celsius or fahrenheit\"}"}short, actionable message
3. Mistakes
- Raising exceptions that crash the loop
- Returning raw stack traces
- Retrying forever
- Retrying payments or emails without safeguards
- No timeouts or permission checks on destructive tools
MCP Basics
1. What MCP Is
- MCP (Model Context Protocol): open standard linking AI apps to tools and data
- A host app runs several MCP clients
- Each client connects to one server
- Messages use JSON-RPC 2.0
2. Primitives and Transports
- Tools: model-invoked actions
- Resources: readable data
- Prompts: reusable templates
tools/listdiscovers toolstools/callinvokes one
| Transport | Runs | Server |
|---|---|---|
stdio | Local subprocess | Same machine |
| Streamable HTTP | Over the network | Remote |
3. Compare and Avoid
| MCP | Function calling | |
|---|---|---|
| Discovery | Standard tools/list | Hand-written |
| Reuse | One server, many apps | Custom glue per app |
| Schemas | JSON schema | Same JSON schema |
- Trusting third-party servers: prompt injection via tool output
- Over-broad permissions
- Too many tools bloat context
- Logging to stdout on
stdiocorrupts protocol; use stderr
Tool Calling Cheat Sheet
1. The Loop
- Model callmessages + tools
- Tool call?name, id, args
- Run in your codemodel never executes
- Result by idthen repeat
Always cap turns
Loop until a plain answer, but set max_turns. Results must match the call id.
2. Tool Definition
| Part | Rule |
|---|---|
| Name | specific, not do_action |
| Description | what, when, when not |
| Schema | types, enum, required, additionalProperties: false |
| Server | validate args anyway |
3. Structured Output and Errors
| Topic | Do |
|---|---|
| Structured | schema-constrained output, or a forced tool |
| Still check | values, refusal, max_tokens truncation |
| Errors | flagged result (is_error), short actionable message |
| Retries | limit them; never auto-retry payments or emails |
4. Interview Point
- Round trip? Send tools, get call, run it, return result by id, get answer
- Function calling vs MCP? Same schemas; MCP standardizes discovery and calls
- Safe agent? Cap turns, validate, least privilege, timeouts, confirm destructive
Check yourself
A loop appends only the tool result, not the assistant message that made the call. What happens?
- The API rejects it: the result has no matching call id.
- Keep the assistant tool-call message first, then the results by id.
A send_email tool times out, so the loop retries it three times. What is the bug?
- Sending is non-idempotent, so the user may get duplicates.
- Use an idempotency key or ask for confirmation; auto-retry only safe tools.
A stdio MCP server runs print('ready') for debugging. Why does the client break?
- Stdout carries the JSON-RPC messages, so stray text corrupts them.
- Log to stderr instead.