The loadout is a decision you make every turn

Lesson 1 of 5 in Tool Selection at Scale: Forty Tools, One Context.

You have already learned to design a tool contract and to test a set for overlap: can a stranger, reading only your names and descriptions, say without hesitation which tool handles a given request? That test is about the tools. This module is about the list — what happens when there are forty of them, from nine teams and a dozen MCP servers, and no amount of description polish makes the pile small.

Start by noticing an assumption almost every codebase makes without deciding to. Somewhere at startup, a registry is built. Every call thereafter sends the same array:

tools = registry.all()          # 41 contracts, every request, forever
response = model.create(messages=..., tools=tools)

Nothing in any provider’s API requires that. The tool list is a per-request parameter, the same as the messages. You may send a different one on every single turn of the same conversation. Once you see that, the design question changes from which tools does this agent have? to which tools is this agent holding right now? — its loadout.

Where the loadout gets chosen

  1. Turn begins
  2. Read the signals

    Three are usually available before you call the model: the routed task or intent, the phase the run is in (what has already succeeded), and the principal on whose behalf the agent is acting.

  3. Assemble the loadout

    Plain code selecting from the registry. Deterministic, logged, and cheap — no model call, no embedding, no network hop.

  4. Add the floor set

    Tools that must never be filtered out: ask_human / escalate, and whatever discovery tool the agent uses to find more. Without a floor set, a filter bug leaves the agent mute rather than asking for help.

  5. Model call with N tools, not all tools

    The exposed set is now a value you can log. That log line is what makes every later selection bug falsifiable.

  6. Execute — and re-check permission here

    The loadout shaped what the model could see. It did not authorize anything. Authorization is enforced at the execution point, every time, regardless of how the tool got into the list.

  7. Next turn — decide again
Three signals you can filter on before the model ever sees the list — and what each one costs you
Filter byThe signalWhat it buysHow it fails

Task

A router’s intent label, or the endpoint the request arrived on

The biggest cut for the least machinery: a refund conversation never sees the eleven analytics tools

Inherits the router’s misclassification cascade — a mislabeled request gets a loadout with no correct move in it, and the model improvises

Run state

What has already succeeded this run — order resolved, invoice fetched, approval granted

Removes calls that cannot be valid yet, so the model stops proposing issue_refund before an order exists

Needs real state, not a guess from the transcript. If you infer phase by grepping messages, the loadout flickers

Principal

The user or agent identity the run acts for, and their role

A support agent never sees the finance tools, so the menu stops advertising things it will be refused

Tempting to mistake for security. Hiding a tool is not denying it — see the last lesson. Treat it as noise reduction that happens to align with permissions

Key terms: tool allowlist, prompt caching, tool overlap, context window

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.