Skip to content

Recognize multi round-trip input_required results per SEP-2322#433

Open
koic wants to merge 1 commit into
modelcontextprotocol:mainfrom
koic:mrtr_result_type
Open

Recognize multi round-trip input_required results per SEP-2322#433
koic wants to merge 1 commit into
modelcontextprotocol:mainfrom
koic:mrtr_result_type

Conversation

@koic

@koic koic commented Jun 30, 2026

Copy link
Copy Markdown
Member

Motivation and Context

SEP-2322 (modelcontextprotocol/modelcontextprotocol#2322, merged for the 2026-07-28 spec release) introduces Multi Round-Trip Requests: instead of issuing in-flight server-to-client JSON-RPC requests, a server answers with a result whose resultType is "input_required", carrying an inputRequests map (of sampling/createMessage, roots/list, and elicitation/create request shapes) and an opaque requestState; the client fulfills the requests and re-issues the original request with inputResponses and the echoed requestState.

The wire contract (the resultType discriminator and the inputRequests/requestState shape) stayed stable across all three closed TypeScript prototype iterations (typescript-sdk#2062/#2065, the v2-stateless stack, and #2251) and the Python draft (python-sdk#2322), but the server-side suspend/resume mechanism is still unsettled in both SDKs (typescript-sdk#2251 was put on hold on 2026-06-08). This change therefore implements only the stable, additive vocabulary and the client-side recognition, leaving server emission and automatic resumption for a follow-up once the reference design lands:

  • New MCP::ResultType module with COMPLETE and INPUT_REQUIRED constants documenting the resultType values.
  • MCP::Client raises the new MCP::Client::InputRequiredError (exposing input_requests, request_state, and the raw result) when any response carries resultType: "input_required", instead of silently returning a non-final result as if it were the answer. The check lives in the shared request path, so every client method is covered. Servers on stable protocol versions never emit resultType, so default behavior is unchanged.

Part of #382.

How Has This Been Tested?

New tests in test/mcp/client_test.rb:

  • call_tool raises InputRequiredError for an input_required result and exposes input_requests, request_state, and the full raw result
  • call_tool returns normally when resultType is "complete" and when it is absent (wire-compat regression for stable-protocol servers)
  • list_tools also raises for input_required results, proving the recognition covers the shared request path

bundle exec rake (tests, RuboCop, and conformance baseline) passes.

Breaking Changes

None for spec-compliant stable servers, which never send resultType. A response that does carry resultType: "input_required" now raises MCP::Client::InputRequiredError instead of being returned as a final result, which was always a misinterpretation of the draft semantics.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

## Motivation and Context

SEP-2322 (modelcontextprotocol/modelcontextprotocol#2322, merged for the 2026-07-28 spec release)
introduces Multi Round-Trip Requests: instead of issuing in-flight server-to-client JSON-RPC requests,
a server answers with a result whose `resultType` is `"input_required"`, carrying an `inputRequests` map
(of `sampling/createMessage`, `roots/list`, and `elicitation/create` request shapes) and
an opaque `requestState`; the client fulfills the requests and re-issues the original request with
`inputResponses` and the echoed `requestState`.

The wire contract (the `resultType` discriminator and the `inputRequests`/`requestState` shape)
stayed stable across all three closed TypeScript prototype iterations (typescript-sdk#2062/#2065,
the v2-stateless stack, and #2251) and the Python draft (python-sdk#2322), but the server-side
suspend/resume mechanism is still unsettled in both SDKs (typescript-sdk#2251 was put on hold on 2026-06-08).
This change therefore implements only the stable, additive vocabulary and the client-side recognition,
leaving server emission and automatic resumption for a follow-up once the reference design lands:

- New `MCP::ResultType` module with `COMPLETE` and `INPUT_REQUIRED` constants documenting the `resultType` values.
- `MCP::Client` raises the new `MCP::Client::InputRequiredError` (exposing `input_requests`, `request_state`,
  and the raw `result`) when any response carries `resultType: "input_required"`, instead of silently returning
  a non-final result as if it were the answer. The check lives in the shared request path,
  so every client method is covered. Servers on stable protocol versions never emit `resultType`,
  so default behavior is unchanged.

Part of modelcontextprotocol#382.

## How Has This Been Tested?

New tests in `test/mcp/client_test.rb`:

- `call_tool` raises `InputRequiredError` for an `input_required` result and exposes `input_requests`,
  `request_state`, and the full raw result
- `call_tool` returns normally when `resultType` is `"complete"` and when it is absent
  (wire-compat regression for stable-protocol servers)
- `list_tools` also raises for `input_required` results, proving the recognition covers the shared request path

`bundle exec rake` (tests, RuboCop, and conformance baseline) passes.

## Breaking Changes

None for spec-compliant stable servers, which never send `resultType`. A response that does carry
`resultType: "input_required"` now raises `MCP::Client::InputRequiredError` instead of being returned as
a final result, which was always a misinterpretation of the draft semantics.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant