MCP官方教程(3)核心概念

核心架构

Model Context Protocol (MCP) 构建在一个灵活、可扩展的架构之上,能够实现 LLM 应用程序与集成之间的无缝通信。本文档涵盖了核心架构组件和概念。

概述

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

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

核心组件

协议层

协议层处理消息帧、请求/响应链接以及高级通信模式。

typescript 复制代码
class Protocol<Request, Notification, Result> {
    // 处理传入的请求
    setRequestHandler<T>(schema: T, handler: (request: T, extra: RequestHandlerExtra) => Promise<Result>): void

    // 处理传入的通知
    setNotificationHandler<T>(schema: T, handler: (notification: T) => Promise<void>): void

    // 发送请求并等待响应
    request<T>(request: Request, schema: T, options?: RequestOptions): Promise<T>

    // 发送单向通知
    notification(notification: Notification): Promise<void>
}

关键类包括:

  • Protocol
  • Client
  • Server

传输层

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

  1. Stdio 传输

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

    • 使用 Server-Sent Events 进行服务器到客户端的消息传递
    • 使用 HTTP POST 进行客户端到服务器的消息传递

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

消息类型

MCP 有以下主要类型的消息:

  1. 请求 期望从对方获得响应:
typescript 复制代码
interface Request {
  method: string;
  params?: { ... };
}
  1. 结果 是请求的成功响应:
typescript 复制代码
interface Result {
  [key: string]: unknown;
}
  1. 错误 表示请求失败:
typescript 复制代码
interface Error {
  code: number;
  message: string;
  data?: unknown;
}
  1. 通知 是单向消息,不期望响应:
typescript 复制代码
interface Notification {
  method: string;
  params?: { ... };
}

连接生命周期

1. 初始化

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

2. 消息交换

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

  • 请求-响应:客户端或服务器发送请求,对方响应
  • 通知:任一方发送单向消息

3. 终止

任一方都可以终止连接:

  • 通过 close() 进行干净关闭
  • 传输断开
  • 错误条件

错误处理

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

typescript 复制代码
enum ErrorCode {
  // 标准 JSON-RPC 错误代码
  ParseError = -32700,
  InvalidRequest = -32600,
  MethodNotFound = -32601,
  InvalidParams = -32602,
  InternalError = -32603
}

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

错误通过以下方式传播:

  • 对请求的错误响应
  • 传输上的错误事件
  • 协议级错误处理程序

实现示例

以下是实现 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: {}
  }
});

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

// 连接传输
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. 测试

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

资源

资源是Model Context Protocol (MCP)中的基本元素,允许服务器暴露数据和内容,供客户端读取并用作LLM交互的上下文。

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

  • Claude Desktop目前要求用户在使用资源之前明确选择资源
  • 其他客户端可能会基于启发式自动选择资源
  • 某些实现甚至可能允许AI模型本身决定使用哪些资源

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

概述

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

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

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

资源URI

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

plaintext 复制代码
[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端点暴露具体资源列表。每个资源包括:

typescript 复制代码
{
  uri: string;           // 资源的唯一标识符
  name: string;          // 人类可读的名称
  description?: string;  // 可选描述
  mimeType?: string;     // 可选的MIME类型
}

资源模板

对于动态资源,服务器可以暴露URI模板,客户端可以使用这些模板构建有效的资源URI:

typescript 复制代码
{
  uriTemplate: string;   // 遵循RFC 6570的URI模板
  name: string;          // 此类资源的人类可读名称
  description?: string;  // 可选描述
  mimeType?: string;     // 所有匹配资源的可选MIME类型
}

读取资源

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

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

typescript 复制代码
{
  contents: [
    {
      uri: string;        // 资源的URI
      mimeType?: string;  // 可选的MIME类型
      // 以下之一:
      text?: string;      // 用于文本资源
      blob?: string;      // 用于二进制资源(base64编码)
    }
  ]
}

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

资源更新

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

列表更改

服务器可以通过notifications/resources/list_changed通知客户端可用资源列表的变化。

内容更改

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

  1. 客户端发送带有资源URI的resources/subscribe
  2. 当资源发生变化时,服务器发送notifications/resources/updated
  3. 客户端可以使用resources/read获取最新内容
  4. 客户端可以使用resources/unsubscribe取消订阅

示例实现

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

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

// 列出可用资源
server.setRequestHandler(ListResourcesRequestSchema, async () => {
  return {
    resources: [
      {
        uri: "file:///logs/app.log",
        name: "Application Logs",
        mimeType: "text/plain"
      }
    ]
  };
});

// 读取资源内容
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");
});

