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. 使用适当的错误跟踪
相关推荐
带刺的坐椅15 小时前
Solon AI 正试发布(支持 java8+,RAG,MCP)
ai·solon·java8·rag·mcp
不买Huracan不改名2 天前
Tare使用MCP|Win11安装UV
uv·mcp·tare
救救孩子把2 天前
打造一个支持MySQL查询的MCP同步插件:Java实现
java·mysql·mcp·stdio
deephub3 天前
5个开源MCP服务器:扩展AI助手能力,高效处理日常工作
人工智能·深度学习·大语言模型·mcp
Ai野生菌3 天前
MCP专题 | 探索MCP服务器世界:增强AI能力的精选推荐
服务器·人工智能·mcp·mcp server
带刺的坐椅3 天前
100% 自主可控,Java Solon v3.3.1 发布(国产优秀应用开发基座)
java·spring·ai·信创·solon·mcp
带刺的坐椅4 天前
高德地图 MCP,可用 Java SolonMCP 接入(支持 java8, java11, java17, java21)
java·ai·solon·高德地图·lbs·mcp
csdn5659738505 天前
中国版 Cursor---腾讯云 CodeBuddy | 从安装VSCode到数独小游戏问世
腾讯云·mcp·mcp server·codebuddy首席试玩官·mcp头号玩家
梦醒沉醉5 天前
MCP(一)——QuickStart
python·mcp
勇敢牛牛_5 天前
使用Rust开发的智能助手系统,支持多模型、知识库和MCP
ai·rust·rag·mcp