diff --git a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java index dd7c6539..5ed82228 100644 --- a/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java +++ b/mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/client/transport/WebClientStreamableHttpTransport.java @@ -36,6 +36,9 @@ import reactor.util.function.Tuple2; import reactor.util.function.Tuples; +import static io.modelcontextprotocol.spec.McpSchema.Headers.LAST_EVENT_ID; +import static io.modelcontextprotocol.spec.McpSchema.Headers.MCP_SESSION_ID; + /** * An implementation of the Streamable HTTP protocol as defined by the * 2025-03-26 version of the MCP specification. @@ -128,7 +131,7 @@ public Mono connect(Function, Mono> onClose = sessionId -> sessionId == null ? Mono.empty() : webClient.delete().uri(this.endpoint).headers(httpHeaders -> { - httpHeaders.add("mcp-session-id", sessionId); + httpHeaders.add(MCP_SESSION_ID, sessionId); }) .retrieve() .toBodilessEntity() @@ -187,9 +190,9 @@ private Mono reconnect(McpTransportStream stream) { .uri(this.endpoint) .accept(MediaType.TEXT_EVENT_STREAM) .headers(httpHeaders -> { - transportSession.sessionId().ifPresent(id -> httpHeaders.add("mcp-session-id", id)); + transportSession.sessionId().ifPresent(id -> httpHeaders.add(MCP_SESSION_ID, id)); if (stream != null) { - stream.lastId().ifPresent(id -> httpHeaders.add("last-event-id", id)); + stream.lastId().ifPresent(id -> httpHeaders.add(LAST_EVENT_ID, id)); } }) .exchangeToFlux(response -> { @@ -247,12 +250,11 @@ public Mono sendMessage(McpSchema.JSONRPCMessage message) { .uri(this.endpoint) .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) .headers(httpHeaders -> { - transportSession.sessionId().ifPresent(id -> httpHeaders.add("mcp-session-id", id)); + transportSession.sessionId().ifPresent(id -> httpHeaders.add(MCP_SESSION_ID, id)); }) .bodyValue(message) .exchangeToFlux(response -> { - if (transportSession - .markInitialized(response.headers().asHttpHeaders().getFirst("mcp-session-id"))) { + if (transportSession.markInitialized(response.headers().asHttpHeaders().getFirst(MCP_SESSION_ID))) { // Once we have a session, we try to open an async stream for // the server to send notifications and requests out-of-band. reconnect(null).contextWrite(sink.contextView()).subscribe(); diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index 59713094..cda76531 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -141,6 +141,28 @@ public static final class ErrorCodes { } + // --------------------------- + // JSON-RPC Protocol Headers + // --------------------------- + /** + * Standard header name used in MCP JSON-RPC request and responses. + */ + public static final class Headers { + + /** + * The Model Context Protocol (MCP) session ID header name. + */ + public static final String MCP_SESSION_ID = "Mcp-Session-Id"; + + /** + * The Last-Event-ID HTTP request header reports an EventSource object's last + * event ID string to the server when the user agent is to reestablish the + * connection. + */ + public static final String LAST_EVENT_ID = "Last-Event-ID"; + + } + public sealed interface Request permits InitializeRequest, CallToolRequest, CreateMessageRequest, ElicitRequest, CompleteRequest, GetPromptRequest {