最佳实践

在实现资源支持时:

  1. 使用清晰、描述性的资源名称和URI
  2. 包含有用的描述以指导LLM理解
  3. 在已知时设置适当的MIME类型
  4. 为动态内容实现资源模板
  5. 对频繁变化的资源使用订阅
  6. 使用清晰的错误消息优雅地处理错误
  7. 考虑对大型资源列表进行分页
  8. 在适当时缓存资源内容
  9. 在处理前验证URI
  10. 记录自定义URI方案

安全考虑

在暴露资源时:

  • 验证所有资源URI
  • 实施适当的访问控制
  • 清理文件路径以防止目录遍历
  • 谨慎处理二进制数据
  • 考虑对资源读取进行速率限制
  • 审计资源访问
  • 在传输中加密敏感数据
  • 验证MIME类型
  • 对长时间运行的读取实施超时
  • 适当地处理资源清理

Prompts(提示)

Prompts 使服务器能够定义可重用的提示模板和工作流,客户端可以轻松地向用户和 LLM 公开这些模板。它们提供了一种强大的方式来标准化和共享常见的 LLM 交互。

注意: Prompts 设计为 用户可控,意味着它们从服务器暴露给客户端,用户可以明确选择它们进行使用。

概述

MCP(Model Context Protocol)中的 Prompts 是预定义的模板,可用于:

  • 接受动态参数
  • 包含资源中的上下文
  • 链接多个交互
  • 引导特定的工作流
  • 作为 UI 元素(如斜杠命令)展示

Prompt 结构

每个 Prompt 由以下字段定义:

typescript 复制代码
{
  name: string;              // Prompt 的唯一标识符
  description?: string;      // 人类可读的描述
  arguments?: [              // 可选参数列表
    {
      name: string;          // 参数标识符
      description?: string;  // 参数描述
      required?: boolean;    // 该参数是否必填
    }
  ]
}

发现 Prompts

客户端可以通过 prompts/list 端点获取可用的 Prompts:

css 复制代码
typescript
复制编辑
// 请求
{
  method: "prompts/list"
}

// 响应
{
  prompts: [
    {
      name: "analyze-code",
      description: "分析代码以寻找潜在的改进点",
      arguments: [
        {
          name: "language",
          description: "编程语言",
          required: true
        }
      ]
    }
  ]
}

使用 Prompts

客户端可以通过 prompts/get 请求使用 Prompt:

swift 复制代码
typescript
复制编辑
// 请求
{
  method: "prompts/get",
  params: {
    name: "analyze-code",
    arguments: {
      language: "python"
    }
  }
}

// 响应
{
  description: "分析 Python 代码以寻找潜在的改进点",
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: "请分析以下 Python 代码并提出改进建议:\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```"
      }
    }
  ]
}

动态 Prompts

Prompts 可以是动态的,并包括:

嵌入资源上下文

json 复制代码
json
复制编辑
{
  "name": "analyze-project",
  "description": "分析项目日志和代码",
  "arguments": [
    {
      "name": "timeframe",
      "description": "要分析日志的时间范围",
      "required": true
    },
    {
      "name": "fileUri",
      "description": "要审查的代码文件 URI",
      "required": true
    }
  ]
}

