What progressive disclosure actually changes

Almost every production agent I have looked at this year makes the same architectural assumption without ever stating it: that the model should be handed the complete catalogue of everything it might conceivably do, before it has read the user's first sentence. It is an assumption inherited from a time when agents had six tools. It survives because connecting a new integration is now a two-line change, and because nothing about the failure is loud. Nobody gets paged for it. The bill goes up gradually and the answers get slightly worse, and both of those look like the cost of doing business.

Progressive tool disclosure is the alternative: keep the whole catalogue available, but let the model see a definition only when it becomes relevant. That single change attacks a cost you are paying on every turn of every conversation and, less obviously, a quality problem — because a model choosing among eight candidates makes a better decision than one choosing among a hundred near-duplicates.

This guide is about not loading the tools in the first place. It is deliberately not about writing better descriptions for the tools you do load — that is the job of its companion piece on writing tool descriptions that stop wrong tool calls, and the two compose: disclosure decides which descriptions the model reads, descriptions decide what it does with them. Read that one if your agent picks the wrong tool from a short list. Read this one if your agent has a long list.

  • Definitions are a per-turn cost, not a one-off. A twelve-turn conversation pays for the tool block twelve times unless caching intervenes.
  • Three architectural shapes cover the space: deferred loading, code execution, and namespacing or scoping. Vendors will change; these three will not.
  • Accuracy is the surprise. Teams adopt this to cut cost and find that tool selection improves more than the invoice does.
  • The cache interaction decides everything. Load definitions into the tool block dynamically and you can spend more than you save.
  • Under about fifteen tools, do nothing. Every disclosure layer is a new failure mode; earn it before you add it.
  • Discovery is not authorisation. Hiding a destructive tool behind a search step makes it less visible, not less dangerous.

The tax you pay on every turn

Start with the arithmetic, because the shape of the cost is what makes the fix worth building. Tool definitions are part of the prompt. They are not sent once at the start of a session and remembered; they are serialised into every single request, alongside the system prompt and the entire conversation so far. A turn where the agent says "could you share the policy number?" and calls nothing carries exactly the same tool block as a turn that does real work. That is the tell most teams eventually notice: an empty turn that costs as much as a productive one.

How much depends entirely on how verbose your schemas are, and the honest answer is that you have to measure your own. A terse tool with two string parameters might cost sixty tokens. A generated wrapper around a REST endpoint with fifteen optional query parameters and documented enum values can exceed five hundred. What has changed since 2024 is not the per-tool cost but the count: the Model Context Protocol made adding a server trivial, and a server typically brings its entire surface with it rather than the three operations you wanted. Industry write-ups circulating in 2026 put the ecosystem at more than ten thousand enterprise MCP servers and around ninety-seven million SDK downloads by April 2026 — figures worth treating as directional estimates rather than established fact, but directionally they match what teams report. For the wider ecosystem picture, our news coverage of the stateless MCP migration tracks how fast that surface has been moving.

The published measurements are more useful than any model I could build here.

Reported before-and-after figures for deferred tool loading. Attribute each to its source; your own numbers will differ.
Setup Measured cost Source and confidence
A dozen popular MCP servers connected, all definitions preloaded 50,000–66,000 tokens consumed before the agent sees the user's question Anthropic engineering — primary source
Full tool library, definitions discovered on demand via a tool-search step Roughly 85% reduction in token usage, with the whole library still reachable Anthropic engineering — primary source
Reference task, code execution with MCP, all tool definitions preloaded ~150,000 input tokens Anthropic engineering — primary source
The same task, definitions loaded only when used ~2,000 input tokens — a 98.7% reduction Anthropic engineering — primary source
Cloudflare's Code Mode approach (February 2026) Reported to cut input tokens by a very large margin; no figure I would quote precisely Vendor and press reporting — treat as directional only

The last row is there on purpose. A 98.7% reduction on a reference task is a real, published measurement of a specific workload, not a promise about yours, and the honest way to read the whole table is as evidence that the effect size is large enough to be worth measuring rather than as a target to quote in a planning document.

Where the cache breakpoint has to sit

Here is the part that decides whether any of this saves you money, and it is the part that most hand-rolled implementations get wrong.

