MCP 学习路线:按角色、按规范版本选路径
MCP 学习路线:按角色、按规范版本选路径
走完 MCP 最短的路线不是一份固定的章节清单。它取决于两件一分钟就能定下来的事:你扮演什么角色,以及你的对端说哪一代协议。下面把这两个决定做成表格,并在每个阶段的末尾放一个可以运行的检查,它要么通过,要么不通过。
建议按这个顺序读:先选角色(第一节),再选你要面向的协议代际(第二节),如果手上已有旧修订版的实现就做迁移(第三节),然后按阶段推进(第四节),出了问题回到第五节。
一、选你的角色
| 角色 | 你想要 | 按这个顺序读 | 先运行 | 做到这样才算完成 |
|---|---|---|---|---|
| 评估 MCP | 判断 MCP 是否适合,以及它覆盖什么 | 简介、架构、服务器概念 | 暂时不用 | 你能说出每个用例需要工具、资源、提示中的哪一种(提示详解第一节) |
| 服务器开发者 | 做一个别人的客户端能调用的服务器 | 构建服务器、工具、资源、提示、最佳实践 | “构建服务器”里的 SDK 示例,再对它运行 lint_tools.py | lint_tools.py(以及你提供的资源与提示对应的检查脚本)退出码为 0 |
| 客户端或宿主开发者 | 让你的应用连接不是你写的服务器 | 构建客户端、客户端最佳实践、版本与兼容 | 在 _meta 里带协议版本发请求,或先发 server/discover | 收到 UnsupportedProtocolVersionError 时,你的客户端会用 supported 里的版本重试,遇到旧代服务器会报告而不是卡住 |
| 集成者 | 把现成的服务器接进 IDE 或聊天应用这类宿主 | 连接本地服务器、连接远程服务器、Inspector 指南 | 在宿主看到服务器之前,先对它运行 inspector_smoke.py | Inspector 列出你预期的工具,冒烟脚本退出码为 0 |
| 安全或平台负责人 | 把服务器放到认证、网关或防火墙后面 | 授权、安全最佳实践、最佳实践 | 对预发布端点运行 probe_http.py | probe_http.py 退出码为 0,并且你清楚每条 WARN 为什么可以接受 |
| 发布者 | 登记一个服务器,让别人能找到并安装 | 发布、使用 | 对你的 server.json 运行 preflight.py | 运行 mcp-publisher publish 之前,preflight.py 退出码为 0 |
二、选协议代际
MCP 一共有五个修订版。下表列出每个修订版新增的、会出现在你代码里的东西;最后一行是你今天该起步的地方。
| 修订版 | 新增的、你会注意到的东西 | 官方变更日志 |
|---|---|---|
| 2024-11-05 | 站内记录的第一个修订版;下一份变更日志就是相对它写的 | 2025-03-26 变更日志 |
| 2025-03-26 | 基于 OAuth 2.1 的授权框架、Streamable HTTP 取代 HTTP+SSE、JSON-RPC 批处理、工具注解、音频内容、completions 能力 | 主要变更 |
| 2025-06-18 | 批处理又被移除、结构化工具输出、服务器作为 OAuth 资源服务器并提供受保护资源元数据、资源指示符(RFC 8707)、征询(elicitation)、工具结果里的资源链接、MCP-Protocol-Version 头、title 字段 | 主要变更 |
| 2025-11-25 | OpenID Connect 发现、图标、增量范围授权、URL 模式征询、采样里的工具调用、客户端 ID 元数据文档、实验性任务、JSON Schema 2020-12 成为默认方言 | 主要变更 |
| 2026-07-28 | 没有会话,也没有 initialize 握手:每个请求都带版本和能力。server/discover、subscriptions/listen、多轮往返请求、必填的 resultType、缓存提示、Mcp-Method 与 Mcp-Name 头 | 主要变更 |
2026-07-28 修订版把直到 2025-11-25 的版本称为旧代(legacy,先用 initialize 握手建立会话),把 2026-07-28 及之后的版本称为新代(modern,版本、身份和能力随每个请求发送)。两代都支持的实现叫双代(dual-era)。两者相遇时会怎样,由规范的兼容矩阵定义:
| 客户端 | 服务器 | 结果 | 怎么做 |
|---|---|---|---|
| 新代 | 新代 | 可用 | server/discover 是可选的。收到 UnsupportedProtocolVersionError 时,用 supported 里的版本重试 |
| 新代 | 旧代 | 失败 | 在 stdio 上先发 server/discover,让失败是确定的,并给用户一条可操作的错误 |
| 双代 | 新代 | 可用 | 客户端保持新代 |
| 双代 | 旧代 | 可用 | 探测以非新代错误失败(或超时),客户端回退到 initialize |
| 旧代 | 新代 | 失败 | 只支持新代的服务器应当在对 initialize 的错误里写明它支持的版本:这条消息可能是旧代客户端唯一能展示的信息 |
| 旧代 | 双代 | 可用 | 服务器回答 initialize,并按协商出的旧代修订版为客户端服务 |
旧代对旧代按旧代修订版运作,不在 2026-07-28 文本的范围内。该做哪一代,由这张表推出:
- 客户端由你掌控的新服务器:只做新代。实现更小,客户端也由你来升级。
- 有你无法掌控的客户端的服务器,例如仍在发送
initialize的宿主:做双代。服务器可以同时实现两种行为,并在同一个端点上同时服务两代。 - 面向你无法掌控的服务器的客户端:做双代,因为新代客户端对旧代服务器会失败。在 stdio 上用
server/discover探测,遇到任何不是已识别新代错误的错误就回退;在 Streamable HTTP 上先发新代请求,读400的响应体后再决定是否回退。按服务器进程或来源缓存这个判断。 - 面向你掌控的服务器的客户端:只做新代。
三、从 2025-11-25 迁到 2026-07-28
每一行是官方 2026-07-28 变更日志里的一项变更,加上你该怎么办,以及站内在哪里讲它。
| 变了什么 | 你该做什么 | 阅读 |
|---|---|---|
移除协议层会话与 Mcp-Session-Id 头;列表端点不再因连接而异 | 不再用会话来存状态或区分列表;状态放你自己的存储,或放进 requestState | 最佳实践第二节 |
移除 initialize 握手;每个请求在 _meta 里带协议版本与客户端能力 | 每个请求都带 _meta 字段;不再等待初始化结果 | 版本与兼容 |
新增 server/discover,服务器必须实现 | 实现它;只在想预先知道支持的版本时才先调用它 | Discover |
subscriptions/listen 取代 GET 端点与 resources/subscribe | 用你要的通知类型打开一条流;变更后重新列出 | 资源详解第四节 |
多轮往返请求取代 roots/list、sampling/createMessage、elicitation/create 这类服务器发起的请求 | 回答 input_required 结果,并带 inputResponses 与 requestState 重试 | 多轮往返请求 |
每个结果都带必填的 resultType | 普通结果发 "complete";客户端先读它再读别的 | 提示详解第五节 |
列表结果与 resources/read 必须带 ttlMs 和 cacheScope | 拿不准就发 ttlMs: 0 与 "private";只有对所有用户都相同的列表才用 "public" | 工具详解、提示详解第四节 |
Streamable HTTP 的 POST 请求必须带 Mcp-Method 与 Mcp-Name | 两个都发;非 ASCII 的 Mcp-Name 用 Base64 编码;让你的网关原样转发 | 提示详解第六节 |
资源不存在的错误码从 -32002 改为 -32602 | 返回 -32602;客户端对旧服务器仍应接受 -32002 | 资源详解第七节 |
移除 ping、logging/setLevel 与 notifications/roots/list_changed | 日志级别改为在 _meta 里逐请求设置;不再发送被移除的消息 | 变更日志 |
| 实验性任务移出核心协议,成为官方扩展 | 把任务当作通过 extensions 协商的扩展 | 扩展 |
移除 SSE 流的可恢复性与 Last-Event-ID 头 | 响应流断开就丢了这个请求:客户端重新发起 | 变更日志 |
四、分阶段的路径
每个阶段都以你能拿出来的东西收尾。下面提到的脚本,都是嵌在各自所属页面里的那一份。每个脚本在没有检查 FAIL 时退出码为 0,至少有一项 FAIL 时退出码为 1。
| 阶段 | 做什么 | 检查点 | 页面 |
|---|---|---|---|
| 1. 理解 | 读架构与服务器、客户端概念。弄清每种原语由谁控制:用户(提示)、应用(资源)、模型(工具) | 你能说出自己设计里每种原语的控制方 | 架构、提示详解第一节 |
| 2. 构建并运行 | 按你所用语言的指南,用官方 SDK 做一个服务器 | Inspector 能连接、协商版本并列出你的工具:inspector_smoke.py 退出码为 0 | 构建服务器、Inspector 指南 |
| 3. 原语 | 按各页面的说明定义工具、资源与提示,包括失败的情形 | lint_tools.py、lint_resources.py、lint_prompts.py 对你的端点各自退出码为 0 | 工具、资源、提示 |
| 4. 远程与安全 | 通过 Streamable HTTP 提供服务,加上授权,检查 Origin、令牌受众与请求头 | probe_http.py 对预发布环境退出码为 0 | 最佳实践、授权 |
| 5. 发布 | 用 server.json 描述服务器,证明所有权,发布 | preflight.py 退出码为 0 | 发布 |
| 6. 保持兼容 | 定下代际(第二节),测试兼容表中适用于你的行 | 每个适用的行都以表里写的结果收尾,而不是卡住 | 版本与兼容 |
五、不工作时看这里
| 现象 | 最可能的原因 | 去哪里 |
|---|---|---|
| 2026-07-28 的客户端拒绝你的列表结果 | 结果缺 resultType、ttlMs 或 cacheScope(2025-11-25 的形态) | 三个检查脚本的 list-envelope 检查;提示详解第九节 |
每个请求都是 HTTP 400 和 -32020 | Mcp-Method 或 Mcp-Name 缺失、与正文不一致,或非 ASCII 的值没有做 Base64 编码 | 提示详解第六节 |
-32022 Unsupported protocol version | 服务器没有实现你发送的版本 | 用 data.supported 里的版本重试;版本与兼容 |
| 旧代客户端连不上你的新服务器 | 只支持新代的服务器会拒绝 initialize | 第二节的“旧代对新代”一行;做双代,或升级客户端 |
resources/subscribe 回复 404 与 -32601 | 该方法在 2026-07-28 里已不存在 | 改用 subscriptions/listen;资源详解第四节 |
资源不存在时回复 -32002 | 服务器在 2025-11-25 或更早的版本 | 客户端两种码都接受;资源详解第七节 |
| 模型总是调错工具,或发来无效参数 | 定义没有让模型能够恢复 | 工具详解、最佳实践第四节 |
| 服务器在本地能用,放到网关后面就失败 | 网关丢掉或改写了 Mcp-Method、Mcp-Name 或 MCP-Protocol-Version | probe_http.py;最佳实践第五节 |
版本与验证说明
以上都以 2026-07-28 修订版及之前四个修订版为准;事实来自规范的官方变更日志与版本页,固定在同一个提交。
来源
- 官方变更日志:2025-03-26、2025-06-18、2025-11-25、2026-07-28
- 官方版本与兼容页,含兼容矩阵
- 站内的 MCP 工具详解、MCP 资源详解、MCP 提示详解、MCP 服务器最佳实践、MCP Inspector与发布到注册表