Skip to content

R6: Modern MCP core, with legacy MCP as a deletable bridge edge #1671

Open

Description

@jlouvel

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:

  1. Modern core. Interceptors and tool filters run on modern types. The 2025-11-25 projection is produced at the edge, after policy, so policy runs once for both eras.
  2. Server edge for legacy clients. initialize is answered from the same data as server/discover, advertising only tools, resources and prompts. No server-to-client requests (no sampling, no roots/list, no request-style elicitation). A modern input_required (MRTR) result returns an error to legacy clients; it is not mapped to legacy elicitation.
  3. 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 same mcp-core artifact, so a module would add release overhead without removing a dependency. Dropping the bridge later means deleting one package and the seam.
  4. Public API, breaking. McpServerAdapter and McpClientAdapter getters and setters that take 2025-11-25 SDK 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.
  5. Architecture test (ArchUnit, or a plain import-scanning test to avoid a new test dependency):
    • Nothing outside internal.legacy depends on it except through the seam.
    • Only internal.legacy imports the SDK's legacy server and spec packages; everything else uses io.modelcontextprotocol.modern, public API included, with no allow-list.
    • internal.legacy references no SDK logging, sampling or roots types. The server edge never issues Mcp-Session-Id; the client edge sends one only when the caller opts in through the legacySession* properties (R7: Dual-era MCP client: detect the server's era and fall back to legacy servers #1670).
  6. Behavior tests:
    • A legacy initialize response advertises no logging, roots or sampling capability and carries no session ID.
    • Legacy GET returns 405 and logging/setLevel returns -32601.
    • The same registrations serve a legacy and a modern client, with policy applied once.

The client side of the bridge (fallback to legacy servers) is #1670, under the same limits.

Open questions:

  • Should tools that can return input_required be hidden from legacy tools/list rather than fail on call?
  • Should the legacy initialize error 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

Related: #1640, #1670, #1664, #1665.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions