Skip to content

1.5 MCP协议规范 ​

🎯 学习目标:掌握 MCP 协议的分层结构、消息格式与无状态请求模型,能对照官方规范读懂并实现 MCP 通信
⏱️ 预计时间:40分钟
📊 难度等级:⭐⭐⭐

⚠️ 版本说明:本节内容对齐 MCP 官方规范 2026-07-28 版本。这一版是一次破坏性重构——协议从"有状态 + 初始化握手"改为无状态 + 每请求自带元数据。如果你看到"必须先 initialize 握手""WebSocket 传输""Mcp-Session-Id 会话"这类说法,都属于 2025-11-25 及更早的旧模型。旧版差异见 11.2 迁移到 2026-07-28。

📋 协议规范概览 ​

MCP 协议建立在 JSON-RPC 2.0 之上。官方规范把它拆成七个部分:

规范把「MUST 支持」和「MAY 支持」分得很清楚:

部分要求说明
基础协议、版本与兼容性、消息模式MUST所有实现都必须支持
授权SHOULD(仅 HTTP 传输)stdio 传输不应用它,凭据从环境变量取
服务器功能(工具/资源/提示词)MAY按需实现
客户端功能(信息征询)MAY按需实现
通用工具(日志/补全/分页/缓存)MAY按需实现

🔑 一句话理解这次重构 ​

每个请求都是自解释的:协议版本、客户端身份、客户端能力,全部塞在请求自己的 _meta 里。服务器不再"记住连接",也不再有"连接建立"这个概念。

🔧 1. 基础协议层 ​

📡 JSON-RPC 2.0 消息类型 ​

MCP 完全兼容 JSON-RPC 2.0,定义了三种消息。

📝 请求(Request) ​

typescript
{
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: { [key: string]: unknown };
}

关键约束(与基础 JSON-RPC 不同):

  • id 必须是字符串或整数,不允许为 null
  • id 不得与发送方已发出但尚未收到响应的其他请求重复
  • 方向是单向的:请求只能由客户端发往服务器

✅ 结果响应(Result Response) ​

typescript
{
  jsonrpc: "2.0";
  id: string | number;
  result: {
    resultType: string;      // 必需
    [key: string]: unknown;
  };
}

resultType 是 2026-07-28 新增的多态结果标记,取值:

resultType含义
"complete"请求已正常完成,result 里是最终内容
"input_required"请求尚未完成,需要客户端补充输入(见 MRTR)

两条兼容规则很重要:

  • 扩展可以增加新的 resultType 取值,但必须来自核心协议与已通过能力声明的扩展
  • 客户端遇到无法识别的 resultType,必须视为非法
  • 对实现旧版协议的服务器(不带 resultType),客户端必须按 "complete" 处理

❌ 错误响应(Error Response) ​

typescript
{
  jsonrpc: "2.0";
  id?: string | number;
  error: {
    code: number;
    message: string;
    data?: unknown;
  };
}

📢 通知(Notification) ​

typescript
{
  jsonrpc: "2.0";
  method: string;
  params?: { [key: string]: unknown };
}

通知不得包含 id,接收方不得回复。

🔢 错误码分区(2026-07-28 重新划分) ​

JSON-RPC 2.0 把 -32000 ~ -32099 留给实现自定义。MCP 把它切成了两段:

区间归属说明
-32000 ~ -32019历史遗留规范前时代各实现自行分配的码。不得再新增,新实现不应使用
-32020 ~ -32099MCP 规范保留规范独占,实现不得发出未定义的码

MCP 当前定义的错误码:

错误码名称触发场景
-32020HeaderMismatchStreamable HTTP 头部与请求体元数据不一致
-32021MissingRequiredClientCapability请求依赖了客户端未声明的能力,data.requiredCapabilities 列出缺失项
-32022UnsupportedProtocolVersion服务器不支持请求声明的协议版本,data.supported 列出它支持的版本

两个已停止使用但客户端仍应容忍的码:

  • -32002 —— 资源不存在(2025-11-25 及更早),已被 -32602(Invalid Params)取代。客户端仍应接受旧服务器返回的 -32002
  • -32042 —— 需要 URL 征询(仅 2025-11-25)

