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

80 阅读12分钟

核心架构

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

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

概述

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

  • 主机是发起连接的 LLM 应用程序(如 Claude Desktop 或 IDE)
  • 客户端在主机应用程序内部与服务器保持 1:1 连接
  • 服务器向客户提供上下文、工具和提示

Server Process

Server Process

Host

Transport Layer

Transport Layer

MCP Client

MCP Client

MCP Server

MCP Server

核心组件

协议层

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

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

ServerClientServerClientConnection ready for useinitialize requestinitialize responseinitialized notification

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

理解 MCP 中的根源

根是 MCP 中的一个概念,它定义了服务器可以运行的边界。它为客户端提供了一种方式,可以告知服务器相关资源及其位置。

什么是根?

根是客户端建议服务器关注的 URI。当客户端连接到服务器时,它会声明服务器应该使用哪些根。虽然根主要用于文件系统路径,但它可以是任何有效的 URI,包括 HTTP URL。

例如,根可以是:

file:///home/user/projects/myapp
https://api.example.com/v1

为什么要使用 Roots?

根有几个重要用途:

  1. 指导:它们向服务器告知相关资源和位置
  2. 清晰度:根目录明确哪些资源属于您的工作空间
  3. 组织:多个根使您可以同时使用不同的资源

根如何工作

当客户端支持根时,它:

  1. roots在连接期间声明能力
  2. 向服务器提供建议的根列表
  3. 当根目录发生改变时通知服务器(如果支持)

虽然根是信息性的且不严格执行,但服务器应该:

  1. 尊重提供的根
  2. 使用根 URI 来定位和访问资源
  3. 优先考虑根边界内的操作

常见用例

根通常用于定义:

  • 项目目录
  • 存储库位置
  • API 端点
  • 配置位置
  • 资源边界

最佳实践

处理根时:

  1. 仅建议必要的资源
  2. 使用清晰、描述性的名称来命名根
  3. 监控根可访问性
  4. 妥善处理根更改

例子

以下是典型的 MCP 客户端可能暴露根的方式:

{
  "roots": [
    {
      "uri": "file:///home/user/projects/frontend",
      "name": "Frontend Repository"
    },
    {
      "uri": "https://api.example.com/v1",
      "name": "API Endpoint"
    }
  ]
}

此配置建议服务器关注本地存储库和 API 端点,同时保持它们在逻辑上分离。

最佳实践

运输选择

  1. 本地沟通

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

    • 对于需要 HTTP 兼容性的场景使用 SSE
    • 考虑安全影响,包括身份验证和授权

消息处理

  1. 请求处理

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

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

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

安全注意事项

  1. 运输安全

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

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

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

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

调试和监控

  1. 日志记录

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

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

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

资源

将服务器中的数据和内容公开给 LLM

资源是模型上下文协议 (MCP) 中的核心原语,允许服务器公开可由客户端读取并用作 LLM 交互上下文的数据和内容。

资源被设计为由应用程序控制,这意味着客户端应用程序可以决定如何以及何时使用它们。不同的 MCP 客户端可能会以不同的方式处理资源。例如:

  • Claude Desktop 目前要求用户明确选择资源才能使用
  • 其他客户端可能会根据启发式方法自动选择资源
  • 一些实现甚至可能允许人工智能模型本身决定使用哪些资源

在实现资源支持时,服务器开发者应该准备好处理这些交互模式。为了自动向模型公开数据,服务器开发者应该使用模型控制的原语,例如Tools

概述

资源是指 MCP 服务器希望向客户端提供的任何类型的数据。这些可以包括:

  • 文件内容
  • 数据库记录
  • API 响应
  • 实时系统数据
  • 截图和图片
  • 日志文件
  • 以及更多

每个资源由唯一的 URI 标识,并且可以包含文本或二进制数据。

资源 URI

资源使用遵循以下格式的 URI 进行标识:

[protocol]://[host]/[path]

例如:

  • file:///home/user/documents/report.pdf
  • postgres://database/customers/schema
  • screen://localhost/display1

协议和路径结构由 MCP 服务器实现定义。服务器可以定义自己的自定义 URI 方案。

资源类型

资源可以包含两种类型的内容:

文本资源

文本资源包含 UTF-8 编码的文本数据。这些资源适用于:

  • 源代码
  • 配置文件
  • 日志文件
  • JSON/XML 数据
  • 纯文本

二进制资源

二进制资源包含以 Base64 编码的原始二进制数据。这些资源适用于:

  • 图片
  • PDF
  • 音频文件
  • 视频文件
  • 其他非文本格式

