Basic version negotiation capabilities

- We do not support full version negotiation, but this commit allows the
  client to the send the correct version to match that of the server.
- There is one limitation: the GET /mcp request happens too early,
  before we have deserialized the initialization response, so we do not
  know the negotiated version yet. This request will have the client
  latest version as Mcp-Protocol-Version value.

Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
Daniel Garnier-Moiroux 2025-11-20 15:50:46 +01:00 • committed by Daniel Garnier-Moiroux
commit e6045f7287
2 changed files with 53 additions and 27 deletions

View file

@ -89,7 +89,7 @@ class HttpClientStreamableHttpVersionNegotiationIntegrationTests {
}
@Test
void usesCustomLatestVersion() {
void usesServerSupportedVersion() {
startTomcat();
var transport = HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
@ -101,19 +101,21 @@ class HttpClientStreamableHttpVersionNegotiationIntegrationTests {
McpSchema.CallToolResult response = client.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()));
var calls = requestRecordingFilter.getCalls();
assertThat(calls).filteredOn(c -> !c.body().contains("\"method\":\"initialize\""))
// GET /mcp ; POST notification/initialized ; POST tools/call
.hasSize(3)
// Initialize tells the server the Client's latest supported version
// FIXME: Set the correct protocol version on GET /mcp
assertThat(calls).filteredOn(c -> c.method().equals("POST") && !c.body().contains("\"method\":\"initialize\""))
// POST notification/initialized ; POST tools/call
.hasSize(2)
.map(McpTestRequestRecordingServletFilter.Call::headers)
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version", "2263-03-18"));
.allSatisfy(headers -> assertThat(headers).containsEntry("mcp-protocol-version",
ProtocolVersions.MCP_2025_06_18));
assertThat(response).isNotNull();
assertThat(response.content()).hasSize(1)
.first()
.extracting(McpSchema.TextContent.class::cast)
.extracting(McpSchema.TextContent::text)
.isEqualTo("2263-03-18");
.isEqualTo(ProtocolVersions.MCP_2025_06_18);
mcpServer.close();
}

View file

@ -46,7 +46,7 @@ public class McpTestRequestRecordingServletFilter implements Filter {
.collect(Collectors.toUnmodifiableMap(Function.identity(),
name -> String.join(",", Collections.list(req.getHeaders(name)))));
var request = new CachedBodyHttpServletRequest(req);
calls.add(new Call(headers, request.getBodyAsString()));
calls.add(new Call(req.getMethod(), headers, request.getBodyAsString()));
filterChain.doFilter(request, servletResponse);
}
else {
@ -60,7 +60,7 @@ public class McpTestRequestRecordingServletFilter implements Filter {
return List.copyOf(calls);
}
public record Call(Map<String, String> headers, String body) {
public record Call(String method, Map<String, String> headers, String body) {
}