feat: Add transport context extraction support to all MCP servers (#477)

- Add McpTransportContextExtractor to WebFlux/WebMVC SSE and Streamable transport providers
- Enable extraction of HTTP transport metadata (headers, etc.) for use during request processing
- Pass transport context through reactive context chain using McpTransportContext.KEY
- Add contextExtractor() builder methods for configuring custom extractors
- Update HttpServlet transport providers with same context extraction capability
- Modify McpServerSession to properly propagate transport context to handlers
- Add test coverage with TEST_CONTEXT_EXTRACTOR in integration tests

This allows MCP feature implementations to access HTTP transport level metadata
that was present at request time, enabling use cases like authentication,
request tracing, and custom header processing.

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>

Co-authored-by: Christian Tzolov <christian.tzolov@broadcom.com>
Co-authored-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com>
This commit is contained in:
Kevin Stanton 2025-08-10 00:18:05 -05:00 • committed by Christian Tzolov
commit b4fef5241f
2 changed files with 374 additions and 24 deletions

View file

@ -13,6 +13,7 @@ import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -22,6 +23,7 @@ import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServer.AsyncSpecification;
import io.modelcontextprotocol.server.McpServer.SingleSessionSyncSpecification;
import io.modelcontextprotocol.server.McpTransportContextExtractor;
import io.modelcontextprotocol.server.TestUtil;
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider;
import reactor.netty.DisposableServer;
@ -40,6 +42,11 @@ class WebFluxSseIntegrationTests extends AbstractMcpClientServerIntegrationTests
private WebFluxSseServerTransportProvider mcpServerTransportProvider;
static McpTransportContextExtractor<ServerRequest> TEST_CONTEXT_EXTRACTOR = (r, tc) -> {
tc.put("important", "value");
return tc;
};
@Override
protected void prepareClients(int port, String mcpEndpoint) {
@ -75,6 +82,7 @@ class WebFluxSseIntegrationTests extends AbstractMcpClientServerIntegrationTests
.objectMapper(new ObjectMapper())
.messageEndpoint(CUSTOM_MESSAGE_ENDPOINT)
.sseEndpoint(CUSTOM_SSE_ENDPOINT)
.contextExtractor(TEST_CONTEXT_EXTRACTOR)
.build();
HttpHandler httpHandler = RouterFunctions.toHttpHandler(mcpServerTransportProvider.getRouterFunction());

View file

@ -13,6 +13,7 @@ import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -22,6 +23,7 @@ import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServer.AsyncSpecification;
import io.modelcontextprotocol.server.McpServer.SyncSpecification;
import io.modelcontextprotocol.server.McpTransportContextExtractor;
import io.modelcontextprotocol.server.TestUtil;
import io.modelcontextprotocol.server.transport.WebFluxStreamableServerTransportProvider;
import reactor.netty.DisposableServer;
@ -38,6 +40,11 @@ class WebFluxStreamableIntegrationTests extends AbstractMcpClientServerIntegrati
private WebFluxStreamableServerTransportProvider mcpStreamableServerTransportProvider;
static McpTransportContextExtractor<ServerRequest> TEST_CONTEXT_EXTRACTOR = (r, tc) -> {
tc.put("important", "value");
return tc;
};
@Override
protected void prepareClients(int port, String mcpEndpoint) {
@ -71,6 +78,7 @@ class WebFluxStreamableIntegrationTests extends AbstractMcpClientServerIntegrati
this.mcpStreamableServerTransportProvider = WebFluxStreamableServerTransportProvider.builder()
.objectMapper(new ObjectMapper())
.messageEndpoint(CUSTOM_MESSAGE_ENDPOINT)
.contextExtractor(TEST_CONTEXT_EXTRACTOR)
.build();
HttpHandler httpHandler = RouterFunctions