💡 实现提示:本地错误(比如 SDK 内部的超时)目前没有规范分配的码。如果你要把本地错误包装成 JSON-RPC 结构,务必让它不会被误认为是对端发来的错误。

🌐 2. 传输层规范 ​

🔗 只有两种标准传输 ​

这是被中文资料误传最多的一点。MCP 官方只定义两种标准传输:

❗ 没有 WebSocket 传输。官方规范里不存在 new WebSocket('ws://...') 这种用法,也没有 {"transport": {"type": "http", "host": ..., "port": ...}} 这种配置格式。这些都是早期社区文章的臆测。

一个传输(transport)在 MCP 里是一种绑定(binding):它只定义消息如何分帧、如何投递、如何携带元数据、如何取消与终止。协议语义与传输无关——消息模式属于核心协议,在任何传输上表现一致。

📟 stdio 传输 ​

通过客户端启动的子进程的标准流通信,消息按换行符分隔。

适用场景:本地工具、命令行程序、IDE 集成——这也是绝大多数 MCP 服务器(文件系统、Git、数据库)用的方式。

bash
# 输入(客户端写入子进程 stdin)
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}

# 输出(子进程写到 stdout)
{"jsonrpc":"2.0","id":1,"result":{"resultType":"complete","tools":[]}}

要点:

  • 消息必须是 UTF-8 编码,必须以换行符结尾,不得内嵌换行
  • 只有 stdout 用于协议消息;日志一律写 stderr
  • 取消请求:客户端发送 notifications/cancelled

🌊 Streamable HTTP 传输 ​

每条消息都是一个 HTTP POST,发往同一个 MCP 端点;回复以单个 JSON 对象、或请求作用域的 SSE 流返回。

http
POST /mcp HTTP/1.1
Host: example.com
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": { "location": "北京" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "my-client", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

要点:

  • 请求必须带 Mcp-Method(方法名)和 Mcp-Name(资源名/工具名/提示词名,适用时)头部,让中间层无需解析请求体即可路由与鉴权
  • 工具参数可以通过 x-mcp-header 提升为自定义 HTTP 头
  • MCP-Protocol-Version 头部镜像请求体里的 io.modelcontextprotocol/protocolVersion;请求体才是真相来源,两者不一致时返回 HeaderMismatch(-32020)
  • 取消请求:客户端关闭该请求的响应流
  • 没有会话:Mcp-Session-Id 头部已在 2026-07-28 中移除

🔌 自定义传输 ​

客户端与服务器可以实现额外传输。要求:

  • 必须保留 JSON-RPC 消息格式、消息模式、以及每请求元数据模型
  • 应该说明自己的连接建立、分帧、取消方式
  • 若跑在可靠的双向字节流上(Unix domain socket、TCP),应该直接复用 stdio 的分帧格式,而不是另造一套

🧭 该选哪个传输? ​

场景推荐原因
本地工具、IDE 插件、CLIstdio零网络配置,生命周期由客户端管理
远程/多租户服务、SaaSStreamable HTTP走标准 HTTP 生态(网关、鉴权、可观测)
内网高性能进程间通信自定义传输复用 stdio 分帧,需自行处理发现与鉴权

📨 3. 无状态请求模型 ​

🚀 没有初始化握手了 ​

旧模型(≤ 2025-11-25):

新模型(2026-07-28):

规范原文很直白:

MCP 是一个无状态协议:处理一个请求所需的全部信息都包含在这个请求自身之中。服务器独立处理每个请求,不应从先前的请求(哪怕是在同一条连接或流上)推断任何状态。

这意味着几条硬约束:

  • 服务器不得依赖同连接上的先前请求来建立上下文(能力、协议版本、客户端身份)——每个请求都在 _meta 里自带
  • 服务器不应要求客户端复用同一条连接或同一个进程
  • 客户端不应把单个任务/会话当作 stdio 进程的生命周期边界
  • 需要跨请求延续的状态(长时间任务、应用级句柄),必须用显式标识符在每次请求里传递