Prompt caching is prefix-based. The provider caches a contiguous run of tokens from the start of the request, and anything that changes invalidates everything downstream of it. Tool definitions sit at the very front of the prompt — ahead of the system prompt, ahead of the conversation. That position makes them the single most expensive thing in the whole request to mutate. If your loader rewrites the tool array on turn four because the user changed subject, you have not merely paid to re-write the tool block; you have thrown away the cached system prompt and the entire conversation prefix behind it, and you pay a full cache write on a payload that grows with every turn. I have seen a home-made retrieval layer that removed 70% of the tool tokens and increased the bill, entirely through this mechanism.

So the placement rule is simple to state and easy to violate: put the breakpoint at the end of the last genuinely stable block, and keep everything before it byte-identical across turns. For most agents that means the boundary falls after the tool definitions and after the system prompt, with the variable material — retrieved documents, discovered schemas, the conversation itself — all living behind it.

This is also the structural reason the provider-native approach beats a home-made one. When Anthropic's Tool Search Tool defers a definition, the tool is still supplied to the API; it is simply marked with defer_loading: true so that it becomes discoverable rather than preloaded. The array is therefore identical on every turn, the prefix survives, and the definitions the model discovers arrive as tool results at the tail of the message list, exactly where variable content belongs. A hand-rolled loader that swaps entries in and out of the array gets the token reduction and loses the cache. If you must load definitions yourself, load them into the conversation, never into the tool block.

The mechanics of breakpoint placement, cache TTLs and the write-versus-read economics are covered properly in our guide to programmatic tool calling and cache breakpoints, and I will not repeat them here. The point specific to disclosure is only this: the tool block's position at the front of the prompt is what makes it both the best thing to shrink and the worst thing to churn.

Watch out

Dynamic tool loading and prompt caching pull in opposite directions. Caching rewards a byte-identical prefix; naive per-turn retrieval rewrites the prefix every turn. Before you ship any disclosure layer, log your cache read tokens alongside your input tokens. A change that cuts the tool block by 70% and drops your cache hit rate from 90% to zero is a regression wearing a success metric.

The accuracy argument nobody expected

Almost everyone arrives at progressive disclosure through the invoice. The more interesting finding is what happens to quality.

Anthropic's internal evaluations of the Tool Search Tool report Opus 4 improving from 49% to 74% on tool-use accuracy with deferred loading enabled, and Opus 4.5 improving from 79.5% to 88.1%. Read those two pairs together, because the pattern in them matters more than either number. The weaker model gained twenty-five points; the stronger one gained nearly nine. Both gained. A context-management technique adopted to save money made the model measurably better at its job, and it helped the model that was struggling most.

Once stated, the mechanism is not mysterious. Tool selection is a discrimination problem. Every additional definition in the context window is another candidate competing for the same intent, and in a hundred-tool block a great many of those candidates are near-duplicates — three different search tools from three different servers, two ways to fetch a customer, a CRM get_contact and an internal get_account whose descriptions both open with the word "customer". They were written at different times, by different teams, in different registers, and nobody has ever read them side by side. Handing the model eight relevant candidates instead of a hundred mixed ones removes most of that interference before the decision is made.

This reframes the whole exercise, and it changes who should care. If you read progressive disclosure as a cost optimisation, it is something you get to after the product works. If you read it as a selection accuracy intervention, it belongs in the same bucket as your evals, and it is worth doing at a scale where the token savings alone would not justify the work. The cheapest version of the intervention, incidentally, is still deletion: a tool that never wins a case in your selection eval is not neutral, it is a permanent distractor on every request, and removing it improves every other tool.

Two caveats keep this honest. Version numbers and percentages age fast — these are September 2026 figures on one vendor's evaluations, and the sensible reading is the direction and the mechanism, not the decimals. And the gain is not automatic: it depends on the discovery step surfacing the right candidates. A search layer that retrieves eight plausible-but-wrong tools produces confident wrong answers rather than confused ones, which is worse. Which is why the measurement section below is not optional.

Pro tip

Before building anything, print your tool block the way the model receives it — every name and description in one continuous list, stripped of code. Most engineers have never seen this view of their own agent. Ambiguities that are invisible while reading the implementation become obvious in about thirty seconds when the descriptions sit next to each other, and you will usually find three tools to delete before you write a line of retrieval code.

The three shapes that outlive the vendors

