Cloud Tech by Victor
BackendAdvanced

Tool Use & Agents

How a tool call actually completes as a round trip, a tool_use block your code executes and a tool_result you send back, and why every tool definition you provide adds real tokens to every request whether or not it's ever called.

Updated 2026-07-244 min read

Overview

Tool use lets a model call functions your application (or the provider) executes rather than only generating text: you declare each tool's name, description, and input schema, the model decides mid-response whether a tool fits the request, and if so returns a structured tool-call block instead of (or alongside) prose. For a client tool, your own code executes the call and sends the result back in a follow-up request referencing the original call's ID, only then does the model produce a final answer grounded in the actual result. A server tool skips that entirely, running on the provider's own infrastructure so your application just sees the outcome. What's easy to miss is that every declared tool, called or not, adds real input tokens to every request that includes it, plus a fixed per-model overhead for enabling tool use at all.

Quick Reference

ConceptWhat it means
Client toolYour application executes it; you write the handler and error handling
Server toolProvider's infrastructure executes it; you only declare it
tool_choice: {"type": "auto"}Model decides per turn whether to call a tool
tool_choice: {"type": "any"}Model must call some tool, any of them
tool_choice: {"type": "tool", "name": "get_weather"}Forces that exact named tool to be called
tool_choice: {"type": "none"}Model cannot call a tool
Tool round tripRequest → model returns tool call → your code executes → result sent back → model answers

Syntax

python
tools = [{
    "name": "get_weather",
    "description": "Get the current weather for a given location.",
    "input_schema": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
    },
}]
response = client.messages.create(model="claude-sonnet-5", tools=tools, messages=messages)

Examples

python
# The round trip: the model's tool call has to be executed by your
# code, then the result sent back before it can actually answer.
tool_use = next(b for b in response.content if b.type == "tool_use")
result = run_weather_lookup(tool_use.input["location"])   # your own code

messages += [
    {"role": "assistant", "content": response.content},
    {"role": "user", "content": [
        {"type": "tool_result", "tool_use_id": tool_use.id, "content": result}
    ]},
]
followup = client.messages.create(model="claude-sonnet-5", tools=tools, messages=messages)

Declared tools cost tokens even when never called

Every tool's name, description, and schema in the request counts as input tokens on every single call, plus a fixed system-prompt overhead for enabling tool use at all. A large, rarely-used tool library has a real, continuous cost across every request, not just when a tool actually gets invoked.

Visual Diagram

Common Mistakes

  • Assuming the model executes the tool itself; for client tools, your application code is entirely responsible for actually running the call.
  • Declaring a large library of tools "just in case," paying their token cost on every request regardless of whether they're ever called that turn.
  • Not handling the case where the model omits a required parameter, particularly on prompts too ambiguous to have supplied one.
  • Treating tool_choice: auto as a guarantee the model will call a tool when you expect it to; the boundary is steerable via the system prompt, not fixed.

Performance

  • Every declared tool adds real input tokens on every request that includes it, whether or not it's called, this scales directly with the number and verbosity of tool definitions in play.
  • Enabling tool use at all adds a fixed, model-specific system-prompt token overhead on top of whatever the tool definitions themselves cost.
  • For applications with many available tools, loading only the tool definitions relevant to the current task (rather than every tool, every request) is a direct lever on both cost and latency.

Best Practices

  • Write clear, specific tool names and descriptions; the model decides whether to call a tool based substantially on how well the description matches the request.
  • Use tool_choice to force a specific tool when the application logic already knows one is needed, rather than relying on auto in situations where ambiguity is costly.
  • Prune or dynamically load tool definitions for applications with a large tool library, instead of declaring every tool on every request regardless of relevance.
  • Design a clear error-signaling convention for tool results (success vs. failure) so the model can react appropriately rather than treating every result as a success.

Interview questions

Walk through what actually happens end to end when a model decides to call a tool you defined.

You send a request with a `tools` array describing each tool's name, description, and input schema; the model decides a tool fits the request and responds not with plain text but with a specific stop reason indicating tool use, plus one or more blocks naming the tool and its arguments (matching your schema). Your application code, not the model, actually executes that call, a real function, API request, or command, then sends a follow-up request containing the result in a block referencing the original tool-call's ID. Only after that round trip does the model produce its final answer, incorporating the tool's actual result rather than a guess.

What is the difference between a client tool and a server tool, and why does that distinction matter for what your application has to do?

A client tool executes in your own application, the model only returns the structured request to call it; your code is responsible for actually running it (hitting your database, calling your API) and returning the result. A server tool, like a web search or code execution tool a provider offers, executes on the provider's own infrastructure, so your application sees the final result directly without ever writing execution code for it. The distinction determines how much you have to build: every client tool needs your own execution and error-handling code, while server tools need none, just declaring them in the request.

Why does simply including a tool definition in a request add cost even on a turn where the model never actually calls it?

Every tool's name, description, and input schema in the `tools` parameter counts as real input tokens on every single request, and using tools at all triggers an additional fixed system-prompt token overhead that varies by model, regardless of whether the model ends up calling any tool that turn. This means a large library of rarely-used tool definitions has a real, continuous token cost across every request that includes them, which is exactly why techniques like on-demand tool discovery (only loading the tool definitions actually relevant to the current task) exist as a mitigation for applications with many available tools.

References

ShareXLinkedInReddit
Was this page helpful?
Suggest an improvement