Skip to content

Multi-protocol McpServer for supporting upcoming 2026-07-28 spec #6617

Closed

Description

@lloydrichards

This stems from a discord thread about the state of the v2026-07-28 spec for McpServer

For context, currently the McpServer is one spec behind on v2025-06-18 and there is another issue for adding the latest v2025-11-25 (#6157). However the current RC for v2026-07-28 throws a bit of a spanner in this as it will no longer be a progressive enhancement on top of the existing features but instead a bit of architectural shift toward a stateless protocol:

  • the initialization handshake and protocol-level session are removed;
  • protocol version and client capabilities are provided per request;
  • server/discover advertises supported versions and server capabilities;
  • some existing features are deprecated or moved into extensions;
  • server-to-client interactions and HTTP behavior change.

This makes updating one shared set of MCP schemas increasingly difficult. Supporting one version by widening or replacing the current schemas risks changing the behavior expected by clients using another version.

Related work:

Goal/Scope

Allow one McpServer endpoint to support multiple MCP protocol versions without combining incompatible versions into one permissive schema.

The server should declare the protocols it supports, then when a request arrives, the protocol version used by that client or request should determine:

  • how the request is interpreted
  • which lifecycle and capabilities apply
  • which functionality is available
  • the shape of the response returned to the client

Clients using different protocol versions should therefore be able to communicate with the same endpoint while each observes the schema and behavior defined by its selected version.

The protocol version must resolve the server to a specific wire shape and set of behaviors, rather than being metadata attached after the request has already been interpreted.

Initial built-in support would start at 2025-06-18. Older revisions can be considered separately if they are still needed and we can leave out the extensions for the time being until we have a clear direction.

Proposal

Allow an McpServer to declare the protocol versions it supports:

const Server = McpServer.layerHttp({
  name: "Acme MCP",
  version: "1.0.0",
  path: "/mcp",
  protocols: [
    McpProtocol.v2026_07_28,
    McpProtocol.v2025_11_25,
    McpProtocol.v2025_06_18
  ]
})

At a high level, each supported version would provide the protocol-specific interpretation of the server:

sequenceDiagram
  participant Client as McpClient
  participant Ingress as McpServer ingress
  participant Protocol as Selected protocol behavior
  participant Core as Shared MCP functionality

  alt 2025-06-18 or 2025-11-25
    Client->>Ingress: initialize with protocol version
    Ingress->>Protocol: Select stateful protocol behavior
    Protocol-->>Ingress: Version-specific initialize result
    Ingress-->>Client: Negotiated version and session
    Client->>Ingress: MCP request with session
  else 2026-07-28
    Client->>Ingress: MCP request with protocol version
    Ingress->>Protocol: Select stateless protocol behavior
  end

  Protocol->>Protocol: Validate and decode request using selected schema
  Protocol->>Core: Canonical MCP operation
  Core-->>Protocol: Canonical result
  Protocol->>Protocol: Encode result using the same selected schema
  Protocol-->>Ingress: Version-specific response
  Ingress-->>Client: Response matching the client's protocol version
Loading

Only one version-specific path is selected for a request. The same selection governs both request decoding and response encoding, so a request using 2025-06-18, for example, cannot be decoded or returned using the shape of 2025-11-25.

The shared MCP functionality represents what the server can do: tools, prompts, resources, and their handlers.

The version-specific behavior represents how a particular MCP specification exposes that functionality, including:

  • request and response shapes
  • capability representation
  • initialization, discovery, and session behavior
  • notifications and server-to-client interactions
  • protocol-specific transport requirements

The exact implementation is open for discussion. One possible direction is a protocol adapter or similar boundary between the wire protocol and the shared server functionality. The important behavioral requirement is that protocol selection happens early enough that incompatible versions are interpreted independently, rather than being combined into one permissive schema.

For legacy protocols, selection may happen during initialization and remain associated with the session. For 2026-07-28, selection would instead happen per request using the version information carried by that request.

The questions I would like feedback on are:

  1. Does this describe the right behavioral boundary for supporting both stateful and stateless MCP revisions on one endpoint?
  2. Are there capabilities or lifecycle behaviors that cannot reasonably be projected from shared tools, prompts, and resources?
  3. Are there transport, authentication, or conformance requirements that should be considered before settling on an implementation?

Activity

  1. lloydrichards commented on Jul 25, 2026

    @lloydrichards
    ContributorAuthor

    I'm having quite a bit of success with the adapter-pattern here. What I've done and made is a ProtocolAdaptor and a ProtocolRegistry which can be used to build out different RpcGroup for the different protocols a server can support and then using the protocolVersion and clientId the request can be routed through the right group so the encoding and decoding are specific to the protocol.

    raw initialize
      → inspect protocolVersion
      → select adapter
      → route to versioned key
      → decode and execute through RpcServer
      → store adapter in session
    

    This is all hidden inside the McpServer so the client doesn't need to know whats happening but basically when a request for something like tools/list is made, internally it checks the protocol version and then if supported will route it through the specific RpcGroup

    Client (v2025-06-18) asks
      → tools/list
    
    Server routes
      → @effect/mcp/2025-06-18/tools/list
      - @effect/mcp/2025-11-25/tools/list
    

    for now I'll try keep this seem as small as possible and just stick with the current v2025-06-18, but if it works then we can then parallelize the work for adding v2025-11-25 and v2026-07-28 without them stepping on each other 🤞

    export const v2025_06_18 = Internal.make({
      protocolVersion: "2025-06-18",
      clientRpcs: McpSchema.ClientRpcs,
      clientNotificationRpcs: McpSchema.ClientNotificationRpcs,
      serverRequestRpcs: McpSchema.ServerRequestRpcs,
      serverNotificationRpcs: McpSchema.ServerNotificationRpcs
    })
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