第 2 章 协议核心概念

本章导读:一次售后查询,Agent 底层发生了什么?「模型选了工具」之前的目录从哪来?「无状态」的 Server 怎么知道该按哪个协议版本处理请求?本章对照 2026-07-28 官方规范原文(basic/index、versioning、server/discover),把协议核心拆开讲透:七个组件的分层地图、JSON-RPC 三种消息形态与 resultType 多态、三种消息模式、无状态的精确语义(每请求 _meta 元数据)、能力发现(server/discover)。消息示例均取自规范或经 TypeScript SDK v2.4.0 实测。读完本章,你能:画出一次调用的分层地图;读懂任意一条 MCP 消息的每个字段;说出三类能力的建模边界;用协议语言解释「为什么这版没有握手」。

flowchart LR A["疑问
一次调用的底层是什么?"] --> B["协议核心概念
分层 · 消息 · 无状态 · 发现"] B --> C["分层地图
必读三层 + 按需选装"] B --> D["消息
JSON-RPC 三形态 + resultType"] B --> E["三种模式
请求响应 / MRTR / 订阅通知"] B --> F["无状态
_meta 每请求声明"] B --> G["发现
server/discover 能力名片"] C --> H["收益
读懂任意 MCP 消息
为后续章节建好概念地图"] D --> H E --> H F --> H G --> H

本章导览图

你什么时候需要它

要读懂或手写一条 MCP 消息——2.3(消息)与 2.5(_meta)是字典:每个字段的含义、哪些必填、缺了会怎样(-32602/400)。

在「工具、资源、提示」之间拿不定建模方式——2.3 的决策表 + 三条经验法则,一分钟出结论。

要向团队解释「为什么 2026-07-28 没有 initialize 了」——2.5 的无状态四规则与实测印证,就是答案。

前置:无。本章是全书概念地基,后续每章都引用这里的编号。

先看一次真实的调用。用户对 agentscope-java 客服 Agent 说:「我的售后单 T-2026-1031 到哪一步了?」Agent 决定调用售后工具,底层发生了什么?

1. Host(agentscope Agent)向 Client 说:帮我调 after_sales_query_ticket
2. Client 构造一个 JSON-RPC 请求,带上"我是谁、我说哪个版本的协议、我有什么能力"
3. 请求到达 Server(可能穿过 Gateway、可能被任意一个副本接住)
4. Server 读参数、调中台接口、组装结果——不记得也不需要记得"上次和这个 client 聊过什么"
5. 结果原路返回,Agent 拿到结构化数据,组织成自然语言答复

第 3 步的"可能被任意一个副本接住"和第 4 步的"不需要记得",就是 2026-07-28 规范的全部灵魂。本章把这个过程拆开讲透:协议分几层、消息长什么样、无状态到底指什么、能力怎么发现。

2.1 协议的分层地图

规范把 MCP 拆成七个组件,要求不同:

组件 内容 实现要求
基础协议 JSON-RPC 消息类型 必须
版本与兼容 版本协商、扩展协商、旧版互操作 必须
消息模式 请求/响应、MRTR、订阅/通知 必须
授权 HTTP 传输的认证授权框架 HTTP 传输应遵循
Server 能力 resources、prompts、tools 按需
Client 能力 elicitation、sampling、roots 按需
实用件 logging、参数补全等横切关注点 按需

模块化的用意:实现方只做必须的三层,其余按需选装。一个只暴露工具的最小 Server,不必实现 sampling;一个纯资源型 Server 可以没有任何 tool。这个分层也是全书各章的地图——第 3 章讲第二层,第 5 章讲 tools,第 8 章讲授权。

2.2 角色:Host、Client、Server

三个角色各管一件事,边界清楚:

一句话记牢:Server 管"能力是什么",Host 管"模型能看到什么"。第 6 章的规模化治理,治理的就是 Host 侧的可见性。

2.3 消息:一切皆 JSON-RPC

所有消息必须符合 JSON-RPC 2.0,共三种形态。

请求:client 发起操作,必须带 string 或 number 的 id(与 JSON-RPC 的一处差异:id 不得为 null),且不得与"已发出未应答"的其他请求重号:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "after_sales_query_ticket", "arguments": { "ticket_id": "T-2026-1031" } }
}

响应:成功时 result 必须携带 resultType 字段——这是 2026-07-28 的新设计,初见容易忽略:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "complete",
    "content": [{ "type": "text", "text": "售后单 T-2026-1031 当前处于维修阶段" }]
  }
}

resultType 让结果多态:取值 "complete" 表示完成(携带最终内容);"input_required" 表示请求未完成、需要 client 补充信息(携带 InputRequiredResult 对象,详见 2.6 MRTR);扩展可以定义更多取值。client 遇到不认识的 resultType 必须视为非法;而缺失 resultType 视为 "complete"——这条兼容规则让旧版 Server 的响应在新协议下依然可解析。

通知:单向消息,无 id,接收方不得回应。Server 用它推送事件(如工具目录变化),不需要"答复"这个动作。

错误码:一个被规范严格管理的区间

JSON-RPC 保留区(-32700、-32600~-32603)照旧用于协议层故障。MCP 对实现自定义区(-32000~-32099)做了分区:

应用自定义错误应落在保留区之外(-32768~-32000 之外)。把错误码分区当成 API 对待:你的 Server 不该发明一个 -32050 还指望所有 client 理解它。

2.4 三种消息模式

规范定义三种交互模式,复杂度递增:

① 请求/响应:上面演示的一问一答,覆盖绝大多数场景。

② 多轮请求(MRTR):Server 处理到一半需要 client 补充信息(向用户要个表单、借 Host 的模型推理一下),返回 resultType: "input_required" 的结果,内嵌它需要的请求;client 完成后发起新的自含请求继续。无状态世界的交互性方案,2.6 节展开。

③ 订阅/通知:client 订阅一个事件流(如 subscriptions/listen),Server 持续推送通知。注意它的无状态语义:长连接式的 listen 仍是"一个请求"——响应是一个打开的通知流,状态属于请求而非底层连接。

2.5 无状态:规范原文怎么说

basic/index 里这段话值得整段读(本书译述,语义以原文为准):

MCP 是无状态协议:处理请求所需的全部信息都包含在请求自身之中。Server 独立处理每个请求;不得从先前的请求推断任何状态——哪怕它们来自同一条连接。

具体拆成四条规则:

  1. Server 不得依赖同一连接上的先前请求建立上下文(能力、协议版本、client 身份)——这些元数据每个请求自带
  2. Server 应准备好同时处理来自多个任务/线程/会话的请求
  3. Server 不应要求 client 用同一条连接执行相关操作
  4. 需要跨请求延续的状态(长任务、应用级句柄)必须用显式标识符引用,client 每次请求都带上

配套的核心机制是 _meta 每请求协议元数据——client 的每个请求都要在 params._meta 里声明:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}
键 必填 说明
io.modelcontextprotocol/protocolVersion 是 本请求使用的协议版本
io.modelcontextprotocol/clientCapabilities 是 本次请求相关的 client 能力声明
io.modelcontextprotocol/clientInfo 否(建议带) client 名字与版本
io.modelcontextprotocol/logLevel 否 期望的日志级别下限

缺必填字段就是畸形请求:Server 必须以 -32602(Invalid params)拒绝,HTTP 上返回 400。如果请求需要 client 没声明的能力,Server 返回 -32021 并在 data.requiredCapabilities 里列出缺什么。

两个推论值得咀嚼:

实测印证

第 4 章我们对 TS SDK v2.4.0 的实测(无握手直发 tools/list 立即得到结果)印证了"无握手";SDK 的便捷方法内部正是自动拼装了上述 _meta 必填字段——手写裸 JSON-RPC 时别漏。

2.6 能力发现:server/discover

无状态带来一个直白的问题:握手没了,client 怎么知道对面是谁、支持什么版本、有什么能力?规范给了一个 Server 必须实现的方法:

{ "jsonrpc": "2.0", "id": "d1", "method": "server/discover", "params": { "_meta": { /* 同 2.5 */ } } }
{
  "jsonrpc": "2.0", "id": "d1",
  "result": {
    "resultType": "complete",
    "supportedVersions": ["2026-07-28"],
    "capabilities": { "tools": {}, "resources": {} },
    "_meta": { "io.modelcontextprotocol/serverInfo": { "name": "after-sales", "version": "1.0.0" } },
    "instructions": "本 Server 提供售后单查询/创建/试算能力。查询物流请用物流域。",
    "ttlMs": 3600000,
    "cacheScope": "public"
  }
}

一次请求拿到:支持的协议版本(supportedVersions)、能力清单(capabilities)、身份(serverInfo)、给模型的使用说明(instructions),并声明可缓存时长(ttlMs)。

