MCP 的架构、协议分层与通信过程
MCP(Model Context Protocol,模型上下文协议)规定 AI 应用与外部能力之间如何发现功能、交换数据和发起调用。它解决的是应用与能力提供方的对接问题;模型如何规划任务、Host 如何管理对话,不由 MCP 规定。
理解 MCP 可以沿着三条线索展开:谁参与通信、消息表达什么、消息怎样送达。前者对应 Host、Client、Server;后两者分别对应数据层与传输层。
一、架构:Host、Client 与 Server
#mermaid-svg-YvwkusTJnzpVEiXu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YvwkusTJnzpVEiXu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YvwkusTJnzpVEiXu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YvwkusTJnzpVEiXu .error-icon{fill:#552222;}#mermaid-svg-YvwkusTJnzpVEiXu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YvwkusTJnzpVEiXu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YvwkusTJnzpVEiXu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YvwkusTJnzpVEiXu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YvwkusTJnzpVEiXu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YvwkusTJnzpVEiXu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YvwkusTJnzpVEiXu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YvwkusTJnzpVEiXu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YvwkusTJnzpVEiXu .marker.cross{stroke:#333333;}#mermaid-svg-YvwkusTJnzpVEiXu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YvwkusTJnzpVEiXu p{margin:0;}#mermaid-svg-YvwkusTJnzpVEiXu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YvwkusTJnzpVEiXu .cluster-label text{fill:#333;}#mermaid-svg-YvwkusTJnzpVEiXu .cluster-label span{color:#333;}#mermaid-svg-YvwkusTJnzpVEiXu .cluster-label span p{background-color:transparent;}#mermaid-svg-YvwkusTJnzpVEiXu .label text,#mermaid-svg-YvwkusTJnzpVEiXu span{fill:#333;color:#333;}#mermaid-svg-YvwkusTJnzpVEiXu .node rect,#mermaid-svg-YvwkusTJnzpVEiXu .node circle,#mermaid-svg-YvwkusTJnzpVEiXu .node ellipse,#mermaid-svg-YvwkusTJnzpVEiXu .node polygon,#mermaid-svg-YvwkusTJnzpVEiXu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YvwkusTJnzpVEiXu .rough-node .label text,#mermaid-svg-YvwkusTJnzpVEiXu .node .label text,#mermaid-svg-YvwkusTJnzpVEiXu .image-shape .label,#mermaid-svg-YvwkusTJnzpVEiXu .icon-shape .label{text-anchor:middle;}#mermaid-svg-YvwkusTJnzpVEiXu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YvwkusTJnzpVEiXu .rough-node .label,#mermaid-svg-YvwkusTJnzpVEiXu .node .label,#mermaid-svg-YvwkusTJnzpVEiXu .image-shape .label,#mermaid-svg-YvwkusTJnzpVEiXu .icon-shape .label{text-align:center;}#mermaid-svg-YvwkusTJnzpVEiXu .node.clickable{cursor:pointer;}#mermaid-svg-YvwkusTJnzpVEiXu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YvwkusTJnzpVEiXu .arrowheadPath{fill:#333333;}#mermaid-svg-YvwkusTJnzpVEiXu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YvwkusTJnzpVEiXu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YvwkusTJnzpVEiXu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YvwkusTJnzpVEiXu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YvwkusTJnzpVEiXu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YvwkusTJnzpVEiXu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YvwkusTJnzpVEiXu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YvwkusTJnzpVEiXu .cluster text{fill:#333;}#mermaid-svg-YvwkusTJnzpVEiXu .cluster span{color:#333;}#mermaid-svg-YvwkusTJnzpVEiXu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YvwkusTJnzpVEiXu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YvwkusTJnzpVEiXu rect.text{fill:none;stroke-width:0;}#mermaid-svg-YvwkusTJnzpVEiXu .icon-shape,#mermaid-svg-YvwkusTJnzpVEiXu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YvwkusTJnzpVEiXu .icon-shape p,#mermaid-svg-YvwkusTJnzpVEiXu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YvwkusTJnzpVEiXu .icon-shape .label rect,#mermaid-svg-YvwkusTJnzpVEiXu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YvwkusTJnzpVEiXu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YvwkusTJnzpVEiXu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YvwkusTJnzpVEiXu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MCP 消息
MCP 消息
使用者
Host:Claude Code、Cursor 等 AI 应用
语言模型
MCP Client A
MCP Client B
MCP Server A
MCP Server B
文件、代码索引或外部 API
数据库或远端服务
| 角色 | 职责 |
|---|---|
| Host | 运行 AI 应用,管理对话、模型调用和接入的 MCP Server;把工具的 inputSchema 转成模型工具 schema,并可在调用前校验参数或请求用户确认;决定何时把工具结果送回模型。 |
| MCP Client | Host 内负责与某个 MCP Server 通信的协议组件,发送请求并接收结果;SDK 可以提供本地 schema 校验,但 MCP 规范不保证 Client 必须做参数校验。 |
| MCP Server | 对外提供工具、资源或提示模板的程序,处理 Client 发来的 MCP 消息;必须校验工具参数是否符合 inputSchema(如必填、类型和枚举),并检查权限与业务规则后再执行。 |
一个 Host 可以管理多个 MCP Client;通常每个 Client 维护与一个 Server 的逻辑连接。Server 可以运行在本机,也可以部署在远端。"Server"表示协议角色,不表示机器一定在远端。
在 Host 中添加 MCP 配置,通常是登记 Server 的启动命令或访问地址。Host 根据配置建立连接并创建 Client。一个 Server 可以提供多个工具,因此"Server 已出现在配置列表中"与"Host 已取得该 Server 的具体工具目录"是两个不同的状态。Host 自带的内置工具也不一定通过 MCP 实现。
以查询工单为例,典型通路是:Host 连接 Server,Client 取得工具说明;模型根据任务提出工具调用;Host 将调用交给对应 Client;Server 查询实际系统并返回结果;Host 再将结果交给模型生成回答。模型提出的工具调用与 Client 发出的 MCP 消息是相邻的两步,不是同一条消息。
二、协议分层:数据层表达含义,传输层负责送达
text
┌────────────────────────────────────────────────────┐
│ 数据层:JSON-RPC 消息、方法、参数、结果、错误与能力 │
├────────────────────────────────────────────────────┤
│ 传输层:stdio 或 Streamable HTTP;负责消息的承载 │
└────────────────────────────────────────────────────┘
数据层 定义"调用什么、参数是什么、结果属于哪次调用"。MCP 使用 JSON-RPC 2.0 编码消息。一个典型请求包含 jsonrpc、id、method 和 params;相应的响应使用同一个 id,并携带 result 或 error。通知没有 id,也不期待 JSON-RPC 响应。
json
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_issues","arguments":{"query":"timeout"}}}
其中,jsonrpc: "2.0" 是 JSON-RPC 格式版本,不是 MCP 协议版本 ;id: 3 用来配对请求与响应;method 指明 MCP 操作;params 承载该操作的参数。
MCP Server 常见的能力包括:
| 能力 | 用途 | 常见方法 |
|---|---|---|
| Tools(工具) | 执行查询、计算或操作 | tools/list、tools/call |
| Resources(资源) | 向应用提供可读取的数据 | resources/list、resources/read |
| Prompts(提示模板) | 提供可复用的交互模板 | prompts/list、prompts/get |
tools/list 是 Client 询问"有哪些工具,以及各工具的名称、说明和参数格式"。tools/call 是 Client 指定工具名与参数,请 Server 执行。二者是 JSON-RPC 方法名 ,不是 /tools/list 和 /tools/call 两个 HTTP 路由,也不要求源码中存在完全同名的函数。
"先发现再调用"表示 Host 通常先取得工具目录,再决定怎样向模型提供工具说明。它不是每次 tools/call 的强制前置步骤 :工具目录可能已经缓存,或由其他可靠方式预先获知;目录变化时可以重新获取。tools/list 还支持分页。
工具结果与协议错误需要区分。工具执行失败时,Server 可以在正常 JSON-RPC result 中返回 isError: true,使模型了解业务失败原因;请求结构错误、未知工具等协议问题则可通过 JSON-RPC error 表达。HTTP 传输还额外存在 HTTP 状态码,例如认证失败或无法访问端点。这三类信号不应混为一谈。
传输层 定义进程或网络连接怎样建立、消息怎样分帧、认证信息怎样传递,以及连接断开和取消如何表达。对处理 tools/call 的业务代码而言,两种传输可以承载相同的工具语义;对 MCP Client 的实现和故障排查而言,进程退出、HTTP 状态码、SSE 流和超时都是实际差异。
三、两种标准传输
| 维度 | stdio | Streamable HTTP |
|---|---|---|
| Server 运行形态 | Host 通常启动一个本地子进程 | Server 独立运行,提供 HTTP 端点 |
| Client 发送消息 | 写入子进程的 stdin |
每条消息通过一次 HTTP POST 发送 |
| Client 接收消息 | 从子进程的 stdout 读取 |
接收单个 JSON 响应或 SSE 响应流 |
| 消息边界 | 一行一条 UTF-8 JSON-RPC 消息 | HTTP 请求及其对应响应;SSE 中每个事件承载消息 |
| 日志与认证 | 日志可写 stderr;权限常由本地进程环境决定 |
HTTP 头可携带认证信息;还需处理网络状态 |
| 生命周期 | 关注子进程启动、退出和标准流关闭 | 关注地址、请求超时、响应流及服务端可用性 |
stdio 中,stdout 专供有效 MCP 消息使用;普通日志应写入 stderr,否则 Client 可能把日志误解析为协议消息。Streamable HTTP 中,Client 对 MCP 端点发送 POST;Server 对请求返回 application/json 或 text/event-stream。SSE 是 HTTP 响应内的事件流格式,并非与 stdio、Streamable HTTP 并列的第三种当前标准传输。早期版本的独立 HTTP+SSE 传输与后来的 Streamable HTTP 也应区分。在 2025 系列 Streamable HTTP 中,Client 还可用 GET 打开独立 SSE 流;2026-07-28 版移除了这种 GET 流。
"本地"和"远端"是常见部署形态,不是传输的严格定义:HTTP Server 可以运行在本机,stdio Server 也可以在执行工具时访问外部 API。判断传输方式应看 MCP Client 与 MCP Server 之间的连接。
四、真实服务示例:CodeGraph 与 Context7
以下两个示例用我本机常用的两个 MCP 工具,分别展示直接 stdio 和直接 Streamable HTTP。配置中的服务与工具名称是真实的;后文的结果文本和报文编号为博客用 Mock 数据。
4.1 CodeGraph:本地 stdio
json
{
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp"]
}
Host 启动 codegraph serve --mcp,然后持续使用这个子进程的标准流:
text
Host / MCP Client CodeGraph 子进程
│ JSON-RPC + 换行 → stdin │
├───────────────────────────────────────→│
│ stdout ← JSON-RPC + 换行│
│←───────────────────────────────────────┤
│ stderr ← 可选日志 │
例如,Client 发出的 tools/list 在线路上是一行 JSON,末尾接换行符。下方的 \n 表示实际换行符,并非 JSON 内容的一部分:
text
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}\n
Server 可以在 stdout 上返回包含 codegraph_explore 的工具目录。调用该工具时,同样把 tools/call 的 JSON-RPC 消息写入 stdin,而不是发 HTTP 请求。这个例子的关键在于进程管道就是传输通道,不存在 HTTP 请求头或 HTTP 状态码。
4.2 Context7:原生 Streamable HTTP
Context7 官方提供可直接连接的远端 MCP 端点。不同 Host 的配置字段可能不同,下例展示支持 type 与 url 字段的 Host 配置形态;正式接入时应以所用 Host 的配置语法为准。
json
{
"type": "http",
"url": "https://mcp.context7.com/mcp"
}
这种连接不要求 Host 启动 Context7 子进程。MCP Client 直接向该地址发送 HTTP POST,并接收 HTTP 响应。Context7 官方登记信息将此端点标记为 streamable-http;基础用法可不附带 API Key,认证方式与配额则以服务方说明为准。
text
Host / MCP Client Context7 MCP Server
│ HTTPS POST /mcp,正文为 JSON-RPC │
├───────────────────────────────────────→│
│ HTTP 响应:JSON 或 SSE 事件流 │
│←───────────────────────────────────────┤
CodeGraph 示例中的工具名为 codegraph_explore。Context7 的工具目录可包含 resolve-library-id 和 query-docs。工具名称和参数属于数据层;标准流、HTTP POST 与 SSE 属于传输层。
五、原始报文:以 2025-11-25 版 Streamable HTTP 为例
协议版本会改变生命周期与部分头字段。以下完整流程固定使用 MCP 2025-11-25 ,与上面的 Context7 原生 HTTP 端点配套。请求体与结果内容为结构示意;Content-Length、代理添加的头等 HTTP 分帧细节省略,实际字节数由 HTTP 客户端计算。示例假定 Server 不分配可选的 Mcp-Session-Id。
第一步:initialize 协商版本和能力
Client 首先发送初始化请求。此时尚未协商出版本,因此示例不在首个请求中添加后续请求使用的 MCP-Protocol-Version 头。
http
POST /mcp HTTP/1.1
Host: mcp.context7.com
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"blog-demo","version":"1.0.0"}}}
Server 可以返回普通 JSON,也可以返回 SSE。下面展示 SSE 形式;data: 后仍是 JSON-RPC 响应:
http
HTTP/1.1 200 OK
Content-Type: text/event-stream
data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{}},"serverInfo":{"name":"Context7","version":"example"}}}
id: 1 使 Client 能把响应对应回本次初始化。result.protocolVersion 表示最终采用的 MCP 版本;capabilities.tools 表示 Server 提供工具能力,但尚未列出具体工具。
第二步:notifications/initialized 通知就绪
Client 接着发送没有 id 的通知。通知不期待 JSON-RPC 响应;Server 接受后,HTTP 层可以返回 202 Accepted 和空响应体。
http
POST /mcp HTTP/1.1
Host: mcp.context7.com
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-11-25
{"jsonrpc":"2.0","method":"notifications/initialized"}
http
HTTP/1.1 202 Accepted
第三步:tools/list 获取工具说明书
http
POST /mcp HTTP/1.1
Host: mcp.context7.com
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-11-25
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
http
HTTP/1.1 200 OK
Content-Type: text/event-stream
data: {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"resolve-library-id","description":"Resolve a library name to an ID","inputSchema":{"type":"object","properties":{"query":{"type":"string"},"libraryName":{"type":"string"}},"required":["query","libraryName"]}},{"name":"query-docs","description":"Query documentation for a library","inputSchema":{"type":"object","properties":{"libraryId":{"type":"string"},"query":{"type":"string"}},"required":["libraryId","query"]}}]}}
name 是后续调用时使用的标识;description 说明用途;inputSchema 是参数的 JSON Schema。此处只展示两个工具。如果结果包含 nextCursor,Client 还可以按游标取得下一页。
第四步:tools/call 执行工具
Host 确定要调用 resolve-library-id 后,MCP Client 发送独立的 HTTP POST。HTTP 路由依旧是 /mcp;实际操作由 JSON-RPC 请求体的 method 与 params.name 指定。
http
POST /mcp HTTP/1.1
Host: mcp.context7.com
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-11-25
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"resolve-library-id","arguments":{"query":"Find official Go net/http documentation","libraryName":"Go"}}}
http
HTTP/1.1 200 OK
Content-Type: text/event-stream
data: {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"Library ID: /golang/go"}],"isError":false}}
最后一段 text 是 Mock 结果,用于展示结构,不代表 Context7 对该查询的固定返回值。响应可能只有一个文本块,也可能包含其他内容类型或结构化数据。Host 取得结果后,可以把它交回模型继续处理。
如果 Server 不需要流式返回,同一个请求也可以收到 Content-Type: application/json,响应体直接是一个 JSON-RPC 对象,不带 SSE 的 data: 前缀。Client 必须能够处理两种返回形式。
同一条数据层消息放到 stdio 中
把上述 tools/call 换成 stdio 时,核心 JSON-RPC 消息仍可保持相同的 method、name 和 arguments,但外面不再有 HTTP 请求行和请求头:Client 写入一行 JSON;Server 从 stdout 返回一行 JSON。CodeGraph 等实际 Server 的工具名和参数当然取决于其自身的 tools/list 结果。
text
stdin → {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"example_tool","arguments":{"query":"example"}}}\n
stdout ← {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"example result"}]}}\n
六、协议版本不能混用
前面的完整报文采用 2025-11-25 ,因为该版包含 initialize 握手。其他版本的捕获结果可能不同:
| 版本范围 | 关键差异 |
|---|---|
2024-11-05 |
包含 initialize;远端 HTTP+SSE 属于较早的独立传输形式。 |
2025-03-26 至 2025-11-25 |
采用 initialize 和 notifications/initialized;Streamable HTTP 使用 POST,响应可以是 JSON 或 SSE;服务端可以选择分配 Mcp-Session-Id。 |
2026-07-28 |
改为每次请求携带协议版本与能力信息;可用 server/discover 了解 Server;Streamable HTTP 不再采用协议会话和独立 GET 事件流。 |
2026-07-28 版的 tools/call 可以长成下面这样。HTTP 头中的 MCP-Protocol-Version、Mcp-Method 和 Mcp-Name 与请求体相应字段对应;Server 需要校验头与体一致。示例只展示关键字段。
http
POST /mcp HTTP/1.1
Host: mcp.example.test
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search_issues
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_issues","arguments":{"query":"timeout"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}
此时 server/discover 用于了解支持的版本与能力。下方展示一组最小的 2026-07-28 版数据层消息;通过 HTTP 发送时,请求仍要置于 POST 正文中,并附带该版本要求的请求头。
json
{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}
json
{"jsonrpc":"2.0","id":1,"result":{"resultType":"complete","supportedVersions":["2026-07-28"],"capabilities":{"tools":{}}}}
server/discover 与 tools/list 的问题不同:前者问"Server 支持什么协议能力",后者问"具体有哪些工具"。server/discover 必须由现代 Server 实现,但 Client 可以直接发送具体请求并处理版本错误。因此,server/discover → tools/list → tools/call 是便于理解的通路,不是每次调用都必须重新执行的固定三连。
七、阅读 MCP 报文时的检查顺序
- 看协议版本。 先确认使用握手式流程还是按请求携带元数据的流程。
- 看连接入口。
command加子进程通常意味着 stdio;直接url加 HTTP 传输配置意味着 Streamable HTTP。 - 看传输包装。 stdio 按换行分帧;HTTP 则先看请求方法、状态码、
Content-Type和可选 SSE 事件。 - 看 JSON-RPC 消息。
id配对请求与响应,method决定操作,params放参数,result或error表示协议处理结果。 - 看工具结果。
tools/list给出名称与参数格式;tools/call的content、structuredContent、isError说明工具实际返回了什么。
这一顺序同时保留两层边界:传输层说明消息怎样抵达,数据层说明抵达后应执行什么操作。