Skip to content

GlossaryFloor 2 · The Harnessthe block and its bolted-on plates: what gets added to itFloor 2 · The Harness

tool calling

No. 038 · v2026-08FR: appel d’outils

Tool calling is the moment when the model requests an action instead of writing: finding a file, sending a message. Like a customer filling in an order form: they write down what they want, someone else fetches it and brings it back to them.

What it is not

A tool call is not an action by the model. The model produces formatted text that names a tool and its parameters: it is a request, nothing more, and it can be refused. The harness intercepts it, checks the permissions, executes it if it accepts it, then hands the outcome back for the model to read. Confusing the request with the execution leads to believing that a system is secured by persuading the model to behave, when the only point of control is the place where the action happens.

In depth

The four-stage circuit

The circuit holds in four stages, always the same ones. The harness first describes the available tools in the context: a name, a role, expected parameters. The model, when it judges a call relevant, produces a request in an agreed format rather than an answer in plain language. The harness recognises that request, validates it, carries out the action, then puts the result back into the context and calls the model again, which picks up where it left off. None of this ever leaves the text: the model has no direct access to the information system.

What the call does not guarantee

A tool call is no guarantee of accuracy. The model chooses on the strength of a name and a few lines of description, and it fills in the parameters as it fills in everything else: by plausibility. An identifier absent from your request will therefore sometimes be invented, with the same assurance as an ordinary hallucination, and two tools with overlapping scopes produce erratic choices. Validation by the harness is not an implementation detail but the condition of the whole arrangement: it decides whether the call is admissible before it produces the slightest effect.

The chaining

The sensitive point is the chaining. As soon as a tool result comes back into the context, it becomes text read by the model, on the same footing as an instruction: a document brought back or a page consulted can therefore contain instructions that trigger the next call. A system that reads outside content and holds tools able to write, to send or to pay brings together every condition for a hijacking, without any technical vulnerability being needed. The answer lies less in the model than in the architecture: separate what reads from what acts, reduce the permissions of each tool to the strict minimum, have the irreversible confirmed, and keep a trace of every call.

Under the hood4 steps · the real shape of the objects