资源发现

客户端可以通过两种主要方法发现可用资源:

直接资源

服务器通过端点公开具体资源列表resources/list。每个资源包括:

{
  uri: string;           // Unique identifier for the resource
  name: string;          // Human-readable name
  description?: string;  // Optional description
  mimeType?: string;     // Optional MIME type
}

资源模板

对于动态资源,服务器可以公开URI 模板,客户端可以使用它来构建有效的资源 URI:

{
  uriTemplate: string;   // URI template following RFC 6570
  name: string;          // Human-readable name for this type
  description?: string;  // Optional description
  mimeType?: string;     // Optional MIME type for all matching resources
}

阅读资源

要读取资源,客户端需要resources/read使用资源 URI 发出请求。

服务器以资源内容列表进行响应:

{
  contents: [
    {
      uri: string;        // The URI of the resource
      mimeType?: string;  // Optional MIME type

      // One of:
      text?: string;      // For text resources
      blob?: string;      // For binary resources (base64 encoded)
    }
  ]
}

服务器可能会在一次请求中返回多个资源resources/read。例如,当读取目录时,可以返回目录中的文件列表。

资源更新

MCP通过两种机制支持资源的实时更新:

列出更改

当服务器可用资源列表发生变化时,服务器可以通过notifications/resources/list_changed通知通知客户端。

内容变更

客户可以订阅特定资源的更新:

  1. 客户端发送resources/subscribe资源 URI
  2. notifications/resources/updated资源发生变化时服务器发送
  3. 客户端可以通过以下方式获取最新内容resources/read
  4. 客户端可以取消订阅resources/unsubscribe

示例实现

以下是在 MCP 服务器中实现资源支持的简单示例:

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

// List available resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
  return {
    resources: [
      {
        uri: "file:///logs/app.log",
        name: "Application Logs",
        mimeType: "text/plain"
      }
    ]
  };
});

// Read resource contents
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  const uri = request.params.uri;

  if (uri === "file:///logs/app.log") {
    const logContents = await readLogFile();
    return {
      contents: [
        {
          uri,
          mimeType: "text/plain",
          text: logContents
        }
      ]
    };
  }

  throw new Error("Resource not found");
});

提示

创建可重复使用的提示模板和工作流程

提示使服务器能够定义可重复使用的提示模板和工作流程,客户端可以轻松地将其呈现给用户和 LLM。它们提供了一种强大的方法来标准化和共享常见的 LLM 交互。

提示被设计为由用户控制,这意味着它们从服务器暴露给客户端,目的是让用户能够明确地选择它们来使用。

概述

MCP 中的提示是预定义的模板,可以:

  • 接受动态参数
  • 包括资源中的上下文
  • 链接多个交互
  • 指导具体的工作流程
  • 表面作为 UI 元素(如斜线命令)

提示结构

每个提示定义为:

{
  name: string;              // Unique identifier for the prompt
  description?: string;      // Human-readable description
  arguments?: [              // Optional list of arguments
    {
      name: string;          // Argument identifier
      description?: string;  // Argument description
      required?: boolean;    // Whether argument is required
    }
  ]
}

发现提示

客户端可以通过prompts/list端点发现可用的提示:

// Request
{
  method: "prompts/list"
}

// Response
{
  prompts: [
    {
      name: "analyze-code",
      description: "Analyze code for potential improvements",
      arguments: [
        {
          name: "language",
          description: "Programming language",
          required: true
        }
      ]
    }
  ]
}

使用提示

要使用提示,客户端需要提出prompts/get请求:

// Request
{
  method: "prompts/get",
  params: {
    name: "analyze-code",
    arguments: {
      language: "python"
    }
  }
}

// Response
{
  description: "Analyze Python code for potential improvements",
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: "Please analyze the following Python code for potential improvements:\n\n```python\ndef calculate_sum(numbers):\n    total = 0\n    for num in numbers:\n        total = total + num\n    return total\n\nresult = calculate_sum([1, 2, 3, 4, 5])\nprint(result)\n```"
      }
    }
  ]
}

动态提示

提示可以是动态的,包括:

嵌入式资源上下文

{
  "name": "analyze-project",
  "description": "Analyze project logs and code",
  "arguments": [
    {
      "name": "timeframe",
      "description": "Time period to analyze logs",
      "required": true
    },
    {
      "name": "fileUri",
      "description": "URI of code file to review",
      "required": true
    }
  ]
}

