Hacktoberfest 2026: die Issues, die Maintainer für den Oktober markiert haben – offen und einsteigerfreundlich. Hacktoberfest-Issues durchsuchen

Streamable HTTP GET after initialize sends latest MCP-Protocol-Version instead of negotiated version

Offen
#883 2 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Anfängerfreundlichkeit
76/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Bereich
api, backend, networking

Rechercherichtung

Beginne bei WebClientStreamableHttpTransport und HttpClientStreamableHttpTransport und verfolge anschließend sendMessage(), reconnect() und LifecycleInitializer.withInitialization(), um ihre Reactor-Kontexte zu vergleichen. Reproduziere dies mit einem Server, der 2025-06-18 aushandelt und den Header validiert; abgeschlossen ist die Aufgabe, wenn der anschließende GET die ausgehandelte Version verwendet und erfolgreich ist, statt 400 zurückzugeben.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

waiting for user

Disclaimer: I am not a native English speaker. This issue was drafted with AI translation assistance. I apologize for any awkward phrasing.

Bug description

After a successful initialize handshake where the server negotiates down to an older protocol version (e.g. 2025-06-18), the subsequent GET request to open the SSE stream still sends the client's original latest version (2025-11-25) in the MCP-Protocol-Version HTTP header rather than the negotiated version. Servers that validate this header (such as rmcp) reject the request with 400 Bad Request.

Root cause

In both WebClientStreamableHttpTransport and HttpClientStreamableHttpTransport, the MCP-Protocol-Version header is resolved via Reactor Context:

ctx.getOrDefault(McpAsyncClient.NEGOTIATED_PROTOCOL_VERSION, this.latestSupportedProtocolVersion)

The negotiated version is written into the Reactor Context by LifecycleInitializer.withInitialization():

.flatMap(res -> operation.apply(res)
    .contextWrite(c -> c.put(McpAsyncClient.NEGOTIATED_PROTOCOL_VERSION,
        res.initializeResult().protocolVersion())));

However, the GET reconnect is triggered as a side effect inside sendMessage() when the transport session is first marked as initialized:

if (transportSession.markInitialized(response.headers()...getFirst(HttpHeaders.MCP_SESSION_ID))) {
    reconnect(null).contextWrite(sink.contextView()).subscribe();
}

At this point, sink.contextView() does not yet contain NEGOTIATED_PROTOCOL_VERSION because the contextWrite in LifecycleInitializer has not executed yet — it runs after the initialize request's sendMessage() completes. So the GET reconnect falls back to latestSupportedProtocolVersion (2025-11-25).

Timeline:

  1. LifecycleInitializer.doInitialize() → calls mcpClientSession.sendRequest("initialize", ...) → transport.sendMessage()
  2. sendMessage() POSTs to /mcp with header MCP-Protocol-Version: 2025-11-25 ✅ (no negotiation yet, expected)
  3. Server responds with protocolVersion: "2025-06-18" ✅
  4. Inside sendMessage(), transportSession.markInitialized(sessionId) returns true → immediately calls reconnect(null) which fires a GET with MCP-Protocol-Version: 2025-11-25 ❌
  5. Later, LifecycleInitializer.withInitialization() runs .contextWrite(c -> c.put(NEGOTIATED_PROTOCOL_VERSION, "2025-06-18")) — too late for the GET in step 4

Environment

  • Spring AI: 2.0.0-M3
  • MCP Java SDK (io.modelcontextprotocol.sdk:mcp-core): 1.1.0
  • Spring Boot: 4.0.4
  • Java: 25
  • Transport: WebClientStreamableHttpTransport (via spring-ai-starter-mcp-client-webflux)
  • MCP Server: rmcp 1.2.0 (Rust, Streamable HTTP, supporting protocol version 2025-06-18)

Steps to reproduce

  1. Set up an MCP server that supports protocolVersion: "2025-06-18" and validates the MCP-Protocol-Version HTTP header (rejecting unsupported versions).
  2. Configure a Spring AI MCP client with spring-ai-starter-mcp-client-webflux using default settings (no custom supportedProtocolVersions).
  3. Start the application and observe the HTTP traffic.

Observed:

POST /mcp  →  MCP-Protocol-Version: 2025-11-25  →  200 OK (negotiated to 2025-06-18)
GET  /mcp  →  MCP-Protocol-Version: 2025-11-25  →  400 Bad Request

Expected behavior

After the server responds with protocolVersion: "2025-06-18", all subsequent requests (including the GET SSE reconnect) should use MCP-Protocol-Version: 2025-06-18 in the HTTP header:

POST /mcp  →  MCP-Protocol-Version: 2025-11-25  →  200 OK (negotiated to 2025-06-18)
GET  /mcp  →  MCP-Protocol-Version: 2025-06-18  →  200 OK

Workaround

Register a McpClientCustomizer bean to remove 2025-11-25 from the supported versions list, so the fallback value aligns with the server:

@Component
public class McpTransportCustomizer implements McpClientCustomizer<WebClientStreamableHttpTransport.Builder> {

    @Override
    public void customize(String name, WebClientStreamableHttpTransport.Builder builder) {
        builder.supportedProtocolVersions(List.of(
                ProtocolVersions.MCP_2024_11_05,
                ProtocolVersions.MCP_2025_03_26,
                ProtocolVersions.MCP_2025_06_18
        ));
    }
}
Vorherrschende Sprache
Java
Sterne
3.7k
Forks
1.1k
Ø Merge
1 T. 15 Std.
Gemergte PRs (30 T.)
9

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus modelcontextprotocol/java-sdk

Alle Issues in modelcontextprotocol/java-sdk

Ähnliche Issues

Weitere Issues zu Java

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.