当处理 prompts/get 请求时:

swift 复制代码
json
复制编辑
{
  "messages": [
    {
      "role": "user",
      "content": {
        "type": "text",
        "text": "分析这些系统日志和代码文件中的问题:"
      }
    },
    {
      "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    # 连接实现\n    pass",
          "mimeType": "text/x-python"
        }
      }
    }
  ]
}

多步骤工作流

vbnet 复制代码
typescript
复制编辑
const debugWorkflow = {
  name: "debug-error",
  async getMessages(error: string) {
    return [
      {
        role: "user",
        content: {
          type: "text",
          text: `我遇到了一个错误:${error}`
        }
      },
      {
        role: "assistant",
        content: {
          type: "text",
          text: "我会帮助分析这个错误。你尝试过什么方法?"
        }
      },
      {
        role: "user",
        content: {
          type: "text",
          text: "我尝试了重启服务,但错误仍然存在。"
        }
      }
    ];
  }
};

示例实现

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

javascript 复制代码
typescript
复制编辑
import { Server } from "@modelcontextprotocol/sdk/server";
import {
  ListPromptsRequestSchema,
  GetPromptRequestSchema
} from "@modelcontextprotocol/sdk/types";

const PROMPTS = {
  "git-commit": {
    name: "git-commit",
    description: "生成 Git 提交信息",
    arguments: [
      {
        name: "changes",
        description: "Git diff 或更改描述",
        required: true
      }
    ]
  },
  "explain-code": {
    name: "explain-code",
    description: "解释代码的工作原理",
    arguments: [
      {
        name: "code",
        description: "要解释的代码",
        required: true
      },
      {
        name: "language",
        description: "编程语言",
        required: false
      }
    ]
  }
};

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

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

server.setRequestHandler(GetPromptRequestSchema, async (request) => {
  const prompt = PROMPTS[request.params.name];
  if (!prompt) {
    throw new Error(`未找到 Prompt:${request.params.name}`);
  }

  if (request.params.name === "git-commit") {
    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `为以下更改生成简洁但描述性的提交信息:\n\n${request.params.arguments?.changes}`
          }
        }
      ]
    };
  }

  if (request.params.name === "explain-code") {
    const language = request.params.arguments?.language || "未知";
    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `解释这段 ${language} 代码的工作原理:\n\n${request.params.arguments?.code}`
          }
        }
      ]
    };
  }

  throw new Error("未找到 Prompt 实现");
});

最佳实践

  1. 使用清晰、描述性的 Prompt 名称
  2. 为 Prompts 和参数提供详细的描述
  3. 验证所有必填参数
  4. 友好地处理缺失参数
  5. 考虑 Prompt 模板的版本管理
  6. 在合适的场景下缓存动态内容
  7. 实现错误处理
  8. 记录参数格式
  9. 考虑 Prompt 的可组合性
  10. 使用不同输入测试 Prompts

UI 集成

Prompts 可以在客户端 UI 中以以下方式展示:

  • 斜杠命令
  • 快捷操作
  • 右键菜单项
  • 命令面板入口
  • 指导性工作流
  • 交互式表单

安全考虑

  • 验证所有参数
  • 清理用户输入
  • 考虑速率限制
  • 实施访问控制
  • 审计 Prompt 使用
  • 适当处理敏感数据
  • 验证生成内容
  • 实施超时机制
  • 考虑 Prompt 注入风险
  • 记录安全要求

Tools

Tools 是 Model Context Protocol (MCP) 中的一个强大基础元素,它使服务器能够向客户端暴露可执行的功能。通过 Tools,LLMs 可以与外部系统交互、执行计算并在现实世界中采取行动。

注意 : Tools 被设计为模型控制的,这意味着 Tools 从服务器暴露给客户端时,AI 模型能够自动调用它们(需要人工批准)。

概述