Product names in this area have a shelf life of roughly a year. The underlying shapes do not, and there are only three of them. Whatever your provider calls its feature in 2028, it will be one of these, or a combination.

Shape one: deferred loading

Keep the catalogue, load the definitions on demand. The model is told what exists in compressed form — or is given a search tool over the catalogue — and pulls the full parameter schema for a tool only at the point it intends to call it. This is the lowest-friction shape, because it changes nothing about how your tools are implemented or invoked. It changes only when the model reads about them.

# Illustrative shape only — check your provider's current tool-use
# documentation for exact field names before wiring this up.
# As of September 2026 this reflects Anthropic's Tool Search Tool.

tools = [
    # 1. The search tool itself is never deferred. The model has to be
    #    able to see that discovery is possible.
    {"type": "<provider tool-search type>", "name": "tool_search"},

    # 2. Pin the handful of tools used on almost every task. A pin costs
    #    a permanent slot in the prompt and buys a round trip back.
    {
        "name": "get_account",
        "description": "Fetch an account by ID or registered email. ...",
        "input_schema": {"type": "object", "properties": {}},
        # no defer_loading -> always visible
    },

    # 3. Everything else is STILL SUPPLIED to the API, just marked as
    #    discoverable rather than preloaded. This is the detail that
    #    matters: the array stays byte-identical across turns, so the
    #    cache prefix survives, and discovered definitions arrive as
    #    tool RESULTS at the tail of the message list.
    {
        "name": "billing_list_invoices",
        "description": "List invoices for an account, newest first. ...",
        "input_schema": {"type": "object", "properties": {}},
        "defer_loading": True,
    },
    # ... 90 more, all deferred
]

If your provider has no native equivalent, you can build this with two meta-tools over an embedded catalogue — a search_tools and a load_tool_schema — but be clear-eyed that you are then responsible for the cache behaviour the native version handles for you. Our guide to dynamic tool retrieval when your agent has 200 tools walks through that build in detail, including the index text, the score floor and the pinning policy, so this guide does not duplicate it.

Shape two: code execution

Expose the tools as an API surface the model writes code against, inside a sandbox, so intermediate results never round-trip through the context window. This is a bigger architectural change than deferral, and it attacks a different half of the bill: not the definitions, but the results. Where a chained task would otherwise tokenise three hundred records into the conversation so the model can filter them, the generated program filters them in the sandbox and returns five rows.

# The tools become importable functions. Only the return value is
# tokenised back into the conversation.
#
# BEFORE — one tool call per step, every full result in context:
#   claims.list_open(policy_id)   -> 340 records, tens of thousands of tokens
#   claims.get(claim_id) x N      -> another full payload each time
#
# AFTER — one generated program, one small result:

async def run(tools):
    claims = await tools.claims.list_open(policy_id="POL-88213")
    recent = [c for c in claims if c["opened_at"] >= "2026-08-01"]

    totals = {}
    for c in recent:
        detail = await tools.claims.get(c["id"])
        cat = detail["category"]
        totals[cat] = totals.get(cat, 0) + detail["reserve_amount"]

    # Only these five rows re-enter the model's context.
    return sorted(totals.items(), key=lambda kv: -kv[1])[:5]

The reference figure quoted earlier — roughly 150,000 input tokens falling to about 2,000 when definitions were loaded only on use — comes from exactly this arrangement. The trade is a sandbox to run, secure and observe, plus a class of failure that did not exist before: generated code that throws. The full treatment, including where the sandbox boundary should sit, is in our guide to code execution with MCP. The related discipline of capping what a tool is allowed to return in the first place is covered in bounding agent tool output without wrecking your cache, and the two are complementary: bounding shrinks each result, code execution stops most results existing as tokens at all.

Shape three: namespacing and scoping

Decide the subset before the model runs. A claims agent in the triage phase does not need document-generation tools. A read-only analyst account should never see write tools at all. A tenant on your basic plan should not be shown the tools their plan does not include. This is the least fashionable of the three and frequently the correct first move, because it is deterministic, trivially debuggable, needs no embedding infrastructure and no extra round trip, and it doubles as a security control rather than merely a cost one.

Its weakness is that a human maintains the mapping, and a task that spans two phases needs an escape hatch. Most mature systems I have seen combine scoping with one of the other two: scope hard by role and tenant first, then defer or execute within whatever remains. Scoping is a filter; the other two are compression. They stack.

