模型上下文协议(MCP)建立在一个灵活、可扩展的架构之上,旨在实现大型语言模型(LLM)应用程序与集成之间的无缝通信。本文档涵盖了核心架构组件和概念。

概述

MCP 遵循客户端-服务器架构,其中
  • 主机是发起连接的大型语言模型应用程序(如 Claude Desktop 或 IDE)
  • 客户端在主机应用程序内部,与服务器保持 1:1 连接
  • 服务器向客户端提供上下文、工具和提示

核心组件

协议层

协议层处理消息分帧、请求/响应链接以及高级通信模式。
class Protocol<Request, Notification, Result> {
  // Handle incoming requests
  setRequestHandler<T>(
    schema: T,
    handler: (request: T, extra: RequestHandlerExtra) => Promise<Result>,
  ): void;

  // Handle incoming notifications
  setNotificationHandler<T>(
    schema: T,
    handler: (notification: T) => Promise<void>,
  ): void;

  // Send requests and await responses
  request<T>(request: Request, schema: T, options?: RequestOptions): Promise<T>;

  // Send one-way notifications
  notification(notification: Notification): Promise<void>;
}
关键类包括
  • Protocol
  • 客户端
  • 服务器

传输层

传输层负责客户端和服务器之间的实际通信。MCP 支持多种传输机制
  1. Stdio 传输
    • 使用标准输入/输出进行通信
    • 适用于本地进程
  2. 可流式 HTTP 传输
    • 使用 HTTP,并可选地使用服务器发送事件(Server-Sent Events)进行流式传输
    • 使用 HTTP POST 发送客户端到服务器的消息
所有传输都使用 JSON-RPC 2.0 来交换消息。有关模型上下文协议消息格式的详细信息,请参见规范

消息类型

MCP 具有以下主要消息类型
  1. 请求期望从另一方获得响应
    interface Request {
      method: string;
      params?: { ... };
    }
    
  2. 结果是对请求的成功响应
    interface Result {
      [key: string]: unknown;
    }
    
  3. 错误表示请求失败
    interface Error {
      code: number;
      message: string;
      data?: unknown;
    }
    
  4. 通知是单向消息,不期望响应
    interface Notification {
      method: string;
      params?: { ... };
    }
    

连接生命周期

1. 初始化

  1. 客户端发送包含协议版本和功能的 initialize 请求
  2. 服务器以其协议版本和功能进行响应
  3. 客户端发送 initialized 通知作为确认
  4. 正常的消息交换开始

2. 消息交换

初始化后,支持以下模式
  • 请求-响应:客户端或服务器发送请求,另一方响应
  • 通知:任一方发送单向消息

3. 终止

任一方都可以终止连接
  • 通过 close() 进行干净关停
  • 传输层断开连接
  • 错误条件

错误处理

MCP 定义了以下标准错误代码
enum ErrorCode {
  // Standard JSON-RPC error codes
  ParseError = -32700,
  InvalidRequest = -32600,
  MethodNotFound = -32601,
  InvalidParams = -32602,
  InternalError = -32603,
}
SDK 和应用程序可以定义自己的高于 -32000 的错误代码。 错误通过以下方式传播:
  • 对请求的错误响应
  • 传输层上的错误事件
  • 协议级错误处理程序

实现示例

以下是实现 MCP 服务器的基本示例
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

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

// Handle requests
server.setRequestHandler(ListResourcesRequestSchema, async () => {
  return {
    resources: [
      {
        uri: "example://resource",
        name: "Example Resource",
      },
    ],
  };
});

// Connect transport
const transport = new StdioServerTransport();
await server.connect(transport);

最佳实践

传输选择

  1. 本地通信
    • 对本地进程使用 stdio 传输
    • 对于同一台机器上的通信效率高
    • 简单的进程管理
  2. 远程通信
    • 在需要 HTTP 兼容性的场景中使用可流式 HTTP
    • 考虑包括身份验证和授权在内的安全影响

消息处理

  1. 请求处理
    • 彻底验证输入
    • 使用类型安全的模式
    • 优雅地处理错误
    • 实现超时
  2. 进度报告
    • 对长时间操作使用进度令牌
    • 增量报告进度
    • 在已知时包含总进度
  3. 错误管理
    • 使用适当的错误代码
    • 包含有用的错误消息
    • 在出错时清理资源

安全注意事项

  1. 传输安全
    • 对远程连接使用 TLS
    • 验证连接来源
    • 在需要时实现身份验证
  2. 消息验证
    • 验证所有传入消息
    • 清理输入
    • 检查消息大小限制
    • 验证 JSON-RPC 格式
  3. 资源保护
    • 实施访问控制
    • 验证资源路径
    • 监控资源使用情况
    • 对请求进行速率限制
  4. 错误处理
    • 不要泄露敏感信息
    • 记录与安全相关的错误
    • 实施正确的清理
    • 处理 DoS 场景

调试与监控

  1. 日志记录
    • 记录协议事件
    • 跟踪消息流
    • 监控性能
    • 记录错误
  2. 诊断
    • 实施健康检查
    • 监控连接状态
    • 跟踪资源使用情况
    • 分析性能
  3. 测试
    • 测试不同的传输方式
    • 验证错误处理
    • 检查边缘情况
    • 对服务器进行负载测试