MCP 中的 Tools 允许服务器暴露可执行的函数,客户端可以调用这些函数,并由 LLMs 使用来执行操作。Tools 的关键方面包括:

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

resources 类似,Tools 通过唯一的名称进行标识,并且可以包含描述以指导其使用。然而,与 resources 不同,Tools 表示可以修改状态或与外部系统交互的动态操作。

Tool 定义结构

每个 Tool 的定义结构如下:

typescript 复制代码
{
  name: string;          // Tool 的唯一标识符
  description?: string;  // 人类可读的描述
  inputSchema: {         // Tool 参数的 JSON Schema
    type: "object",
    properties: { ... }  // Tool 特定的参数
  }
}

实现 Tools

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

typescript 复制代码
const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {
    tools: {}
  }
});

// 定义可用的 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"]
      }
    }]
  };
});

// 处理 Tool 执行
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");
});

示例 Tool 模式

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

系统操作

与本地系统交互的 Tools:

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

API 集成

封装外部 API 的 Tools:

typescript 复制代码
{
  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" } }
    }
  }
}

数据处理

转换或分析数据的 Tools:

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

最佳实践

在实现 Tools 时:

  1. 提供清晰、描述性的名称和描述
  2. 使用详细的 JSON Schema 定义参数
  3. 在 Tool 描述中包含示例,以演示模型应如何使用它们
  4. 实现适当的错误处理和验证
  5. 对长时间操作使用进度报告
  6. 保持 Tool 操作专注且原子化
  7. 记录预期的返回值结构
  8. 实现适当的超时
  9. 考虑对资源密集型操作进行速率限制
  10. 记录 Tool 使用情况以便调试和监控

安全考虑

在暴露 Tools 时:

输入验证

  • 根据 Schema 验证所有参数
  • 清理文件路径和系统命令
  • 验证 URL 和外部标识符
  • 检查参数大小和范围
  • 防止命令注入

访问控制

  • 在需要时实施身份验证
  • 使用适当的授权检查
  • 审核 Tool 使用情况
  • 限制请求速率
  • 监控滥用行为

错误处理

  • 不要向客户端暴露内部错误
  • 记录与安全相关的错误
  • 适当处理超时
  • 在错误后清理资源
  • 验证返回值

Tool 发现和更新

MCP 支持动态 Tool 发现:

  1. 客户端可以随时列出可用的 Tools
  2. 服务器可以使用 notifications/tools/list_changed 通知客户端 Tools 的变化
  3. Tools 可以在运行时添加或删除
  4. Tool 定义可以更新(尽管应谨慎进行)

错误处理

Tool 错误应在结果对象中报告,而不是作为 MCP 协议级别的错误。这允许 LLM 查看并可能处理错误。当 Tool 遇到错误时:

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

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

typescript 复制代码
try {
  // Tool 操作
  const result = performOperation();
  return {
    content: [
      {
        type: "text",
        text: `Operation successful: ${result}`
      }
    ]
  };
} catch (error) {
  return {
    isError: true,
    content: [
      {
        type: "text",
        text: `Error: ${error.message}`
      }
    ]
  };
}

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

测试 Tools

MCP Tools 的全面测试策略应包括:

  • 功能测试: 验证 Tools 在有效输入下正确执行,并正确处理无效输入
  • 集成测试: 使用真实和模拟的依赖项测试 Tool 与外部系统的交互
  • 安全测试: 验证身份验证、授权、输入清理和速率限制
  • 性能测试: 检查负载下的行为、超时处理和资源清理
  • 错误处理: 确保 Tools 通过 MCP 协议正确报告错误并清理资源

Sampling

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

注意:MCP 的这一功能在 Claude Desktop 客户端中尚未支持。

How sampling works

采样流程遵循以下步骤:

  1. 服务器向客户端发送 sampling/createMessage 请求
  2. 客户端审查请求并可以修改它
  3. 客户端从 LLM 中采样
  4. 客户端审查补全
  5. 客户端将结果返回给服务器

