作者在同一 agentgateway 环境下构建平铺式与渐进式的 50 工具目录,提供演示仓库并比较两种方案。文章指出原生发现能力仅限企业版,同时区分渐进发现与 Code Mode 脚本执行机制。
本文最初于 2026-10-18 发布在 webofmike.com。发布前,演示仓库及其中的每条命令都已实际运行验证。
2026-08-22 发布的 MCP 路线图将渐进式工具发现列为优先事项,措辞大致是:服务器应该提供一个小型入口,再逐步展示目录中的更多内容,而不是一上来就发送所有工具定义。我想知道,要真正实现它需要做些什么,而不只是讨论它的好处。于是,我把两种方案都做了出来:一个平铺的 50 工具 MCP 目录,以及一个渐进式目录。两者都通过同一个真实的 agentgateway v1.6.0 对外提供服务,然后测量差异。代码位于 themsquared/progressive-tool-discovery。
这次实验最值得如实交代的发现,并不是数字,而是 agentgateway 已经提供了实现这一机制的基础能力,但仅限 Enterprise 版本。因此,本文也会说明,哪些能力可以免费获得,哪些需要自己构建。
几周前,我写过一篇关于 MCP 工具目录规模失控的文章,收到最多的反驳是:“Code Mode 不是已经解决这个问题了吗?”它解决的是另一个问题。
Code Mode 把 N 个工具替换成一个工具:脚本执行器。模型编写 JavaScript,像调用函数一样调用底层工具;gateway 在沙箱中运行代码,最后只返回组合后的结果。对于需要串联多次工具调用的任务,这确实很有价值,因为模型不再需要为每一个中间结果付出上下文开销。
渐进式发现是另一种思路。工具仍然是工具,改变的是 Agent 在发出任何请求之前,能看到多少目录内容。平铺目录每次响应 tools/list 时,都会发送每个工具的完整定义,不管 Agent 在本次会话中究竟会调用其中多少个工具。渐进式发现让初始响应保持精简,再由 Agent 按需请求更多内容。
两种思路都能减少 token。只有其中一种是在需要之前隐藏目录,而这才是路线图真正提出的要求。
agentgateway 确实提供了实现这件事的基础能力。我想把它具体位于哪个版本说清楚,因为公开文档很容易让人混淆两个版本层级。
Solo Enterprise 的 agentgateway 文档介绍了 MCP backend 上的四个 toolMode 值:Standard(全部透传)、Search(通过 get_tool 和 invoke_tool 两个元工具,每次披露一个工具)、Code(只提供一个 run_code 工具,也就是 Code Mode 的形式),以及 CodeSearch(同时提供两者)。文档说得很直接:“Tool mode 是 agentgateway 为 MCP 实现渐进式披露的方式。”Search 模式正是本文讨论的机制,已经内置,只需配置一个字段。
这个字段是 entMcp.toolMode,设置在 EnterpriseAgentgatewayBackend 资源上。这两个名称都表明了它属于哪个版本层级。为确认这一点,我也检查了开源版本:agentgateway.dev 的 MCP 连接文档列出了配置模式、服务器、认证、guardrails、目标策略、可观测性以及规范兼容性,但没有介绍 tool mode 的页面。这个仓库运行的 OSS 二进制程序不具备该功能。
OSS agentgateway v1.6.0 提供的是 MCP 多路复用和独立路径路由:把多个 MCP 服务器放在一个 backend 后面,它们的工具就会合并到同一个 tools/list 中,并加上目标名称前缀以避免冲突;或者,也可以为每个服务器分配独立路由。这属于联邦聚合,与这里需要的方向正好相反。多路复用让目录变得更大、更统一,而渐进式发现需要让一个目录保持精简。因此,这个演示采用独立路径路由:每种目录形式对应一条路由,而不是合并成一个端点。
我也检查了 agentregistry v0.4.0,因为它是这个系列涉及的另一个 registry 项目,自然会让人想到它。它维护的是一个目录,记录 npm、PyPI、OCI 和原始端点上有哪些 MCP 服务器、Agent 和 skill,并提供 arctl 来部署它们。这属于服务器级发现。工具级发现关注的是:一个正在运行的服务器如何组织自己的 tools/list。这不是它的职责,它的 API 也没有为此提供类似分页或层级结构的能力。要先找到提供渐进式目录的服务器,它是个不错的选择。但找到之后,服务器应该如何运行,它并不干涉,所以我没有把它硬塞进下面的架构。
由于 OSS gateway 没有 Search 模式,我就在 MCP 服务器内部,手动实现了这个模式背后的机制。代码分布在两个文件中,总共约 130 行。
无论背后的真实目录增长到多大,服务器始终只通过 tools/list 暴露三个工具:
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 返回简短摘要:分类名称、描述和工具数量。expand_category 接收一个分类名称,只返回该分类下的完整工具定义,包括 schema。Agent 知道某个工具的准确名称和参数后,call_tool 会将调用分派给真正的处理函数。除非主动请求,否则 Agent 永远不会看到其他分类中的工具。
作为对照,平铺服务器使用相同的目录,但没有这些机制:tools/list 每次都返回全部 50 个工具定义,分为 5 类,每类 10 个。两个服务器共用同一个 catalog_data.py,确保是在同等条件下比较,而不是拿两套不同目录、两套不同命名来对比。
两者都是仅使用 Python 标准库构建的普通 HTTP 服务器,不依赖 MCP SDK。这不是偷懒:agentgateway 自己的 Virtual MCP 文档就指出过一个真实的故障案例,mcp-server-time 固定的 SDK 版本与 uvx 的依赖解析发生冲突,导致服务器无法启动。对于这么小的演示,跳过 SDK 就能避开这一整类问题,代价是服务器没有完整实现 MCP 规范,例如不支持 SSE 推送和会话恢复。这也是 config.yaml 在两个服务器前面运行 agentgateway 时,都设置 statefulMode: stateless 的原因,避免 agentgateway 尝试把会话固定到某个 backend,而这两个服务器实际上都不跟踪会话。
config.yaml 本身采用 agentgateway 基于路由的配置方式:一个 gateway 监听 3000 端口,两条路由,每条路由都有自己的 MCP backend,且各自只有一个 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/ }
这里运行的是真正的 agentgateway v1.6.0,没有修改。入口行为完全由 Python 服务器实现。这如实呈现了 gateway 不提供该功能时,“需要自己手写的部分”究竟是什么。
我通过真实的 gateway,对这两条路由运行 measure.py,而不是直接访问 Python 服务器,因此字节数也包含 agentgateway 在网络传输中添加的内容。下面是某次运行的真实输出:
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%
有两点值得仔细想想。首先,随着真实目录增长,tools/list 的差距(94.4%)只会扩大,因为无论背后有多少工具,渐进式入口始终固定为 3 个工具。一个包含 500 个工具的 backend 会拉大差距,而不是缩小差距。其次,更贴近实际场景的数字(66.2%)刻意低于最显眼的那个数字:它计入了实际使用工具所需的开销,而不仅仅是列出目录。这才是应该如实报告的数字;对于只需要五个分类中某一类的 Agent,它仍然意味着约三分之二的降幅。
这些数字是网络传输的字节数,不是模型 token 数。我没有用 tokenizer 统计它们,因此不会声称自己测出了某个 token 数字。可以确定的是,两种载荷包含同类内容:工具名称、自然语言描述和 JSON schema。因此,对两者使用同一个 tokenizer,结果的变化方向会与字节数一致,即使具体百分比有所不同。至于这会如何影响模型准确率,而不仅是 token 数,Anthropic 自己给出的数据见本文所跟进的上一篇文章。
只需要 Docker 和 Compose v2+,没有其他依赖。已在 arm64 macOS 上使用 Docker 29.8.1 / Compose v5.5.1 验证,运行的镜像为 ghcr.io/agentgateway/agentgateway:v1.6.0。
git clone https://github.com/themsquared/progressive-tool-discovery
cd progressive-tool-discovery
./demo.sh
demo.sh 会构建两个服务器、启动 agentgateway、等待它能够响应、运行测量脚本,最后清理所有资源。算上拉取镜像,整个过程也远不到 15 分钟。它打印的每个数字都来自当次运行,而不是从本文中抄来的。
路线图认为目录不应该一开始就把所有内容全部塞过来,这个判断是对的。这次构建补充的是讨论中很容易被略过的一部分:agentgateway 已经拥有路线图描述的功能,只是需要相应的许可证版本。如果你使用开源版 agentgateway,这个模式并非不可用,只是不能通过一个配置字段开启。你需要在 MCP 服务器中写约 130 行代码,实现与 Enterprise 的 Search 模式相同的形式:一个固定的小型入口,先展示分类,再按需提供工具定义。
如果你的目录规模小而且稳定,这些都不重要:使用 Standard 模式就对了,也更简单。如果工具已经有四十个,而且还在增长,那么无论你用哪个版本,都应该先构建入口。如果想了解工具目录为什么会发展到这种规模,以及为什么 Code Mode 和工具搜索无法解决底层的治理问题,可以阅读《Conway 定律正向你的 MCP 服务器袭来》。至于另一个能减少 Agent 阅读量的 gateway 层技巧——在 gateway 处提供 Markdown 而不是 HTML——本质上是把同一种思路应用到不同载荷上。
代码:themsquared/progressive-tool-discovery,采用 Apache-2.0 许可证。
这是一种模式:MCP 服务器的 tools/list 响应保持精简且固定,只有 Agent 提出请求时,服务器才会披露目录中的更多内容。2026-08-22 发布的 MCP 路线图直接提出了这一点:服务器应该提供一个小型入口,再逐步展示目录中的更多内容,而不是每次连接都发送所有工具定义,无论 Agent 是否需要。
不是。Code Mode 用一个脚本执行器工具替代 N 个工具,让模型编写代码,像调用函数一样调用工具,而不是阅读 N 份定义。这属于组合调用。渐进式发现保留工具本身,只是逐步披露其定义:先提供一个小型入口,其余内容按需提供。两者机制不同,动机相同。
Solo Enterprise 的 agentgateway 版本支持,通过在 EnterpriseAgentgatewayBackend 上设置 entMcp.toolMode: Search,将 backend 的工具列表替换成 get_tool 和 invoke_tool 两个元工具。这项功能仅限 Enterprise:字段和资源名称都仅在 Enterprise 中提供,开源版 agentgateway 的 MCP 文档甚至完全没有介绍 tool mode 的页面。
在一次实测中,一个人工构造的 50 工具目录通过真实的 agentgateway v1.6.0 对外提供服务,包含 3 个工具的渐进式入口,其 tools/list 响应比平铺目录小 94.4%。把 Agent 实际列出分类、展开所需分类的开销也计算在内,实际总量仍然更小:对于只需要五个分类中某一类的 Agent,降幅约为三分之二。
本文的规范版本位于 webofmike.com/progressive-discovery/,机器可读的 Markdown 版本位于 webofmike.com/progressive-discovery/index.md。
如需采取进一步行动,可以考虑屏蔽此人和/或举报滥用行为。