从静态工具到动态工具池:一次 MCP 接入重构了 Agent 架构
🎯 学习目标
- 六个术语(Alias/Connection/Published Tool/Policy/Exposed Tool/Snapshot)各回答什么?最关键的区分是?
- 进程内 handler dict 算 MCP 吗?缺什么?
- Published 和 Exposed 的区别?server 说只读就只读吗?
- connect_mcp 回复里调用新工具会怎样?第几次请求才看到?
- 断开 alpha 后,beta 的工具会受影响吗?
- Subagent 和 Teammate 能看到 MCP 管理工具和远程工具吗?
🧠 核心概念
1 · 六个术语 ▸
| 术语 | 回答的问题 | 是否可信 |
|---|---|---|
| MCP Server Alias | 本地 allowlist 用哪个稳定名字代表 server | 本地可信 |
| MCP Connection | 这个 alias 当前是否有已初始化且存活的协议会话 | 运行时状态 |
| Published MCP Tool | server 通过 tools/list 发布了什么名称/描述/schema | 外部数据 |
| MCP Tool Policy | 本地如何给某个远程工具指定 effect | 本地可信 |
| Exposed MCP Tool | 远程声明经验证后本地真正暴露什么 | 本地能力 |
| Registry Snapshot | 一次模型请求及其回复共同使用哪一版工具集合 | 不可变快照 |
2 · 进程内 handler dict 不是 MCP ▸
旧版做 MCPClient 放 dict[str, callable],tools/call 只是字典取函数。这没证明任何 MCP 行为:没有 transport、没有官方 Client 与 StdioClientTransport、没有 initialize、没有 tools/list 分页、没有 tools/call、没有子进程退出/超时/取消/关闭语义,server 实现和 Agent 同进程。
P19 直接用锁文件中的 @modelcontextprotocol/sdk,传输路径:McpServerSpec → StdioClientTransport → Client.connect(initialize) → Client.listTools(分页) → Client.callTool。演示 server 也是独立 TypeScript 子进程,真启动两进程通过 stdin/stdout 交换 MCP 消息。
3 · 动态注册与原子撤销 ▸
连接 demo_alpha 和 demo_beta(都发布 lookup),本地工具名变成 mcp__demo_alpha__lookup 和 mcp__demo_beta__lookup(前缀防同名串线)。
断开 alpha 后只撤销 alpha 的全部定义,beta 仍可调用(原子撤销,不影响其他 connection)。超时或子进程退出会撤销该 alias 的全部工具。Runner 关闭后所有 MCP connection 与 stdio 子进程都被回收。三类故障三种迁移:远程 isError:true → mcp_remote_error,连接保留;调用超时 → mcp_timeout,连接丢弃;子进程退出/transport 断 → mcp_connection_lost,连接丢弃,空闲时由 Watchdog 主动撤销。撤销按对象身份而不只按名字——同名工具被另一连接重新注册过,按名撤销会误删别人的。
4 · 权限来自本地,不来自 server 声明 ▸
MCP server 可以在 description 中声称自己只读,但权限只能来自本地可信策略(MCP Tool Policy)。远程业务错误不泄露 server 私有错误细节(脱敏)。effect 由本地 allowlist 指定,不是 server 说了算。
这和第3章一脉相承:模型只能提出调用,是否执行由 Harness 决定。MCP server 也是外部不可信声明,本地策略才是硬边界。补充三道本地边界:① connect_mcp 只接受 alias,不接 command/args/cwd——否则它退化成另一种 shell 入口;② 本地 policy 集合与远程申报精确相等(不多不少,不是取交集),少发或多发都拒绝整条连接;③ JSON Schema 是执行边界:顶层必须 type:object,拒外部 $ref,发布前用 Ajv 严格编译,调用时先 Ajv 校验再出网,坏参数不出网;错误信封字段全部本地构造,远程私有细节不进上下文。名称规范化后若碰撞(如 lookup-one 与 lookup_one 都变 lookup_one)也拒绝整条连接。
5 · 只进入 Lead,不进入 Subagent/Teammate ▸
动态工具只进入 Lead 的 live ToolRegistry。Subagent 与 Teammate 始终看不到 MCP 管理工具(connect_mcp/disconnect_mcp)和远程工具。
这避免子 Agent 或队友擅自连接/断开 MCP server,也避免远程工具的不可信声明进入隔离执行环境。MCP 生命周期由 Lead 统一管理。