From 6db4a9cd3f165faff715e20a6eb762db0e820bf0 Mon Sep 17 00:00:00 2001 From: Sylwester Lachiewicz Date: Thu, 27 Aug 2026 15:33:07 +0200 Subject: [PATCH] test(mcp-test): deflake streamable HTTP version-negotiation integration test - tolerate an absent MCP-Protocol-Version header in the context extractor: Map.of rejects null values while spec-correct clients legitimately omit the header on initialize requests - await the asynchronously opened GET /mcp stream before asserting recorded calls; observed intermittently in CI as AssertionError Expected size: 3 but was: 2 (evidence: actions/run/33069467622 Jackson 2 Integration Tests) --- ...bleHttpVersionNegotiationIntegrationTests.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java b/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java index 563e52061..6506576d4 100644 --- a/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java +++ b/mcp-test/src/test/java/io/modelcontextprotocol/common/HttpClientStreamableHttpVersionNegotiationIntegrationTests.java @@ -4,8 +4,10 @@ package io.modelcontextprotocol.common; +import java.time.Duration; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.BiFunction; import io.modelcontextprotocol.client.McpClient; @@ -22,6 +24,7 @@ import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; import org.apache.catalina.startup.Tomcat; +import static org.awaitility.Awaitility.await; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -37,8 +40,10 @@ class HttpClientStreamableHttpVersionNegotiationIntegrationTests { private final HttpServletStreamableServerTransportProvider transport = HttpServletStreamableServerTransportProvider .builder() - .contextExtractor( - req -> McpTransportContext.create(Map.of("protocol-version", req.getHeader("MCP-protocol-version")))) + // The MCP-Protocol-Version header may legitimately be absent on initialize + // requests, so a missing header must not break context extraction. + .contextExtractor(req -> McpTransportContext + .create(Map.of("protocol-version", Objects.requireNonNullElse(req.getHeader("MCP-protocol-version"), "")))) .build(); private final McpSchema.Tool toolSpec = McpSchema.Tool.builder("test-tool") @@ -72,6 +77,12 @@ void usesLatestVersion() { McpSchema.CallToolResult response = client .callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build()); + // The GET /mcp stream is opened asynchronously once the initialize response + // creates the session, so wait for it to be recorded before asserting. + await().atMost(Duration.ofSeconds(5)) + .untilAsserted(() -> assertThat(requestRecordingFilter.getCalls()).filteredOn(c -> "GET".equals(c.method())) + .hasSize(1)); + var calls = requestRecordingFilter.getCalls(); assertThat(calls).filteredOn(c -> !c.body().contains("\"method\":\"initialize\""))