处理prompts/get请求时:

{
  "messages": [
    {
      "role": "user",
      "content": {
        "type": "text",
        "text": "Analyze these system logs and the code file for any issues:"
      }
    },
    {
      "role": "user",
      "content": {
        "type": "resource",
        "resource": {
          "uri": "logs://recent?timeframe=1h",
          "text": "[2024-03-14 15:32:11] ERROR: Connection timeout in network.py:127\n[2024-03-14 15:32:15] WARN: Retrying connection (attempt 2/3)\n[2024-03-14 15:32:20] ERROR: Max retries exceeded",
          "mimeType": "text/plain"
        }
      }
    },
    {
      "role": "user",
      "content": {
        "type": "resource",
        "resource": {
          "uri": "file:///path/to/code.py",
          "text": "def connect_to_service(timeout=30):\n    retries = 3\n    for attempt in range(retries):\n        try:\n            return establish_connection(timeout)\n        except TimeoutError:\n            if attempt == retries - 1:\n                raise\n            time.sleep(5)\n\ndef establish_connection(timeout):\n    # Connection implementation\n    pass",
          "mimeType": "text/x-python"
        }
      }
    }
  ]
}

多步骤工作流程

const debugWorkflow = {
  name: "debug-error",
  async getMessages(error: string) {
    return [
      {
        role: "user",
        content: {
          type: "text",
          text: `Here's an error I'm seeing: ${error}`
        }
      },
      {
        role: "assistant",
        content: {
          type: "text",
          text: "I'll help analyze this error. What have you tried so far?"
        }
      },
      {
        role: "user",
        content: {
          type: "text",
          text: "I've tried restarting the service, but the error persists."
        }
      }
    ];
  }
};

示例实现

以下是在 MCP 服务器中实现提示的完整示例:

  • TypeScript
  • Python
import { Server } from "@modelcontextprotocol/sdk/server";
import {
  ListPromptsRequestSchema,
  GetPromptRequestSchema
} from "@modelcontextprotocol/sdk/types";

const PROMPTS = {
  "git-commit": {
    name: "git-commit",
    description: "Generate a Git commit message",
    arguments: [
      {
        name: "changes",
        description: "Git diff or description of changes",
        required: true
      }
    ]
  },
  "explain-code": {
    name: "explain-code",
    description: "Explain how code works",
    arguments: [
      {
        name: "code",
        description: "Code to explain",
        required: true
      },
      {
        name: "language",
        description: "Programming language",
        required: false
      }
    ]
  }
};

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

// List available prompts
server.setRequestHandler(ListPromptsRequestSchema, async () => {
  return {
    prompts: Object.values(PROMPTS)
  };
});

// Get specific prompt
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
  const prompt = PROMPTS[request.params.name];
  if (!prompt) {
    throw new Error(`Prompt not found: ${request.params.name}`);
  }

  if (request.params.name === "git-commit") {
    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `Generate a concise but descriptive commit message for these changes:\n\n${request.params.arguments?.changes}`
          }
        }
      ]
    };
  }

  if (request.params.name === "explain-code") {
    const language = request.params.arguments?.language || "Unknown";
    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `Explain how this ${language} code works:\n\n${request.params.arguments?.code}`
          }
        }
      ]
    };
  }

  throw new Error("Prompt implementation not found");
});

工具

启用 LLM 通过您的服务器执行操作

工具是模型上下文协议 (MCP) 中一个强大的原语,它使服务器能够向客户端公开可执行功能。通过工具,LLM 可以与外部系统交互、执行计算并在现实世界中采取行动。

工具被设计为模型控制,这意味着工具从服务器暴露给客户端,目的是让 AI 模型能够自动调用它们(由人工参与批准)。

概述

MCP 中的工具允许服务器公开可执行函数,这些函数可供客户端调用,并由 LLM 用来执行操作。这些工具的关键方面包括:

  • 发现tools/list:客户端可以通过端点列出可用的工具
  • 调用:使用端点调用工具tools/call,服务器执行请求的操作并返回结果
  • 灵活性:工具范围从简单的计算到复杂的 API 交互

与资源类似,工具也由唯一名称标识,并可包含用于指导其使用的描述。然而,与资源不同的是,工具代表着动态操作,可以修改状态或与外部系统交互。

工具定义结构

每个工具都按照以下结构定义:

{
  name: string;          // Unique identifier for the tool
  description?: string;  // Human-readable description
  inputSchema: {         // JSON Schema for the tool's parameters
    type: "object",
    properties: { ... }  // Tool-specific parameters
  },
  annotations?: {        // Optional hints about tool behavior
    title?: string;      // Human-readable title for the tool
    readOnlyHint?: boolean;    // If true, the tool does not modify its environment
    destructiveHint?: boolean; // If true, the tool may perform destructive updates
    idempotentHint?: boolean;  // If true, repeated calls with same args have no additional effect
    openWorldHint?: boolean;   // If true, tool interacts with external entities
  }
}

实施工具

以下是在 MCP 服务器中实现基本工具的示例:

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

// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [{
      name: "calculate_sum",
      description: "Add two numbers together",
      inputSchema: {
        type: "object",
        properties: {
          a: { type: "number" },
          b: { type: "number" }
        },
        required: ["a", "b"]
      }
    }]
  };
});

// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "calculate_sum") {
    const { a, b } = request.params.arguments;
    return {
      content: [
        {
          type: "text",
          text: String(a + b)
        }
      ]
    };
  }
  throw new Error("Tool not found");
});

工具图案示例

以下是服务器可以提供的工具类型的一些示例:

系统操作

与本地系统交互的工具:

{
  name: "execute_command",
  description: "Run a shell command",
  inputSchema: {
    type: "object",
    properties: {
      command: { type: "string" },
      args: { type: "array", items: { type: "string" } }
    }
  }
}

API 集成

包装外部 API 的工具:

{
  name: "github_create_issue",
  description: "Create a GitHub issue",
  inputSchema: {
    type: "object",
    properties: {
      title: { type: "string" },
      body: { type: "string" },
      labels: { type: "array", items: { type: "string" } }
    }
  }
}

数据处理

转换或分析数据的工具:

{
  name: "analyze_csv",
  description: "Analyze a CSV file",
  inputSchema: {
    type: "object",
    properties: {
      filepath: { type: "string" },
      operations: {
        type: "array",
        items: {
          enum: ["sum", "average", "count"]
        }
      }
    }
  }
}

错误处理

工具错误应在结果对象中报告,而不是作为 MCP 协议级错误报告。这样,LLM 才能发现并处理错误。当工具遇到错误时:

  1. 在结果中设置isErrortrue
  2. content在数组中包含错误详细信息

以下是工具正确错误处理的示例:

  • TypeScript
  • Python
try {
  // Tool operation
  const result = performOperation();
  return {
    content: [
      {
        type: "text",
        text: `Operation successful: ${result}`
      }
    ]
  };
} catch (error) {
  return {
    isError: true,
    content: [
      {
        type: "text",
        text: `Error: ${error.message}`
      }
    ]
  };
}

这种方法允许 LLM 发现发生的错误并可能采取纠正措施或请求人工干预。

工具注释

工具注释提供有关工具行为的额外元数据,帮助客户端了解如何呈现和管理工具。这些注释只是描述工具性质和影响的提示,不应作为安全决策的依据。

工具注释的用途

工具注释有几个主要用途:

  1. 在不影响模型上下文的情况下提供特定于 UX 的信息
  2. 帮助客户适当分类和展示工具
  3. 传达有关工具潜在副作用的信息
  4. 协助开发直观的工具审批界面

可用的工具注释

MCP 规范为工具定义了以下注释:

注解类型默认描述
title细绳-工具的可读标题,有助于 UI 显示
readOnlyHint布尔值错误的如果为 true,则表示该工具不会修改其环境
destructiveHint布尔值真的如果为真,该工具可能会执行破坏性更新(仅当为readOnlyHint假时才有意义)
idempotentHint布尔值错误的如果为真,则使用相同参数重复调用该工具不会产生额外效果(仅当为readOnlyHint假时才有意义)
openWorldHint布尔值真的如果属实,该工具可能会与外部实体的“开放世界”进行交互

示例用法

以下是针对不同场景使用注释定义工具的方法:

// A read-only search tool
{
  name: "web_search",
  description: "Search the web for information",
  inputSchema: {
    type: "object",
    properties: {
      query: { type: "string" }
    },
    required: ["query"]
  },
  annotations: {
    title: "Web Search",
    readOnlyHint: true,
    openWorldHint: true
  }
}

// A destructive file deletion tool
{
  name: "delete_file",
  description: "Delete a file from the filesystem",
  inputSchema: {
    type: "object",
    properties: {
      path: { type: "string" }
    },
    required: ["path"]
  },
  annotations: {
    title: "Delete File",
    readOnlyHint: false,
    destructiveHint: true,
    idempotentHint: true,
    openWorldHint: false
  }
}

