上一篇结束时留了个问题:薄壳、目录、协议、工具都在变,版本怎么管?
这个问题的默认答案是一个版本号——给整个 Skill 定一个 version: 1.0.0,每次改动升一位,客户端比对版本决定要不要更新。个人项目这样完全够用。但在「平台维护 Skill + 协议在服务端 + 一堆异构 Agent 宿主」的形态下,单一版本号很快会变成灾难:
- 版本号通胀:改一行话术也得升版本,因为不升版本就无法表达「有变化」;
- 更新绑架:任何小改都触发客户端更新流程,哪怕这次改动用户根本感知不到;
- 语义丢失:看到版本从 3.2 升到 3.3,没有人知道是流程变了、接口变了还是触发词变了。
我们的答案不是「更好的版本号」,而是承认系统里有四种独立的变化节奏,给每种节奏配自己的版本号。

四套版本号总览
| 版本 | 挂在哪 | 管什么 | 变化频率 |
|---|---|---|---|
| Skill version | 薄壳 SKILL.md frontmatter | 触发词、会话规则 | 极低(一年几次) |
| serverInfo.version | MCP Server 初始化握手 | 工具集结构:增删工具、改 schema、改 instructions、改 scope 定义 | 低 |
| protocol_version | 每份协议文件 frontmatter | 单个能力的流程内容 | 中(随时可改) |
| capability version | 每个用户自定义能力(int) | 结构化 workflow 的并发控制 | 用户每次编辑 |
先给一个直观的对照:我们系统现在的真实版本是——薄壳 3.0.1,MCP Server 3.5.0,工作汇报协议 4.0.1,个人自动化协议 3.0.0。四个数字互不相干,这是特性不是混乱。
逐套拆解
Skill version:管「启动包本身」
薄壳变得很少,因为它的内容只剩元规则。真实历史里最典型的一次升级:3.0.0 → 3.0.1,全部改动是给 description 加了几个触发词。
注意这次升级的成本结构:服务端零改动,但需要用户重新安装 Skill。这是四套版本号里唯一需要「重装」的一套——正因如此,我们才把变化最快的东西(流程)从薄壳里抽走,让「要重装的改动」收缩到触发词和会话规则这个最小集合。
serverInfo.version:管「工具集结构」
MCP 协议在初始化握手时返回 serverInfo.version。客户端感知「工具集结构变了」靠的就是它——工具列表在握手后缓存,后续不会自动刷新,版本号是客户端判断「该重连了」的唯一信号。
所以这套版本号的升级约定必须严格。我们把它写进了平台的工程检查清单,原话是:
如果本次变更涉及 MCP Server 能力修改(新增/删除/重命名工具、修改工具参数 schema、变更 instructions、变更 toolset scope 定义),commit 前必须确认是否升级 serverInfo.version。
不升的后果很具体:客户端拿着旧的工具清单和新的服务端对话,调用一个已被删除的工具,或者用旧 schema 传参。这类故障在日志里表现为零星的「工具不存在」错误,极难和权限问题区分。
一个配套细节:这些约定挂在 commit 检查清单而不是代码里,靠流程保证就有遗漏的一天。我们在 AGENTS.md 里写约定之外,还让 AI 编码助手在每次涉及 MCP 的变更时被强制询问「要不要升版本」——把防呆做进日常流程,而不是指望人记得。
protocol_version:管「单个能力的流程」
每份协议文件自带版本(4.0.1)和更新日期。这套版本号最有意思的地方是:它的变更对客户端是免费的。
传统快照 Skill 里,版本号的作用是驱动更新——客户端比对版本,发现旧了,拉新副本。而在我们的体系里,协议每个会话都重新拉取(薄壳规则 5 禁止缓存),根本不存在「客户端持有旧版本」这个状态。protocol_version 不驱动任何行为,它只回答两个问题:
- 溯源:这次会话执行的协议是哪一版?排查问题时,Agent 的行为对不上预期,先看它当时拉到的
protocolVersion; - 审计:协议内容变了,版本号和日期给变更留一条线。
一个真实的例子:某次我们调整了语义检索工具的默认行为(不传类型时只查日报,避免和周报月报重复)。改动就是协议文件里一段话加 4.0.0 → 4.0.1。这次变更没有动 MCP Server、没有动薄壳、没有任何用户感知——用户下一个会话自动拿到新流程。这就是「把版本管理挪回服务端」的直接收益。
capability version:其实不是版本号,是乐观锁
用户自定义能力(user: 前缀)的 version 是个递增整数,下发时拼成 user-capability-v3 这样的字符串。但它的设计目的不是「客户端感知更新」,而是并发控制:
用户同时开着两个会话编辑同一个自定义能力(或者一个会话在编辑、另一个在执行),乐观锁保证后提交的改动会失败并要求重读。它被复用为下发版本号,纯属顺路——Agent 拿到 user-capability-v3,至少知道自己在执行第几版 workflow。
这套「版本」还有一个特殊性质:它的变更连 MCP 会话都不用重建。用户激活、停用、修改专属能力后,Agent 下一次拉目录直接反映最新状态——因为目录和协议都是实时查询的结果,不存在需要失效的缓存。
版本号跟着变化的节奏拆,不跟着模块拆
四套版本号背后是一条可迁移的设计原则:
按「谁需要因这个变化而行动」来拆版本号,而不是按代码模块。
回头看这四套,每一套都对应一个明确的行动方:
| 版本 | 谁需要行动 |
|---|---|
| Skill version | 用户(重新安装) |
| serverInfo.version | Agent 宿主(重新握手) |
| protocol_version | 没有人——只需要知道 |
| capability version | 正在编辑的用户(处理冲突) |
单一版本号之所以灾难,是因为它把四种行动方混在一个数字里:升了一位,你不知道该通知谁;不升,某个行动方就被漏掉了。拆开之后,每次变更「该惊动谁」是精确的——大多数变更惊动 nobody,这就是设计的目标态。
诚实的管理成本
四套版本号不是免费的,有两个真实的代价:
1. 人得记住哪类改动碰哪套版本。 改协议只升 protocol_version,加工具必须升 serverInfo.version,动 instructions 两者都要考虑……这是一张需要内化的规则表。我们的缓解办法就是前面说的 commit 检查清单,但这依赖流程纪律。
2. 版本号的真相只在代码里,文档会撒谎。 我们踩过:AGENTS.md 里还写着「serverInfo.version 当前为 1.0.0」,实际代码早已是 3.5.0。四套版本号分散在三处(SKILL.md、协议 frontmatter、MCP Server 构造参数),没有任何一处能看到全貌。如果重来,我会考虑加一个启动时打印版本清单的日志,或者一个返回四套版本号的诊断工具——让「现在的版本是什么」永远一查便知。
另外还有一套「预留未启用」的版本:启动包的 manifest.json 里有 minServerVersion 字段,本意是让客户端在握手时校验「服务端太旧则拒绝使用」。目前它是分发清单的一部分,运行时没有任何代码读取。留这个字段是因为预见到了「薄壳新版依赖新目录特性」的场景——但目前没有真实需求驱动,我们克制着没有实现。版本协商机制最好等第一个真实的不兼容案例出现再设计,提前设计大概率设计错。
小结
- 四套版本号对应四种变化节奏和四个行动方:用户重装、宿主重连、无需行动、编辑冲突;
- 协议版本变更是免费的——不触发任何更新流程,这是「单一事实源在服务端」的直接红利;
- 版本号的升级约定要做成防呆(commit 检查清单 + 工具提醒),指望人记得一定会漏;
- 版本真相只活在代码里,文档里的版本号一定会过期——要么自动生成,要么加诊断工具。
到这里,内置能力的完整闭环讲完了:薄壳怎么薄、目录怎么给、协议怎么发、版本怎么管。但还剩最后一块,也是安全密度最高的部分:让用户自己定义能力,而不打开注入的后门。下一篇讲 user: 能力的结构化 workflow 设计,以及它与「托管外部工作流」路线的对比。
本系列下一篇:《让用户自定义能力,而不打开注入的后门》