关于能力漂移( Capability Drift )、持久管理意图( Durable Management Intent )与原子能力面( Atomic Capability Surface )的实践思考
本文证据截止于 2026-07-24 ; MCP 官方议题、拉取请求与规范状态最后核验于 2026-07-26 。文中的 MCP 2026-07-28 仍按候选发布版表述,正式发布后需要按最终规范再次核验。
1. 从“连接几个 Server”到一个长期状态问题
我们最初把 MCPMate 看成一个相对直接的桌面网关( Desktop Gateway ):连接多个 MCP Server ,处理名称冲突( Name Collision ),再把 Tools 、Prompts 、Resources 和 Resource Templates 暴露给不同的宿主应用( Host App )。为了提供更细粒度的管理能力,MCPMate 允许用户通过 Profile 和 Direct Exposure 选择具体要暴露的能力( Capability ),避免向宿主应用加载并不需要的能力。
真正开始保存 Profile 、Direct Exposure 和用户选择后,问题却变了。
Server 会升级。原有 Tool 可能改名、被合并、从默认列表移出,或者在名称不变时修改输入模式( Input Schema )、输出模式( Output Schema )、描述( Description )和注解( Annotations );新的 Prompt 或 Resource 可能出现,已有能力也可能暂时消失。此时,几个月前保存的那条设置到底还是什么意思?
我们把这类变化统称为能力漂移。它至少包括:
- 发现漂移( Discovery Drift ):能力新增、消失或重新出现;
- 命名漂移( Naming Drift ):名称、URI 或 URI 模板改变;
- 契约漂移( Contract Drift ):模式、描述、参数、注解等模型可见定义改变;
- 暴露漂移( Exposure Drift ):默认工具集、Profile 、功能开关或授权上下文改变可见集合;
- 投影漂移( Projection Drift ):网关对上游能力改名、裁剪或添加覆盖信息;
- 行为漂移( Behavioral Drift ):通信契约不变,但后端实际行为改变。
最后一种变化尤其需要划清边界:定义哈希( Definition Hash )可以发现被纳入定义的字段变化,却不能证明 Server 的后端行为没有改变。那需要测试、可信证明( Attestation )或其他信任机制。
本文想讨论的不是“名称不好”,也不是“给每个 Tool 加一个 UUID 就结束了”。我们真正关心的是:
当控制系统已经保存了用户对于某项能力的长期决定,面对后续变化,它该如何“保留用户的管理意图”?要回答这个问题,系统至少需要分别判断:逻辑上是否仍是同一项能力、能力定义( Capability Definition )是否已经改变,以及哪个定义版本( Definition Revision )当前实际进入了面向该用户的发布面。
2. 什么叫“保留用户的管理意图”?
这里的“管理意图”不是某一种权限系统的专有术语。我们采用一个中性定义:
管理意图,是用户或管理员对于某项能力应如何被控制系统处理所作出的持久决策。
例如,Claude Code 的权限规则使用 allow、ask 和 deny 控制 Tool 调用; MCP Tool 会以 mcp__<server>__<tool> 一类名称进入规则。Claude Code 还有相邻但不同的受管 MCP 与 Server 准入设置。
MCPMate 的控制点不同:
- Profile 保存用户选择的能力集合( Capability Set );
- Direct Exposure 决定向某个使用方( Consumer )直接暴露哪些 Server 或能力;
- 变更策略( Change Policy )决定上游变化后,新版本是自动
follow,还是等待用户review。
Tool 权限、Server 准入和网关暴露并不具有完全相同的授权语义( Authorization Semantics )。我们也不需要先统一这些概念,才能看到它们共享的问题:
只要一个系统持久保存了“以后应如何处理这项能力”,它就必须判断未来观察到的对象是不是原来的逻辑能力,以及旧决定是否仍适用于新的定义。
“保留管理意图”因此不等于“无条件保留旧授权”,更不等于“继续调用已经不存在的旧实现”。它意味着保留用户建立的逻辑关系,同时独立决定新的定义版本是否可以进入当前生效的能力面( Active Surface )。
3. 一个真实案例:Exa MCP Server 一年内发生了什么?
我们冻结了 8 个具有代表性的开源 MCP Server 代码仓库,观察 2025-07-24 至 2026-07-24 的公开历史,并分别核查:
- 固定代码版本中的能力注册信息( Capability Registration );
- 能够复现时的 MCP 运行时能力面( Runtime Surface );
- 维护者提交的拉取请求、代码提交和审查意见;
- 与变化直接相关的用户议题。
这个样本用于寻找可审计案例,不用于估计整个 MCP 生态的发生率。证据最完整的案例是 exa-labs/exa-mcp-server。
3.1 起点:源代码中默认启用了 10 个 Tool
在冻结的窗口起点版本,固定的能力注册代码列出了 10 个默认启用的 Tool 。
这个历史版本的 package.json 和 package-lock.json 不一致,使用锁定依赖无法完成构建。因此,10 个 Tool 是静态源代码事实( Static Source Fact ),不是我们已经复现的运行时 tools/list。我们如实保留了这项证据缺失( Missingness ),而没有改用新的依赖解析结果补出一份“看起来可运行”的历史版本。
3.2 变化一:默认能力面收缩
PR #225 把 6 个 Tool 标记为弃用,并从默认能力面移出。代码中仍可保留旧 Tool ,但默认可见集合已经收缩。
这首先是暴露范围变化( Exposure Change ),而不等于 Tool 已被删除。
3.3 变化二:改名同时伴随契约变化( Contract Change )
PR #273 明确把 crawling_exa 改名为 web_fetch_exa,并为旧名称保留了一项已弃用的兼容入口。
但这不是一次单纯的改名。固定的变更前版本与变更后版本显示,同一个变更还移除了:
maxAgeHours;subpages;subpageTarget。
维护者提供的别名( Alias )明确证明了逻辑连续性( Logical Continuity ),却不能让改名前后的两份能力定义变成同一份内容。
3.4 变化三:Prompt 被移除
PR #280 删除了 code_search_help Prompt 。
这说明能力漂移不限于 Tool 。如果宿主应用或网关保存的是 Prompt 选择,同样会遇到关系是否保留、缺失如何表达的问题。
3.5 变化四:四个 Agent Tool 合并为一个
PR #383 把:
agent_create_run;agent_wait_for_run;agent_get_run_output;agent_cancel_run
合并为一个 agent_run。
旧 MCP URL 中的 Tool 选择参数可以被映射到新 Tool ,但四项旧能力已经不再作为四个独立能力存在。这不是四次一对一改名,而是多对一合并。
3.6 终点:固定配置下实际列出 2 个 Tool 、1 个 Prompt 、1 个 Resource
在冻结的窗口终点版本,我们使用无凭据的本地 stdio 配置构建 Server ,并通过 MCP Inspector 1.0.0 实际执行列表请求:
tools/list
web_search_exa
web_fetch_exa
prompts/list
web_search_help
resources/list
exa://tools/list
这只代表该固定版本及配置,不代表 Exa 在所有托管方式、凭据状态或显式 tools= 配置下的能力面。
3.7 用户确实感知到了变化
Issue #275 报告托管默认端点只显示两个 Tool ,旧 crawling_exa 返回 Tool not found,而文档仍描述旧的默认能力面。维护者随后确认文档已经落后,并说明旧 Tool 仍可通过显式选择启用。
这条真实时间线让问题变得具体:
crawling_exa和web_fetch_exa有明确连续性意图,但能力定义同时改变;- 多个旧 Tool 合并为一个更宽的新 Tool 时,旧选择应该如何继承?
- Prompt 消失后,持久 Profile 应删除关系,还是保留为“当前缺失”?
- 从默认能力面移出和从 Server 中彻底删除,是不是同一种事件?
只看名称,回答不了这些问题。
4. 如果用户选择过 crawling_exa,网关应该怎么做?
几种做法都看似合理:
- 严格按名称查找:旧 Tool 消失,原 Profile 立即失效;
- 看到别名就自动替换:逻辑关系恢复,但三个输入字段已经消失;
- 旧名称还能解析就视为旧 Tool:调用可能成功,却掩盖了规范名称和契约已经改变;
- 把新定义当作完全陌生的对象:最保守,但所有长期关系都要重建;
- 始终跟随 Server 最新默认面:干预最少,却把“维护者默认启用”误当成“用户已经批准”。
这些策略并非在所有场景下都有统一的对错。临时开发环境可能优先便利,企业控制面可能优先默认拒绝,个人桌面网关则可能追求少干预但可解释。
真正缺少的不是唯一策略,而是让系统知道自己正在对哪一层作决定:
- 逻辑上是哪项能力;
- 当前是哪一份定义;
- 哪个版本真正暴露给哪个使用方。
5. 为什么名称、固定 ID 和哈希都只解决一部分?
5.1 身份与版本机制
- 只使用名称可以完成当前路由和调用,但改名会断开关系,名称不变的契约变化又可能静默继承旧决定;
- 只使用固定 ID可以表达逻辑连续性,但内容改变而 ID 不变时,新定义可能无提示继承旧设置;
- 只使用哈希可以精确检测定义变化,但每次变化都会成为全新对象,长期关系随之丢失;
- 只使用 Server 版本可以给整个 Server 一个发布边界,却难以表达能力级、使用方级和随授权变化的能力面。
5.2 更新策略
- 始终跟随最新版本干预最少,但新增、合并或权限更强的定义可能静默进入旧发布面;
- 始终要求审查最为保守,却可能让无效变化或低风险变化制造大量审查噪声。
如果 Server 提供可信的命名空间( Namespace )、Server 身份( Server Identity )、能力类型( Kind )和稳定 Capability ID ,这个组合确实可以显著改善逻辑身份问题。但它仍未回答:
- 新内容是否应该生成新的不可变定义版本;
- 合并、拆分和跨 Server 移动如何表达;
- 网关改名、裁剪或添加覆盖信息后,派生能力的身份归谁;
- 旧决定是否覆盖新的定义版本;
- 哪个定义版本当前对哪个使用方生效。
因此,“稳定逻辑身份”和“不可变定义版本”不能压在同一个 ID 上。
6. MCPMate 正在尝试的分层模型
我们当前把问题拆成四层:
CapabilityRef
= 对稳定 Server ID + 能力类型 + 精确来源键( Origin Key )的持久逻辑关系
CapabilityId
= 一份版本化、规范化且包含来源与路由信息的有效能力记录的不可变内容身份( Immutable Content Identity )
SurfaceManifest
= 面向一个使用方、固定到精确能力定义版本的不可变能力面清单( Surface Manifest )
Publication
= 使用方与当前生效能力面清单之间的原子绑定( Atomic Binding )
来源信息参与 CapabilityId 的计算,因此两个 Server 即使暴露字节级相同的定义,也会得到不同的内容身份。
6.1 Ref 保留关系,ID 固定内容
CapabilityRef 不是对所有未来内容的永久批准。同一 CapabilityRef 的能力定义发生变化时:
CapabilityRef 不变
CapabilityId 更新
Profile / Direct Exposure 关系保留
按策略 follow 或 review
如果来源键发生改变,又没有可信的稳定 UID:
生成新的 CapabilityRef
生成新的 CapabilityId
旧关系保留为“未解析”
需要人工重新绑定
相似度可以提示人工审查,却不应自行搬运旧设置。维护者提供的别名是更强的连续性证据,但新定义是否继承旧决定,仍然需要控制系统判断。
6.2 follow 和 review 是变化后的策略,不是初始允许或拒绝
record:始终保存变化证据;follow:符合条件的新定义版本无需人工复审即可重新进入发布面;review:保留原有关系,创建使用方级审查事项( Consumer-scoped Review Item ),但目标定义版本暂不进入当前生效的能力面;manual_rebind:旧CapabilityRef到新CapabilityRef的关系必须显式确认。
一个容易忽略的安全边界是:上游定义已经变化后,旧能力面清单并不能让 Server 恢复旧实现。因此,需要审查的能力不能简单地“继续使用旧版本”。
MCPMate 设计中的处理是:
- 先为受影响的使用方发布不含该能力项的安全收缩面( Safe Contraction );
- 保留 Profile 或 Direct Exposure 中记录的管理意图;
- 执行
follow或approve_target后,把目标定义版本加入新的发布面; - 执行
reject_target后,关系仍然保留,但不发布目标定义版本。
这一区分很重要:
保留用户意图,不等于保留一个已经不可执行的旧版本。
6.3 混合更新策略
- 没有有效定义变化:Ref 、ID 和发布面都不改变,只记录证据,不制造审查事项;
- 同一 Ref 的模型可见定义变化:Ref 保持不变,生成新 ID ;先安全收缩,再按策略
follow或review; - 安全或执行语义变化:Ref 保持不变,生成新 ID ,并强制
review; - 来源键变化:生成新 Ref 和新 ID ,不自动替换,执行
manual_rebind; - 完整能力清单中旧 Ref 消失:旧 Ref 标记为当前缺失,历史 ID 保留;发布安全收缩面并保留管理关系;
- 失败或不完整的能力观察中出现缺失:Ref 、ID 和发布面都不改变,只记录失败,不判定为移除;
- 能力级选择下出现新 Ref:生成新 Ref 和新 ID ,默认不吸收;
- Server 级暴露下出现新 Ref:生成新 Ref 和新 ID ,仅在 Server 策略允许时
follow。
6.4 “原子”能保证什么,不能保证什么?
这里的“原子”指:
- 一次发布切换( Publication Cutover )把使用方从完整的旧能力面清单切换到完整的新能力面清单;
- 一次请求开始解析后,不再用能力目录( Catalog )中的最新记录替换清单已经固定的
CapabilityId; - 同一个当前生效的能力面不会混入两次发布的部分结果。
它不意味着:
- 网关可以恢复 Server 已删除的旧实现;
- 两个独立请求天然使用同一个能力面世代;
- 内容哈希可以证明后端行为未改变;
- MCP 核心客户端自动理解网关内部的 Ref 、ID 和能力面清单。
如果发现能力和调用能力是两个独立请求,并且中间发生了已经批准的发布切换,更强的“从发现到使用的连续性( Discovery-to-Use Continuity )”仍可能需要能力面世代( Surface Epoch )、宽限路由( Grace Routing )或 MCP 扩展。
这个模型的成本同样真实:更多持久状态、差异分类、别名生命周期、审查队列、能力面清单物化( Manifest Materialization )和发布切换一致性。它可能对许多轻量宿主应用来说属于过度设计。
7. 生态已经在怎么解决?
7.1 GitHub MCP Server:用别名保护持久配置
GitHub PR #1563 引入 DeprecatedToolAliases,直接动机是避免用户在 X-MCP-Tools 或 --tools 中保存旧 Tool 名称后遭遇破坏性变化。
维护者支持该方向,同时指出这些映射关系不应该永久保留。
随后,PR #1652 为合并后的 GitHub Actions Tools 增加别名。我们在固定的代码版本 eb088dfe...上,以旧选择器 list_workflows 启动 Server ,使用非真实占位 Token 只执行 tools/list,没有调用 GitHub API 。实际注册的是新 Tool actions_list:
configured selector: list_workflows
registered tool: actions_list
这是一个应当正面承认的现有方案:Server 最了解自己的改名意图,也最适合发布别名。
但别名本身仍未决定:
- 映射关系何时可以删除;
- 多个旧 Tool 合并后,任意一个旧的允许或暴露决定是否足以放行新 Tool 的全部能力;
- Prompt 、Resource 、跨 Server 移动或网关投影如何表达;
- 旧名称仍可解析时,旧能力定义是否仍然存在。
7.2 Context7:同名契约变化与空能力面兼容
Context7 PR #2524 在 query-docs 名称不变时移除了 researchMode 输入项和相关描述,因为多个客户端的长调用超时问题难以统一解决。
同一代码仓库中的 Issue #2527 还报告,部分客户端会无条件调用可选的 prompts/list 和 resources/list,并把 -32601 Method not found 当作致命错误;随后 PR #2534 增加了空的 Prompt 、Resource 和 Resource Template 列表处理。这并没有新增实际能力,却提示网关需要区分“完整但为空”“不支持某类能力”和“观察不完整”。本文只把它作为移除判定边界的补充,而不把它写成 Context7 违反 MCP 规范的案例。
8. MCP 官方其实已经讨论到哪里?
这个问题不是 MCP 社区的空白。
8.1 已处理的命名与冲突问题
- PR #701 已明确承认聚合时的 Tool 名称冲突,并列出用户定义 Server 名称、随机前缀、Server URI 前缀等消歧策略;
- SEP-986 与已合并的 PR #1603 统一了 Tool 名称格式。
这些工作改善了语法、互操作性和当前路由,但没有定义改名连续性或能力定义版本。
8.2 版本、摘要与模式哈希提案
- SEP-1575 / PR #1602 提议 Tool 语义版本( Semantic Versioning );
- SEP-1766 讨论由摘要( Digest )固定的 Tool 版本,并区分兼容版本、精确摘要和运行时策略;
- 尚未合并的 PR #2636 在渐进式 Tool 披露( Progressive Tool Disclosure )方案中提议为每个 Tool 提供模式哈希( Schema Hash )
schemaHash; - 已合并的 Security IG Charter PR #2912 把“跨 Server 的 Tool 身份”列为待讨论问题。
这里尤其需要澄清 SEP-1575 。它不是“稳定名称或版本的想法被官方粗暴拒绝”。公开讨论包含:
- 单项能力版本管理的维护复杂度;
- Server 级版本管理是否已经足够;
- 多个 Tool 之间的发布耦合;
- Prompt 和 Resource 是否也需要版本管理。
对应的草案拉取请求没有合并,议题后来按 SEP 流程进入休眠并关闭。另一个 Issue #1915 被关闭,是因为未批准的 SEP 不能被写成现行文档,而不是对稳定名称本身作出技术否定。
因此,我们目前能提出的增量主张很窄:
名称格式、前缀和命名空间主要处理语法、路由与冲突;语义版本表达维护者预期的兼容性;摘要和
schemaHash固定或比较某一份能力定义。控制系统仍需要决定:旧管理意图是否关联到新的逻辑对象、新定义是否需要审查,以及哪个精确定义版本进入哪个使用方当前生效的能力面。
这是截至 2026-07-26 对公开一手来源的有限结论,不是完整系统综述。
我们也核验了已经锁定的 MCP 2026-07-28 候选发布版及其官方代码仓库快照。新规范通过无状态核心、随授权变化的列表、缓存信息和显式订阅改善了能力面的新鲜度与隔离,但仍没有定义跨版本稳定能力 ID 、能力定义版本、变更审查或使用方级发布生命周期。因此,它没有让本文的问题消失;与此同时,我们也必须承认,原子能力面只能避免单次请求解析到未经发布的最新定义,不能单独保证两个独立请求始终使用同一版本。
正式发布本文时,我们会再次比较候选发布版与最终版;如果最终规范新增相关机制,本文结论也会随之更新。
9. 调研范围与限制
我们冻结的 8 个代码仓库及其在首篇文章中的用途如下:
exa-labs/exa-mcp-server,主要案例:终点运行时已复现;起点只有静态源代码证据;github/github-mcp-server,辅助案例:别名运行时已复现;未找到直接对应的用户议题;upstash/context7,辅助案例:使用源代码以及用户与修复讨论;未独立复现历史运行时;firecrawl/firecrawl-mcp-server,筛查证据:主要是描述或文档与真实能力面不一致;cloudflare/mcp-server-cloudflare,筛查证据:必须按 Radar 、Browser Rendering 等应用分别编码;makenotion/notion-mcp-server,筛查证据:新增 Markdown Tool 并缩减模式;托管版本难以映射;getsentry/XcodeBuildMCP,筛查证据:同名但破坏性的输入契约变化;无直接对应的用户议题;microsoft/playwright-mcp,证据缺失:尚未解析 Verification Tool 首次在上游注册的边界。
这项调研存在明显限制:
- 样本不是随机抽样,不能估计生态发生率;
- 只覆盖公开的 GitHub 代码仓库;
- 议题是用户主动提交的反馈,不代表受影响用户比例;
- 部分历史代码版本无法按锁定依赖复现;
- 静态注册、运行时能力面、维护者意图和用户报告分别回答不同问题;
- Reddit 回复可以提供反例和新的质性材料,但不能被当作统计共识。
因此,到这里,我们的观点可以明确为:
对任何需要长期保存用户管理意图的宿主应用或网关来说,能力名称不足以同时承担逻辑身份和定义版本两种职责。Server 提供的别名、版本与摘要都很重要,但它们不能替控制系统决定:旧关系是否延续、新定义是否需要审查,以及哪个精确版本可以进入某个使用方当前生效的能力面。
这并不意味着 MCP 核心规范必须完整接纳 MCPMate 的分层模型;但无论责任最终由 Server 、宿主应用、网关、注册中心还是扩展协议承担,持久保存管理意图的一方都需要解决一组等价的问题。忽略它们并不会消除能力漂移,只会让系统以“设置失效”或“旧决定静默覆盖新能力”的方式作出隐式选择。
10. 我们想向社区请教什么?
如果你也在使用或维护 MCP Server 、宿主应用或网关,我们很想知道:
- 你是否遇到过名称、模式、描述、注解或默认工具集变化,使原有规则失效或静默继续生效?
- GitHub 式的 Server 端别名加上 Server 级版本,是否已经足以覆盖大部分场景?别名的失效时间应由谁决定?
- 来源键改变或多个旧 Tool 合并时,什么证据足以证明逻辑关系仍然延续?旧的允许或暴露决定又应当自动跟随、等待审查,还是默认失效?
- 空列表、不支持某类能力、暂时失败和随授权变化的列表,应如何表达移除判定权( Removal Authority )?
- 这类机制更适合放在 MCP 核心规范、扩展、注册中心,还是留给网关和宿主应用?我们是否把一个更简单的问题过度建模了?
具体案例、现有实现和反例对我们都很有价值;如果 Server 别名、版本或其他更简单的方法已经足够,也欢迎直接指出。
附录:引用与一手来源
超长,附后