实现JSON-RPC 2.0 - MCP底层消息协议

271 阅读3分钟
  • MCP 底层消息协议是怎么实现的?

image.png

从 MCP 的官方文档可以看到

All transports use JSON-RPC 2.0 to exchange messages.

MCP 底层使用了 JSON-RPC 2.0 协议,它是一种轻量级的 RPC(远程过程调用)协议,使用 JSON(JavaScript Object Notation)作为数据格式。它的规范非常简单,www.jsonrpc.org/specificati… ,只有几个关键字,这也是 MCP 选择此协议的原因,在原来的基础上只需要很少的适配即可使用,更复杂的协议例如 protobuf,虽然效率更高,但是适配起来大量工作,调试也不如 Json 方便,在大模型发展日新月异的时代,MCP 还需要不断完善,它需要一个足够简单和通用的协议来快速推广。

然而直接使用 Json 容易出错,它过于灵活,需要封装一下,让它用起来和 protobuf 一样简单。MCP 官方开源了多种语言的 SDK,golang 有 mcp-go,常用的语言中 cpp 没有好用的 SDK,为了弥补这个空缺,我们使用 cpp 来实现JSON-RPC 2.0 协议

基本结构

JSON-RPC 2.0 的消息结构非常简单,主要分为请求(Request object)和响应(Response object)两个部分,多个请求和多个响应又组成批量(Batch)请求和批量(Batch)响应

请求

请求包含四个字段:

  • jsonrpc:指定 JSON-RPC 的版本,必须是字符串 "2.0"。
  • method:要调用的方法名称,字符串类型。
  • params:传递给方法的参数,可以是数组或对象。
  • id:请求的唯一标识符,可以是字符串、数字或 null。服务器在响应时会返回相同的 id。

如果请求不包含id字段,那么它就是一个通知(Notification),通知不需要响应,定义请求

class Request {
 public:
  Request(std::string jsonrpc_version, std::string method, Parameter params, Identifier id);

  Status ParseJson(const Json& json);

  [[nodiscard]] Json ToJson() const;

  [[nodiscard]] bool IsInternalMethod() const;

  [[nodiscard]] bool IsNotification() const;

 private:
  std::string jsonrpc_version_ = kJsonRpcVersion;
  std::string method_;
  Parameter params_;
  Identifier id_;
};

响应

用于服务端返回结果,分为正常和错误结果两种

正常结果

  • jsonrpc:协议版本,必须是字符串 "2.0"。
  • result:调用方法的返回结果。
  • id:与请求对象中的id一致,用于匹配响应。

错误结果

  • jsonrpc:协议版本,必须是字符串 "2.0"。
  • error:包含错误信息的对象。
    • code:标准错误码。
    • message:错误描述信息。
    • data:可选的额外错误信息。
  • id:如果无法解析请求或无法响应,则id设为null
class Response {
 public:
  explicit Response(Identifier id) : id_(std::move(id)) {};

  [[nodiscard]] Json ToJson() const;

  void SetError(Error error) {
    error_ = std::move(error);
  }

  void SetResult(Json result) {
    result_ = std::move(result);
  }

  void SetId(Identifier id) {
    id_ = std::move(id);
  }

 private:
  std::string jsonrpc_version_ = kJsonRpcVersion;
  Json result_;
  Error error_;
  Identifier id_;
};

可以看到响应和结果都必须包含协议版本等公共的字段,并且有特定的格式,封装可以减少很多重复工作,关键字可以定义一些常量,避免使用字符串可能用错不一致的问题

constexpr auto kJsonRpcVersionName = "jsonrpc";
constexpr auto kMethodName = "method";
constexpr auto kParamsName = "params";
constexpr auto kIdName = "id";
constexpr auto kResultName = "result";
constexpr auto kErrorName = "error";
constexpr auto kCodeName = "code";
constexpr auto kMessageName = "message";
constexpr auto kDataName = "data";

开源地址

github.com/mcp-cpp/jso…