⚠️ 常见误解:stdio 进程不等于会话。客户端可以在同一条 stdio 上交错发起互不相关的请求,服务器不得把进程身份当作会话连续性的代理。

🏷️ _meta:每请求元数据 ​

_meta 是 MCP 用来挂载协议级元数据的字段。键名格式为「可选前缀 + 名称」,前缀用反域名写法。第二段标签为 modelcontextprotocol 或 mcp 的前缀一律保留给 MCP。

规范的保留键:

键位置必需说明
io.modelcontextprotocol/protocolVersion请求是本请求使用的协议版本,如 "2026-07-28"
io.modelcontextprotocol/clientCapabilities请求是与本次请求相关的客户端能力
io.modelcontextprotocol/clientInfo请求否客户端名称与版本(SHOULD 每次携带)
io.modelcontextprotocol/logLevel请求否服务器对该请求应输出的最低日志级别
io.modelcontextprotocol/serverInfo响应否服务器名称与版本(SHOULD 每个结果都带)
io.modelcontextprotocol/subscriptionId通知是(订阅流)把通知关联回它的订阅请求
progressToken请求否让该请求进入进度通知模式
traceparent / tracestate / baggage任意否OpenTelemetry 链路上下文(W3C 格式)

两条拒绝规则:

  • 请求缺少任一必需字段即为畸形 → 返回 -32602(Invalid params),HTTP 上状态码必须是 400 Bad Request
  • 服务器不得依赖客户端未声明的能力。若处理请求需要的能力不在 clientCapabilities 里 → 返回 MissingRequiredClientCapabilityError(-32021),data.requiredCapabilities 列出缺失项;HTTP 上状态码必须是 400

一个完整的请求长这样:

json
{
  "jsonrpc": "2.0",
  "id": "req-1",
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": { "location": "北京" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "my-client", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {},
      "traceparent": "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
    }
  }
}

🔒 clientInfo 与 serverInfo 都是自报的,协议不做校验。它们只用于展示、日志和调试——不要用它们做安全决策。

🔍 server/discover:一次性问清服务器 ​

服务器必须实现 server/discover。它让客户端在发任何业务请求之前,一次拿到服务器支持的协议版本、能力与身份。

请求:

json
{
  "jsonrpc": "2.0",
  "id": "discover-1",
  "method": "server/discover",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "ExampleClient", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

响应:

json
{
  "jsonrpc": "2.0",
  "id": "discover-1",
  "result": {
    "resultType": "complete",
    "supportedVersions": ["2026-07-28"],
    "capabilities": {
      "tools": {},
      "resources": {}
    },
    "_meta": {
      "io.modelcontextprotocol/serverInfo": { "name": "ExampleServer", "version": "1.0.0" }
    },
    "instructions": "本服务器提供天气与资源工具。",
    "ttlMs": 3600000,
    "cacheScope": "public"
  }
}

对客户端来说调用它是可选的——你完全可以直接发业务请求,遇到 UnsupportedProtocolVersionError 再处理。但两种场景下它很有用:

  1. 展示服务器信息:一次请求拿到服务器身份、能力和支持版本,而不用分别探测 tools/list、prompts/list、resources/list
  2. stdio 上的向后兼容探测:stdio 没有 HTTP 状态码可用,需要同时兼容新旧两代服务器的客户端应该先发 server/discover

🔄 版本协商与新旧互操作 ​

术语先统一(官方原文):

  • Modern(现代):把版本、身份、能力作为每请求元数据传递的版本,即 2026-07-28 及之后
  • Legacy(旧版):用 initialize 握手建立会话的版本,即 2025-11-25 及之前
  • Dual-era(双代):同时支持两者的实现

版本不匹配时,服务器必须返回:

json
{
  "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 里挑一个双方都支持的版本重试;如果不存在,就把错误抛给用户。

客户端/服务器两代组合的结果(官方兼容矩阵的浓缩版):

客户端服务器结果
现代现代✅ 正常
现代旧版❌ 失败。stdio 上应该先发 server/discover 以便确定性失败
双代现代✅ 正常,保持现代路径
双代旧版✅ 回退到 initialize
旧版现代❌ 失败。旧客户端没有向前回退机制
旧版双代✅ 正常,按协商的旧版语义服务

双代服务器怎么决定走哪条路:

  • 请求带现代 _meta → 按本版规范无状态处理
  • 收到 initialize → 进入旧版语义

🧩 扩展协商 ​

能力对象里的 extensions 字段用来协商可选扩展。它是一张「扩展标识符 → 该扩展的设置对象」的映射表,标识符遵循 _meta 的键名规则(必须带前缀)。

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

空对象表示「支持该扩展,但没有额外设置」。一方支持而另一方不支持时,支持方必须要么退回核心协议行为,要么返回恰当的错误。

🔀 4. 消息模式 ​

核心协议定义了三种交互模式,所有传输上表现一致:

模式方向用途
请求与响应客户端 → 服务器常规调用
多轮请求 MRTR客户端 ↔ 服务器(通过重试)服务器需要客户端补充输入
订阅与通知服务器 → 客户端变更推送

🔁 多轮请求(MRTR) ​

这是 2026-07-28 的另一个关键变更:服务器不再主动发起 JSON-RPC 请求。

规范原文:服务器不得发起 JSON-RPC 请求,客户端不得发送 JSON-RPC 响应。

旧模型里服务器要"反过来问客户端"(roots/list、sampling/createMessage、elicitation/create),这在无状态的 HTTP 世界里很别扭。新模型改成:服务器返回一个中间结果,客户端带着补充信息重试原请求。

服务器返回的中间结果:

json
{
  "jsonrpc": "2.0",
  "id": 55,
  "result": {
    "resultType": "input_required",
    "inputRequests": {
      "ask-1": {
        "method": "elicitation/create",
        "params": {
          "message": "请选择要部署的环境",
          "requestedSchema": {
            "type": "object",
            "properties": {
              "environment": { "type": "string", "enum": ["staging", "production"] }
            },
            "required": ["environment"]
          }
        }
      }
    }
  }
}

客户端重试时带上 inputResponses:

json
{
  "jsonrpc": "2.0",
  "id": 56,
  "method": "tools/call",
  "params": {
    "name": "deploy_service",
    "arguments": { "service": "api" },
    "inputResponses": {
      "ask-1": {
        "action": "accept",
        "content": { "environment": "staging" }
      }
    },
    "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {} }
  }
}

要点:

  • 重试是一个全新的 JSON-RPC 请求(新的 id)
  • 服务器要跨重试关联状态时,把自定义标识放在结果的 requestState 里,客户端原样带回
  • 需要用到的客户端能力,必须已经在 clientCapabilities 里声明,否则服务器返回 -32021

📡 订阅与通知 ​

subscriptions/listen 取代了旧的 HTTP GET 端点和 resources/subscribe / resources/unsubscribe 两个方法。它开一条长生命周期的通知流。

请求(带订阅过滤器):

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "subscriptions/listen",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {}
    },
    "notifications": {
      "toolsListChanged": true,
      "resourceSubscriptions": ["file:///project/config.json"]
    }
  }
}

订阅过滤器字段:

字段类型说明
toolsListChangedboolean接收 notifications/tools/list_changed
promptsListChangedboolean接收 notifications/prompts/list_changed
resourcesListChangedboolean接收 notifications/resources/list_changed
resourceSubscriptionsstring[]接收这些资源 URI 的 notifications/resources/updated

服务器不得发送客户端未显式请求的通知类型。流的第一条消息必须是确认:

json
{
  "jsonrpc": "2.0",
  "method": "notifications/subscriptions/acknowledged",
  "params": {
    "_meta": { "io.modelcontextprotocol/subscriptionId": 1 },
    "notifications": {
      "toolsListChanged": true,
      "resourceSubscriptions": ["file:///project/config.json"]
    }
  }
}

notifications 字段反映服务器实际同意的那部分——不支持的类型会被省略。客户端应该核对确认结果与自己的请求,优雅处理被拒的类型。

之后每条通知都带订阅 ID:

