Repository navigation
Description
Follow-up to #1640. McpServerAdapter now serves both eras (#1664, #1665), but with the legacy model at the core: requests with MCP-Protocol-Version: 2026-07-28 go to the SDK's modern stateless server, everything else to the 2025-11-25 path, and interceptors and tool filters run on 2025-11-25 types, with modern tools seen through their legacy projection.
This issue inverts that. The modern API becomes the only registration and policy model, and legacy MCP (2025-11-25 and earlier) is accepted only as a translation edge in front of it. The edge exposes nothing that 2026-07-28 deprecates or removes, so it can be deleted when the deprecation window closes.
What the legacy edge must not expose:
| Feature | 2026-07-28 status | Legacy edge |
|---|---|---|
| Roots, Sampling, Logging | Deprecated | Not advertised, not requested |
| HTTP+SSE transport | Deprecated | Not supported |
ping |
Removed | Empty result at the edge, no feature behind it |
logging/setLevel |
Removed | -32601 |
notifications/roots/list_changed |
Removed | 202, dropped |
Mcp-Session-Id sessions |
Removed | Never issued |
Streamable HTTP GET stream |
Removed | 405 |
Last-Event-ID resumption |
Removed | Not supported |
Delivery:
- Modern core. Interceptors and tool filters run on modern types. The
2025-11-25projection is produced at the edge, after policy, so policy runs once for both eras. - Server edge for legacy clients.
initializeis answered from the same data asserver/discover, advertising only tools, resources and prompts. No server-to-client requests (no sampling, noroots/list, no request-style elicitation). A moderninput_required(MRTR) result returns an error to legacy clients; it is not mapped to legacy elicitation. - Code layout. The edge lives in
org.restlet.ext.mcp.internal.legacy, behind one seam (McpLegacyEdge) that the core calls for non-modern requests. No new Maven module: the SDK ships both servers in the samemcp-coreartifact, so a module would add release overhead without removing a dependency. Dropping the bridge later means deleting one package and the seam. - Public API, breaking.
McpServerAdapterandMcpClientAdaptergetters and setters that take2025-11-25SDK stateless specifications are removed and replaced by modern-only registration. No deprecation step: 3.0 is still at M1, so legacy types leave the public API now. - Architecture test (ArchUnit, or a plain import-scanning test to avoid a new test dependency):
- Nothing outside
internal.legacydepends on it except through the seam. - Only
internal.legacyimports the SDK's legacy server and spec packages; everything else usesio.modelcontextprotocol.modern, public API included, with no allow-list. internal.legacyreferences no SDK logging, sampling or roots types. The server edge never issuesMcp-Session-Id; the client edge sends one only when the caller opts in through thelegacySession*properties (R7: Dual-era MCP client: detect the server's era and fall back to legacy servers #1670).
- Nothing outside
- Behavior tests:
- A legacy
initializeresponse advertises no logging, roots or sampling capability and carries no session ID. - Legacy
GETreturns 405 andlogging/setLevelreturns-32601. - The same registrations serve a legacy and a modern client, with policy applied once.
- A legacy
The client side of the bridge (fallback to legacy servers) is #1670, under the same limits.
Open questions:
- Should tools that can return
input_requiredbe hidden from legacytools/listrather than fail on call? - Should the legacy
initializeerror code match Ikanos (-32022) or the SDK (-32602)? - Which release removes the legacy edge, given the twelve-month deprecation window from 2026-07-28?
Design memo: https://claude.ai/artifact/73QEwzKyg79YHVHjAyHdLd
Activity