[Server] Serve both protocol eras from one endpoint (#455)

* [Server] Serve both protocol eras from one endpoint

The two lifecycles share a transport, not a dispatcher. StreamableHttpTransport
classifies each request - a 2026-07-28 envelope, an initialize handshake, or a
session-bound follow-up - through InboundClassifier and routes it to the
dispatcher that owns it, so one URL answers a modern client and a handshake-era
one alike. Builder::build() carries both; withoutModernEra() opts out and
setModernVersions() narrows what the modern leg answers for.

InputRequiredShim lets a handler written for multi round-trip requests also
serve a handshake-era client, by turning each ask into the request/response
exchange that era has - so a handler is written once rather than twice.

The conformance fixture collapses into one server for the same reason: both
legs now hit the same URL.

* Trim redundant comments, fix stale in-memory claim in conformance fixture

* Drop stale json-schema-2020-12 baseline entries

The new fixture tool makes this scenario pass in both eras now.

* Reject handshake revisions in setModernVersions(), warn on unsafe custom middleware

Copilot review: setModernVersions() accepted revisions InboundClassifier
would never route to the modern leg. StreamableHttpTransport now warns
when a custom middleware list carries ProtocolVersionMiddleware, since
it runs before era classification and rejects modern-era traffic by
default. Also fixed a misleading docblock claim on the same middleware.
This commit is contained in:
Christopher Hertel 2026-08-19 01:36:56 +02:00 • committed by GitHub
commit cbceef06eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5,6 +5,7 @@ All notable changes to `mcp/sdk` will be documented in this file.
0.8.0
-----
* Serve both protocol eras from one endpoint: `StreamableHttpTransport` classifies each request — a `2026-07-28` envelope, an `initialize` handshake, or a session-bound follow-up — through the new `Mcp\Server\Wire\InboundClassifier` and routes it to the dispatcher that owns it, so a single URL answers a modern client and a handshake-era one alike. `Server::builder()->build()` now carries both dispatchers; `Builder::withoutModernEra()` opts out and `Builder::setModernVersions()` narrows what the modern leg answers for. `Mcp\Server\InputRequiredShim` lets a handler written for multi round-trip requests also serve a handshake-era client, by turning each ask into the request/response exchange that era has.
* Carry W3C trace context through a request (SEP-414): `traceparent`, `tracestate` and `baggage` in a request's `_meta` are exposed to handlers as `RequestContext::getTraceContext()` and echoed onto the notifications that request causes, so a span stays joined across the response stream. Values pass through exactly as they arrived, and no OpenTelemetry dependency is added.
* Deliver notifications on a `subscriptions/listen` stream (SEP-2575), which previously acknowledged and then carried nothing for the rest of its life. New `Mcp\Server\Subscription\NotificationBusInterface` with two implementations — `InMemoryNotificationBus` for stdio and persistent runtimes, `Psr16NotificationBus` for PHP-FPM, where the worker holding the stream open and the worker publishing are different processes — set with `Builder::setNotificationBus()`. Registry changes are published automatically through a `PublishingEventDispatcher` that wraps whatever PSR-14 dispatcher was configured. `Builder::setSubscriptionLifetime()` replaces the hard-coded 30-second ceiling, where `0` means "until the client or the runtime ends it".
* Add `Mcp\Server\Wire\CachePolicy`, set with `Builder::setCachePolicy()`, to configure the SEP-2549 caching hints the 2026-07-28 lifecycle stamps on a cacheable result. The conservative `ttlMs: 0, cacheScope: private` stays the default, since `public` lets a shared proxy serve one caller's answer to another and only the operator can make that call. A `ReadResourceResult` may also carry its own `ttlMs`/`cacheScope`, which win over the policy.