json
{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": {
    "_meta": { "io.modelcontextprotocol/subscriptionId": 1 },
    "uri": "file:///project/config.json"
  }
}

几个容易踩坑的点:

  • 请求作用域的通知不走这条流。notifications/progress、notifications/message 走的是它们所关联请求的响应流
  • SSE 流的可恢复性与消息重投已被移除(Last-Event-ID 头和 SSE event ID 都没了)。响应流断了,在途请求就丢了,客户端必须用新的请求 ID 重新发起
  • 订阅结束的三种方式:客户端取消(HTTP 关流 / stdio 发 notifications/cancelled)、服务器主动关闭(应该先返回一个完成结果表示优雅结束)、底层传输断开
  • stdio 上重连后必须重发 subscriptions/listen——服务器不跨重连保存订阅状态

🚫 已被删除的方法 ​

下面这些方法在旧教程里还作为"标准方法"出现,但 2026-07-28 已经删除:

方法现状替代方案
ping已删除无(无状态协议不需要保活)
logging/setLevel已删除请求级 _meta 里的 io.modelcontextprotocol/logLevel
notifications/roots/list_changed已删除目录改由工具参数或资源 URI 传递
resources/subscribe / resources/unsubscribe已删除subscriptions/listen
initialize / notifications/initialized已删除每请求 _meta + server/discover
tasks/result / tasks/list移出核心Tasks 扩展:tasks/get 轮询 + tasks/update

服务器不得为没有携带 logLevel 的请求发送 notifications/message。

🛠️ 5. 服务器功能 ​

🔨 工具(Tools) ​

服务器支持工具就必须声明 tools 能力:

json
{
  "capabilities": {
    "tools": { "listChanged": true }
  }
}

📌 确定性与缓存:tools/list 的结果不应随连接变化,也不应被同连接上其他请求的副作用影响。服务器应该以确定顺序返回工具,这样客户端能可靠缓存、也能提高 LLM 的提示缓存命中率。(它可以随请求携带的授权变化——比如只返回调用方 scope 允许的工具,因为凭据是每请求输入。)

列出工具 ​

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": { "cursor": "optional-cursor-value" }
}
json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "complete",
    "tools": [
      {
        "name": "get_weather",
        "title": "天气信息提供者",
        "description": "获取指定位置的当前天气",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": { "type": "string", "description": "城市名或邮编" }
          },
          "required": ["location"]
        },
        "icons": [
          { "src": "https://example.com/weather-icon.png", "mimeType": "image/png", "sizes": ["48x48"] }
        ]
      }
    ],
    "nextCursor": "next-page-cursor",
    "ttlMs": 300000,
    "cacheScope": "public"
  }
}

注意结果里的 ttlMs 与 cacheScope——2026-07-28 要求 tools/list、prompts/list、resources/list、resources/read、resources/templates/list 的结果实现 CacheableResult 接口:

  • ttlMs:新鲜度提示(毫秒),让客户端放心缓存、减少轮询
  • cacheScope:"public" 或 "private",决定共享中间层能否缓存

调用工具 ​

json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": { "location": "北京" }
  }
}
json
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resultType": "complete",
    "content": [
      {
        "type": "text",
        "text": "北京当前天气:\n温度:25°C\n天气:晴"
      }
    ],
    "isError": false
  }
}

结构化输出 ​

工具可以声明 outputSchema,并在结果里返回 structuredContent。2026-07-28 已放宽限制:inputSchema / outputSchema 允许使用任意 JSON Schema 2020-12 关键字,structuredContent 允许任意 JSON 值。

json
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resultType": "complete",
    "content": [{ "type": "text", "text": "{\"temperature\":25,\"conditions\":\"晴\"}" }],
    "structuredContent": { "temperature": 25, "conditions": "晴" },
    "isError": false
  }
}

📁 资源(Resources) ​

资源用 URI 标识,供客户端读取上下文数据。resources/list 列出静态资源,resources/templates/list 列出带参数模板,resources/read 读取内容。

