12.3 官方扩展体系
🎯 学习目标:理解 MCP 扩展的协商机制,知道何时该用 Tasks / Apps / Skills ⏱️ 预计时间:45 分钟 📊 难度等级:⭐⭐⭐(概念为主,配有协议示例)
📌 一句话结论:2026-07-28 把一批"实验特性"正式移出核心协议,改为官方扩展。扩展是可选、可组合、独立演进的,一律默认关闭,必须由开发者显式开启并做能力协商;每个扩展还处在 Experimental / Beta / Stable 三阶段之一,阶段决定了它能怎么变、你敢指望它多少。
🧩 什么是 MCP 扩展
扩展是对规范的可选补充,定义了核心协议之外的能力。 它们可以是:
- 模块化的(如独立的认证机制)
- 专业化的(如某行业特有逻辑)
- 实验性的(正在孵化、未来可能进核心)
扩展标识符
每个扩展用一个唯一标识符命名,格式为:
{vendor-prefix}/{extension-name}- 官方扩展用
io.modelcontextprotocol前缀,例如io.modelcontextprotocol/tasks - 第三方扩展请用你拥有的域名倒写做前缀,避免冲突(类似 Java 包命名)。拥有
example.com的公司用com.example/,例如com.example/my-extension
💡 标识符规则与
_meta键一致,只是强制要求前缀。
🌱 扩展生命周期(Extension Lifecycle)
每个扩展都处在三个阶段之一,之后还可能被提升进核心规范。阶段决定了允许哪些改动、由谁批准、实现者能指望什么。完整定义见 SEP-3392: Extension Lifecycle(Process 类型,已 Final)。
| 阶段 | 协议改动 | 改动由谁批准 | 进入方式 | 实现者能指望什么 |
|---|---|---|---|---|
| Experimental | 任何改动,随时可改,无需通知 | 扩展维护者 | 由扩展维护者直接加入 | 什么都指望不上,设计可能变也可能消失 |
| Beta | 可以改,但破坏性变更应当罕见 | 扩展维护者 | 一份 Extensions Track SEP | 欢迎真实使用;破坏性变更会被记录下来 |
| Stable | 只随新的核心协议修订而改变 | 扩展维护者 | 第二份 Extensions Track SEP | 在一个协议修订内行为固定 |
- 扩展从 Experimental 起步,必须经过 Beta;从 Beta 要么变成 Stable,要么由一份 Standards Track SEP 提升进核心规范
- 处于 Beta 的扩展会声明自己的目标去向(Stable 还是 Core),这个去向在 Beta 期间可以调整
- 改变阶段不会改变扩展的标识符
📦 官方扩展清单
官方扩展放在 MCP GitHub 组织下、以 ext- 为前缀的仓库里。一个仓库可以容纳多个不同阶段的扩展——扩展规范所在的目录就标明了它的阶段:specification/experimental/、specification/beta/、specification/stable/;被提升进核心规范或已终止的扩展,移到 specification/archived/。
🔐 Authorization 扩展(ext-auth)
核心授权之外的补充机制。
| 扩展 | 阶段 | 说明 |
|---|---|---|
| OAuth Client Credentials | Stable | 机器对机器(M2M)认证的 OAuth 2.0 客户端凭据流 |
| Enterprise-Managed Authorization | Stable | 需要集中访问控制的企业环境框架 |
核心授权流程见 12.1 OAuth 2.1 授权体系。
⏳ Tasks 扩展(ext-tasks)
| 扩展 | 阶段 | 说明 |
|---|---|---|
| MCP Tasks | Beta(目标:Core) | 长时操作的异步执行:轮询、途中请求输入、持久化句柄 |
📌 宿主支持正在铺开:官方客户端支持矩阵的 Tasks 一栏目前登记了 MCPJam、Claude(web)、Claude Code;Claude Desktop、VS Code GitHub Copilot、ChatGPT、Cursor、Postman、Goose、Microsoft 365 Copilot 等仍尚未登记支持。生产落地前请先看该矩阵确认你面向的客户端当前是否可用。
🖼️ MCP Apps 扩展(ext-apps)
| 扩展 | 阶段 | 说明 |
|---|---|---|
| MCP Apps | Stable | 允许服务器在对话里内嵌交互式 UI(图表、表单、视频播放器) |
📚 Skills over MCP 扩展(ext-skills)
| 扩展 | 阶段 | 说明 |
|---|---|---|
| Skills over MCP | Beta(去向尚未声明) | 通过 MCP 资源发现工作流指令并读取配套文件 |
🪪 Server Cards 扩展(ext-server-card)
| 扩展 | 阶段 | 说明 |
|---|---|---|
| Server Cards | Beta(去向尚未声明) | 让客户端在连接之前就发现一个服务器的元数据(HTTP 发现) |
📌 Tasks 为什么是扩展? 它在 2025-11-25 是实验特性,2026-07-28 被正式移出核心、变成
io.modelcontextprotocol/tasks扩展(SEP-2663)。这是"实验特性 → 官方扩展"的标准路径。
🧪 实验性扩展
官方给工作组/兴趣组提供了一条孵化通道:在提交 SEP 之前或同期,先用实验扩展原型验证想法。
- 实验扩展要么放在某个
ext-仓库的specification/experimental/目录下,要么是自己的experimental-ext-前缀仓库(如experimental-ext-interceptors);experimental-ext-仓库里的一切都是 Experimental - 每个实验扩展必须挂靠一个工作组或兴趣组
- 已发布的包必须明确标注实验状态(README、包名)
- 实现者应当锁定到某个 tag 或 commit——Experimental 扩展的两个修订不保证互相兼容
- 核心维护者保留监督权,包括归档或移除
转正路径:走 Extensions Track SEP(见下节的「提案 Beta」),孵化期的实验仓库与参考实现可以作为"该扩展切实可行"的证据。
🔄 创建一个官方扩展的七步
- 原型:在
ext-仓库的specification/experimental/下加入扩展,或建一个experimental-ext-仓库 - 提案 Beta:按标准 SEP 指南在规范仓库里提一份 Extensions Track SEP,并声明目标是 Stable 还是被提升进 Core
- 实现:至少在一个官方 SDK 里做出参考实现——SEP 进入评审前的硬门槛
- 评审:核心维护者评审,对是否纳入有最终决定权
- 发布:SEP 通过后,提 PR 把扩展移到
ext-仓库的specification/beta/,由核心维护者批准这次移动 - 收集证据:Beta 期间持续维护一份公开清单,记录已知实现与收到的反馈
- 稳定或提升:第二份 Extensions Track SEP 把扩展推进 Stable——它需要两个互相独立且可互操作的实现(一客户端侧、一服务器侧)和一份一致性场景;或者由一份 Standards Track SEP 把它提升进核心规范
要求:
- 扩展规范必须使用 RFC 2119 语言(MUST / SHOULD / MAY)
- 扩展必须有对应的 WG 或 IG,且至少有一名 Extension Maintainer登记在仓库的
MAINTAINERS.md里 - 实现是硬门槛:SEP 进入评审前,至少要有一个官方 SDK 的参考实现
SDK 支持:SDK 可以实现扩展,但不是协议一致性所必需的;SDK 维护者自主决定支持哪些扩展,支持的话应在文档里列出。
⚠️ 扩展默认关闭,必须由开发者显式开启——在三个阶段都如此。
🤝 能力协商(Negotiation)
双方通过各自能力声明里的 extensions 字段表明支持的扩展。
📌 下面两个示例用的是 2026-07-28 及之后的写法。2025-11-25 及更早版本请见本节末尾的基于握手的版本。
客户端:在每个请求的 _meta 里声明
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": { "location": "New York" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
},
"io.modelcontextprotocol/clientInfo": { "name": "ExampleClient", "version": "1.0.0" }
}
}
}注意:这是无状态模型的体现——扩展声明随每个请求携带,而不是靠一次握手协商后记住。
服务器:在 server/discover 响应里声明
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": {
"tools": {},
"extensions": { "io.modelcontextprotocol/ui": {} }
},
"_meta": {
"io.modelcontextprotocol/serverInfo": { "name": "ExampleServer", "version": "1.0.0" }
},
"ttlMs": 3600000,
"cacheScope": "public"
}
}每个扩展自己定义其设置对象的 schema;空对象表示无设置。
基于握手的版本(Initialization-Based Versions)
扩展是到 2026-07-28 才成为协议正式组成部分的。在那之前,很多 SDK、客户端和服务器已经在 2025-11-25 及更早版本上通过 initialize 握手协商扩展了:请求里用 params.capabilities.extensions,响应用 result.capabilities.extensions。扩展标识符与设置对象的规则和上面一致。
⚠️ 在这些协议版本里,
extensions字段不属于已发布的 schema,支持它也不是一致性要求。能透传capabilities.extensions的 SDK 被鼓励这样做,好让扩展在各版本间表现一致。请确认你的 SDK 能收发这个字段,且该扩展支持协商出来的协议版本。若对方能力里没有extensions,或没有某个扩展,就把它当作不支持,按下面的优雅降级处理。
客户端请求
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {
"roots": { "listChanged": true },
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
},
"clientInfo": { "name": "ExampleClient", "version": "1.0.0" }
}
}服务器响应
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-11-25",
"capabilities": {
"tools": {},
"extensions": {
"io.modelcontextprotocol/ui": {}
}
},
"serverInfo": { "name": "ExampleServer", "version": "1.0.0" }
}
}优雅降级(Graceful Degradation)
若一方支持某扩展、另一方不支持,支持方必须二选一:
- 回退到核心协议行为,或
- 若该扩展是强制的,用合适的错误拒绝请求
💡 好的扩展文档会写明预期回退行为。例如:提供 UI 增强的服务器,对不支持 UI 扩展的客户端仍应返回有意义的文本内容;而依赖某认证扩展的服务器,则可以拒绝不支持它的客户端。
演进规则
扩展的改动由它的 Extension Maintainers 批准,不需要核心维护者复审。但能改什么取决于阶段:
- Experimental:什么都能改,随时可改
- Beta:优先做加法(新增可选字段、在设置对象里加能力标志)。破坏性变更允许,但应当罕见、集中成批处理,先在 WG/IG 里公告,并记进 changelog。做了破坏性变更的扩展,应在设置对象里新增或递增一个
revision字符串,好让对端检测出不匹配 - Stable:按核心协议修订发布,形如
specification/stable/<name>/2026-07-28/;已发布的修订不可变。改动先累积在draft/目录里,只在发布新的核心协议修订时生效。一对对端看到的扩展行为,就是它们协商出的那个协议修订所定义的行为
破坏性变更保留原标识符。 只有在同一个协议修订内必须让新旧行为共存时,才用新标识符(如 io.modelcontextprotocol/my-extension-v2)。
- 破坏性变更包括:删除/重命名字段、改字段类型、改既有语义、新增必填字段
提升进核心(Promotion to Core)
当一份 Standards Track SEP 把某个扩展提升进核心规范、落在协议修订 R 上:对于声明了 R 或更高版本的请求,该特性就是核心行为,无论是否声明了该扩展;对于更早的修订,扩展的最后一份修订仍然适用——所以支持旧修订的客户端应当继续声明这个扩展。修订 R 发布后,扩展的规范移到 specification/archived/。
⏳ 深入一:Tasks(长任务)
不是每次工具调用都立刻返回。CI 流水线、批处理、人工审批可能耗时数秒到数小时。Tasks 让服务器返回一个持久化句柄而不是阻塞。
为什么不能"直接阻塞"
| 阻塞的问题 | Tasks 如何解决 |
|---|---|
| 长期占用连接,中间层超时撑不住 | 不长期占用连接 |
| 客户端崩溃/重启就丢了 | task ID 是持久句柄,可用同一 ID 恢复轮询 |
| 看不到进度 | 携带状态元数据与可选进度消息 |
| 需要用户输入时没法交互 | 转 input_required 并浮出请求,客户端用 tasks/update 回 |
| 每工具都要预热/传参开关 | 服务器按请求决定是否建任务,客户端一次性 opt-in |
生命周期
| 状态 | 含义 |
|---|---|
working | 进行中 |
input_required | 需要客户端输入(见 inputRequests) |
completed | 完成,result 含最终输出 |
failed | 执行中发生 JSON-RPC 错误,error 含细节 |
cancelled | 已取消(不保证被遵守) |
completed / failed / cancelled 是终态,一旦到达状态不再改变。
主流程
要点:
- 服务器返回
CreateTaskResult(resultType: "task"),含taskId、初始状态、TTL、建议轮询间隔;任务必须在响应发出前持久化创建 - 客户端用
tasks/get轮询;终态时result(completed)或error(failed)落位 - 没有
tasks/list;用tasks/get轮询客户端已知的句柄 tasks/cancel是协作式取消——服务器确认意图,但不保证停下- 服务器可以通过
notifications/tasks推送(客户端经subscriptions/listenopt-in),省掉轮询往返
何时该用 Tasks:长时操作(CI、批处理、模型训练)、人在环流程(审批门)、已用 job ID 的外部系统、不可靠连接、需要进度可见的批量处理。
🖼️ 深入二:MCP Apps(对话内 UI)
文本响应有极限。有时用户需要与数据交互,而不只是读它。MCP Apps 让服务器返回交互式 HTML 界面(可视化、表单、看板),直接渲染在聊天里。
相比独立 Web 应用的优势:
| 优势 | 说明 |
|---|---|
| 上下文保留 | 应用就在对话里,用户不必切标签页、丢位置 |
| 双向数据流 | 应用可调服务器上任意工具,宿主能把新结果推给应用 |
| 宿主能力整合 | 应用可把动作委托给宿主,由宿主走用户已连接的既有能力(经用户同意) |
| 安全保证 | 运行在宿主控制的沙箱 iframe 里,无法访问父页、偷 cookie、逃逸容器 |
核心模式:一个在描述里声明了 UI 资源的工具 + 一个把数据渲染成交互式 HTML 的 ui:// 资源。
安全模型:所有通信走 postMessage;宿主决定应用能访问哪些能力(如限制可调工具、禁用 sendOpenLink)。资源可带 _meta.ui.csp(允许的外部源)与 _meta.ui.permissions(麦克风/摄像头等)。
何时该用:探索复杂数据(可钻取的地图)、多选项配置(一次性表单)、富媒体查看(PDF/3D/视频)、实时监控、多步工作流。
框架无关:是标准 Web 原语,React / Vue / Svelte / Preact / Solid / 原生 JS 都行;@modelcontextprotocol/ext-apps 的 App 类只是便利封装,不是必需。
环境要求:官方构建指南要求 Node.js 20 或更高(早期文档写的是 18,已上调)。
📚 深入三:Skills over MCP(工作流指令分发)
📌 状态:Skills 已是已发布(published)的官方扩展,扩展标识
io.modelcontextprotocol/skills,对应 SEP-2640 已 Final。不过 SDK 与宿主对它的支持仍在陆续落地——生产落地前请看官方 客户端支持矩阵 与 实现清单 确认当前覆盖度。
Skills 扩展让服务器把工作流指令与配套文件暴露给客户端,客户端用既有的 Resources 原语发现元数据、读取内容。
一个 skill 就是一个目录:含一个 SKILL.md 与可选配套文件,遵循 Agent Skills 规范。这个扩展定义的是通过 MCP 发现与读取的方式。
能力声明:服务器必须同时声明 resources 能力和 io.modelcontextprotocol/skills 扩展,并必须实现 skills/list 与 skills/get;技能文件经 resources/read 提供。
{
"capabilities": {
"resources": {},
"extensions": {
"io.modelcontextprotocol/skills": { "directoryRead": true }
}
}
}三种操作:
| 操作 | 作用 |
|---|---|
skills/list | 发现可用技能,返回 frontmatter 与完整文件清单(含每个文件的 URI、SHA-256 摘要、字节大小) |
skills/get | 按 URI 取单个技能条目(也用于刷新已缓存条目) |
resources/read | 读取 SKILL.md 或配套文件内容;可选 resources/directory/read 列目录(需 directoryRead: true) |
缓存提示:skills/list / skills/get 与 resources/directory/read 的结果都带 resultType: "complete", 并附两个缓存字段:ttlMs 是服务端给出的新鲜期(毫秒),cacheScope 为 "public" / "private", 决定响应能不能被共享的中间缓存存下来(语义类比 HTTP 的 Cache-Control: public / private)。
完整性校验(关键):宿主在技能"在用"期间保留其条目,并必须
- 把文件读取限制在清单内的 URI
- 每次使用前校验原始字节大小与 SHA-256 摘要
- 解析
SKILL.mdfrontmatter 并逐字段与条目里的frontmatter比对
校验失败的内容不得使用;持久化的授权绑定到完整的文件 URI + 摘要集合——文件有增删改,授权即失效,需重新获批。
⚠️ 读取
SKILL.md本身不会激活技能。宿主需经其"技能加载路径"处理,校验内容并在需要时获得用户批准后才载入模型上下文。技能内容按不可信输入对待。上限建议:单个技能不超过 512 个文件 / 16 MiB(含
SKILL.md)。
🧭 我该用哪个扩展
| 你的需求 | 选它 |
|---|---|
| 工具跑很久,不能阻塞连接 | Tasks |
| 想在对话里展示图表 / 表单 / 播放器 | MCP Apps |
| 有一整套工作流指令 + 参考文件要随服务分发 | Skills over MCP |
| 机器对机器、或企业集中管控授权 | Auth 扩展(ext-auth) |
| 想在连接之前就知道服务器是什么 | Server Cards(ext-server-card) |
📖 官方资料
- 📋 Extensions Overview
- 🌱 SEP-3392: Extension Lifecycle
- ⏳ MCP Tasks | ext-tasks
- 🖼️ MCP Apps | ext-apps
- 📚 Skills over MCP | ext-skills
- 🪪 Server Cards(SEP-2127) | ext-server-card
- 🔐 Authorization 扩展
- 🧩 扩展客户端支持矩阵