> ## Documentation Index
> Fetch the complete documentation index at: https://mcp.developerdoc.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 多轮往返请求

<div id="enable-section-numbers" />

<Note>
  多轮往返请求（Multi Round-Trip Requests，MRTR）是在此版本的 MCP 规范中引入的。
  它替代了以前发送服务器发起请求的做法。服务器 **MUST** 使用 MRTR 模式发送服务器到客户端请求
  （例如 `roots/list`、`sampling/createMessage` 或 `elicitation/create`）。
  以前的服务器发起请求模式不再受支持。这是一个破坏性变更。
</Note>

## Multi Round-Trip Requests

Model Context Protocol (MCP) 定义了几种方式，使服务器可以在处理客户端请求期间向用户请求额外信息
（例如 `roots/list`、`sampling/createMessage` 或 `elicitation/create`）。
**多轮往返请求**模式提供了一种标准化方式来处理这些服务器请求，而不需要跨服务器实例的共享存储层，
也不需要有状态负载均衡。

高层流程如下：

1. 客户端向服务器发送初始请求，其中包含执行操作所需的参数。
2. 服务器确定需要额外信息才能满足该请求，并以请求更多信息的响应回答。
3. 客户端从用户或其他来源收集所请求的信息，然后带上额外请求的信息重试原始请求。
4. 服务器确定已有足够信息完成操作，并以最终结果响应。

