MCP 是什么? Link to heading

MCP 是一个开放协议,它规范了应用程序向大型语言模型提供上下文的方式。你可以把 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 为你的设备连接各种外设和配件提供了一种标准化的方式一样,MCP 也为 AI 模型连接不同的数据源和工具提供了一种标准化的方式。

MCP 帮助我们在 LLM 之上构建代理和复杂的业务流程。LLM 经常需要与数据和工具集成,MCP 提供了:

  • 让 LLM 可以直接接入一个不断增长的预构建集成服务列表的能力
  • 能够在大型语言模型服务提供商和供应商之间灵活切换
  • 保护基础设施中数据的安全最佳实践

通用架构 Link to heading

MCP 核心采用客户端-服务器(CS)架构,主机应用可以连接多个服务器:

flowchart LR subgraph "Your Computer" Host["Host with MCP Client
(Claude, IDEs, Tools)"] S1["MCP Server A"] S2["MCP Server B"] S3["MCP Server C"] Host <-->|"MCP Protocol"| S1 Host <-->|"MCP Protocol"| S2 Host <-->|"MCP Protocol"| S3 S1 <--> D1[("Local
Data Source A")] S2 <--> D2[("Local
Data Source B")] end subgraph "Internet" S3 <-->|"Web APIs"| D3[("Remote
Service C")] end
  • MCP主机:如 Claude 桌面版、集成开发环境(IDE)或 AI 工具等,需要通过 MCP 访问数据的程序
  • MCP客户端:与服务器保持 1:1 连接的协议客户端
  • MCP服务器:轻量级程序,通过标准化的模型上下文协议(Model Context Protocol)暴露特定功能
  • 本地数据源:MCP 服务器可安全访问的计算机文件、数据库和服务
  • 远程服务:可通过互联网(如 API)连接的外部系统,MCP 服务器可与之交互

核心架构 Link to heading

模型上下文协议(MCP)基于灵活且可扩展的架构,能够实现大型语言模型应用与集成之间的无缝通信。接下来介绍 MCP 核心的架构组件与概念,帮助我们理解 MCP 如何连接客户端、服务器和 LLM。

flowchart LR subgraph "Host" client1[MCP Client] client2[MCP Client] end subgraph "Server Process" server1[MCP Server] end subgraph "Server Process" server2[MCP Server] end client1 <-->|Transport Layer| server1 client2 <-->|Transport Layer| server2

核心组件 Link to heading

协议层 Link to heading

协议层负责消息封装、请求/响应关联以及高级通信模式。

以 Typescript 为例,MCP 协议层可以采用以下方式实现:

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
  • Client
  • Server

传输层 Link to heading

传输层负责客户端与服务器之间的实际通信。MCP 支持多种传输机制:

  1. Stdio 传输

    • 使用标准输入/输出进行通信
    • 非常适合本地进程
  2. HTTP with SSE 传输

    • 使用 Server-Sent Events 实现服务器向客户端的消息传递
    • 使用 HTTP POST 实现客户端向服务器的消息传递

所有传输方式均使用 JSON-RPC 2.0 交换消息。有关 Model Context Protocol 消息格式的详细信息,请参阅 MCP 规范文档

消息 Link to heading

MCP 主要包含以下几种类型的消息:

  1. Requests 向对方发送请求,等待对方的回应:

    interface Request {
      method: string;
      params?: { ... };
    }
    
  2. Results 针对请求的回应:

    interface Result {
      [key: string]: unknown;
    }
    
  3. Errors 表示请求失败:

    interface Error {
      code: number;
      message: string;
      data?: unknown;
    }
    
  4. Notifications 一种单向信息,无需回复:

    interface Notification {
      method: string;
      params?: { ... };
    }
    

连接生命周期 Link to heading

1. 初始化 Link to heading

sequenceDiagram participant Client participant Server Client->>Server: initialize request Server->>Client: initialize response Client->>Server: initialized notification Note over Client,Server: Connection ready for use
  1. 客户端发送 initialize 请求,包含协议版本和功能特性
  2. 服务器响应其协议版本和功能特性
  3. 客户端发送 initialized 通知,表示确认
  4. 正常消息交互开始

2. 消息交换 Link to heading

初始化完成后,支持以下模式:

  • Request-Response:客户端或服务器一方发送请求,另一方进行响应
  • Notifications:任何一方单向发送消息

3. 终止 Link to heading

任何一方都可以终止连接:

  • 通过 close() 进行正常关闭
  • 传输层断开连接
  • 出现错误情况

错误处理 Link to heading

MCP 定义了以下标准错误代码:

enum ErrorCode {
  // Standard JSON-RPC error codes
  ParseError = -32700,
  InvalidRequest = -32600,
  MethodNotFound = -32601,
  InvalidParams = -32602,
  InternalError = -32603
}

SDK 和应用程序可以定义自己的错误码,范围在 -32000 以上。

错误通过以下方式传播:

  • 请求的错误响应
  • 传输层的错误事件
  • 协议层的错误处理器

实现示例 Link to heading

这里有一个实现MCP服务器的简单 Typescript 示例:

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);

对最佳实践的一些建议 Link to heading

对协议实现的一些建议 Link to heading

传输层模式选择 Link to heading

  1. 本地通信

    • 使用 stdio 传输进行本地进程通信
    • 同机通信效率高
    • 进程管理简单
  2. 远程通信

    • 需要 HTTP 兼容的场景使用 SSE
    • 考虑安全影响,包括认证和授权

消息处理 Link to heading

  1. 请求处理

    • 彻底验证输入
    • 使用类型安全的模式
    • 优雅处理错误
    • 实现超时机制
  2. 进度报告

    • 长操作使用进度令牌
    • 分步报告进度
    • 知道总进度时包含在内
  3. 错误管理

    • 使用合适的错误码
    • 提供有价值的错误信息
    • 错误时清理资源

对安全的一些建议 Link to heading

  1. 传输安全

    • 远程连接使用 TLS
    • 验证连接来源
    • 需要时实施认证
  2. 消息验证

    • 验证所有传入消息
    • 消毒输入
    • 检查消息大小限制
    • 验证 JSON-RPC 格式
  3. 资源保护

    • 实施访问控制
    • 验证资源路径
    • 监控资源使用情况
    • 限制请求频率
  4. 错误处理

    • 不泄露敏感信息
    • 记录安全相关的错误
    • 实施正确清理
    • 处理拒绝服务攻击场景

对调试与监控的一些建议 Link to heading

  1. 日志记录

    • 记录协议事件
    • 追踪消息流向
    • 监控性能
    • 记录错误
  2. 诊断

    • 实施健康检查
    • 监控连接状态
    • 追踪资源使用情况
    • 性能分析
  3. 测试

    • 测试不同传输方式
    • 验证错误处理
    • 检查边界情况
    • 对服务器进行压力测试