这种"人在回路"的设计确保用户对 LLM 看到和生成的内容保持控制。

Message format

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

typescript 复制代码
{
  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>
}

Request parameters

Messages

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

  • role: 可以是 "user" 或 "assistant"
  • content: 消息内容,可以是:
    • 带有 text 字段的文本内容
    • 带有 data(base64)和 mimeType 字段的图像内容

Model preferences

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

  • hints: 模型名称建议数组,客户端可以使用这些建议来选择适当的模型:
    • name: 可以匹配完整或部分模型名称的字符串(例如 "claude-3", "sonnet")
    • 客户端可以将提示映射到不同提供商的等效模型
    • 多个提示按优先顺序评估
  • 优先级值(0-1 标准化):
    • costPriority: 最小化成本的重要性
    • speedPriority: 低延迟响应的重要性
    • intelligencePriority: 高级模型能力的重要性

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

System prompt

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

Context inclusion

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

  • "none": 不包含额外的上下文
  • "thisServer": 包含来自请求服务器的上下文
  • "allServers": 包含来自所有连接的 MCP 服务器的上下文

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

Sampling parameters

通过以下参数微调 LLM 采样:

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

Response format

客户端返回补全结果:

typescript 复制代码
{
  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
  }
}

Example request

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

json 复制代码
{
  "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
  }
}

Best practices

在实现采样时:

  1. 始终提供清晰、结构良好的提示
  2. 正确处理文本和图像内容
  3. 设置合理的 token 限制
  4. 通过 includeContext 包含相关上下文
  5. 在使用前验证响应
  6. 优雅地处理错误
  7. 考虑对采样请求进行速率限制
  8. 记录预期的采样行为
  9. 使用各种模型参数进行测试
  10. 监控采样成本

Human in the loop controls

采样设计时考虑了人工监督:

For prompts

  • 客户端应向用户显示提议的提示
  • 用户应能够修改或拒绝提示
  • 系统提示可以被过滤或修改
  • 上下文包含由客户端控制

For completions

  • 客户端应向用户显示补全
  • 用户应能够修改或拒绝补全
  • 客户端可以过滤或修改补全
  • 用户控制使用哪个模型

Security considerations

在实现采样时:

  • 验证所有消息内容
  • 清理敏感信息
  • 实施适当的速率限制
  • 监控采样使用情况
  • 加密传输中的数据
  • 处理用户数据隐私
  • 审计采样请求
  • 控制成本暴露
  • 实施超时
  • 优雅地处理模型错误

Common patterns

Agentic workflows

采样支持以下代理模式:

  • 读取和分析资源
  • 根据上下文做出决策
  • 生成结构化数据
  • 处理多步骤任务
  • 提供交互式帮助

Context management

上下文的最佳实践:

  • 请求最小必要的上下文
  • 清晰地构建上下文
  • 处理上下文大小限制
  • 根据需要更新上下文
  • 清理过时的上下文

Error handling

稳健的错误处理应:

  • 捕获采样失败
  • 处理超时错误
  • 管理速率限制
  • 验证响应
  • 提供后备行为
  • 适当记录错误

Limitations

请注意以下限制:

  • 采样取决于客户端的能力
  • 用户控制采样行为
  • 上下文大小有限制
  • 可能适用速率限制
  • 应考虑成本
  • 模型可用性不同
  • 响应时间不同
  • 并非所有内容类型都支持

Roots

Roots 是 MCP 中的一个概念,用于定义服务器可以操作的边界。它们提供了一种方式,让客户端能够告知服务器相关的资源及其位置。

什么是 Roots?

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

例如,roots 可以是:

plaintext 复制代码
file:///home/user/projects/myapp
https://api.example.com/v1

为什么使用 Roots?

Roots 有几个重要的用途:

  1. 指导:它们告知服务器相关的资源和位置
  2. 清晰:Roots 明确了哪些资源属于你的工作区
  3. 组织:多个 roots 允许你同时处理不同的资源

