Skip to content

通讯机制

了解 MCP 的通信机制

模型上下文协议 (MCP) 中的传输为客户端和服务器之间的通信提供了基础。传输处理消息发送和接收方式的基本机制。

一、消息格式

MCP 使用 JSON-RPC 2.0 作为其通讯格式。传输层负责将 MCP 协议消息转换为 JSON-RPC 格式进行传输,并将收到的 JSON-RPC 消息转换回 MCP 协议消息。

《模型上下文协议中文网》附注:json-rpc理论上是存在性能瓶颈的,不如gRPC。但也能理解Anthropic为什么一开始优先支持json-rpc,因为MCP协议正常是面向AI客户端场景的。一般而言用户客户端确实不会存在太大并发,除非超大尺寸的数据交互,而json对开发者的普适性远超一切。

使用三种类型的 JSON-RPC 消息:

1、请求

json
{
  jsonrpc: "2.0",      // 声明使用 JSON-RPC 2.0 协议
  id: number | string, // 请求的唯一标识符,可以是数字或字符串,用于匹配响应
  method: string,      // 需要调用的方法名称
  params?: object      // 可选的参数对象,传递给方法的参数
}

2、响应

json
{
  jsonrpc: "2.0",   // 声明使用 JSON-RPC 2.0 协议
  id: number | string,  // 请求的唯一标识符,与请求中的 id 对应,用于匹配请求和响应
  result?: object,   // 可选,表示调用成功时的返回结果
  error?: {         // 可选,表示调用失败时的错误信息
    code: number,   // 错误代码,表示错误类型
    message: string, // 错误描述信息
    data?: unknown  // 可选,包含额外的错误信息,类型不固定
  }
}

3、通知

json
{
  jsonrpc: "2.0",   // 声明使用 JSON-RPC 2.0 协议
  method: string,   // 需要调用的通知方法名称
  params?: object   // 可选,传递给方法的参数
}

二、内置通讯类型

MCP 包括两个标准传输实现:

1、标准输入/输出 (stdio)

stdio 传输支持通过标准输入和输出流进行通信。这对于本地集成和命令行工具特别有用。

在以下情况下使用 stdio:

  • 构建命令行工具
  • 实施本地集成
  • 需要简单的过程通信
  • 使用 shell 脚本
TypeScript范例 (服务器)
typescript
const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {}
});

const transport = new StdioServerTransport();
await server.connect(transport);
TypeScript范例 (客户端)
typescript
const client = new Client({
  name: "example-client",
  version: "1.0.0"
}, {
  capabilities: {}
});

const transport = new StdioClientTransport({
  command: "./server",
  args: ["--option", "value"]
});
await client.connect(transport);

2、服务器发送的事件 (SSE)

SSE 传输支持使用 HTTP POST 请求进行服务器到客户端流式处理,以实现客户端到服务器的通信。

在以下情况下使用 SSE:

  • 只需要服务器到客户端流式传输
  • 使用受限网络
  • 实施简单更新
TypeScript范例 (服务器)
typescript
import express from "express";

const app = express();

const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {}
});

let transport: SSEServerTransport | null = null;

app.get("/sse", (req, res) => {
  transport = new SSEServerTransport("/messages", res);
  server.connect(transport);
});

app.post("/messages", (req, res) => {
  if (transport) {
    transport.handlePostMessage(req, res);
  }
});

app.listen(3000);
TypeScript范例 (客户端)
typescript
const client = new Client({
  name: "example-client",
  version: "1.0.0"
}, {
  capabilities: {}
});

const transport = new SSEClientTransport(
  new URL("http://localhost:3000/sse")
);
await client.connect(transport);

三、自定义通讯

MCP 可以轻松实现针对特定需求的自定义通讯机制,比如以下场景:

  • 自定义网络协议
  • 专门的沟通渠道
  • 与现有系统集成
  • 性能优化

只需要实现 Transport 接口:

TypeScript范例
typescript
interface Transport {
  // 开始处理消息
  start(): Promise<void>;

  // 发送一条JSON-RPC消息
  send(message: JSONRPCMessage): Promise<void>;

  // 关闭连接
  close(): Promise<void>;

  // 定义回调
  onclose?: () => void;
  onerror?: (error: Error) => void;
  onmessage?: (message: JSONRPCMessage) => void;
}

四、错误处理

Transport 实现应处理各种错误场景:

  1. 连接错误
  2. 消息解析错误
  3. 协议错误
  4. 网络超时
  5. 资源清理

错误处理示例:

TypeScript范例
typescript
class ExampleTransport implements Transport {
  async start() {
    try {
      // 连接逻辑
    } catch (error) {
      this.onerror?.(new Error(`Failed to connect: ${error}`));
      throw error;
    }
  }

  async send(message: JSONRPCMessage) {
    try {
      // 发送逻辑
    } catch (error) {
      this.onerror?.(new Error(`Failed to send message: ${error}`));
      throw error;
    }
  }
}

其他

1、最佳实践

实施或使用 MCP 传输时:

  1. 正确处理连接生命周期
  2. 实施适当的错误处理
  3. 在连接关闭时清理资源
  4. 使用适当的超时
  5. 发送前验证消息
  6. 用于调试的日志传输事件
  7. 在适当的时候实施重新连接逻辑
  8. 处理消息队列中的背压
  9. 监控连接运行状况
  10. 实施适当的安全措施

2、安全注意事项

实施 transport 时:

身份验证和授权

  • 实施适当的身份验证机制
  • 验证客户端凭据
  • 使用安全令牌处理
  • 实施授权检查

数据安全

  • 使用 TLS 进行网络传输
  • 加密敏感数据
  • 验证消息完整性
  • 实施消息大小限制
  • 清理输入数据

网络安全

  • 实施速率限制
  • 使用适当的超时
  • 处理拒绝服务场景
  • 监控异常模式
  • 实施适当的防火墙规则

3、调试传输

调试传输问题的提示:

  1. 启用调试日志记录
  2. 监视消息流
  3. 检查连接状态
  4. 验证消息格式
  5. 测试错误场景
  6. 使用网络分析工具
  7. 实施运行状况检查
  8. 监控资源使用情况
  9. 测试边缘案例
  10. 使用适当的错误跟踪