跳到主要内容
协议修订: 2025-11-25
模型上下文协议 (MCP) 提供了一种标准化的方式,允许服务器通过客户端向语言模型请求 LLM 采样(“补全”或“生成”)。这种流程允许客户端保持对模型访问、选择和权限的控制,同时使服务器能够利用 AI 能力——且无需服务器持有 API 密钥。服务器可以请求文本、音频或基于图像的交互,并可以选择在提示词中包含来自 MCP 服务器的上下文。

用户交互模型

MCP 中的采样允许服务器通过使 LLM 调用能够“嵌套”在其他 MCP 服务器功能中,来实现智能体(agentic)行为。 实现方案可以自由地通过任何适合其需求的界面模式来公开采样——协议本身不强制要求任何特定的用户交互模型。
出于信任、安全和保障的考虑,应当 (SHOULD) 始终有一个“人在回路”中,并具备拒绝采样请求的能力。应用程序 应当 (SHOULD)
  • 提供易于且直观地审查采样请求的 UI
  • 允许用户在发送前查看和编辑提示词
  • 在交付前展示生成的响应以供审查

采样中的工具

服务器可以通过在采样请求中提供 tools 数组和可选的 toolChoice 配置,请求客户端的 LLM 在采样期间使用工具。这使服务器能够实现智能体行为,即 LLM 可以调用工具、接收结果并继续对话——这一切都在单个采样请求流程中完成。 客户端 必须 (MUST) 通过 sampling.tools 能力声明对工具使用的支持,才能接收启用工具的采样请求。服务器 不得 (MUST NOT) 向未通过 sampling.tools 能力声明支持工具使用的客户端发送启用工具的采样请求。

功能

支持采样的客户端 必须 (MUST)初始化 期间声明 sampling 能力: 基础采样:
{
  "capabilities": {
    "sampling": {}
  }
}
支持工具使用
{
  "capabilities": {
    "sampling": {
      "tools": {}
    }
  }
}
支持上下文包含(软弃用)
{
  "capabilities": {
    "sampling": {
      "context": {}
    }
  }
}
includeContext 参数值 "thisServer""allServers" 已软弃用。服务器 应当 (SHOULD) 避免使用这些值(例如可以省略 includeContext,因为它默认为 "none"),并且 不应 (SHOULD NOT) 使用它们,除非客户端声明了 sampling.context 能力。这些值可能会在未来的规范版本中移除。

协议消息

创建消息

为了请求语言模型生成,服务器发送 sampling/createMessage 请求: 请求:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sampling/createMessage",
  "params": {
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "What is the capital of France?"
        }
      }
    ],
    "modelPreferences": {
      "hints": [
        {
          "name": "claude-3-sonnet"
        }
      ],
      "intelligencePriority": 0.8,
      "speedPriority": 0.5
    },
    "systemPrompt": "You are a helpful assistant.",
    "maxTokens": 100
  }
}
响应
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "role": "assistant",
    "content": {
      "type": "text",
      "text": "The capital of France is Paris."
    },
    "model": "claude-3-sonnet-20240307",
    "stopReason": "endTurn"
  }
}

带工具的采样

下图说明了带工具采样的完整流程,包括多轮工具循环 为了请求具有工具使用能力的 LLM 生成,服务器在请求中包含 tools 以及可选的 toolChoice 请求 (服务器 -> 客户端):
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sampling/createMessage",
  "params": {
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "What's the weather like in Paris and London?"
        }
      }
    ],
    "tools": [
      {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "inputSchema": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string",
              "description": "City name"
            }
          },
          "required": ["city"]
        }
      }
    ],
    "toolChoice": {
      "mode": "auto"
    },
    "maxTokens": 1000
  }
}
响应 (客户端 -> 服务器)
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "role": "assistant",
    "content": [
      {
        "type": "tool_use",
        "id": "call_abc123",
        "name": "get_weather",
        "input": {
          "city": "Paris"
        }
      },
      {
        "type": "tool_use",
        "id": "call_def456",
        "name": "get_weather",
        "input": {
          "city": "London"
        }
      }
    ],
    "model": "claude-3-sonnet-20240307",
    "stopReason": "toolUse"
  }
}

多轮工具循环

在收到来自 LLM 的工具使用请求后,服务器通常:
  1. 执行请求的工具使用。
  2. 发送一个新的采样请求,并附加工具结果
  3. 接收 LLM 的响应(可能包含新的工具使用)
  4. 根据需要重复多次(服务器可能会限制最大迭代次数,例如在最后一次迭代中传递 toolChoice: {mode: "none"} 以强制得出最终结果)
带有工具结果的后续请求 (服务器 -> 客户端)
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "sampling/createMessage",
  "params": {
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "What's the weather like in Paris and London?"
        }
      },
      {
        "role": "assistant",
        "content": [
          {
            "type": "tool_use",
            "id": "call_abc123",
            "name": "get_weather",
            "input": { "city": "Paris" }
          },
          {
            "type": "tool_use",
            "id": "call_def456",
            "name": "get_weather",
            "input": { "city": "London" }
          }
        ]
      },
      {
        "role": "user",
        "content": [
          {
            "type": "tool_result",
            "toolUseId": "call_abc123",
            "content": [
              {
                "type": "text",
                "text": "Weather in Paris: 18°C, partly cloudy"
              }
            ]
          },
          {
            "type": "tool_result",
            "toolUseId": "call_def456",
            "content": [
              {
                "type": "text",
                "text": "Weather in London: 15°C, rainy"
              }
            ]
          }
        ]
      }
    ],
    "tools": [
      {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "inputSchema": {
          "type": "object",
          "properties": {
            "city": { "type": "string" }
          },
          "required": ["city"]
        }
      }
    ],
    "maxTokens": 1000
  }
}
最终响应 (客户端 -> 服务器)
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "role": "assistant",
    "content": {
      "type": "text",
      "text": "Based on the current weather data:\n\n- **Paris**: 18°C and partly cloudy - quite pleasant!\n- **London**: 15°C and rainy - you'll want an umbrella.\n\nParis has slightly warmer and drier conditions today."
    },
    "model": "claude-3-sonnet-20240307",
    "stopReason": "endTurn"
  }
}

消息内容限制

工具结果消息

当用户消息包含工具结果(类型:“tool_result”)时,它 必须 (MUST) 仅包含工具结果。不允许在同一条消息中将工具结果与其他内容类型(文本、图像、音频)混合。 此约束确保了与为工具结果使用专用角色的提供者 API 的兼容性(例如 OpenAI 的 “tool” 角色,Gemini 的 “function” 角色)。 有效 - 单个工具结果:
{
  "role": "user",
  "content": {
    "type": "tool_result",
    "toolUseId": "call_123",
    "content": [{ "type": "text", "text": "Result data" }]
  }
}
有效 - 多个工具结果
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "toolUseId": "call_123",
      "content": [{ "type": "text", "text": "Result 1" }]
    },
    {
      "type": "tool_result",
      "toolUseId": "call_456",
      "content": [{ "type": "text", "text": "Result 2" }]
    }
  ]
}
无效 - 混合内容
{
  "role": "user",
  "content": [
    {
      "type": "text",
      "text": "Here are the results:"
    },
    {
      "type": "tool_result",
      "toolUseId": "call_123",
      "content": [{ "type": "text", "text": "Result data" }]
    }
  ]
}

工具使用与结果平衡

在采样中使用工具时,每个包含 ToolUseContent 块的助手消息 必须 (MUST) 紧跟一条完全由 ToolResultContent 块组成的用户消息,其中每个工具使用(例如具有 id: $id)都由相应的工具结果(具有 toolUseId: $id)匹配,然后才能发送任何其他消息。 此要求确保了:
  • 在对话继续之前,工具使用总是得到解析
  • 提供者 API 可以并发处理多个工具使用并并行获取其结果
  • 对话保持一致的请求-响应模式
有效序列示例
  1. 用户消息:“巴黎和伦敦的天气怎么样?”
  2. 助手消息:ToolUseContent (id: "call_abc123", name: "get_weather", input: {city: "Paris"}) + ToolUseContent (id: "call_def456", name: "get_weather", input: {city: "London"})
  3. 用户消息:ToolResultContent (toolUseId: "call_abc123", content: "18°C, 多云") + ToolResultContent (toolUseId: "call_def456", content: "15°C, 有雨")
  4. 助手消息:比较两座城市天气的文本响应
无效序列 - 缺失工具结果
  1. 用户消息:“巴黎和伦敦的天气怎么样?”
  2. 助手消息:ToolUseContent (id: "call_abc123", name: "get_weather", input: {city: "Paris"}) + ToolUseContent (id: "call_def456", name: "get_weather", input: {city: "London"})
  3. 用户消息:ToolResultContent (toolUseId: "call_abc123", content: "18°C, 多云") ← 缺少 call_def456 的结果
  4. 助手消息:文本响应(无效 - 并非所有工具使用都已解析)

