You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make forwarding tests' Legacy dependency explicit (#6051)
The seven real-backend forwarding tests exercise server-initiated elicitation
and sampling, which MCP 2026-07-28 removes outright: go-sdk's
assertServerInitiatedRequestAllowed refuses them on negotiated protocol version
alone, never consulting client capabilities. They passed only because
newRealTestServer leaves Modern dispatch disabled, so the downstream client
landed on Legacy incidentally.
Pin the downstream client to Legacy explicitly by answering its server/discover
probe with a successful Legacy-only DiscoverResult -- what a Legacy-only server
actually sends -- and assert the pin fired, so deleting it fails loudly instead
of silently relying on a server-side default.
Two of the seven passed vacuously without the pin: a sessionless connect error
satisfied their lenient assertions regardless of the capability advertisement
they exist to exercise. Verified by falsifying their premise -- with the
capability granted they pass unpinned and fail pinned.
Add Modern-path tests recording today's actual behaviour for progress and
logging, so all five dispositions are executable rather than prose, and
document why each is what it is: progress is pinned because dispatchModern
cannot stream, not because Modern lacks the channel.
Refs #5959, #6033, #5743, #6058, #6062.
elicitation/sampling/roots purely by negotiated protocol version, so no
335
+
capability negotiation can restore the Legacy forwarding model for Modern
336
+
clients.
337
+
- A server that never returns `input_required` is fully SEP-2575-conformant:
338
+
the per-request `clientCapabilities` a client declares are an offer the
339
+
server may use, not an obligation.
340
+
- SEP-2577 deprecates sampling (and logging and roots) outright as of
341
+
2026-07-28, with direct LLM-provider integration as the sanctioned
342
+
replacement — so elicitation is the only durable consumer a future MRTR
343
+
implementation would serve.
344
+
345
+
Legacy clients keep the full mid-call forwarding behaviour unchanged; the
346
+
forwarding integration tests pin their downstream clients to Legacy explicitly
347
+
(`legacyPinningRoundTripper` in `pkg/vmcp/server`'s external test package)
348
+
because that surface exists only on a Legacy session.
349
+
350
+
**Bridging was considered, costed, and rejected.** Serving MRTR to Modern
351
+
clients on top of a *Legacy*
352
+
backend would require parking the live, mid-flight backend call server-side
353
+
(the blocked goroutine and its open session cannot be serialized into the
354
+
opaque `requestState` the SEP designed for handler re-invocation) and keying
355
+
the resume on an unguessable token — per-round server state with TTL/eviction,
356
+
identity binding on a token that becomes a capability to resume someone else's
357
+
in-flight call, and replica affinity with no `Mcp-Session-Id` to route on. That
358
+
would reintroduce, in different clothes, the per-request server state the
359
+
2026-07-28 revision removed. The spec's own sanctioned path for genuinely stateful
360
+
`input_required` work is the **Tasks** extension (SEP-2663: `tools/call`
361
+
returns `resultType: "task"` with a `taskId`; the client polls `tasks/get` and
362
+
answers outstanding `inputRequests` via `inputResponses` on `tasks/update`;
363
+
note SEP-2663 supersedes SEP-1686 and removed the blocking `tasks/result`
364
+
method for the same reasons argued here) — if Modern-client elicitation over
365
+
Legacy backends is ever truly demanded, that is the machinery to reach for,
366
+
not parked `tools/call`.
367
+
368
+
The coherent future MRTR shape for a re-aggregating gateway is
369
+
**Modern-client ↔ Modern-backend pass-through** — relay a Modern backend's
370
+
`inputRequests`/`requestState` to the client and the client's
371
+
`inputResponses` back, genuinely stateless at vMCP. It requires the egress
372
+
half first (today a Modern backend's `input_required` surfaces as
373
+
`errModernInputRequired`, the seam left in `pkg/vmcp/client`), and by the time
374
+
Modern backends exist to relay from, SEP-2577's deprecations make elicitation
375
+
its only durable consumer; see #5743.
376
+
377
+
Progress and log notifications toward Modern clients are a separate concern
378
+
from MRTR: they remain spec-legal as request-scoped notifications on the
379
+
POST-initiated SSE response stream (SEP-2260 requires messages on that stream
380
+
to relate to the originating request; `progressToken` is unchanged), which the
381
+
single-shot dispatcher does not produce today — a vMCP streaming-dispatch gap,
382
+
not a spec absence.
383
+
292
384
## Served MCP Capabilities
293
385
294
386
Beyond tools, vMCP aggregates and serves the full complement of MCP capabilities. Every served capability flows through the domain **core** (`pkg/vmcp/core`), so the same admission decision that filters `tools/list` also gates reads, gets, and completions.
@@ -484,6 +576,13 @@ While a backend `tools/call` (or other request) is in flight, the backend may is
**Known limitation (Modern clients)**: everything in this section describes a
580
+
**Legacy (2025-11-25) client session**. For Modern (2026-07-28) clients there
581
+
is no session and no server-initiated request channel, so none of this
582
+
forwarding applies — see
583
+
[Limitation: elicitation and sampling are unavailable to Modern clients](#limitation-elicitation-and-sampling-are-unavailable-to-modern-clients)
584
+
for what a Modern caller gets instead.
585
+
487
586
**Known limitation (logging level)**: forwarded backend logging is not yet filtered to the downstream client's requested `logging/setLevel`. vMCP requests debug-level logging from the backend so it emits `notifications/message`, and every such notification is forwarded — the downstream client's own level preference is not applied to the relayed stream.
488
587
489
588
**Known limitation (resource-template authorization)**: a resource template is advertised on the template-string entity (e.g. `file:///logs/{date}.txt`), but a concrete read is admission-checked on the **expanded** URI (e.g. `file:///logs/2025-01-01.txt`). Operators should therefore author resource authorization policies against concrete URI patterns, not the template string.
0 commit comments