调用它是 client 的自由,不是义务——client 也可以直接发业务请求,版本不支持就吃 -32022 错误(错误体里同样会列出 server 支持的版本,第 3 章)。两个场景值得主动调用:一是接入前一次性呈现 Server 概况(替代 tools/prompts/resources 三个 list 探测);二是 stdio 上的旧版兼容探测(第 3 章 Dual-era 机制)。

注意 instructions:这是写给模型的自然语言——"这个 Server 是干什么的、该和谁配合"。它是描述质量工程在 Server 级的延伸,第 5 章的工具描述是它的兄弟。

2.7 MRTR:无状态世界的交互性

场景:模型调 after_sales_create_ticket,但缺"退款方式"这个用户必须选的字段。有状态协议会开个会话等用户;无状态的答案分三步:

① client: tools/call(create_ticket, {已知参数})
② server: result { resultType: "input_required", ... 内嵌 elicit 请求(选退款方式) }
③ client 完成收集后,发起全新请求携带应答继续

关键在 ③:续接不靠 Server 记忆,靠请求自带上下文。每一跳都是独立、完整、可被任意副本处理的请求。对比 v1 的会话内 server→client 请求:交互性保留,状态外置。SDK 层面(TS v2.4.0),工具处理器返回 inputRequired 对象即可,内嵌请求的构造器覆盖 elicit(表单)、createMessage(借模型推理)、listRoots 三类 client 能力。

2.8 同一调用的三层视角

官方架构教程(learn/architecture)用一个贯穿示例把「一次工具调用」在三个抽象层级上各走一遍——这个视角切换是理解 MCP 的捷径:

① 数据层(协议视角):一切是 JSON-RPC 消息。discover、tools/list、tools/call 每一步都带 _meta 必填字段(协议版本、clientCapabilities、建议带 clientInfo);响应带 resultType 与缓存字段(ttlMs/cacheScope)。教程逐步展开的四步——发现 → 工具发现 → 工具执行 → 通知——每步的请求/响应 JSON 都值得手抄一遍,第 4 章的实测命令就是这一层的可运行版。

② SDK 层(开发者视角):_meta 拼装、schema 校验、通知分发被 SDK 吸收。教程的伪代码展示了 AI 应用的典型集成形态:

async with Client(stdio_client(server_config)) as client:
    if client.server_capabilities.tools:          # discover 的结果挂在 client 对象上
        app.register_mcp_server(client, supports_tools=True)
    app.set_server_ready(client)

③ AI 应用层(产品视角):应用聚合所有已连 Server 的工具为「统一工具注册表」交给模型;模型在对话中产生工具调用,应用执行后回填——第 8.3 节的 Agent 循环正是这一层的最小实现。

三个视角的分工:排查协议问题回到 ①,写业务代码停在 ②,理解产品行为看 ③。本书章节也按此组织——第 3/5/8 章深入 ①,第 4/6 章居 ②,第 7/12 章在 ③。

另外,规范特性自身有生命周期三态(Active / Deprecated / Removed,弃用窗最少 12 个月,learn/versioning 与第 3 章)——读规范时先看特性处于哪一态,Deprecated 的能力不要进新代码。

2.9 小结

概念 一句话
分层 必须三层(基础协议/版本兼容/消息模式),其余选装
消息 JSON-RPC 三形态;resultType 让结果多态;错误码分区管理
模式 请求/响应、MRTR、订阅/通知
无状态 状态从连接搬进每请求的 _meta;stdio 连接 ≠ 会话
发现 server/discover 一次拿版本+能力+身份+给模型的说明
交互 input_required + 自含续接

→ 有了这层概念地图,下一站可以选:第 4 章动手(推荐,30 分钟跑通双端),或第 3 章先弄清"我们怎么走到无状态"。

验收清单

读到这里,你应该能:

参考速查

项 值
必填 _meta io.modelcontextprotocol/protocolVersion + /clientCapabilities;建议带 /clientInfo
resultType complete(完成)/ input_required(待补输入);缺省视为 complete
规范保留错误码 -32020 HeaderMismatch / -32021 MissingRequiredClientCapability / -32022 UnsupportedProtocolVersion
三种模式 请求响应 / MRTR / 订阅通知(subscriptions/listen)
无状态四规则 不依赖连接上下文;多任务并发;不要求同连接;跨请求状态用显式标识符
发现 server/discover(Server 必须实现):supportedVersions + capabilities + serverInfo + instructions
JSON Schema 默认 2020-12;$ref 不自动解引用网络 URI

延伸阅读

复核清单(发布前)