跨 API 兼容性

采样规范旨在跨多个 LLM 提供者 API(Claude、OpenAI、Gemini 等)工作。兼容性的关键设计决策如下:

消息角色

MCP 使用两个角色:“user”(用户)和 “assistant”(助手)。 工具使用请求在具有 “assistant” 角色的 CreateMessageResult 中发送。工具结果在具有 “user” 角色的消息中发回。包含工具结果的消息不能包含其他类型的内容。

工具选择模式

CreateMessageRequest.params.toolChoice 控制模型的工具使用能力
  • {mode: "auto"}:模型决定是否使用工具(默认)
  • {mode: "required"}:模型在完成前必须至少使用一个工具
  • {mode: "none"}:模型不得使用任何工具

并行工具使用

MCP 允许模型并行发出多个工具使用请求(返回 ToolUseContent 数组)。所有主流提供者 API 都支持此功能
  • Claude:原生支持并行工具使用
  • OpenAI:支持并行工具调用(可通过 parallel_tool_calls: false 禁用)
  • Gemini:原生支持并行函数调用
包装了支持禁用并行工具使用的提供者的实现方案,可以将此作为扩展公开,但这不属于核心 MCP 规范的一部分。

消息流

数据类型

消息

采样消息可以包含

文本内容

{
  "type": "text",
  "text": "The message content"
}

图像内容

{
  "type": "image",
  "data": "base64-encoded-image-data",
  "mimeType": "image/jpeg"
}

音频内容

{
  "type": "audio",
  "data": "base64-encoded-audio-data",
  "mimeType": "audio/wav"
}

模型偏好

MCP 中的模型选择需要精细的抽象,因为服务器和客户端可能使用具有不同模型产品的不同 AI 提供者。服务器不能简单地按名称请求特定模型,因为客户端可能无法访问该确切模型,或者可能更愿意使用不同提供者的等效模型。 为了解决这个问题,MCP 实施了一个偏好系统,该系统将抽象的能力优先级与可选的模型提示相结合:

能力优先级

服务器通过三个归一化的优先级值(0-1)表达其需求
  • costPriority:最小化成本有多重要?值越高越倾向于廉价模型。
  • speedPriority:低延迟有多重要?值越高越倾向于快速模型。
  • intelligencePriority:高级能力有多重要?值越高越倾向于能力更强的模型。

模型提示

虽然优先级有助于根据特性选择模型,但 hints(提示)允许服务器建议特定的模型或模型系列
  • 提示被视为子字符串,可以灵活地匹配模型名称
  • 多个提示按偏好顺序进行评估
  • 客户端 可以 (MAY) 将提示映射到来自不同提供者的等效模型
  • 提示是建议性的——客户端做出最终的模型选择
例如
{
  "hints": [
    { "name": "claude-3-sonnet" }, // Prefer Sonnet-class models
    { "name": "claude" } // Fall back to any Claude model
  ],
  "costPriority": 0.3, // Cost is less important
  "speedPriority": 0.8, // Speed is very important
  "intelligencePriority": 0.5 // Moderate capability needs
}
客户端处理这些偏好,以便从其可用选项中选择合适的模型。例如,如果客户端无法访问 Claude 模型但拥有 Gemini,它可能会根据相似的能力将 sonnet 提示映射到 gemini-1.5-pro

错误处理

客户端 应当 (SHOULD) 为常见的失败情况返回错误
  • 用户拒绝了采样请求:-1
  • 请求中缺失工具结果:-32602(无效参数)
  • 工具结果与其他内容混合:-32602(无效参数)
错误示例
{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": -1,
    "message": "User rejected sampling request"
  }
}
{
  "jsonrpc": "2.0",
  "id": 4,
  "error": {
    "code": -32602,
    "message": "Tool result missing in request"
  }
}

安全注意事项

  1. 客户端 应当 (SHOULD) 实施用户批准控制
  2. 双方 应当 (SHOULD) 验证消息内容
  3. 客户端 应当 (SHOULD) 尊重模型偏好提示
  4. 客户端 应当 (SHOULD) 实施速率限制
  5. 双方 必须 (MUST) 妥善处理敏感数据
在采样中使用工具时,还需考虑额外的安全因素
  1. 服务器 必须 (MUST) 确保在回复 stopReason: "toolUse" 时,每个 ToolUseContent 项都对应一个具有匹配 toolUseIdToolResultContent 项,并且用户消息仅包含工具结果(无其他内容类型)
  2. 双方 应当 (SHOULD) 为工具循环实施迭代限制