Roots 如何工作?

当客户端支持 roots 时,它会:

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

虽然 roots 是信息性的,并不强制执行,但服务器应:

  1. 尊重提供的 roots
  2. 使用 root URI 来定位和访问资源
  3. 优先处理 root 边界内的操作

常见用例

Roots 通常用于定义:

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

最佳实践

在使用 roots 时:

  1. 仅建议必要的资源
  2. 使用清晰、描述性的名称来命名 roots
  3. 监控 root 的可访问性
  4. 优雅地处理 root 的变化

示例

以下是一个典型的 MCP 客户端如何暴露 roots 的示例:

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

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

Transports

在 Model Context Protocol (MCP) 中,Transports 提供了客户端和服务器之间通信的基础。Transport 负责处理消息发送和接收的底层机制。

Message Format

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

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

Requests

typescript 复制代码
{
  jsonrpc: "2.0",
  id: number | string,
  method: string,
  params?: object
}

Responses

typescript 复制代码
{
  jsonrpc: "2.0",
  id: number | string,
  result?: object,
  error?: {
    code: number,
    message: string,
    data?: unknown
  }
}

Notifications

typescript 复制代码
{
  jsonrpc: "2.0",
  method: string,
  params?: object
}

Built-in Transport Types

MCP 包含两种标准的 Transport 实现:

Standard Input/Output (stdio)

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

使用 stdio 的场景:

  • 构建命令行工具
  • 实现本地集成
  • 需要简单的进程通信
  • 使用 shell 脚本
typescript 复制代码
const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {}
});

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

Server-Sent Events (SSE)

SSE Transport 通过 HTTP POST 请求实现客户端到服务器的通信,并通过服务器到客户端的流进行通信。

使用 SSE 的场景:

  • 仅需要服务器到客户端的流
  • 在受限网络中工作
  • 实现简单的更新
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);

Custom Transports

MCP 使得为特定需求实现自定义 Transport 变得容易。任何 Transport 实现只需要符合 Transport 接口:

你可以为以下场景实现自定义 Transport:

  • 自定义网络协议
  • 专用通信通道
  • 与现有系统集成
  • 性能优化
typescript 复制代码
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;
}

Error Handling

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

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

示例错误处理:

typescript 复制代码
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;
    }
  }
}

Best Practices

在实现或使用 MCP Transport 时:

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

Security Considerations

在实现 Transport 时:

Authentication and Authorization

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

Data Security

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

Network Security

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

Debugging Transport

调试 Transport 问题的技巧:

  1. 启用调试日志
  2. 监控消息流
  3. 检查连接状态
  4. 验证消息格式
  5. 测试错误场景
  6. 使用网络分析工具
  7. 实施健康检查
  8. 监控资源使用情况
  9. 测试边缘情况
  10. 使用适当的错误跟踪
相关推荐
cifangyiquan3 小时前
5分钟了解MCP如何使用(Model Context Protocol)
claude·mcp
yaocheng的ai分身20 小时前
模型上下文协议 (MCP) 快速入门
mcp
Captaincc1 天前
awesome MCP server 大全整理
mcp
Captaincc1 天前
Awesome CursorRules - 收集和整理了各种优质的 CursorRules
cursor·mcp·ai 编程
Captaincc1 天前
28 行代码实现 MCP 服务,可用于 Cursor, Windsurf, Claude Code, Zed... 等任意 MCP 客户端
mcp·ai 编程
Captaincc1 天前
MCP协议详解:复刻Manus全靠它,为什么说MCP是Agent进化的一大步?
mcp·ai 编程
一个热爱生活的普通人1 天前
如何用golang实现一个MCP Server
llm·go·mcp
青柠071 天前
MCP极简入门:超快速上手运行简单的MCP服务和MCP客户端
mcp
6confim2 天前
Cursor-用于大型项目
cursor·mcp