// A non-destructive database record creation tool
{
  name: "create_record",
  description: "Create a new record in the database",
  inputSchema: {
    type: "object",
    properties: {
      table: { type: "string" },
      data: { type: "object" }
    },
    required: ["table", "data"]
  },
  annotations: {
    title: "Create Database Record",
    readOnlyHint: false,
    destructiveHint: false,
    idempotentHint: false,
    openWorldHint: false
  }
}

在服务器实现中集成注释

  • TypeScript
  • Python
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [{
      name: "calculate_sum",
      description: "Add two numbers together",
      inputSchema: {
        type: "object",
        properties: {
          a: { type: "number" },
          b: { type: "number" }
        },
        required: ["a", "b"]
      },
      annotations: {
        title: "Calculate Sum",
        readOnlyHint: true,
        openWorldHint: false
      }
    }]
  };
});

采样

让你的服务器请求 LLM 完成

采样是 MCP 的一项强大功能,允许服务器通过客户端请求 LLM 完成,从而实现复杂的代理行为,同时保持安全性和隐私性。

Claude Desktop 客户端尚不支持 MCP 的此功能。

采样的工作原理

采样流程遵循以下步骤:

  1. sampling/createMessage服务器向客户端发送请求
  2. 客户审核请求并可以修改
  3. 来自法学硕士 (LLM) 的客户样本
  4. 客户检查完成情况
  5. 客户端返回结果给服务器

这种人机交互设计确保用户能够控制 LLM 所看到和生成的内容。

消息格式

采样请求使用标准化的消息格式:

{
  messages: [
    {
      role: "user" | "assistant",
      content: {
        type: "text" | "image",

        // For text:
        text?: string,

        // For images:
        data?: string,             // base64 encoded
        mimeType?: string
      }
    }
  ],
  modelPreferences?: {
    hints?: [{
      name?: string                // Suggested model name/family
    }],
    costPriority?: number,         // 0-1, importance of minimizing cost
    speedPriority?: number,        // 0-1, importance of low latency
    intelligencePriority?: number  // 0-1, importance of capabilities
  },
  systemPrompt?: string,
  includeContext?: "none" | "thisServer" | "allServers",
  temperature?: number,
  maxTokens: number,
  stopSequences?: string[],
  metadata?: Record<string, unknown>
}

请求参数

消息

messages数组包含要发送给 LLM 的对话历史记录。每条消息包含:

  • role:“用户”或“助手”

  • content:消息内容,可以为:

    • text带有字段的文本内容
    • data带有(base64)和mimeType字段的图像内容

模型偏好

modelPreferences对象允许服务器指定其模型选择偏好:

  • hints:客户可以使用它来选择合适型号的一系列型号名称建议:

    • name:可以匹配完整或部分模型名称的字符串(例如“claude-3”,“sonnet”)
    • 客户端可以将提示映射到来自不同提供商的等效模型
    • 多个提示按优先顺序进行评估
  • 优先级值(0-1标准化):

    • costPriority:降低成本的重要性
    • speedPriority:低延迟响应的重要性
    • intelligencePriority:高级模型能力的重要性

客户根据这些偏好和可用的模型做出最终的模型选择。

系统提示

可选systemPrompt字段允许服务器请求特定的系统提示。客户端可以修改或忽略此字段。

上下文包含

includeContext参数指定要包含哪些 MCP 上下文:

  • "none":没有其他背景信息
  • "thisServer":包含来自请求服务器的上下文
  • "allServers":包含所有已连接 MCP 服务器的上下文

客户端控制实际包含的上下文。

采样参数

使用以下方法微调 LLM 采样:

  • temperature:控制随机性(0.0 到 1.0)
  • maxTokens:生成的最大令牌数
  • stopSequences:停止生成的序列数组
  • metadata:其他特定于提供商的参数

响应格式

客户端返回完成结果:

{
  model: string,  // Name of the model used
  stopReason?: "endTurn" | "stopSequence" | "maxTokens" | string,
  role: "user" | "assistant",
  content: {
    type: "text" | "image",
    text?: string,
    data?: string,
    mimeType?: string
  }
}

示例请求

以下是向客户请求采样的示例:

{
  "method": "sampling/createMessage",
  "params": {
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "What files are in the current directory?"
        }
      }
    ],
    "systemPrompt": "You are a helpful file system assistant.",
    "includeContext": "thisServer",
    "maxTokens": 100
  }
}

交通

了解 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. 使用适当的错误跟踪