跳到主要内容

02 · 版本协商与服务器发现

旧版 MCP 靠一次 initialize 握手敲定“我们说哪个版本、各自支持什么”。draft 取消了握手——这一章讲它用什么替代:每请求声明版本 + 可选的 server/discover + 一套新旧时代互探规则。

2.1 没有握手了,怎么协商版本

核心一句话:没有协商握手;每个请求自带版本,服务器逐条接受或拒绝docs/specification/draft/basic/versioning.mdx:12)。

Client ──请求(_meta 里带 protocolVersion)──► Server
│ │
│ ┌── 服务器支持该版本 ──► 正常返回 result │
│ └── 不支持 ──► UnsupportedProtocolVersionError
│ (附带 supported 列表)
◄── 客户端从 supported 里挑一个双方都支持的版本,重发

服务器不支持请求里的版本时,必须UnsupportedProtocolVersionError-32022),并在 data.supported 里列出自己支持的版本(docs/specification/draft/basic/versioning.mdx:50):

{
"jsonrpc": "2.0", "id": 1,
"error": {
"code": -32022,
"message": "Unsupported protocol version",
"data": { "supported": ["2026-07-28", "2025-11-25"], "requested": "1900-01-01" }
}
}

客户端supported 里挑一个双方都支持的版本重试,挑不出就报错给用户。

2.2 server/discover:一次拿齐版本、能力、身份

虽然没了握手,但客户端常常想在动手前先知道“这服务器支持啥”。这就是 server/discover——服务器必须实现它(docs/specification/draft/server/discover.mdx:8)。

请求体没有业务参数,只带标准 _meta;响应一口气给齐(docs/specification/draft/server/discover.mdx:38):

{
"jsonrpc": "2.0", "id": "discover-1",
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": { "tools": {}, "resources": {} },
"serverInfo": { "name": "ExampleServer", "version": "1.0.0" },
"instructions": "This server provides weather and resource utilities.",
"ttlMs": 3600000, "cacheScope": "public"
}
}

对客户端是可选的docs/specification/draft/server/discover.mdx:60)——你完全可以直接发 tools/call,万一版本不对就处理 UnsupportedProtocolVersionError。但两种场景下它很有用:

  1. 展示服务器信息 — 一次请求拿到身份、能力、版本,不必分别探 tools/list/prompts/list/resources/list
  2. stdio 向后兼容探测 — stdio 没有 HTTP 状态码可借力,先发 server/discover 能让“对面是不是老服务器”确定性地暴露出来(见 §2.5)。

注意 instructions 字段:给 LLM 看的自然语言用法指引,相当于服务器的“说明书摘要”。

2.3 能力协商:声明了才能用

MCP 是基于能力的协议:双方各自声明支持哪些特性,谁都不能用对方没声明的能力(docs/specification/draft/architecture/index.mdx:116)。

  • 服务器server/discover 的响应里声明 tools/resources/prompts 等。
  • 客户端在每个请求的 _meta.io.modelcontextprotocol/clientCapabilities 里声明 sampling/elicitation/roots 等。

能力对象里还能带子选项,比如工具的 listChanged(清单变动会发通知)、资源的 subscribe(支持订阅更新)。

2.4 扩展协商:核心之外的可选能力

核心协议刻意做小,更多能力走**扩展(extensions)**机制(docs/specification/draft/basic/versioning.mdx:81)。扩展在 capabilities 的 extensions 字段里声明——一个“扩展 id → 设置对象”的 map,id 必须带前缀(遵守 _meta 命名规则):

{
"capabilities": {
"tools": {},
"extensions": { "io.modelcontextprotocol/tasks": {} }
}
}

一方支持、另一方不支持时,支持方必须退回核心行为或明确报错(docs/specification/draft/basic/versioning.mdx:120)。

值得一提:draft 把原先实验性的 tasks(长任务)从核心协议挪进了官方扩展 io.modelcontextprotocol/tasks(changelog docs/specification/draft/changelog.mdx:22)——这正是“核心极简、能力靠扩展”原则的体现。

2.5 新旧时代互通(dual-era)

现实里会同时存在两代实现,规范给了精确的术语和互探规则(docs/specification/draft/basic/versioning.mdx:29):

术语含义
Modern(现代)版本/身份/能力放每请求 _meta2026-07-28 起)
Legacy(传统)initialize 握手建会话(2025-11-25 及更早)
Dual-era(双时代)同时支持两种的实现

客户端怎么判断对面是哪一代?靠传输层特征docs/specification/draft/basic/versioning.mdx:132):

  • stdio:先发 server/discover 探测。返回 DiscoverResult 或某个已知的现代错误 → 现代服务器;返回其它任意错误或超时 → 传统服务器,回退到 initializedocs/specification/draft/basic/transports/stdio.mdx:121)。
  • Streamable HTTP:先试一个现代请求,碰到 400 Bad Request先看响应体——是不是已知的现代 JSON-RPC 错误。是 → 现代;否 → 回退 initialize

关键纪律(docs/specification/draft/basic/transports/stdio.mdx:139):回退判断不能只盯一个错误码,因为老服务器对未知方法的报错是“实现自定义”的(常见 -32601/-32602,也可能干脆不回)。

时代判定是服务器的属性、不是单条请求的:客户端缓存结果(stdio 按进程、HTTP 按 origin),失效再重探(docs/specification/draft/basic/versioning.mdx:148)。完整的客户端×服务器组合结局见 docs/specification/draft/basic/versioning.mdx:159 的兼容矩阵。

代码地图

主题文件路径符号 / 锚点
版本协商规则docs/specification/draft/basic/versioning.mdxProtocol Version Negotiation / Compatibility Matrix
服务器发现docs/specification/draft/server/discover.mdxDiscoverResult
发现请求/结果类型schema/draft/schema.tsDiscoverRequestDiscoverResult
版本不支持错误schema/draft/schema.tsUnsupportedProtocolVersionErrorUNSUPPORTED_PROTOCOL_VERSION
客户端/服务器能力schema/draft/schema.tsClientCapabilitiesServerCapabilities
stdio 兼容探测docs/specification/draft/basic/transports/stdio.mdxBackward Compatibility