Volume VI · Reaching Outside the Model
The JSON Contract
Lecture 12 ended with ReAct's Act step — the model decides to do something in the world, not just say something about it — and left the mechanism of "doing" as a black box. Open that box and there is no trick inside it. There is a schema the model was trained to fill in, a stop reason your code checks for, and a result you hand back. That is the entire contract.
The mental model
Tool use is not the model reaching into your code. It is the model and your code agreeing in advance on a JSON shape — the model produces structured input matching that shape instead of prose, your code recognizes the shape and executes on its behalf, and the result is appended back into the same conversation as one more turn.
Declaring the shape
A tool, in Anthropic's tool-use API, is declared with three fields: a name,
a description, and an input_schema written as JSON Schema —
the same schema language used to validate any structured JSON document, listing the
tool's parameters, their types, and which are required. Nothing about this declaration
is special-purpose machinery; it is a description of a function signature, expressed in
a format a model can read and a validator can check. The model is not given the
function's implementation — it never sees your code — only this description of what
the function is called, what it is for, and what shape of input it expects.
tool_choice controls how much latitude the model has in deciding whether
to reach for a tool at all: auto leaves the decision to the model,
any or a named tool forces a call, and
disable_parallel_tool_use caps the response at one tool call per turn even
when several would otherwise fire together. A strict: true flag on the
tool definition goes one step further than a best-effort schema match — it guarantees
the produced input actually conforms to the declared schema, rather than merely being
likely to.
The loop: request, block, execute, result
Send a request with tools declared, and if the model decides to use one, the response
carries stop_reason: "tool_use" — a value your code checks the way it
would check any other stop reason, such as reaching the token limit or hitting a stop
sequence — alongside one or more tool_use content blocks. Each block
carries the tool's name and a structured input object shaped
to match that tool's input_schema. Your code reads the block, calls the
real function underneath with that input, and packages whatever the function returns
into a tool_result content block that references the original block's
tool_use_id — the pairing that tells the API which call this result
answers. That result is appended to the conversation, and the exchange continues from
there, with the model now able to see the outcome of the action it asked for.
tool_use_id.
Client tools and server tools
Not every tool executes in the same place. Client tools — any tool the developer
defines, plus Anthropic-schema tools like bash, text_editor,
and computer_use — run inside the caller's own application, exactly as
described above: the model asks, the caller's code does the work. Server tools —
web_search, web_fetch, code_execution, and the
MCP connector — instead execute on Anthropic's own infrastructure. The JSON contract is
identical either way — a tool_use block out, a tool_result
block back in — only the party doing the executing changes.
The overhead is disclosed, not hidden
Declaring tools is not free even before a single one is called: tool definitions add
a fixed token overhead to the system prompt, and that overhead depends on which model
is in use and which tool_choice setting is active — Anthropic publishes
an explicit per-model, per-tool_choice token table for this. On top of
that fixed cost sit the ordinary input and output tokens the tool_use and
tool_result blocks themselves consume, scaling with how large the schema
and the returned result are.
The same contract, a different vendor's spelling
OpenAI's function calling is worth naming here because the mechanism it describes is
widely reported as consistent with the pattern above, though the exact current wording
is worth re-verifying directly against developers.openai.com before quoting it
precisely — treat what follows as secondary-sourced, not settled. A function is
declared as {type: "function", function: {name, description, parameters}},
with parameters again a JSON Schema object. The model can request several
tool calls in a single response — "parallel tool calls" — a strict: true
flag enforces schema-conforming arguments, and a tool_choice setting
(auto/required/a named function/none) controls
whether and which tool gets invoked. The vocabulary differs; the shape underneath —
schema in, structured call out, your code executes, result goes back in — is the same
idea this lecture has been describing throughout.
| Piece of the contract | Anthropic | OpenAI (needs re-verification) |
|---|---|---|
| Declaring a tool | name, description, input_schema | type: "function", name, description, parameters |
| Model wants to call it | stop_reason: "tool_use" + tool_use block | a function-call entry in the response |
| Forcing/limiting calls | tool_choice: auto / any / tool; disable_parallel_tool_use | tool_choice: auto / required / named / none |
| Guaranteeing schema conformance | strict: true | strict: true |
| Returning the outcome | tool_result block, matched by tool_use_id | a result message tied to the call |