Tool calling is a format before it is a capability: everything this entry states can be read in the shape of the objects exchanged. Here are the four stages above, in the same order, with the objects in place of the sentences.

  1. 01

    Declare the catalogue

    The catalogue goes into the context, as text, on every turn. There is no other channel: what the model knows of your tools is contained entirely in these few lines, and nothing of what the tool does is accessible to it in any other way.

    json
    {
      "name": "find_case_file",
      "description": "Retrieves a case file by its exact number, as it appears in the conversation. Does not search by name, nor by date.",
      "input_schema": {
        "type": "object",
        "properties": {
          "number": { "type": "string", "pattern": "^[A-Z]-[0-9]{4}$" }
        },
        "required": ["number"],
        "additionalProperties": false
      }
    }
    
    name
    The identifier that the model will copy into its request. It is all that pairs a declaration with a call.
    description
    The only criterion on which the model chooses. It is therefore prompt, not documentation: what is written there changes the behaviour. Naming what falls outside the scope prevents more wrong calls than detailing what falls inside it.
    input_schema
    The expected shape of the parameters. It serves twice: it guides the model when it fills them in, and it arms the validation of the harness when it receives them.
    additionalProperties
    Set to false, an invented field fails the validation instead of passing through unnoticed.

    The trapTwo tools whose descriptions overlap produce erratic choices, and no instruction added elsewhere will settle between them: the problem is fixed here, in the writing of the catalogue, and nowhere else.

  2. 02

    Read the request

    The model has done nothing. It produced this object, then it stopped. Nothing in it has touched an information system: this is the order form of the analogy, and it can still be refused.

    json
    {
      "role": "assistant",
      "stop_reason": "tool_use",
      "content": [
        { "type": "text", "text": "Let me check that case file." },
        {
          "type": "tool_use",
          "id": "call_01",
          "name": "find_case_file",
          "input": { "number": "A-4417" }
        }
      ]
    }
    
    stop_reason
    The reason for stopping. Here it reads “tool use” and not “end of turn”: it is by this value that the harness recognises that something is expected of it.
    id
    The pairing. The result will have to quote this identifier, failing which the model does not know which request it answers, and several calls sent together become impossible to untangle.
    input
    The parameters, filled in by plausibility like the rest of the text. Nothing here guarantees that case file A-4417 ever existed.

    The trapA single turn can carry several tool_use blocks. A harness that reads only the first leaves the others unanswered: the format expects one result per request, and the next turn starts out inconsistent.

  3. 03

    Validate, then execute

    Here is the only place in the arrangement where something happens, and therefore the only one where it can be prevented. The four checks are in this order because each one presupposes the previous one: you do not validate the parameters of a tool that does not exist, you do not check a permission against parameters that have not been validated.

    js
    async function execute(call, session) {
      const tool = CATALOGUE[call.name];
    
      // 1 · does the tool exist? a name is as easily invented as a parameter
      if (!tool) return refuse('unknown tool');
    
      // 2 · do the parameters hold to the schema declared in stage 01?
      const verdict = validate(call.input, tool.input_schema);
      if (!verdict.ok) return refuse(verdict.reason);
    
      // 3 · does the REQUESTER hold that permission? the question is not about the model
      if (!session.permissions.has(tool.permission)) return refuse('permission denied');
    
      // 4 · irreversible: hand control back rather than act
      if (tool.irreversible && !session.hasConfirmed(call)) return refuse('confirmation required');
    
      journal.write({ session: session.id, call });
      return { content: await tool.run(call.input, session), is_error: false };
    }
    
    // a refusal is TOLD to the model: it does not break the loop
    const refuse = (reason) => ({ content: `Refused: ${reason}.`, is_error: true });
    
    session.permissions
    The permissions of the human requester, carried by their session. This is the line that decides whether the arrangement holds.
    is_error
    A refusal remains a result, not an exception. The model reads it, understands why, and can correct its call: an error raised as an exception cuts the loop and leaves the model believing that the action took place.

    The trapThe third check questions the session, never the request. A system that infers permissions from what the model has written has the authorisation signed by the very party it is supposed to constrain: a booby trapped document that obtains a call obtains, by the same move, the right to have it executed.

  4. 04

    Put the result back, and call again

    The result comes back as one more message, and the model picks up where it left off. It is this turn of the loop, and not the model, that produces what is called an agent: the model itself does nothing but answer a context that grows longer with each pass.

    js
    let turns = 0;
    
    while (turns++ < MAX_TURNS) {
      const response = await model.answer({ messages, tools });
      messages.push({ role: 'assistant', content: response.content });
    
      const calls = response.content.filter((b) => b.type === 'tool_use');
      if (calls.length === 0) return response;   // the model is writing: the turn is over
    
      // one result per request, all in ONE message, before calling again
      const results = await Promise.all(
        calls.map(async (call) => ({
          type: 'tool_result',
          tool_use_id: call.id,               // the same identifier as in stage 02
          ...(await execute(call, session)),  // { content, is_error }
        })),
      );
    
      messages.push({ role: 'user', content: results });
    }
    
    MAX_TURNS
    The stopping condition. Without it, two tools that refer to each other loop until the budget runs out: nothing in the format obliges the model to stop.
    content
    Text, and nothing else. What a tool brings back reads exactly like an instruction: this is the route by which outside content can command the next call.

    The trapThe loop does not distinguish what comes from you from what comes from a document brought back: it is all text in the same context. It is here, at this precise point of the circuit, that the prompt injection announced in layer 2 plays out, and the `injection-de-prompt` entry shows the missing field that makes it possible.

What variesField names vary from one supplier to another: input_schema is called parameters elsewhere, the tool_use block is called a function call elsewhere, and the result comes back sometimes in a user message, sometimes in a role of its own. The circuit itself does not vary: a catalogue described in JSON Schema, a request that is named and identified, a paired result returned as text, a loop that calls again. This is why a catalogue travels from one model to another by renaming fields, never by changing architecture.

Relations where the neighbours live

Check 3 questions · click your answer

Level 1 · Recognise

An assistant announces that it is sending an email, and the email goes out. What exactly did the model do?

Level 2 · Distinguish

The model calls a tool with a case file number that you have never mentioned. What happened?

Level 2 · Distinguish

You want to prevent an assistant from deleting files. Where do you put the prohibition?

Who works with this 3 roles

The roles for which this term is part of the ordinary work.

No. 038 · v2026-08 · first written in · editorial responsibility Anthony Capirchio

Lexigraph, "Tool calling", v2026-08, https://www.lexigraph.org/en/tool-calling/, CC BY 4.0.

Report

What goes with your message

Entry · Tool calling
No. 038 · v2026-08 · /en/tool-calling

What is this about
0 / 600

It is used to reply to you, and for nothing else. What is recorded