Summary
When a Modern (2026-07-28) backend answers a relayed tools/call, prompts/get or resources/read with resultType: "input_required", vMCP currently fails with errModernInputRequired — a deliberate seam at pkg/vmcp/client/modern.go:64. This issue tracks implementing the relay.
This is the coherent shape of MRTR support in vMCP, and it is deliberately not the shape that was rejected. Recording the distinction, because the two look similar and only one is buildable.
What to build: pure relay, stateless at vMCP
Per SEP-2322's ephemeral flow, the server holds no state between rounds — the handler is re-invoked from scratch and all cross-round state round-trips through an opaque requestState blob. So:
- Forward the backend's
inputRequests to the Modern client.
- Wrap the backend's opaque
requestState with vMCP routing context (which backend it came from), keeping it opaque to the client.
- On the client's re-issued call carrying
inputResponses plus the echoed requestState, unwrap it and relay to the same backend, which re-invokes its own handler.
vMCP holds no per-round state. That is what makes it viable under a revision that removed sessions.
Prerequisite: the egress half — driving and propagating the loop in pkg/vmcp/client. The errModernInputRequired seam is where that begins.
What was rejected, and why
Bridging a Legacy backend's live mid-call elicitation to a Modern client. That backend interaction is a blocked goroutine holding an open session; it cannot be serialized into requestState, and re-invoking is impossible because side effects have already run. Making it work would mean parking the in-flight call server-side behind an unguessable resume token, with TTL and eviction, replica affinity (no Mcp-Session-Id means a load balancer cannot route the resume), and identity binding on a token that becomes a capability to hijack someone else's in-flight call.
That is a session in disguise — precisely what 2026-07-28 removed. The specification's own answer for genuinely stateful input_required work is the Tasks extension (SEP-2663: tools/call returns resultType: "task" with a taskId; the client polls tasks/get and answers via inputResponses on tasks/update). If Legacy-backend elicitation for Modern clients is ever genuinely required, Tasks is the path — not parked calls.
See the "elicitation and sampling are unavailable to Modern clients" section in docs/arch/10-virtual-mcp-architecture.md for the full rationale as landed.
Scope note worth reading before prioritising
SEP-2577 deprecates sampling, logging and roots as of 2026-07-28 (annotation-only, functional for at least twelve months), with the sanctioned migration for sampling being direct LLM-provider integration. So by the time Modern backends exist to relay from, elicitation is MRTR's only durable consumer. That materially shrinks the value of this work relative to how it looks at first glance, and is why it was deferred rather than built alongside the Modern client-facing surface.
Also relevant: go-sdk's assertServerInitiatedRequestAllowed gates elicitation, sampling and roots on the negotiated protocol version alone and never consults client capabilities — so there is no "advertise less" alternative that recovers these features for Modern clients. It is MRTR or nothing.
Refs #5743. Related: #6051 (the disposition that made the current behaviour explicit and honest).
Summary
When a Modern (2026-07-28) backend answers a relayed
tools/call,prompts/getorresources/readwithresultType: "input_required", vMCP currently fails witherrModernInputRequired— a deliberate seam atpkg/vmcp/client/modern.go:64. This issue tracks implementing the relay.This is the coherent shape of MRTR support in vMCP, and it is deliberately not the shape that was rejected. Recording the distinction, because the two look similar and only one is buildable.
What to build: pure relay, stateless at vMCP
Per SEP-2322's ephemeral flow, the server holds no state between rounds — the handler is re-invoked from scratch and all cross-round state round-trips through an opaque
requestStateblob. So:inputRequeststo the Modern client.requestStatewith vMCP routing context (which backend it came from), keeping it opaque to the client.inputResponsesplus the echoedrequestState, unwrap it and relay to the same backend, which re-invokes its own handler.vMCP holds no per-round state. That is what makes it viable under a revision that removed sessions.
Prerequisite: the egress half — driving and propagating the loop in
pkg/vmcp/client. TheerrModernInputRequiredseam is where that begins.What was rejected, and why
Bridging a Legacy backend's live mid-call elicitation to a Modern client. That backend interaction is a blocked goroutine holding an open session; it cannot be serialized into
requestState, and re-invoking is impossible because side effects have already run. Making it work would mean parking the in-flight call server-side behind an unguessable resume token, with TTL and eviction, replica affinity (noMcp-Session-Idmeans a load balancer cannot route the resume), and identity binding on a token that becomes a capability to hijack someone else's in-flight call.That is a session in disguise — precisely what 2026-07-28 removed. The specification's own answer for genuinely stateful
input_requiredwork is the Tasks extension (SEP-2663:tools/callreturnsresultType: "task"with ataskId; the client pollstasks/getand answers viainputResponsesontasks/update). If Legacy-backend elicitation for Modern clients is ever genuinely required, Tasks is the path — not parked calls.See the "elicitation and sampling are unavailable to Modern clients" section in
docs/arch/10-virtual-mcp-architecture.mdfor the full rationale as landed.Scope note worth reading before prioritising
SEP-2577 deprecates sampling, logging and roots as of 2026-07-28 (annotation-only, functional for at least twelve months), with the sanctioned migration for sampling being direct LLM-provider integration. So by the time Modern backends exist to relay from, elicitation is MRTR's only durable consumer. That materially shrinks the value of this work relative to how it looks at first glance, and is why it was deferred rather than built alongside the Modern client-facing surface.
Also relevant: go-sdk's
assertServerInitiatedRequestAllowedgates elicitation, sampling and roots on the negotiated protocol version alone and never consults client capabilities — so there is no "advertise less" alternative that recovers these features for Modern clients. It is MRTR or nothing.Refs #5743. Related: #6051 (the disposition that made the current behaviour explicit and honest).