MCP 协议

151 阅读9分钟

介绍

复制页面

开始使用模型上下文协议 (MCP)

C# SDK 已发布!快来看看还有什么新功能吧。

MCP 是一个开放协议,它规范了应用程序向 LLM 提供上下文的方式。MCP 就像 AI 应用程序的 USB-C 端口一样。正如 USB-C 提供了一种标准化的方式将您的设备连接到各种外围设备和配件一样,MCP 也提供了一种标准化的方式将 AI 模型连接到不同的数据源和工具。

为什么选择 MCP?

MCP 可帮助您在 LLM 之上构建代理和复杂的工作流。LLM 通常需要与数据和工具集成,而 MCP 可提供以下功能:

  • 越来越多的预建集成可供您的 LLM 直接插入
  • 在 LLM 提供商和供应商之间切换的灵活性
  • 保护基础架构内数据的最佳实践

总体架构

MCP 的核心遵循客户端-服务器架构,其中主机应用程序可以连接到多个服务器:

image.png

  • MCP 主机:像 Claude Desktop、IDE 或 AI 工具这样的程序,需要通过 MCP 访问数据
  • MCP 客户端:与服务器保持 1:1 连接的协议客户端
  • MCP 服务器:轻量级程序,每个程序都通过标准化模型上下文协议公开特定功能
  • 本地数据源:MCP 服务器可以安全访问的您计算机上的文件、数据库和服务
  • 远程服务:MCP 服务器可以通过互联网(例如通过 API)连接到的外部系统

核心架构

复制页面

了解 MCP 如何连接客户端、服务器和 LLM

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

概述

MCP 遵循客户端-服务器架构,其中:

  • 主机是发起连接的 LLM 应用程序(如 Claude Desktop 或 IDE)

  • 客户端在主机应用程序内部与服务器保持 1:1 连接

  • 服务器向客户提供上下文、工具和提示

image.png

核心组件

协议层

协议层处理消息框架、请求/响应链接和高级通信模式。

  • TypeScript
  • Python

复制

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

传输层

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

  1. Stdio 传输

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

    • 使用服务器发送事件来发送服务器到客户端的消息
    • 客户端到服务器消息的 HTTP POST

所有传输均使用JSON-RPC 2.0 交换消息。有关模型上下文协议 (MCP) 消息格式的详细信息,请参阅规范。

消息类型

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.初始化

image.png

  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 服务器的基本示例:

  • TypeScript
  • Python

复制

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 兼容性的场景使用 SSE
    • 考虑安全影响,包括身份验证和授权

消息处理

  1. 请求处理

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

    • 对长时间操作使用进度标记
    • 逐步报告进度
    • 已知时包括总体进度
  3. 错误管理

    • 使用适当的错误代码
    • 包含有用的错误消息
    • 发生错误时清理资源

安全注意事项

  1. 运输安全

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

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

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

    • 不要泄露敏感信息
    • 记录与安全相关的错误
    • 实施适当的清理
    • 处理 DoS 场景

调试和监控

  1. 日志记录

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

    • 实施健康检查
    • 监视连接状态
    • 跟踪资源使用情况
    • 个人资料表现
  3. 测试

    • 测试不同的传输方式
    • 验证错误处理
    • 检查边缘情况
    • 负载测试服务器

传输

了解 MCP 的通信机制

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

消息格式

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

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

请求

复制

{
  jsonrpc: "2.0",
  id: number | string,
  method: string,
  params?: object
}

回应

复制

{
  jsonrpc: "2.0",
  id: number | string,
  result?: object,
  error?: {
    code: number,
    message: string,
    data?: unknown
  }
}

通知

复制

{
  jsonrpc: "2.0",
  method: string,
  params?: object
}

内置传输类型

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

标准输入/输出(stdio)

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

在以下情况下使用 stdio:

  • 构建命令行工具
  • 实施本地整合
  • 需要简单的流程沟通
  • 使用 Shell 脚本
  • TypeScript(服务器)
  • TypeScript(客户端)
  • Python(服务器)
  • Python(客户端)

复制

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

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

服务器发送事件 (SSE)

SSE 传输通过 HTTP POST 请求实现服务器到客户端的流式传输,从而实现客户端到服务器的通信。

在以下情况下使用 SSE:

  • 仅需要服务器到客户端的流式传输
  • 使用受限网络
  • 实现简单更新
安全警告:DNS重新绑定攻击

如果没有妥善保护,SSE 传输可能容易受到 DNS 重新绑定攻击。为了防止这种情况发生:

  1. 始终验证传入 SSE 连接上的 Origin 标头,以确保它们来自预期的来源
  2. ****在本地运行时,避免将服务器绑定到所有网络接口(0.0.0.0) - 仅绑定到本地主机(127.0.0.1)
  3. ****为所有 SSE 连接实施适当的身份验证

如果没有这些保护措施,攻击者可以使用 DNS 重新绑定从远程网站与本地 MCP 服务器进行交互。

  • TypeScript(服务器)
  • TypeScript(客户端)
  • Python(服务器)
  • Python(客户端)

复制

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

定制运输

MCP 可以轻松实现针对特定需求的自定义传输。任何传输实现只需遵循 Transport 接口即可:

您可以为以下对象实现自定义传输:

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

复制

interface Transport {
  // Start processing messages
  start(): Promise<void>;

  // Send a JSON-RPC message
  send(message: JSONRPCMessage): Promise<void>;

  // Close the connection
  close(): Promise<void>;

  // Callbacks
  onclose?: () => void;
  onerror?: (error: Error) => void;
  onmessage?: (message: JSONRPCMessage) => void;
}

错误处理

传输实现应该处理各种错误情况:

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

错误处理示例:

  • TypeScript
  • Python

复制

class ExampleTransport implements Transport {
  async start() {
    try {
      // Connection logic
    } catch (error) {
      this.onerror?.(new Error(`Failed to connect: ${error}`));
      throw error;
    }
  }

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

最佳实践

实施或使用 MCP 传输时:

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

安全注意事项

实施运输时:

身份验证和授权

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

数据安全

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

网络安全

  • 实施速率限制
  • 使用适当的超时
  • 处理拒绝服务情况
  • 监控异常模式
  • 实施适当的防火墙规则
  • 对于 SSE 传输,验证 Origin 标头以防止 DNS 重新绑定攻击
  • 对于本地 SSE 服务器,仅绑定到本地主机 (127.0.0.1),而不是所有接口 (0.0.0.0)

调试传输

调试传输问题的提示:

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