Say the quiet part out loud: vendors will rename all three of these, more than once. The shapes are stable because they follow from the structure of the problem — you can reduce how many definitions the model reads, reduce how much data flows back through it, or reduce the set before it starts. There is no fourth option hiding behind a product launch.

Every article here is written by a Verified Builder. Want your name on the next one?

AI Tech Connect lists AI engineers, founders and researchers across India and the UK — and the people hiring browse it to find them. Adding your profile is free.

Become a Verified Builder →

Which strategy, and when

The thresholds below are judgement calls rather than measured constants, and they should be validated against your own selection eval before you treat them as policy. A common rule of thumb in community write-ups is that selection quality starts to degrade somewhere around twenty tools in context; my own experience puts the inflection in the low dozens, and it arrives earlier when your tools resemble each other and later when they are cleanly separated.

Choosing a disclosure strategy. Thresholds are rules of thumb, not measured constants.
Your situation Do this Why What it costs
Under ~15 tools Nothing. Static full list, cached. Small block caches cleanly and gives the model the easiest possible decision Zero — spend the effort on descriptions instead
~15–50 tools, one round trip per task Deferred loading, provider-native if available Cuts the per-turn block and removes near-duplicate interference One discovery round trip; a recall risk on rare tools
50+ tools, or heavy chained calls with large intermediate results Code execution, with deferral inside it Attacks results as well as definitions; the largest reported reductions A sandbox to run, secure and observe; generated code can fail
Multi-tenant, role-based or phased workflows Namespacing and scoping, decided before the model runs Deterministic, debuggable, and doubles as a permission boundary A mapping somebody has to maintain; needs a cross-phase escape hatch
200+ tools and a genuinely open catalogue All three, layered: scope, then search, then execute No single shape holds at that surface area Real engineering — earn it with measurements first

Two notes on the table. First, the cheapest row is the top one, and a surprising number of teams belong in it and have talked themselves into the third. Second, none of these rows should be entered before the unglamorous audit: list every tool, find the ones that have never been called in production, and delete them. Deleting a tool is strictly better than retrieving around it, and it is the only intervention here with no operational cost.

From a verified Builder

"We went in for the token saving and got a quality fix we had not budgeted for. Two of our support agent's worst failure patterns — calling the CRM lookup when the user asked a billing question, and calling nothing at all on ambiguous requests — dropped off the board once the model stopped reading ninety definitions before every decision. The invoice mattered to our finance lead. The selection accuracy is what stopped the escalations."

— Rishi, Verified Builder · Chennai, India

Proving the win on your own traffic

Vendor numbers tell you the effect exists. They do not tell you whether it exists in your system, and the honest answer is that some agents get almost nothing from this. Instrument first, then change one thing.

Record a baseline over at least a week of real traffic — not a synthetic run, because the mix of turn types is exactly what determines the answer. Then enable the disclosure layer and record the same numbers over a comparable period.

The four measurements that decide whether progressive disclosure paid off.
Metric How to record it What a win looks like The trap
Input tokens per turn, split by turn type Log the provider's reported input tokens on every request, tagged with whether a tool was called The no-tool-call turns get dramatically cheaper — that is the fixed prefix shrinking Averaging across turn types hides the effect entirely
Input tokens per completed task Sum tokens across every request in one user-visible task, then divide by tasks completed Falls, even after the extra discovery round trips are counted in Per-request cost can drop while per-task cost rises — extra turns are not free
Tool-selection accuracy on a fixed set 50–200 labelled request-to-tool pairs from production traces, including no-call cases; score before and after Accuracy holds or improves; recall on rare tools does not collapse Omitting no-call cases hides over-calling completely
Cache hit rate, per layer Log cache read tokens and cache write tokens separately from fresh input tokens Hit rate is unchanged or better; the prefix is still stable A hit-rate number with no per-task cost denominator can hide a regression

Two of these deserve emphasis. The per-task denominator is the one that pays your bill: a change that cuts per-request cost by 40% while adding two discovery turns to every task can leave you flat or worse, and only the per-task number shows it. And the selection eval is what stops you shipping a cheaper agent that is quietly worse. Fifty labelled pairs harvested from real transcripts is a morning's work; run them before and after and treat any drop in recall on a specific tool as a description problem to investigate rather than a reason to revert.

