2.2 MCP 通信基础:JSON-RPC
MCP 使用 JSON-RPC 作为客户端与服务器的通信基础。在介绍 JSON-RPC 之前,我们先来了解一下 JSON 和 RPC。
2.2.1 什么是 JSON
JSON (JavaScript Object Notation,JavaScript 对象表示法) 是一种轻量级的数据交换格式,易于人阅读和编写,也易于机器解析和生成。它基于 JavaScript 编程语言的一个子集,但是独立于编程语言。
JSON 主要有以下特点:
- 使用简单的文本格式,易于传输和存储
- 支持嵌套数据结构,可以表示复杂的数据
- 数据类型包括:对象、数组、字符串、数字、布尔值和 null
- 已成为许多 Web API 和配置文件的标准格式
JSON 的语法简洁明了,使用键值对组织数据,非常适合作为不同系统间的通用数据交换格式。
一个用 JSON 表示的数据结构示例:
{ "name": "张三", "age": 30, "isStudent": false, "hobbies": ["读书", "旅游", "编程"], "address": { "city": "北京", "postcode": "100000" }, "phoneNumbers": [ { "type": "家庭", "number": "010-12345678" }, { "type": "工作", "number": "138XXXXXXXX" } ], "spouse": null}2.2.2 什么是 RPC
RPC (Remote Procedure Call,远程过程调用) 是一种计算机通信协议,它允许程序调用另一台计算机上的子程序,而程序员无需额外编写网络交互代码。
RPC 的主要特点:
- 分布式系统通信的基础机制
- 使远程调用在代码层面表现得与本地调用相似
- 隐藏了底层网络通信的复杂性
- 支持不同编程语言和平台之间的互操作性
- 常见 RPC 框架包括 gRPC、Thrift、Dubbo 等
RPC 系统通常包含两个关键组件:客户端和服务器。客户端发起请求调用远程服务器上的程序,服务器执行请求的操作并返回结果。RPC 通信流程如图所示:
可以看到,RPC 通信核心要做的两件事:
- 选择一种消息格式,对请求参数和响应参数进行序列化和反序列化
- 选择一种传输协议,传递和接收数据
2.2.3 什么是 JSON-RPC
JSON-RPC 是一种基于 JSON 的轻量级 RPC 协议。它定义了一种数据格式和处理规则,允许在不同系统之间进行远程方法调用。
主要特点:
- 使用 JSON 作为消息格式
- 支持请求-响应模式
- 简单易用,不依赖特定编程语言
- 可以通过 HTTP / HTTPS 或其他传输协议传输
- 标准的 JSON-RPC 请求包含:方法名、参数和请求 ID。响应包含:结果或错误信息,以及与请求相匹配的 ID。
当前最新的 JSON-RPC 协议版本是 2.0。MCP 选择了 JSON-RPC 2.0 协议作为通信基础。
2.2.4 JSON-RPC 2.0 协议规范
一、请求对象
JSON-RPC 2.0 的请求对象包含以下成员:
jsonrpc:字符串,指定 JSON-RPC 协议的版本,必须精确地为 “2.0”method:字符串,包含要调用的方法名称params:对象或数组,包含传递给方法的参数(可选)id:字符串、数字或 null,用于将请求与响应关联起来
请求对象主要有两类:
- 通知请求
通知请求不带 id 参数,不关心响应内容,只用于单方面传递信息给服务端,而无需等待和理解服务端的响应内容。
通知请求示例:
{ "method": "notifications/initialized", "jsonrpc": "2.0"}- 待响应请求
待响应请求,需要带上 id 参数,等待服务端的响应内容。服务端必须返回跟请求 id 匹配的响应内容。
待响应请求示例:
{ "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "mcp-client", "version": "1.0.0" } }, "jsonrpc": "2.0", "id": 0}二、响应对象
当接收到 RPC 调用时,服务器必须回复一个响应对象,但通知请求除外。响应对象包含以下成员:
jsonrpc:字符串,指定 JSON-RPC 协议的版本,必须精确地为 “2.0”result:成功时必须包含此成员,包含方法调用的结果error:出错时必须包含此成员,包含错误信息id:必须包含此成员,必须与请求对象中的 id 值相同
响应对象中的 result 和 error 成员必须且只能包含其中一个。
- 成功响应
服务端处理请求成功,返回一个成功响应,响应的 id 与请求的 id 保持一致。在 result 里面返回请求的结果数据。
成功响应示例:
{ "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {} }, "serverInfo": { "name": "mcp-server", "version": "0.0.3" } }}- 失败响应
服务端处理请求失败,通过 error 字段返回错误对象。返回的 id 也需要跟请求的 id 保持一致。
{ "jsonrpc": "2.0", "error": { "code": -32602, "message": "Invalid params " }, "id": 0}三、错误对象
当 RPC 调用遇到错误时,响应对象必须包含一个 error 成员,其值是一个具有以下成员的对象:
code:错误码,表示错误类型的数字,必须是整数message:错误消息,简短描述错误的字符串data:包含关于错误的附加信息(可选)
JSON-RPC 2.0 预定义了一批错误码,如下:
| 错误码 | 消息 | 含义 |
|---|---|---|
| -32700 | Parse error | 服务器收到无效的 JSON |
| -32600 | Invalid Request | 发送的 JSON 不是有效的请求对象 |
| -32601 | Method not found | 方法不存在或不可用 |
| -32602 | Invalid params | 无效的方法参数 |
| -32603 | Internal error | JSON-RPC 内部错误 |
| -32000 到 -32099 | Server error | 保留给实现定义的服务器错误 |
四、批处理
客户端可以同时发送多个请求对象,将它们放在一个数组中。服务器应该用一个包含相应响应对象的数组来响应,但对于通知请求不应该有响应对象。
批处理请求示例
[ { "jsonrpc": "2.0", "method": "sum", "params": [1, 2, 4], "id": "1" }, { "jsonrpc": "2.0", "method": "notify_hello", "params": [7] }, { "jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": "2" }]批处理响应示例
[ { "jsonrpc": "2.0", "result": 7, "id": "1" }, { "jsonrpc": "2.0", "result": 19, "id": "2" }]