Skip to content

Commit f83db45

Browse files
committed
More WIP: renaming, steteless server abstractions, client context extraction
Signed-off-by: Dariusz Jędrzejczyk <[email protected]>
1 parent 8e4c745 commit f83db45

18 files changed

+1989
-547
lines changed

mcp-spring/mcp-spring-webflux/src/main/java/io/modelcontextprotocol/server/transport/WebFluxStreamableServerTransportProvider.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
import com.fasterxml.jackson.core.type.TypeReference;
44
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import io.modelcontextprotocol.spec.DefaultMcpTransportContext;
56
import io.modelcontextprotocol.spec.McpError;
67
import io.modelcontextprotocol.spec.McpSchema;
78
import io.modelcontextprotocol.spec.McpServerTransport;
89
import io.modelcontextprotocol.spec.McpStreamableServerSession;
910
import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider;
11+
import io.modelcontextprotocol.spec.McpTransportContext;
1012
import io.modelcontextprotocol.util.Assert;
1113
import org.slf4j.Logger;
1214
import org.slf4j.LoggerFactory;
@@ -25,6 +27,7 @@
2527

2628
import java.io.IOException;
2729
import java.util.concurrent.ConcurrentHashMap;
30+
import java.util.function.Function;
2831

2932
public class WebFluxStreamableServerTransportProvider implements McpStreamableServerTransportProvider {
3033

@@ -48,6 +51,9 @@ public class WebFluxStreamableServerTransportProvider implements McpStreamableSe
4851

4952
private final ConcurrentHashMap<String, McpStreamableServerSession> sessions = new ConcurrentHashMap<>();
5053

54+
// TODO: add means to specify this
55+
private Function<ServerRequest, McpTransportContext> contextExtractor = req -> new DefaultMcpTransportContext();
56+
5157
/**
5258
* Flag indicating if the transport is shutting down.
5359
*/
@@ -183,6 +189,8 @@ private Mono<ServerResponse> handleGet(ServerRequest request) {
183189
return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE).bodyValue("Server is shutting down");
184190
}
185191

192+
McpTransportContext transportContext = this.contextExtractor.apply(request);
193+
186194
return Mono.defer(() -> {
187195
if (!request.headers().asHttpHeaders().containsKey("mcp-session-id")) {
188196
return ServerResponse.badRequest().build(); // TODO: say we need a session id
@@ -204,11 +212,11 @@ private Mono<ServerResponse> handleGet(ServerRequest request) {
204212
return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
205213
.body(Flux.<ServerSentEvent<?>>create(sink -> {
206214
WebFluxStreamableMcpSessionTransport sessionTransport = new WebFluxStreamableMcpSessionTransport(sink);
207-
McpStreamableServerSession.McpStreamableServerSessionStream genericStream = session.newStream(sessionTransport);
208-
sink.onDispose(genericStream::close);
215+
McpStreamableServerSession.McpStreamableServerSessionStream listeningStream = session.listeningStream(sessionTransport);
216+
sink.onDispose(listeningStream::close);
209217
}), ServerSentEvent.class);
210218

211-
});
219+
}).contextWrite(ctx -> ctx.put(McpTransportContext.KEY, transportContext));
212220
}
213221

