Skip to content

SSE transport advertises 2026-07-28 in server/discover, but that revision only defines stdio and Streamable HTTP #1112

Description

@JAORMX

Describe the bug

A server served via NewSSEHandler advertises 2026-07-28 in its server/discover supportedVersions, so a Modern-first client negotiates that revision over the deprecated 2024-11-05 HTTP+SSE binding.

MCP 2026-07-28 defines exactly two transport bindings — stdio and Streamable HTTP. It says nothing about HTTP+SSE, so advertising it from the SSE handler tells clients the server speaks a revision over a binding that revision does not define.

Cause

filterSupportedVersions (mcp/server.go:915) returns every SDK-supported version unless the Transport implements ProtocolVersionSupporter:

func filterSupportedVersions(t Transport) []string {
	pvs, ok := t.(ProtocolVersionSupporter)
	if !ok {
		return slices.Clone(supportedProtocolVersions)
	}
	...
}

SSEServerTransport does not implement it. Only the streamable transport does (mcp/streamable.go). So ss.supportedVersions (mcp/server.go:1390) ends up containing 2026-07-28 for SSE sessions, and server/discover reports it (mcp/server.go:904).

To Reproduce

  1. Build a Server and serve it with mcp.NewSSEHandler(getServer, nil).
  2. Connect a client and issue server/discover.
  3. supportedVersions contains 2026-07-28.

Expected behavior

The SSE handler's sessions should advertise only the revisions that define the HTTP+SSE binding — i.e. exclude 2026-07-28.

Suggested fix

Have SSEServerTransport implement ProtocolVersionSupporter:

// SupportsProtocolVersion reports whether the transport can serve the given
// protocol version. MCP 2026-07-28 defines only the stdio and Streamable HTTP
// bindings, so the (deprecated) HTTP+SSE transport does not serve it.
func (t *SSEServerTransport) SupportsProtocolVersion(version string) bool {
	return version != protocolVersion20260728
}

That is the smallest change and matches how the streamable transport already gates itself. Note there is currently no way for an SDK consumer to work around this: SSEHandler.ServeHTTP constructs the SSEServerTransport internally and calls server.Connect(ctx, transport, nil) (mcp/sse.go:274), and SSEOptions exposes only DisableLocalhostProtection — so a wrapper cannot supply or decorate the transport to add the interface.

Additional context

Found while adopting v1.7.0-pre.3 in a shim library that offers both an SSE and a Streamable HTTP server. Impact for us is low in practice — our Modern client path only ever POSTs to a configured Streamable endpoint and so never reaches the SSE message endpoint — but the advertisement is observable to any client that speaks to an SSE-fronted server, and it makes version negotiation report something the binding cannot honour.

Happy to send a PR if the suggested fix looks right.

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