主题
多轮往返请求
NOTE
多轮往返请求(MRTR)在本版本的 MCP 规范中引入。它取代了此前由服务端发起请求的方式。服务端 MUST(必须)使用 MRTR 模式发送服务端到客户端的请求(例如 roots/list、sampling/createMessage 或 elicitation/create)。此前由服务端发起请求的模式不再受支持。这是一项破坏性变更。
NOTE
为简洁起见,本页的请求示例省略了 _meta 请求元数据(io.modelcontextprotocol/protocolVersion、io.modelcontextprotocol/clientInfo 和 io.modelcontextprotocol/clientCapabilities)。每个请求 MUST(必须)包含必需的 _meta 字段;参见 _meta。
多轮往返请求
Model Context Protocol (MCP) 定义了多种方式,供服务端在处理客户端请求期间向用户请求额外信息(例如 roots/list、sampling/createMessage 或 elicitation/create)。多轮往返请求模式提供了一种标准化的方式来处理这些服务端请求,而无需跨服务端实例共享存储层,也无需有状态的负载均衡。
高层流程如下:
- 客户端向服务端发送初始请求,并携带执行该操作所需的参数。
- 服务端判定需要额外信息才能完成该请求,并作出响应,请求更多信息。
- 客户端从用户或其他来源收集所请求的信息,然后重试原始请求,并携带所请求的额外信息。
- 服务端判定已拥有足够信息来完成该操作,并返回最终结果。
sequenceDiagram
participant C as Client
participant S as Server
C->>S: client request (id: 1, request params)
note over S: Server needs more info <br/> to process request.
S-->>C: Request for additional input.
note over C: Client gathers input and <br/> retries initial request.
C->>S: client request (id: 2, request params, requested input)
note over S: Server has enough information <br/> to complete the request.
S-->>C: Result (id: 2, result)
核心类型
该流程在 MCP 中使用以下类型实现。
InputRequests
InputRequests 对象是服务端到客户端请求的映射。 键是服务端分配的字符串标识符; 值是请求对象(例如 ElicitRequest、CreateMessageRequest 或 ListRootsRequest)。
json
{
"github_login": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Please provide your GitHub username",
"requestedSchema": {
"type": "object",
"properties": {
"name": { "type": "string" }
},
"required": ["name"]
}
}
},
"capital_of_france": {
"method": "sampling/createMessage",
"params": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "What is the capital of France?"
}
}
],
"systemPrompt": "You are a helpful assistant.",
"maxTokens": 100
}
}
}InputResponses
InputResponses 对象是客户端对服务端请求的响应的映射。 键与 InputRequests 映射中的键相对应;值是客户端对每个请求的结果(例如 ElicitResult、CreateMessageResult 或 ListRootsResult)。
json
{
"github_login": {
"action": "accept",
"content": {
"name": "octocat"
}
},
"capital_of_france": {
"role": "assistant",
"content": {
"type": "text",
"text": "The capital of France is Paris."
},
"model": "claude-3-sonnet-20240307",
"stopReason": "endTurn"
}
}InputRequiredResult
InputRequiredResult 是一种 Result, 表示在请求完成之前还需要额外输入。
inputRequests(可选):一个InputRequests映射,包含客户端必须满足的服务端发起的请求。requestState(可选):一个仅对服务端有意义的不可见字符串。客户端 MUST NOT(不得)检查、解析、修改或对其内容作任何假设。
json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "input_required",
"inputRequests": {
// Elicitation request.
"github_login": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Please provide your GitHub username",
"requestedSchema": {
"type": "object",
"properties": {
"name": { "type": "string" }
},
"required": ["name"]
}
}
},
// Sampling request.
"capital_of_france": {
"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
}
}
},
"requestState": "AEAD-protected blob"
}
}支持的请求
服务端 MAY(可以)对以下客户端请求发送 InputRequiredResult 响应:
| 客户端请求 | 支持 InputRequiredResult |
|---|---|
prompts/get | 是 |
resources/read | 是 |
tools/call | 是 |
服务端 MUST NOT(不得)对任何其他客户端请求发送 InputRequiredResult 响应。
基本工作流
基本工作流描述了服务端如何在客户端-服务端请求过程中向客户端请求额外输入。 在本示例中我们使用 tools/call 作为客户端请求,但同样的模式适用于上面列出的任何受支持的请求。
值得注意的是,它允许服务端在无需维护任何服务端状态的情况下请求额外信息。 服务端将任何所需的上下文编码到 requestState 字段中,客户端在重试时将其原样回传。
sequenceDiagram
participant U as User
participant C as Client
participant S as Server
C->>S: tools/call (id: 1)
note over S: Server needs more info via Elicitation
S-->>C: InputRequiredResult (id: 1, ElicitRequest, requestState)
note over C,S: Initial Request Terminated
C->>U: Prompts user for input
U-->>C: Provides responses
note over C: Client retries tool call <br/> with inputResponses and requestState
C->>S: tools/call (id: 2, ElicitResult, requestState)
note over S: Server reconstitutes state<br/>Completes execution
S-->>C: Result (id: 2, ToolCallResult)
请注意,每一步中的请求都是完全独立的:处理重试的服务端不需要除重试请求中直接存在的信息之外的任何信息。
服务端要求(基本工作流)
服务端 MAY(可以)对任何受支持的客户端请求以
InputRequiredResult作出响应。InputRequiredResultMAY(可以)包含inputRequests字段。inputRequests的键是服务端分配的标识符,且 MUST(必须)在该请求范围内唯一。inputRequests的值是请求对象,且 MUST(必须)是ElicitRequest、CreateMessageRequest或ListRootsRequest之一。
InputRequiredResultMAY(可以)包含requestState字段。如果指定,该字段是一个仅对服务端有意义的不可见字符串。服务端可以自由地以任何格式编码该状态(例如 base64 编码的 JSON、加密的 JWT、序列化的二进制)。如果客户端请求包含
requestState字段,服务端 MUST(必须)将requestState视为攻击者可控的输入。如果requestState影响授权、资源访问或业务逻辑,服务端 MUST(必须)保护其完整性(例如 HMAC 或 AEAD),并且 MUST(必须)拒绝未通过验证的状态。仅当篡改最多只能导致请求失败时,MAY(可以)省略完整性保护。为防止重放,服务端 SHOULD(应当)在受完整性保护的
requestState载荷中包含以下内容,并在收到时逐项验证:- 已认证的主体,拒绝由不同主体出示的状态。
- 较短的过期时间(TTL),拒绝在过期后出示的状态;
- 发起请求的标识符,例如方法名及其关键参数的摘要,拒绝在不匹配的请求上出示的状态。
WARNING
请注意,这些措施限定了重放窗口并防止跨用户和跨请求重用,但其本身并不保证一次性使用。 对于某个给定的 requestState 必须至多被消费一次(例如一次性兑换)的服务端,MUST(必须)在服务端强制实施该不变量。
服务端 MUST(必须)在每个
InputRequiredResult响应中至少包含inputRequests或requestState之一。服务端 MUST NOT(不得)发送客户端未在其能力中声明支持的
inputRequests。例如,如果客户端未声明支持elicitation,则服务端 MUST NOT(不得)在inputRequests字段中包含任何elicitation/create请求。服务端 MUST NOT(不得)假设客户端会满足
inputRequests或重试原始请求。如果服务端希望反复向用户请求信息,直到获得完成请求所需的信息,则 MAY(可以)选择在同一请求的多次尝试中返回InputRequiredResult。
客户端要求(基本工作流)
- 如果客户端收到包含
inputRequests字段的InputRequiredResult,则客户端 MUST(必须)在重试原始请求之前构造所请求的输入。如果InputRequiredResult不包含inputRequests字段,则客户端 MAY(可以)立即重试原始请求。 - 如果
InputRequiredResult包含requestState字段,则客户端在重试原始请求时 MUST(必须)原样回传该字段的确切值。 客户端 MUST NOT(不得)检查、解析、修改或对requestState内容作任何假设。如果InputRequiredResult不包含requestState字段,则客户端 MUST NOT(不得)在重试中包含该字段。 - JSON-RPC
id在初始请求与重试之间 MUST(必须)不同,因为它们是相互独立的请求。 inputRequests和requestState字段都只影响客户端对原始请求的重试。它们 MUST NOT(不得)被用于客户端可能并行发送的任何其他请求。
错误处理
服务端 SHOULD(应当)验证客户端提供的数据是有效的 InputResponses 对象,并且其中的信息可以被正确解析。 协议错误(格式错误的 JSON、无效的 schema、服务端内部错误)SHOULD(应当)返回带有适当错误码和消息的 JSON-RPC 错误响应。
如果在 InputResponses 对象中提供了额外的、非预期的参数,服务端 SHOULD(应当)忽略任何它无法识别或不需要的信息。
如果客户端未能发送先前 InputRequests 中请求的全部信息,而缺失的信息是服务端处理该请求所必需的, 则服务端 SHOULD(应当)以新的 InputRequiredResult 再次请求缺失的信息,而不是返回错误。
安全注意事项
由于 requestState 会经过客户端,恶意或被攻陷的客户端可能试图修改它以改变服务端行为、 绕过授权检查或破坏服务端逻辑。服务端 MUST(必须)按上文服务端要求所述验证请求状态。