214222
/**
@@ -231,6 +239,8 @@ private Mono<ServerResponse> handlePost(ServerRequest request) {
231239
return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE).bodyValue("Server is shutting down");
232240
}
233241

242+
McpTransportContext transportContext = this.contextExtractor.apply(request);
243+
234244
return request.bodyToMono(String.class).<ServerResponse>flatMap(body -> {
235245
try {
236246
McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(objectMapper, body);
@@ -261,7 +271,7 @@ private Mono<ServerResponse> handlePost(ServerRequest request) {
261271
return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
262272
.body(Flux.<ServerSentEvent<?>>create(sink -> {
263273
WebFluxStreamableMcpSessionTransport st = new WebFluxStreamableMcpSessionTransport(sink);
264-
Mono<Void> stream = session.handleStream(jsonrpcRequest, st);
274+
Mono<Void> stream = session.responseStream(jsonrpcRequest, st);
265275
Disposable streamSubscription = stream
266276
.doOnError(err -> sink.error(err))
267277
.contextWrite(sink.contextView())
@@ -276,7 +286,7 @@ private Mono<ServerResponse> handlePost(ServerRequest request) {
276286
logger.error("Failed to deserialize message: {}", e.getMessage());
277287
return ServerResponse.badRequest().bodyValue(new McpError("Invalid message format"));
278288
}
279-
});
289+
}).contextWrite(ctx -> ctx.put(McpTransportContext.KEY, transportContext));
280290
}
281291

282292
private class WebFluxStreamableMcpSessionTransport implements McpServerTransport {

mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import java.util.function.BiFunction;
1717

1818
import io.modelcontextprotocol.spec.DefaultMcpStreamableServerSessionFactory;
19-
import io.modelcontextprotocol.spec.McpSingleSessionServerTransportProvider;
19+
import io.modelcontextprotocol.spec.McpServerTransportProviderBase;
2020
import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider;
2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
@@ -89,7 +89,7 @@ public class McpAsyncServer {
8989

9090
private static final Logger logger = LoggerFactory.getLogger(McpAsyncServer.class);
9191

92-
private final McpServerTransportProvider mcpTransportProvider;
92+
private final McpServerTransportProviderBase mcpTransportProvider;
9393

9494
private final ObjectMapper objectMapper;
9595

@@ -126,7 +126,7 @@ public class McpAsyncServer {
126126
* @param features The MCP server supported features.
127127
* @param objectMapper The ObjectMapper to use for JSON serialization/deserialization
128128
*/
129-
McpAsyncServer(McpSingleSessionServerTransportProvider mcpTransportProvider, ObjectMapper objectMapper,
129+
McpAsyncServer(McpServerTransportProvider mcpTransportProvider, ObjectMapper objectMapper,
130130
McpServerFeatures.Async features, Duration requestTimeout,
131131
McpUriTemplateManagerFactory uriTemplateManagerFactory, JsonSchemaValidator jsonSchemaValidator) {
132132
this.mcpTransportProvider = mcpTransportProvider;

mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServerExchange.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
import java.util.Collections;
99

1010
import com.fasterxml.jackson.core.type.TypeReference;
11+
import io.modelcontextprotocol.spec.DefaultMcpTransportContext;
1112
import io.modelcontextprotocol.spec.McpError;
1213
import io.modelcontextprotocol.spec.McpSchema;
1314
import io.modelcontextprotocol.spec.McpSchema.LoggingLevel;
1415
import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
1516
import io.modelcontextprotocol.spec.McpSession;
17+
import io.modelcontextprotocol.spec.McpTransportContext;
1618
import io.modelcontextprotocol.util.Assert;
1719
import reactor.core.publisher.Mono;
1820

@@ -31,6 +33,8 @@ public class McpAsyncServerExchange {
3133

3234
private final McpSchema.Implementation clientInfo;
3335

36+
private final McpTransportContext transportContext;
37+
3438
private volatile LoggingLevel minLoggingLevel = LoggingLevel.INFO;
3539

3640
private static final TypeReference<McpSchema.CreateMessageResult> CREATE_MESSAGE_RESULT_TYPE_REF = new TypeReference<>() {
@@ -57,6 +61,23 @@ public McpAsyncServerExchange(McpSession session, McpSchema.ClientCapabilities c
5761
this.session = session;
5862
this.clientCapabilities = clientCapabilities;
5963
this.clientInfo = clientInfo;
64+
this.transportContext = new DefaultMcpTransportContext();
65+
}
66+
67+
/**
68+
* Create a new asynchronous exchange with the client.
69+
* @param session The server session representing a 1-1 interaction.
70+
* @param clientCapabilities The client capabilities that define the supported
71+
* features and functionality.
72+
* @param transportContext context associated with the client as extracted from the transport
73+
* @param clientInfo The client implementation information.
74+
*/
75+
public McpAsyncServerExchange(McpSession session, McpSchema.ClientCapabilities clientCapabilities,
76+
McpSchema.Implementation clientInfo, McpTransportContext transportContext) {
77+
this.session = session;
78+
this.clientCapabilities = clientCapabilities;
79+
this.clientInfo = clientInfo;
80+
this.transportContext = transportContext;
6081
}
6182

6283
/**
@@ -75,6 +96,10 @@ public McpSchema.Implementation getClientInfo() {
7596
return this.clientInfo;
7697
}
7798

99+
public McpTransportContext transportContext() {
100+
return this.transportContext;
101+
}
102+
78103
/**
79104
* Create a new message using the sampling capabilities of the client. The Model
80105
* Context Protocol (MCP) provides a standardized way for servers to request LLM

0 commit comments

Comments
 (0)