← Contents Volume VI · Lecture 13

Volume VI · Reaching Outside the Model

13

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.

THE TOOL-USE LOOP conversation + tools declared stop_reason: tool_use your code executes the fn tool_result tool_use_id matched conversation continues
Figure 13a The same loop ReAct traced inside one generation stream (Lecture 12), pulled apart into its wire-level pieces: a stop reason your code branches on, a structured block your code reads, and a result block your code writes back — identified to the model only by a matching 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.

The same contract, two vendors' terms (OpenAI column secondary-sourced — verify before quoting)
Piece of the contractAnthropicOpenAI (needs re-verification)
Declaring a toolname, description, input_schematype: "function", name, description, parameters
Model wants to call itstop_reason: "tool_use" + tool_use blocka function-call entry in the response
Forcing/limiting callstool_choice: auto / any / tool; disable_parallel_tool_usetool_choice: auto / required / named / none
Guaranteeing schema conformancestrict: truestrict: true
Returning the outcometool_result block, matched by tool_use_ida result message tied to the call

Read the primary source