菜鸟AI - 让提示词生成更简单! 全站导航 全站导航
AI工具安装 新手教程 进阶教程 辅助资源 AI提示词 热点资讯 技术资讯 产业资讯 内容生成 模型技术 AI信息库

已有账号?

首页 > AI教程 > MCP协议拆解:JSON-RPC到Agent全链路权威指南
进阶教程 MCP协议拆解

MCP协议拆解:JSON-RPC到Agent全链路权威指南

2026-06-04
阅读 0
热度 0
作者 菜鸟AI编辑部
摘要

摘要

MCP协议采用三层架构,Host、Client、Server各司其职。底层通信基于JSON-RPC2 0信封格式,经历初

MCP 协议深度解析:从 JSON-RPC 通信层到 Agent 完整链路

这篇内容来源于 Agent 工程化学习(Phase 04)的阶段性复盘。核心目标是将对 MCP 协议的认知,从“能用 SDK 跑通”升级到“理解每条消息的意图、每层抽象的设计理由”。所有观点来自实际编码练习与课程任务,并非规范文档的翻译。

MCP 协议深度解析:从 JSON-RPC 通信层到 Agent 完整链路

读者对象:已能成功运行 MCP Server,但对内部运作机制仍感模糊的开发者。

MCP 要解决的核心痛点

AI Agent 需要调用外部能力(读文件、查数据库、发请求),每个 Host 应用都重复实现一套工具接口。同一种工具在不同应用中需要写两遍——就像 USB 标准诞生前,每款外设都要配专属接口。

MCP(Model Context Protocol)正是这个统一接口标准。任何 Host(CatDesk、Cursor、Claude Desktop)都能连接任意 MCP Server。一次编写 Server,处处可用。

它的地位与 HTTP 类似:HTTP 统一了浏览器与服务器的通信格式,MCP 统一了 AI 应用与工具提供方的通信格式。

三层架构:Host、Client、Server

MCP 将参与方划分为三层,各层职责明确:

flowchart LR
subgraph Host["Host (CatDesk / Cursor)"]
UI["UI + 对话管理"]
LLM["LLM 调用"]
C1["MCP Client A"]
C2["MCP Client B"]
end
subgraph Servers["独立进程"]
S1["MCP Server A
(文件系统)"] S2["MCP Server B
(数据库)"] end C1 <-->|"stdio / HTTP"| S1 C2 <-->|"stdio / HTTP"| S2

Host 是用户直接面对的 AI 应用。它负责界面渲染、对话历史管理、LLM 调用等,内部包含一个或多个 Client 实例。

Client 扮演协议适配层角色。每个 Client 对应一个 Server 连接,承担三项职责:管理连接生命周期、转换工具格式(MCP Schema ↔ AI SDK Schema)、将调用请求路由到正确的 Server。

Server 是能力提供方。它以独立进程运行,通过传输层与 Client 通信,暴露 Tools、Resources、Prompts 三类能力。

将 Client 单独抽离的根本原因是关注点分离。Host 只需关心“可用的工具清单”,Server 只需关心“收到请求后的执行逻辑”,中间的连接管理、格式转换、传输层适配全部封装在 Client 中。这样一来,更换 LLM 框架只需调整 Client 的翻译逻辑,更换传输方式只需修改 Client 的底层实现,上下两层均无需改动。

类比浏览器:前端代码只管发 fetch 收数据,TCP 连接池、TLS 握手、HTTP/2 多路复用等底层工作,全由浏览器的网络栈(Client 层)自动处理。

底层通信格式:JSON-RPC 2.0

MCP 的所有消息,无论通过 stdio 还是 HTTP 传输,均采用 JSON-RPC 2.0 格式封装。这相当于 MCP 的“血管系统”——理解它,就能看懂消息在网络中的真实形态。

JSON-RPC 只有三种信封类型:

Request(请求)——包含 id,期望对方返回 Response:

{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "read_file", "arguments": { "path": "config.json" } } }

Response(响应)——id 与 Request 配对,标明是对哪个请求的回复:

{ "jsonrpc": "2.0", "id": 1, "result": { "content": [{ "type": "text", "text": "文件内容..." }] } }

Notification(通知)——不含 id,无需回复:

{ "jsonrpc": "2.0", "method": "notifications/initialized" }

几个关键认知点:

id 是异步配对机制。Client 同时发出 3 个请求(id=1,2,3),Server 可以乱序返回,Client 依靠 id 匹配“这条回复对应哪个请求”。Notification 无需 id,因为没人需要回复它。

错误码遵循标准规范。-32601 Method not found-32602 Invalid params 均由 JSON-RPC 规范定义,Server 返回错误时应使用这些码,不得自行编造。

SDK 自动处理所有序列化与反序列化。生产环境无需手动解析,但理解信封格式能帮你在 Inspector 中看懂消息流,出问题时更快定位。

三阶段生命周期

一个 MCP 连接从建立到关闭,经历三个明确阶段:

sequenceDiagram
participant C as Client
participant S as Server
rect rgb(230,245,255)
Note over C,S: Phase 1: Initialize(能力协商)
C->>S: initialize { protocolVersion, capabilities, clientInfo }
S-->>C: result { protocolVersion, capabilities, serverInfo }
C->>S: notifications/initialized
end
rect rgb(230,255,230)
Note over C,S: Phase 2: Operation(正常工作)
C->>S: tools/list
S-->>C: result { tools: [...] }
C->>S: tools/call { name, arguments }
S-->>C: result { content: [...] }
C->>S: resources/read { uri }
S-->>C: result { contents: [...] }
end
rect rgb(255,240,230)
Note over C,S: Phase 3: Shutdown(关闭)
Note over C,S: 传输层信号:EOF / HTTP disconnect
end

Phase 1 核心是能力协商。Client 声明“我支持 roots 和 sampling”,Server 声明“我有 tools 和 resources”。后续操作不得超出对方声明的能力——例如 Client 未声明 sampling,Server 就不能调用 sampling/createMessage。这是硬性约束。

initialized 为何是 Notification 而非 Request?因为 Server 无需确认,Client 只是单向通知“我已就绪,可进入工作阶段”。一个简单的“我就绪”信号。

Phase 3 没有结构化的 shutdown 方法。关闭由传输层信号完成——stdio 场景关闭 stdin/stdout,HTTP 场景 DELETE session。简洁且足够。

六大原语

MCP 定义了六种能力原语,分为两组:

Server 暴露给 Client(3 个):

  • Tools:Agent 主动调用的操作,可能产生副作用(写文件、发请求)。LLM 在推理过程中自主决定是否调用。
  • Resources:只读数据,通过 URI 寻址(如 file://workspace/info)。通常在对话开始前预加载给 LLM 作为背景知识,也可在推理过程中按需读取。
  • Prompts:预定义的提示词模板。Host 展示给用户选择,用户触发后注入对话。

Client 暴露给 Server(3 个):

  • Roots:告知 Server 可访问的工作区路径。
  • Sampling:Server 请求 Client 侧的 LLM 生成内容(反向调用)。
  • Elicitation:Server 向用户提问获取输入。

日常开发最常接触的是前三个。其中 Tool 是核心——Agent 的“动手能力”全靠 Tool 支撑。

Resource 与 Tool 的本质区别

一个易混淆点:Resource 的 URI(如 file://workspace/info)看似文件路径,实为 Server 自行定义的虚拟地址。当 Client 调用 readResource("file://workspace/info") 时,Server 并非去磁盘找文件,而是执行注册时绑定的 async 函数——可能是读目录拼接 JSON、查数据库、调 API,一切皆可。

Tool 与 Resource 的本质差异在于触发者及是否有副作用:

  • Resource:Host 在对话开始前预加载,为 LLM 提供背景知识。无副作用,类似 GET 请求。
  • Tool:LLM 在推理过程中自主决定调用。可能有副作用(写文件、删记录),类似 POST 请求。

Tool Schema 设计原则

Tool 的 Schema 直接影响 LLM 能否在正确时机选中正确工具。以下四条原则可参考:

命名采用 snake_case + 动宾结构。 read_filelist_directorysearch_files——LLM 一眼就能识别工具用途。避免 fileManagerhandleData 这类模糊命名。

描述采用两句话公式。 第一句说明使用场景,第二句划定边界:

Use when you need to read the contents of a file at a given path.
Do not use for directories or binary files.

第二句“不要用来做什么”至关重要——LLM 常在相似工具间犹豫,边界描述能快速排除错误选项。

粒度选择 Atomic 而非 Monolithic。 read_file + write_file 优于 file_manager(action: "read"|"write"|"delete")。原因在于 LLM 更容易直接选对精确工具,而非先选中大工具再决定 action 参数。

inputSchema 每个字段必须包含 description。 LLM 需要知道每个参数的含义、格式与约束。用 required 明确必选参数,用 enum 限制取值范围。清晰度直接影响工具调用成功率。

完整调用链路:一条消息的旅程

将各层串联,观察一次完整调用:

sequenceDiagram
participant User as 用户
participant Host as Host (CatDesk)
participant LLM as LLM (DeepSeek)
participant Client as MCP Client
participant Server as MCP Server
User->>Host: "帮我读一下 config.json"
Note over Host,LLM: Host 把工具菜单 + 用户消息一起发给 LLM
Host->>LLM: messages + tools 列表
Note over LLM: LLM 自主判断需要调用 read_file
LLM-->>Host: tool_call: read_file(path="config.json")
Note over Host,Client: Host 把 LLM 的决策交给 Client 执行
Host->>Client: 转发 tool_call
Note over Client,Server: Client 翻译成 JSON-RPC 格式发给 Server
Client->>Server: {"method":"tools/call","params":{...}}
Server-->>Client: {"result":{"content":[...]}}
Note over Host,Client: Client 提取结果返回
Client-->>Host: 文件内容
Note over Host,LLM: Host 把工具结果塞回 LLM context
Host->>LLM: tool_result 放入 context
Note over LLM: LLM 基于结果组织自然语言回答
LLM-->>Host: "config.json 的内容是..."
Host-->>User: 展示回答

几个易误解点:

LLM 不是“建议你使用工具”,而是直接输出调用指令。整个流程是自动化的 Agent Loop,用户几乎无感知。

Host 拿到工具结果后不直接展示给用户,而是先塞回 LLM。让 LLM 基于结果组织回答——它可能总结,可能继续调用另一个工具,直到认为任务完成才输出文本。

一次对话可能循环多轮。用户说“帮我找所有 .ts 文件然后读 package.json”,LLM 会先调 search_files,看到结果后再调 read_file,两轮工具调用完成后给出最终回答。

如何观测每一层

调试 MCP 应用时,不同层需要不同观测工具:

观测层观测方式可获取信息
LLM 决策(Host 层)AI SDK 的 onStepFinish 回调每轮选择了哪些工具、传了哪些参数、finishReason 是 tool-calls 还是 stop
Client ↔ Server 通信Client 的 verbose 模式或 JSON-RPC 追踪工具调用耗时、返回内容预览、错误信息
JSON-RPC 原始信封traceJsonRpc 模式或 MCP Inspector每条消息的完整 JSON:id、method、params、result

MCP Inspector 是官方提供的图形化调试工具,使用非常直接:

npx @modelcontextprotocol/inspector npx tsx src/04-tools-mcp/03-file-server/index.ts .

它让你手动扮演 Client 角色:点击 tools/list 查看已注册的工具,点击 tools/call 填写参数测试执行。Inspector 能展示 Client↔Server 段的所有 JSON-RPC 消息,但看不到 LLM 决策——那部分需要在 Host 代码中添加 onStepFinish 回调。

编写 MCP Server 的标准流程

五步即可完成:

1. 声明身份——创建 McpServer 实例,赋予名称和版本号。

2. 注册能力——通过 registerTool / registerResource / registerPrompt 挂载能力。每个 Tool 需定义 name、description、inputSchema、handler。

3. 连接传输层——new StdioServerTransport() + server.connect(transport)。SDK 自动处理 initialize 握手。

4. Inspector 调试——无需编写 Client 代码即可验证 Server 是否正常。手动测试每个 Tool 的输入输出。

5. 接入 Host——将 Server 配置到 CatDesk / Cursor 的 MCP 配置中,Agent 即可使用。

生产环境中,90% 的代码集中在两件事:定义 Schema(告知 LLM 能做什么)和实现 Handler(真正执行逻辑)。握手、路由、序列化全部由 SDK 处理。

总结

MCP 的核心设计哲学清晰明了:Server 声明自己能做什么(Schema),Client 负责连接与翻译,Host 让 LLM 自主决定调用什么。各层各司其职,依靠 JSON-RPC 信封在中间传递。

掌握这个层次后,后续的 Inspector 实操与自定义 Server 开发,不过是在此骨架上填充血肉。

来源:互联网

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

同类文章推荐

相关文章推荐

更多