If you have no per-turn telemetry at all, that is the first thing to build, not the disclosure layer. Our guide to instrumenting agents with OpenTelemetry and cost attribution covers the spans and attributes worth capturing, and this whole exercise is guesswork without them.

A note on why any of this is worth an engineer's week: the constraint is not evenly felt. A well-funded team can absorb a tool block that costs a few thousand tokens a turn and never notice. A bootstrapped team in Bengaluru or Manchester, running a support agent on thin margins, is making a genuine product decision about how many integrations they can afford to expose — and progressive disclosure is what turns that from a trade-off into a non-question. The teams for whom this matters most are usually the ones with the least time to build it, which is a good argument for taking the provider-native path where one exists.

Where progressive disclosure goes wrong

Four failure modes, in roughly the order teams hit them.

Over-deferring, so the model cannot find a tool it needs. The classic version is a rarely-used but critical tool — an escalation path, a refund, a compliance export — that never ranks highly enough to be surfaced. The agent then behaves as though the capability does not exist, and rather than reporting a gap it will usually improvise an answer. The fix has two halves: pin the small must-have set so it is never filtered out, always including a way to finish cleanly and a way to hand off to a human; and include rare-tool cases explicitly in your selection eval, because they are exactly the ones a randomly sampled test set will miss.

A search layer with vague descriptions, which fails twice. This is the most under-appreciated one. Under a static tool block, a thin description costs you a selection error some of the time. Under a discovery layer it costs you twice over: the retrieval step matches against that same text, so a vague description means the tool is not surfaced at all, and if it is surfaced the model still has to choose from it. Deferral does not make description quality less important — it makes it strictly more important. If you have not already done the work in writing descriptions that stop wrong tool calls, do it before you build the search layer, not after.

Breaking the cache, covered above and worth restating because it is the failure that looks like a success on the metric you were watching. If your tool array is not byte-identical across the turns of a conversation, measure the cache before you celebrate the token reduction.

Watch out

Hiding a destructive tool behind a discovery step is not a permission boundary. A delete_account or issue_refund that used to sit visibly in a reviewed tool block can now be discovered and invoked mid-task with no operator having read it in context — the accidental review that a static list provided disappears. Filter by permission scope before ranking, never after; return an identical response for "does not exist" and "not permitted" so discovery cannot enumerate your catalogue; and keep destructive operations behind an explicit confirmation gate regardless of how they were found.

Treating disclosure as a substitute for governance is the fourth, and it is the one that ends badly rather than expensively. Progressive disclosure is a context-management technique. It changes what the model reads, not what it is allowed to do. Authorisation, confirmation gates and blast-radius limits are a separate layer that belongs in your own code, and none of them should be inferred from whether a tool happened to be in context.

One last thing worth planning for. As sessions get long, the discovered definitions accumulate in the conversation tail alongside everything else, and they become part of the context you eventually have to compact. Anything you compact away, the model may need to rediscover — so a compaction step that silently drops a loaded schema produces a confusing repeated-discovery loop. Our guide to compaction for long-running agents covers how to decide what survives a compaction boundary; the rule specific to this pattern is to keep loaded schemas for tools the agent has actually called, and drop the ones it merely looked at.

What to do this week

The sequence matters more than the technology, and the first two steps cost nothing.

  1. Print the tool block as the model receives it, and delete everything that has not been called in production. This alone frequently removes a fifth of it.
  2. Measure the baseline — input tokens per turn split by turn type, input tokens per completed task, cache hit rate. A week of real traffic.
  3. Build a small selection eval, fifty to two hundred labelled pairs from real transcripts, with no-call cases included.
  4. Scope first if you can. Role, tenant and task-phase filters are deterministic and give you a permission boundary for free.
  5. Then defer, using the provider-native path if one exists, and re-run both the cost numbers and the selection eval.
  6. Reach for code execution only when the traces show chained calls passing large intermediate results — that is the shape it pays for.

The vendor features referenced here will be renamed, superseded and repriced; the primary source for the numbers in this guide is Anthropic's engineering write-up on advanced tool use on the Claude Developer Platform, and the protocol side is documented at modelcontextprotocol.io. What will still be true in 2028 is the underlying claim: a model that reads a hundred tool definitions before every decision is paying for them on every turn and making a harder choice than it needs to. Keep the catalogue. Stop shipping all of it.