```mermaid theme={null}
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`](/specification/draft/schema#inputrequests) 对象是服务器到客户端请求的映射。
键是服务器分配的字符串标识符；值是请求对象（例如 [`ElicitRequest`](/specification/draft/schema#elicitrequest)、[`CreateMessageRequest`](/specification/draft/schema#createmessagerequest) 或 [`ListRootsRequest`](/specification/draft/schema#listrootsrequest)）。

```json theme={null}
{
  "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`](/specification/draft/schema#inputresponses) 对象是客户端对服务器请求的响应映射。
键对应于 `InputRequests` 映射中的键；值是客户端针对每个请求给出的结果（例如 [`ElicitResult`](/specification/draft/schema#elicitresult)、[`CreateMessageResult`](/specification/draft/schema#createmessageresult) 或 [`ListRootsResult`](/specification/draft/schema#listrootsresult)）。

```json theme={null}
{
  "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`](/specification/draft/schema#inputrequiredresult) 是一种 [`Result`](/specification/draft/basic#responses)，
表示在请求完成之前需要额外输入。

* `inputRequests` *（可选）*：客户端必须满足的服务器发起请求的 [`InputRequests`](/specification/draft/schema#inputrequests) 映射。
* `requestState` *（可选）*：仅对服务器有意义的不透明字符串。客户端 **MUST NOT** 检查、解析、修改它，也不得对其内容作出任何假设。

```json theme={null}
{
  "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"
  }
}
```

### Supported Requests

服务器 **MAY** 在以下客户端请求上发送 `InputRequiredResult` 响应：

| 客户端请求                                                                       | 支持 InputRequiredResult |
| --------------------------------------------------------------------------- | ---------------------- |
| [`prompts/get`](/specification/draft/server/prompts#getting-a-prompt)       | 是                      |
| [`resources/read`](/specification/draft/server/resources#reading-resources) | 是                      |
| [`tools/call`](/specification/draft/server/tools#calling-tools)             | 是                      |

服务器 **MUST NOT** 在任何其他客户端请求上发送 `InputRequiredResult` 响应。

### 基本工作流

基本工作流描述服务器如何将向客户端请求额外输入作为客户端-服务器请求的一部分。
在此示例中，我们使用 `tools/call` 作为客户端请求，但同一模式适用于上面列出的任何受支持请求。

值得注意的是，它允许服务器在不维护任何服务器端状态的情况下请求额外信息。
服务器会将任何所需上下文编码到 `requestState` 字段中，客户端在重试时将其原样回显。

```mermaid theme={null}
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)
```

请注意，每个步骤中的请求都是完全独立的：处理重试的服务器不需要除重试请求中直接存在的信息之外的任何信息。

#### Server Requirements (Basic Workflow)

1. 服务器 **MAY** 使用 `InputRequiredResult` 响应任何[受支持的客户端请求](#supported-requests)。

2. `InputRequiredResult` **MAY** 包含 `inputRequests` 字段。
   * `inputRequests` 键是服务器分配的标识符，并且在请求作用域内 **MUST** 唯一。
   * `inputRequests` 值是请求对象，**MUST** 是 [`ElicitRequest`](/specification/draft/schema#elicitrequest)、[`CreateMessageRequest`](/specification/draft/schema#createmessagerequest) 或 [`ListRootsRequest`](/specification/draft/schema#listrootsrequest) 之一

3. `InputRequiredResult` **MAY** 包含 `requestState` 字段。如果指定，该字段是仅对服务器有意义的不透明字符串。服务器可以自由使用任何格式编码状态（例如 base64 编码 JSON、加密 JWT、序列化二进制）。

4. 如果客户端请求包含 `requestState` 字段，服务器 **MUST** 将 `requestState` 视为攻击者可控输入。如果 `requestState` 影响授权、资源访问或业务逻辑，服务器 **MUST** 保护其完整性（例如 HMAC 或 AEAD），
   并且 **MUST** 拒绝未通过验证的状态。只有当篡改最多导致请求失败时，完整性保护才 **MAY** 省略。

5. 为防止重放，服务器 **SHOULD** 在受完整性保护的 `requestState` 载荷中包含以下内容，并在收到时逐一验证：
   * 已认证主体；拒绝由不同主体提供的状态。
   * 较短过期时间（TTL）；拒绝在过期后提供的状态；
   * 发起请求的标识符，例如方法名及其关键参数摘要；拒绝在不匹配请求上提供的状态。
     <Warning>
       请注意，这些措施限制了重放窗口，并防止跨用户和跨请求复用，但它们本身并不保证一次性使用。
       如果服务器要求给定 `requestState` 最多只能被消费一次（例如一次性兑换），则 **MUST** 在服务器端强制执行该不变量。
     </Warning>

6. 服务器 **MUST** 在每个 `InputRequiredResult` 响应中至少包含 `inputRequests` 或 `requestState` 之一。

7. 服务器 **MUST NOT** 发送客户端未在其能力中声明支持的 `inputRequests`。例如，如果客户端未声明支持 `elicitation`，服务器 **MUST NOT** 在 `inputRequests` 字段中包含任何 `elicitation/create` 请求。

8. 服务器 **MUST NOT** 假设客户端会满足 `inputRequests` 或重试原始请求。如果服务器希望反复提示用户提供信息，直到获得完成请求所需的信息，它 **MAY** 在同一请求的多次尝试中返回 `InputRequiredResult`。

#### Client Requirements (Basic Workflow)

1. 如果客户端收到包含 `inputRequests` 字段的 `InputRequiredResult`，客户端 **MUST** 在重试原始请求之前构造所请求的输入。如果 `InputRequiredResult` *不* 包含 `inputRequests` 字段，客户端 **MAY** 立即重试原始请求。
2. 如果 `InputRequiredResult` 包含 `requestState` 字段，客户端 **MUST** 在重试原始请求时回显该字段的精确值。
   客户端 **MUST NOT** 检查、解析、修改 `requestState` 内容，或对其作出任何假设。如果 `InputRequiredResult` 不包含 `requestState` 字段，客户端 **MUST NOT** 在重试中包含该字段。
3. 初始请求和重试之间的 JSON-RPC `id` **MUST** 不同，因为它们是独立请求。
4. `inputRequests` 和 `requestState` 字段只影响客户端对原始请求的重试。它们 **MUST NOT** 用于客户端可能并行发送的任何其他请求。

### 错误处理

服务器 **SHOULD** 校验客户端提供的数据是有效的 `InputResponses` 对象，并且其中的信息可以被正确解析。
协议错误（格式错误的 JSON、无效 schema、服务器内部错误）**SHOULD** 返回带有适当错误码和消息的 JSON-RPC 错误响应。

如果 `InputResponses` 对象中提供了额外的非预期参数，服务器 **SHOULD** 忽略任何它不识别或不需要的信息。

如果客户端未能发送先前 `InputRequests` 中请求的全部信息，而缺失的信息是服务器处理请求所必需的，
服务器 **SHOULD** 用新的 `InputRequiredResult` 响应，再次请求缺失信息，而不是返回错误。

### 安全考量

由于 `requestState` 会经过客户端，恶意或受攻陷的客户端可能会尝试修改它，以改变服务器行为、
绕过授权检查或破坏服务器逻辑。服务器 **MUST** 按上方[服务器要求](#server-requirements-basic-workflow)所述校验请求状态。
