MCP Progressive Tool Discovery: Native vs. Hand-Rolled

A 3-tool MCP entry point cuts tools/list bytes 94% vs a flat 50-tool catalog. agentgateway's Search mode does this, Enterprise-only; here's the OSS version.

The 2026-08-22 MCP roadmap names progressive tool discovery as a priority, in almost these words: a server should offer a small entry point and reveal more of the catalog, rather than sending every tool definition up front. I wanted to know what it would take to actually build that, not just argue for it, so I built both sides: a flat 50-tool MCP catalog and a progressive one, fronted by the same real agentgateway v1.6.0, and measured the difference. Code at themsquared/progressive-tool-discovery .

The honest finding was not the number. It was that the primitive for this already exists in agentgateway, and it’s Enterprise-only. So this post also shows what you get for free and what you have to build yourself.

This is not Code Mode

I wrote about MCP tool catalogs growing out of control a few weeks ago , and the pushback I got most was “doesn’t Code Mode already fix this?” It fixes a different problem.

Code Mode takes N tools and replaces them with one tool: a script executor. The model writes JavaScript that calls the underlying tools as functions, the gateway runs it in a sandbox, and only the composed result comes back. That’s a real win for tasks that chain several tool calls, because the model stops paying context for every intermediate result.

Progressive discovery is a different idea. The tools stay tools. What changes is how much of the catalog the agent sees before it has asked for anything. A flat catalog sends every tool’s full definition on tools/list, every time, no matter how many of those tools the agent will actually call this session. Progressive discovery keeps the initial response small and lets the agent ask for more.

Both ideas reduce tokens. Only one of them is about hiding the catalog until it’s needed, which is what the roadmap is actually asking for.

What agentgateway gives you natively, and what it doesn’t

agentgateway does have a real primitive for this, and I want to be specific about where, because the public docs make it easy to conflate the two tiers.

Solo Enterprise’s agentgateway docs describe four toolMode values on an MCP backend: Standard (pass through everything), Search (two meta-tools, get_tool and invoke_tool, that disclose one tool at a time), Code (a single run_code tool, the Code Mode shape), and CodeSearch (both). The docs say it plainly: “Tool modes are how agentgateway implements progressive disclosure for MCP.” Search mode is exactly the mechanism this post is about, built in, configured with one field.

That field is entMcp.toolMode, set on an EnterpriseAgentgatewayBackend resource. Both names tell you what tier it’s in. I checked the open-source side to be sure: agentgateway.dev’s MCP connectivity docs list configuration modes, servers, auth, guardrails, target policies, observability, and spec compatibility. No tool-mode page. The OSS binary this repo runs doesn’t have the feature.

What OSS agentgateway v1.6.0 does have is MCP multiplexing and separate-path routing : put several MCP servers behind one backend and their tools get merged into one tools/list (prefixed by target name to avoid collisions), or give each server its own route. That’s federation, and it’s the opposite direction from what I needed here. Multiplexing makes catalogs bigger and unified. Progressive discovery needs one catalog to stay small. So this demo uses separate-path routing: one route per catalog shape, not one merged endpoint.

I also checked agentregistry v0.4.0 , since it’s this campaign’s other registry project and the obvious thing to ask about. It’s a catalog of which MCP servers, agents, and skills exist across npm, PyPI, OCI, and raw endpoints, with arctl to deploy them. That’s server-level discovery. Tool-level discovery, how one running server shapes its own tools/list, isn’t its job, and its API has nothing resembling pagination or hierarchy for that. It’s a good way to find the progressive-catalog server in the first place. It has no opinion on how that server behaves once you’ve found it, so I didn’t force it into the architecture below.

Building the entry point

Since the OSS gateway doesn’t have Search mode, I built the mechanism it implements, by hand, in the MCP server itself. It’s about 130 lines across two files.

The server exposes three tools from tools/list, always, no matter how big the real catalog behind it gets:

ENTRY_POINT_TOOLS = [
    {"name": "list_categories", "description": "List the tool categories ..."},
    {"name": "expand_category", "description": "List the tools in one category ..."},
    {"name": "call_tool", "description": "Invoke a specific tool by name ..."},
]

list_categories returns a short summary: category names, descriptions, tool counts. expand_category takes a category name and returns the full tool definitions, schemas included, for just that category. call_tool dispatches to the real handler once the agent knows a tool’s exact name and arguments. The agent never sees the other categories’ tools unless it asks for them.

The flat server, for comparison, is the same catalog with none of that: tools/list returns all 50 tool definitions, 5 categories of 10, every time. Both servers share one catalog_data.py so the comparison is apples to apples, not two different catalogs with two different vocabularies.

Both are plain Python standard library HTTP servers, no MCP SDK dependency. That wasn’t laziness: agentgateway’s own Virtual MCP docs call out a real breakage where mcp-server-time’s SDK version pin fights uvx’s dependency resolution and the server fails to start. Skipping the SDK means skipping that whole class of problem for a demo this size, at the cost of not being a spec-complete MCP server (no SSE push, no session resumption). That’s also why config.yaml runs agentgateway with statefulMode: stateless in front of both servers, so agentgateway doesn’t try to pin a session to a backend neither server actually tracks.

config.yaml itself is agentgateway’s routing-based configuration: one gateway on port 3000, two routes, each its own MCP backend with one target.

