Skip to content

Pass InputRequiredResult through the MCPServer prompt and resource pipelines#3020

Merged
maxisbey merged 4 commits into
mainfrom
iir-non-tool-request
Jun 29, 2026
Merged

Pass InputRequiredResult through the MCPServer prompt and resource pipelines#3020
maxisbey merged 4 commits into
mainfrom
iir-non-tool-request

Conversation

@maxisbey

@maxisbey maxisbey commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Motivation and Context

input-required-result-non-tool-request was the last waived server conformance scenario: at 2026-07-28 a server MAY answer prompts/get and resources/read with an InputRequiredResult (SEP-2322 multi-round-trip requests), but only the tools/call pipeline passed one through from MCPServer — the prompt pipeline silently coerced it into a text message and the resource pipeline had no path for it. The low-level Server, the per-version result surfaces, and the client auto-loop (#2998) already supported all three methods, so this closes the gap at the one remaining tier, mirroring the tools pass-through from #2974:

  • Prompt.render / MCPServer.get_prompt pass an InputRequiredResult returned by an @mcp.prompt() function through unchanged; the retry's answers arrive on ctx.input_responses, with ctx.request_state carrying the echoed state.
  • ResourceTemplate.create_resource / MCPServer.read_resource do the same for @mcp.resource() template functions. Static resource functions can never read the retry (they take no Context), so FunctionResource.read now rejects an InputRequiredResult loudly instead of JSON-dumping it as resource text.
  • Context.read_resource stays a pure content reader: an InputRequiredResult from a template raises a clear RuntimeError there. A handler that wants to receive and forward one as its own result calls MCPServer.read_resource(uri, context) directly, which carries the union.
  • Era handling needed zero new code: the per-version result surfaces already reject the frame toward pre-2026 sessions with -32603, exactly as for tools.

The everything-server gains the test_input_required_result_prompt fixture the scenario drives, and the waiver is removed from both expected-failures files — both server conformance baselines are now empty.

How Has This Been Tested?

  • The target scenario passes locally at the pinned harness (2/2 checks; previously failed round 1).
  • All three CI server conformance legs run locally at the pin with the emptied baselines: active 42/42, draft 71/71, 2026-07-28 100/100 — zero failures, no stale entries.
  • Full suite at 100% branch coverage. New tests cover the prompt/resource round trips through an in-memory Client, the seam pass-throughs (identity), both ctx.read_resource arcs, the static-resource rejection, and the legacy-session -32603 posture for both methods.
  • mkdocs build --strict passes with the new docs section and runnable example.

Coverage note: the scenario exercises prompts/get only — no conformance scenario currently drives resources/read returning InputRequiredResult, so the resource side is proven by the SDK test suite alone. Worth adding a scenario in the conformance repo.

Breaking Changes

Type-level, documented in docs/migration.md: MCPServer.get_prompt() / read_resource() (and Prompt.render() / ResourceTemplate.create_resource()) now return ... | InputRequiredResult; direct callers narrow with isinstance. ctx.read_resource() is unchanged — it still returns content, and now raises a clear RuntimeError if the resource requests input (forward via MCPServer.read_resource(uri, context) instead).

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

Additional context

Part of #2891. With this change, .github/actions/conformance/expected-failures.yml and expected-failures.2026-07-28.yml are both fully empty — every scenario in the pinned conformance harness passes against the SDK with no waivers.

AI Disclaimer

…edResult through

At 2026-07-28, prompts/get and resources/read may answer with an
InputRequiredResult (SEP-2322 multi-round-trip requests), but only the
tools/call pipeline passed one through from MCPServer. This closed the last
waived server conformance scenario, input-required-result-non-tool-request.

- Prompt.render and MCPServer.get_prompt pass an InputRequiredResult returned
  by an @mcp.prompt() function through unchanged; the retry's answers arrive
  on ctx.input_responses, with ctx.request_state carrying the echoed state.
- ResourceTemplate.create_resource and MCPServer.read_resource do the same
  for @mcp.resource() template functions. Static resource functions can never
  read the retry (no Context), so FunctionResource.read now rejects an
  InputRequiredResult loudly instead of JSON-dumping it as content.
- Context.read_resource keeps its narrow content type by default and grows
  ClientSession-style allow_input_required overloads so a handler can opt in
  and forward a template's InputRequiredResult as its own result.
- Era handling is unchanged: the per-version result surfaces already reject
  the frame toward pre-2026 sessions with -32603, exactly as for tools.
- The everything-server gains the test_input_required_result_prompt fixture
  the conformance scenario drives; both expected-failures baselines are now
  empty and all three server conformance legs pass with zero waivers.
- Docs: a "Beyond tools" section in multi-round-trip.md with a runnable
  example, the low-level-server.md handler list, and a migration.md entry
  for the widened return types.
@maxisbey maxisbey marked this pull request as ready for review June 29, 2026 14:34

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 21 files

Re-trigger cubic

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any bugs in this change — the pass-through logic mirrors the existing tools/call pattern and the new tests cover both pipelines well — but this is a feature PR with type-level breaking changes to the public MCPServer API, so it deserves a human look.

Extended reasoning...

Overview

This PR widens the MCPServer prompt and resource pipelines to pass InputRequiredResult through (SEP-2322 multi-round-trip requests), mirroring the existing tools/call pass-through. It touches Prompt.render, PromptManager.render_prompt, ResourceTemplate.create_resource, ResourceManager.get_resource, FunctionResource.read, MCPServer.get_prompt/read_resource/_handle_get_prompt/_handle_read_resource, and Context.read_resource (new allow_input_required overloads). It also removes the last server conformance waivers, adds an everything-server fixture, a docs tutorial, migration notes, and a substantial set of new tests.

Security risks

Low. The change passes a result object through unchanged rather than introducing new input handling, and the era gate (pre-2026 sessions rejecting the frame with -32603) is reused from the existing tools path and pinned by new tests. The static-resource case now raises loudly instead of JSON-dumping the object, which is a strictness improvement. No auth, crypto, or permission code is touched.

Level of scrutiny

Moderate. The implementation is consistent and well-tested (round-trip tests through an in-memory client, identity pass-through tests, legacy-session error tests, and the ctx.read_resource opt-in/raise arcs), but the PR changes the declared return types of several public API methods (get_prompt, read_resource, Prompt.render, ResourceTemplate.create_resource), which is a documented breaking change for direct callers. API-surface and protocol-conformance decisions like this should be confirmed by a maintainer rather than auto-approved.

Other factors

No bugs were found by the automated review. Test coverage of the new behavior is thorough, conformance baselines are now empty (CI will fail on stale entries, so that claim is self-checking), and the docs/migration guide are updated. The breadth (21 files) and the API/type changes are the main reasons to keep a human in the loop.

Drop the allow_input_required overloads: ctx.read_resource is a content
reader, so an InputRequiredResult from a resource template always raises,
with the error pointing at the forwarding path. A handler that wants to
receive and forward the result as its own calls
MCPServer.read_resource(uri, context) directly, which carries the union.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread docs/migration.md Outdated
…resource pipelines

tools/call already re-raises MCPError so a handler can answer with a typed
protocol error (e.g. a missing-client-capability rejection with
data.requiredCapabilities); the prompt and resource pipelines wrapped it
into generic internal errors, destroying the code and data. Add the same
carve-out at the five wrap sites (Prompt.render, MCPServer.get_prompt,
ResourceTemplate.create_resource, FunctionResource.read,
MCPServer.read_resource) — this matters now that prompt and template
functions participate in the multi-round-trip flow and must be able to
reject a request themselves.

Also document that a tool whose dependencies elicit owns its request_state
channel, so handlers should not forward another result's InputRequiredResult
from such a tool.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any bugs, but this PR widens the return types of the core prompt/resource pipelines (a documented breaking change) and empties both conformance baselines, so it warrants a human review rather than auto-approval.

Extended reasoning...

Overview

The PR threads InputRequiredResult through the MCPServer prompt and resource pipelines: Prompt.render, PromptManager.render_prompt, ResourceTemplate.create_resource, ResourceManager.get_resource, MCPServer.get_prompt/read_resource, and the corresponding low-level handlers now return a union with InputRequiredResult. It also adds MCPError pass-through in the prompt/resource error handling paths, a static-resource rejection in FunctionResource.read, a RuntimeError guard in Context.read_resource, a new everything-server fixture, a new docs tutorial, and removes the last waived scenarios from both conformance expected-failures files.

Security risks

Low. No auth, crypto, or permission logic is touched. The changes are type-widening pass-throughs plus exception-class handling adjustments (except (ResourceError, MCPError): raise). The era gate that rejects InputRequiredResult toward pre-2026 sessions is unchanged and pinned by new tests, so there is no new data-exposure surface for legacy clients.

Level of scrutiny

This deserves a human pass: it is a feature PR with documented breaking changes to public method signatures (MCPServer.get_prompt/read_resource, Prompt.render, ResourceTemplate.create_resource), it changes exception-propagation behavior in the prompt and resource pipelines (handler-raised MCPError now surfaces with its original code/data instead of being wrapped), and it empties both conformance baselines — all of which affect downstream SDK users and CI posture, not just internals.

Other factors

The implementation closely mirrors the existing tools/call pass-through and is well-covered by new tests (round-trip via in-memory client, legacy-session rejection, static-resource rejection, ctx.read_resource guard, MCPError code/data propagation). The single reviewer comment (cubic) about the migration-doc call form was addressed in a follow-up commit. I found no correctness issues; the deferral is purely about the breadth and breaking nature of the change.

ctx.read_resource passed the handler's own Context into the nested resource
template, so on the outer request's retry the template saw input_responses
and request_state addressed to the outer handler — with a colliding key a
write-once template would silently consume an answer to a different
question. The continuation payload belongs to the wire request's target
dispatch only, so nested invocations now use Context._nested_invocation():
same request infrastructure, no input_responses/request_state — a nested
template always behaves as round one, making ctx.read_resource's
raise-on-input-required contract deterministic across rounds. The explicit
forwarding path (MCPServer.read_resource(uri, context)) is unchanged: there
the caller deliberately re-addresses the payload.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any bugs in this change — the pass-through logic, era gating, and nested-context isolation all look consistent with the existing tools/call path and are well covered by the new tests. That said, this is a breaking type-level change to the core prompt/resource pipelines plus a behavior change in ctx.read_resource and MCPError propagation, so it deserves a human reviewer's sign-off.

Extended reasoning...

Overview

This PR passes InputRequiredResult through the MCPServer prompt and resource pipelines (SEP-2322 multi-round-trip), mirroring the existing tools/call support. It touches Prompt.render, PromptManager.render_prompt, ResourceTemplate.create_resource, ResourceManager.get_resource, MCPServer.get_prompt/read_resource/handlers, Context.read_resource (new _nested_invocation helper and a RuntimeError on InputRequiredResult), FunctionResource.read (rejects InputRequiredResult from static resources), MCPError pass-through in several try/except layers, conformance baselines, docs, the everything-server example, and a sizable batch of new tests.

Security risks

Low. No auth, crypto, or permission code is touched. The main protocol-surface consideration is that the nested-invocation context deliberately strips input_responses/request_state so a nested resource read cannot consume answers addressed to the outer handler — this is a hardening choice and is pinned by a dedicated test. The static-resource rejection of InputRequiredResult avoids accidentally serializing a request object as resource content. The era gate (-32603 toward pre-2026 sessions) reuses the existing tools-path mechanism with no new code.

Level of scrutiny

High. This is production SDK code on the main request-handling pipelines, includes a documented breaking change (return-type unions on MCPServer.get_prompt/read_resource, Prompt.render, ResourceTemplate.create_resource), and changes exception-propagation semantics (handler-raised MCPError now keeps its code/data instead of being wrapped). These are exactly the kinds of API-surface and behavior decisions a maintainer should weigh in on, even though the implementation closely mirrors the already-merged tools/call pattern.

Other factors

The bug hunting system found no bugs, and I didn't find any either: the isinstance pass-throughs are placed before validation/wrapping, MCPError re-raise clauses are added consistently at each wrapping layer, and the new tests cover both rounds of the flow, the legacy-session error posture, ctx.read_resource isolation, and static-resource rejection. The single prior reviewer comment (migration doc call-form) was addressed. Both conformance baselines are now empty, which the author reports verifying locally. Given the breadth and the breaking change, I'm deferring rather than approving.

@maxisbey maxisbey merged commit 8d0f928 into main Jun 29, 2026
35 checks passed
@maxisbey maxisbey deleted the iir-non-tool-request branch June 29, 2026 15:51
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.

2 participants