json
// 请求
{ "jsonrpc": "2.0", "id": 4, "method": "resources/read", "params": { "uri": "file:///docs/readme.md" } }
json
// 响应
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "resultType": "complete",
    "contents": [
      {
        "uri": "file:///docs/readme.md",
        "mimeType": "text/markdown",
        "text": "# 项目说明\n\n这是一个示例项目..."
      }
    ],
    "ttlMs": 60000,
    "cacheScope": "private"
  }
}

⚠️ 资源不存在时的错误码已经从 -32002 改为 -32602(Invalid Params),以对齐 JSON-RPC 规范。客户端仍应接受旧服务器返回的 -32002。

💬 提示词(Prompts) ​

提示词是服务器暴露的模板,客户端可以带参数取回渲染好的消息序列。

json
// 请求
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "prompts/get",
  "params": {
    "name": "code_reviewer",
    "arguments": { "language": "Python", "focus": "安全性" }
  }
}
json
// 响应
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "resultType": "complete",
    "description": "代码审查助手提示词",
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "你是一个专业的 Python 代码审查专家,重点关注安全性..."
        }
      }
    ]
  }
}

📊 通用工具 ​

能力方法说明
分页各 */list 的 cursor / nextCursor大列表分批返回
缓存ttlMs + cacheScope列表与读取结果可缓存
参数补全completion/complete为提示词/资源模板参数提供自动补全
日志_meta.io.modelcontextprotocol/logLevel + notifications/message请求级日志级别

👤 6. 客户端功能 ​

💬 信息征询(Elicitation) ​

这是 2026-07-28 里客户端唯一可提供的核心能力。服务器需要用户补充信息时,通过 MRTR 返回 input_required,客户端负责向用户提问并把答案带回。

json
{
  "capabilities": {
    "elicitation": {}
  }
}

⚰️ 已废弃:roots 与 sampling ​

这两个曾经的核心客户端能力,在 2026-07-28 被 SEP-2577 标记为废弃。它们在废弃窗口内仍能工作,但新实现不应再支持。

官方给出的迁移方向:

废弃特性官方建议的替代方案
Roots通过工具参数、资源 URI 或服务器配置传递目录/文件
Sampling直接对接 LLM 提供方 API
Loggingstdio 上写 stderr,或使用 OpenTelemetry

🔒 7. 授权 ​

MCP 为 HTTP 传输提供了授权框架(基于 OAuth 2.1)。要点:

  • 使用 HTTP 传输的实现 SHOULD 遵循该框架
  • 使用 stdio 传输的实现 SHOULD NOT 使用它——而应从环境变量获取凭据
  • 双方 MAY 协商自定义的鉴权方案

规范里的关键安全要求(2026-07-28 更新):

  • 授权服务器应该在授权响应中带上 iss 参数(RFC 9207),客户端在兑换授权码前必须校验 iss 与记录的颁发者一致
  • 客户端在做动态客户端注册时必须指定合适的 application_type,避免与 OpenID Connect 的重定向 URI 冲突
  • 客户端凭据绑定在签发它的授权服务器上:必须按颁发者标识存储,不得跨授权服务器复用,授权服务器变更时必须重新注册

完整实现细节见 6.2 身份认证与授权。

📊 8. 内容类型 ​

MCP 定义了若干标准内容类型,用于工具结果、提示词消息和资源内容:

类型用途关键字段
text纯文本text
image图像(base64)data、mimeType
audio音频(base64)data、mimeType
resource_link指向资源的链接uri、name、mimeType
resource内嵌资源内容resource.uri、resource.text 或 resource.blob
json
{ "type": "text", "text": "这是一段文本内容" }
json
{
  "type": "image",
  "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
  "mimeType": "image/png"
}
json
{
  "type": "resource",
  "resource": {
    "uri": "file:///path/to/file.txt",
    "mimeType": "text/plain",
    "text": "文件内容..."
  }
}

⚠️ 常见误区速查 ​

中文资料里流传最广的几条过时说法,对照表如下:

常见说法实际情况(2026-07-28)
"MCP 支持 HTTP / WebSocket / Unix Socket / stdio 四种传输"❌ 只有 stdio 和 Streamable HTTP 两种标准传输,另允许自定义传输
"每个连接必须先完成 initialize 初始化握手"❌ 握手已删除,改为无状态 + 每请求 _meta
"用 Mcp-Session-Id 维持会话"❌ 会话与请求头已移除,跨调用状态改用显式句柄
"服务器可以主动发 sampling/createMessage"❌ 改为 MRTR,服务器返回 input_required,客户端重试
"用 SSE 的 Last-Event-ID 续传断掉的流"❌ 可恢复性与重投已移除,必须用新请求 ID 重发
"用 resources/subscribe 订阅资源变更"❌ 改为 subscriptions/listen + 过滤器
"用 logging/setLevel 调整日志级别"❌ 改为请求级 _meta.io.modelcontextprotocol/logLevel
"用 ping 做心跳保活"❌ ping 已删除
"工具结果只有 content"❌ 每个结果必须带 resultType;列表类结果还需 ttlMs + cacheScope

🎯 协议规范总结 ​

📚 核心要点 ​

🏗️ 协议结构 ​

  • ✅ JSON-RPC 2.0 基础:请求 / 结果响应 / 错误响应 / 通知四种消息,结果必须带 resultType
  • ✅ 错误码分区:-32020 ~ -32099 为规范保留;历史码不再新增
  • ✅ 传输层:只有 stdio 与 Streamable HTTP 两种标准传输

📨 无状态模型 ​

  • ✅ 无握手:每请求在 _meta 里自带协议版本与客户端能力
  • ✅ server/discover:服务器必须实现,用于一次性获取版本、能力与身份
  • ✅ 版本协商:不匹配返回 -32022,客户端从 supported 里重选

🔀 消息模式 ​

  • ✅ 请求与响应:客户端单向发起
  • ✅ MRTR:服务器返回 input_required,客户端带 inputResponses 重试
  • ✅ 订阅与通知:subscriptions/listen 长流 + 过滤器 + 订阅 ID

🛠️ 功能规范 ​

  • ✅ 工具:tools/list(可缓存、确定性顺序)、tools/call、结构化输出
  • ✅ 资源:resources/list、resources/read、模板
  • ✅ 提示词:prompts/list、prompts/get
  • ✅ 客户端能力:只剩 Elicitation;roots / sampling / logging 已废弃

🔗 规范地图 ​

🤔 实践思考 ​

💭 理解检测 ​

  1. 无状态的意义:为什么把会话从协议层拿掉,反而让 MCP 更容易做水平扩展?
  2. MRTR 的取舍:相比"服务器主动发起请求",MRTR 牺牲了什么、换来了什么?
  3. 传输选择:什么情况下你会选择自定义传输而不是 Streamable HTTP?
  4. 错误码设计:为什么规范要把 -32000 ~ -32019 冻结为"历史遗留"?

🛠️ 实践挑战 ​

写一个最小但符合 2026-07-28 规范的交互流程:

  1. 发现:客户端发 server/discover,拿到服务器能力与支持版本
  2. 列表:调 tools/list,检查结果里的 ttlMs / cacheScope
  3. 调用:调 tools/call,处理 resultType: "input_required" 并用 inputResponses 重试
  4. 订阅:开一条 subscriptions/listen,正确处理确认消息与订阅 ID

📖 扩展学习 ​

🔗 参考资料 ​

🎓 下一步 ​

掌握了协议规范后,你就可以:

  • 📝 实现符合 2026-07-28 规范的 MCP 客户端
  • 🔧 开发标准的 MCP 服务器(记得实现 server/discover)
  • 🐛 迅速识别旧教程里的过时写法
  • 🔒 设计安全的 MCP 部署方案

📚 想继续深入规范外围:12.1 OAuth 2.1 授权体系(协议级授权)、12.3 官方扩展体系(Tasks / MCP Apps / Skills)、12.4 官方注册表 Registry(发布与发现)。


协议清楚了,接下来补齐架构视图——从组件和数据流的角度把 MCP 串起来。

👉 进入第 2 章:2.1 架构概览

内容依据 MCP 官方规范整理 · 规范以 官方文档 为准