routes:
  - name: flat
    matches:
      - path: { pathPrefix: /flat }
    backends:
      - mcp:
          statefulMode: stateless
          targets:
            - name: flat
              mcp: { host: http://flat-server:3005/mcp/ }
  - name: progressive
    matches:
      - path: { pathPrefix: /progressive }
    backends:
      - mcp:
          statefulMode: stateless
          targets:
            - name: progressive
              mcp: { host: http://progressive-server:3006/mcp/ }

Real agentgateway v1.6.0, unmodified. The entry-point behavior is entirely in the Python server, which is the honest shape of “here’s what you hand-roll” when the gateway doesn’t give you the feature.

The numbers

I ran measure.py against the two routes through the actual gateway, not against the Python servers directly, so the byte counts include whatever agentgateway adds on the wire. Real output from a run:

agentgateway v1.6.0, two routes, same 50-tool catalog, two shapes.

                      tools/list bytes   tools exposed
flat                             16358              50
progressive                        922               3

tools/list reduction: 94.4% fewer bytes, 47 fewer tools in the initial exposure.

Scenario: the agent only ever needs the billing category (10 of 50 tools).

  flat tools/list alone:                        16358 bytes, 50 tools seen
  progressive tools/list:                         922 bytes, 3 tools seen
  + list_categories call:                         781 bytes
  + expand_category('billing') call:             3818 bytes, 10 tools revealed
  progressive total for this scenario:            5521 bytes

  net bytes saved vs. the flat catalog: 66.2%

Two things worth sitting with. First, the tools/list gap (94.4%) only grows as the real catalog grows, because the progressive entry point is fixed at 3 tools regardless of what’s behind it. A 500-tool backend would widen this gap, not close it. Second, the realistic scenario (66.2%) is smaller than the headline number on purpose: it includes the cost of actually using a tool, not just listing the catalog. That’s the honest number, and it’s still a two-thirds reduction for an agent that only needed one of five categories.

These are bytes on the wire, not model tokens. I didn’t run a tokenizer over them, so I’m not going to claim a token figure I didn’t measure. What I can say is that both payloads are the same kind of content (tool names, prose descriptions, JSON schemas), so a tokenizer applied to both would move in the same direction as the byte counts, even if the exact percentage shifted. Anthropic’s own numbers on what this does to model accuracy, not just token count, are in the post this one follows up on .

Running it yourself

Docker with Compose v2+, nothing else. Validated on Docker 29.8.1 / Compose v5.5.1, arm64 macOS, against ghcr.io/agentgateway/agentgateway:v1.6.0.

git clone https://github.com/themsquared/progressive-tool-discovery
cd progressive-tool-discovery
./demo.sh

demo.sh builds the two servers, starts agentgateway, waits for it to answer, runs the measurement script, and tears everything down. Well under 15 minutes including image pulls, and every number it prints comes from that run, not from this post.

Where this leaves the roadmap item

The roadmap is right that catalogs shouldn’t front-load everything. What this build adds is the part that’s easy to skip past in an argument: agentgateway already has the feature the roadmap is describing, and it’s behind a license tier. If you’re on open-source agentgateway, the pattern isn’t unavailable to you, it’s just not a config field. It’s about 130 lines in your MCP server, the same shape Enterprise’s Search mode implements for you: a small fixed entry point, categories first, tool definitions on demand.

If your catalog is small and stable, none of this matters: standard mode is correct and simpler. If it’s forty tools and growing, the entry point is the part to build first, whichever tier you’re on. For the architectural case for why catalogs end up this size in the first place, and why Code Mode and tool search don’t fix the governance problem underneath, see Conway’s Law Is Coming for Your MCP Servers . For another gateway-layer trick that shrinks what an agent has to read, serving markdown instead of HTML at the gateway is the same instinct applied to a different payload.

Code: themsquared/progressive-tool-discovery , Apache-2.0.

Frequently asked questions

What is progressive tool discovery in MCP?

A pattern where an MCP server’s tools/list response stays small and fixed, and the server reveals more of its catalog only when the agent asks for it. The 2026-08-22 MCP roadmap names it directly: servers should offer a small entry point and reveal more of the catalog, instead of sending every tool definition on every connection regardless of whether the agent needs it.

Is progressive tool discovery the same thing as Cloudflare's Code Mode?

No. Code Mode replaces N tools with one script-executor tool, so the model writes code that calls tools as functions instead of reading N definitions. That is composition. Progressive discovery keeps tools as tools but discloses their definitions incrementally: a small entry point first, the rest on demand. Different mechanism, same motivation.

Does agentgateway support progressive MCP tool discovery natively?

Solo Enterprise’s agentgateway build does, through entMcp.toolMode: Search on an EnterpriseAgentgatewayBackend, which replaces a backend’s tool list with get_tool and invoke_tool meta-tools. It is Enterprise-gated: the field and the resource name are both Enterprise-only, and open-source agentgateway’s MCP docs have no tool-mode page at all.

How much does progressive discovery reduce the tools/list payload?

In a measured run against a synthetic 50-tool catalog fronted by real agentgateway v1.6.0, a 3-tool progressive entry point returned a tools/list response 94.4% smaller than the flat catalog’s. Once an agent actually lists categories and expands the one it needs, the realistic total is smaller still: a two-thirds reduction for an agent that only needed one of five categories.