Skip to content

Commit 6080874

Browse files
author
yinh
committed
Add streamable support to spring-ai-starter-mcp-server-webflux
- Enable streaming functionality for MCP server WebFlux - Support real-time data streaming capabilities - Maintain backward compatibility with existing implementations Signed-off-by: yinh <[email protected]>
1 parent e0ccc13 commit 6080874

File tree

8 files changed

+379
-7
lines changed

8 files changed

+379
-7
lines changed

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/pom.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@
6868
<scope>test</scope>
6969
</dependency>
7070

71-
71+
<dependency>
72+
<groupId>jakarta.servlet</groupId>
73+
<artifactId>jakarta.servlet-api</artifactId>
74+
<scope>provided</scope>
75+
</dependency>
7276
</dependencies>
7377

7478
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2025-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.mcp.server.autoconfigure;
18+
19+
import com.fasterxml.jackson.databind.ObjectMapper;
20+
import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider;
21+
import io.modelcontextprotocol.spec.McpServerTransportProvider;
22+
import org.springframework.beans.factory.ObjectProvider;
23+
import org.springframework.boot.autoconfigure.AutoConfiguration;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
26+
import org.springframework.context.annotation.Bean;
27+
import org.springframework.context.annotation.Conditional;
28+
29+
/**
30+
* {@link AutoConfiguration Auto-configuration} for MCP HttpServlet Streamable Server
31+
* Transport.
32+
* <p>
33+
* This configuration class sets up the HttpServlet-specific Streamable transport
34+
* components for the MCP server, providing HTTP streaming communication through standard
35+
* Java Servlet API. It is activated when:
36+
* <ul>
37+
* <li>The HttpServletStreamableServerTransportProvider class is on the classpath (from
38+
* mcp-server-http-servlet dependency)</li>
39+
* <li>The MCP server is enabled and STDIO transport is disabled</li>
40+
* <li>The transport type is set to STREAMABLE</li>
41+
* <li>No other MCP server transport provider is available</li>
42+
* </ul>
43+
* <p>
44+
* The configuration provides:
45+
* <ul>
46+
* <li>A HttpServletStreamableServerTransportProvider bean for handling HTTP streaming
47+
* communication</li>
48+
* </ul>
49+
* <p>
50+
* This configuration has the lowest priority and will only be activated if no WebFlux or
51+
* WebMVC streamable transports are available.
52+
* <p>
53+
* Required dependencies: <pre>{@code
54+
* <dependency>
55+
* <groupId>io.modelcontextprotocol.sdk</groupId>
56+
* <artifactId>mcp-server-http-servlet</artifactId>
57+
* </dependency>
58+
* }</pre>
59+
*
60+
* @author yinh
61+
* @since 1.0.1
62+
* @see McpServerProperties
63+
* @see HttpServletStreamableServerTransportProvider
64+
*/
65+
@AutoConfiguration(
66+
after = { McpWebFluxStreamableServerAutoConfiguration.class, McpWebMvcStreamableServerAutoConfiguration.class })
67+
@ConditionalOnClass({ HttpServletStreamableServerTransportProvider.class })
68+
@ConditionalOnMissingBean(McpServerTransportProvider.class)
69+
@Conditional(McpServerStreamableTransportCondition.class)
70+
public class McpHttpServletStreamableServerAutoConfiguration {
71+
72+
@Bean
73+
@ConditionalOnMissingBean
74+
public HttpServletStreamableServerTransportProvider httpServletStreamableTransport(
75+
ObjectProvider<ObjectMapper> objectMapperProvider, McpServerProperties serverProperties) {
76+
77+
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
78+
79+
// Create an HttpServlet Streamable transport provider using the builder pattern
80+
return HttpServletStreamableServerTransportProvider.builder()
81+
.mcpEndpoint(serverProperties.getSseMessageEndpoint())
82+
.objectMapper(objectMapper)
83+
.build();
84+
}
85+
86+
}

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpServerAutoConfiguration.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import io.modelcontextprotocol.spec.McpSchema;
4343
import io.modelcontextprotocol.spec.McpSchema.Implementation;
4444
import io.modelcontextprotocol.spec.McpServerTransportProvider;
45+
import io.modelcontextprotocol.spec.McpServerTransportProviderBase;
46+
import io.modelcontextprotocol.spec.McpStreamableServerTransportProvider;
4547
import reactor.core.publisher.Mono;
4648

4749
import org.springframework.ai.mcp.McpToolUtils;
@@ -93,7 +95,7 @@
9395
* Server configuration is managed through {@link McpServerProperties} with support for:
9496
* <ul>
9597
* <li>Server identification (name, version)</li>
96-
* <li>Transport selection</li>
98+
* <li>Transport selection (STDIO, SSE, STREAMABLE)</li>
9799
* <li>Change notification settings for tools, resources, and prompts</li>
98100
* <li>Sync/Async operation mode selection</li>
99101
* </ul>
@@ -106,9 +108,14 @@
106108
* @see McpServerProperties
107109
* @see McpWebMvcServerAutoConfiguration
108110
* @see McpWebFluxServerAutoConfiguration
111+
* @see McpWebMvcStreamableServerAutoConfiguration
112+
* @see McpWebFluxStreamableServerAutoConfiguration
113+
* @see McpHttpServletStreamableServerAutoConfiguration
109114
* @see ToolCallback
110115
*/
111-
@AutoConfiguration(after = { McpWebMvcServerAutoConfiguration.class, McpWebFluxServerAutoConfiguration.class })
116+
@AutoConfiguration(after = { McpWebMvcServerAutoConfiguration.class, McpWebFluxServerAutoConfiguration.class,
117+
McpWebMvcStreamableServerAutoConfiguration.class, McpWebFluxStreamableServerAutoConfiguration.class,
118+
McpHttpServletStreamableServerAutoConfiguration.class })
112119
@ConditionalOnClass({ McpSchema.class, McpSyncServer.class })
113120
@EnableConfigurationProperties(McpServerProperties.class)
114121
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
@@ -170,7 +177,7 @@ private List<McpServerFeatures.SyncToolSpecification> toSyncToolSpecifications(L
170177
@Bean
171178
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
172179
matchIfMissing = true)
173-
public McpSyncServer mcpSyncServer(McpServerTransportProvider transportProvider,
180+
public McpSyncServer mcpSyncServer(McpServerTransportProviderBase transportProvider,
174181
McpSchema.ServerCapabilities.Builder capabilitiesBuilder, McpServerProperties serverProperties,
175182
ObjectProvider<List<SyncToolSpecification>> tools,
176183
ObjectProvider<List<SyncResourceSpecification>> resources,
@@ -183,7 +190,14 @@ public McpSyncServer mcpSyncServer(McpServerTransportProvider transportProvider,
183190
serverProperties.getVersion());
184191

185192
// Create the server with both tool and resource capabilities
186-
SyncSpecification serverBuilder = McpServer.sync(transportProvider).serverInfo(serverInfo);
193+
SyncSpecification<?> serverBuilder;
194+
if (transportProvider instanceof McpStreamableServerTransportProvider) {
195+
serverBuilder = McpServer.sync((McpStreamableServerTransportProvider) transportProvider);
196+
}
197+
else {
198+
serverBuilder = McpServer.sync((McpServerTransportProvider) transportProvider);
199+
}
200+
serverBuilder.serverInfo(serverInfo);
187201

188202
// Tools
189203
if (serverProperties.getCapabilities().isTool()) {
@@ -300,7 +314,7 @@ private List<McpServerFeatures.AsyncToolSpecification> toAsyncToolSpecification(
300314

301315
@Bean
302316
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
303-
public McpAsyncServer mcpAsyncServer(McpServerTransportProvider transportProvider,
317+
public McpAsyncServer mcpAsyncServer(McpServerTransportProviderBase transportProvider,
304318
McpSchema.ServerCapabilities.Builder capabilitiesBuilder, McpServerProperties serverProperties,
305319
ObjectProvider<List<AsyncToolSpecification>> tools,
306320
ObjectProvider<List<AsyncResourceSpecification>> resources,
@@ -313,7 +327,14 @@ public McpAsyncServer mcpAsyncServer(McpServerTransportProvider transportProvide
313327
serverProperties.getVersion());
314328

315329
// Create the server with both tool and resource capabilities
316-
AsyncSpecification serverBuilder = McpServer.async(transportProvider).serverInfo(serverInfo);
330+
AsyncSpecification<?> serverBuilder;
331+
if (transportProvider instanceof McpStreamableServerTransportProvider) {
332+
serverBuilder = McpServer.async((McpStreamableServerTransportProvider) transportProvider);
333+
}
334+
else {
335+
serverBuilder = McpServer.async((McpServerTransportProvider) transportProvider);
336+
}
337+
serverBuilder.serverInfo(serverInfo);
317338

318339
// Tools
319340
if (serverProperties.getCapabilities().isTool()) {

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpServerProperties.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ public class McpServerProperties {
5959
*/
6060
private boolean stdio = false;
6161

62+
/**
63+
* The transport type for web-based communication.
64+
* <p>
65+
* Supported transport types:
66+
* <ul>
67+
* <li>SSE - Server-Sent Events transport (default)</li>
68+
* <li>STREAMABLE - Streamable HTTP transport</li>
69+
* </ul>
70+
* Only relevant when stdio is disabled.
71+
*/
72+
private TransportType transportType = TransportType.SSE;
73+
6274
/**
6375
* The name of the MCP server instance.
6476
* <p>
@@ -170,6 +182,23 @@ public enum ServerType {
170182

171183
}
172184

185+
/**
186+
* Transport types supported by the MCP server for web communication.
187+
*/
188+
public enum TransportType {
189+
190+
/**
191+
* Server-Sent Events transport
192+
*/
193+
SSE,
194+
195+
/**
196+
* Streamable HTTP transport
197+
*/
198+
STREAMABLE
199+
200+
}
201+
173202
/**
174203
* (Optional) response MIME type per tool name.
175204
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.springframework.ai.mcp.server.autoconfigure;
2+
3+
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
4+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
5+
6+
/**
7+
* This class defines a condition met when the MCP server is enabled, STDIO transport is
8+
* disabled, and STREAMABLE transport type is selected.
9+
*
10+
* @since 1.1.0
11+
* @author yinh
12+
*/
13+
public class McpServerStreamableTransportCondition extends AllNestedConditions {
14+
15+
public McpServerStreamableTransportCondition() {
16+
super(ConfigurationPhase.PARSE_CONFIGURATION);
17+
}
18+
19+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
20+
matchIfMissing = true)
21+
static class McpServerEnabledCondition {
22+
23+
}
24+
25+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "stdio", havingValue = "false",
26+
matchIfMissing = true)
27+
static class StdioDisabledCondition {
28+
29+
}
30+
31+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "transport-type",
32+
havingValue = "STREAMABLE")
33+
static class StreamableTransportCondition {
34+
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2025-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.mcp.server.autoconfigure;
18+
19+
import com.fasterxml.jackson.databind.ObjectMapper;
20+
import io.modelcontextprotocol.server.transport.WebFluxStreamableServerTransportProvider;
21+
import io.modelcontextprotocol.spec.McpServerTransportProvider;
22+
import org.springframework.beans.factory.ObjectProvider;
23+
import org.springframework.boot.autoconfigure.AutoConfiguration;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
26+
import org.springframework.context.annotation.Bean;
27+
import org.springframework.context.annotation.Conditional;
28+
import org.springframework.web.reactive.function.server.RouterFunction;
29+
30+
/**
31+
* {@link AutoConfiguration Auto-configuration} for MCP WebFlux Streamable Server
32+
* Transport.
33+
* <p>
34+
* This configuration class sets up the WebFlux-specific Streamable transport components
35+
* for the MCP server, providing reactive HTTP streaming communication through Spring
36+
* WebFlux. It is activated when:
37+
* <ul>
38+
* <li>The WebFluxStreamableServerTransportProvider class is on the classpath (from
39+
* mcp-spring-webflux dependency)</li>
40+
* <li>Spring WebFlux's RouterFunction class is available (from
41+
* spring-boot-starter-webflux)</li>
42+
* <li>The MCP server is enabled and STDIO transport is disabled</li>
43+
* <li>The transport type is set to STREAMABLE</li>
44+
* </ul>
45+
* <p>
46+
* The configuration provides:
47+
* <ul>
48+
* <li>A WebFluxStreamableServerTransportProvider bean for handling reactive HTTP
49+
* streaming communication</li>
50+
* <li>A RouterFunction bean that sets up the reactive streaming endpoint</li>
51+
* </ul>
52+
* <p>
53+
* Required dependencies: <pre>{@code
54+
* <dependency>
55+
* <groupId>io.modelcontextprotocol.sdk</groupId>
56+
* <artifactId>mcp-spring-webflux</artifactId>
57+
* </dependency>
58+
* <dependency>
59+
* <groupId>org.springframework.boot</groupId>
60+
* <artifactId>spring-boot-starter-webflux</artifactId>
61+
* </dependency>
62+
* }</pre>
63+
*
64+
* @author yinh
65+
* @since 1.0.1
66+
* @see McpServerProperties
67+
* @see WebFluxStreamableServerTransportProvider
68+
*/
69+
@AutoConfiguration
70+
@ConditionalOnClass({ WebFluxStreamableServerTransportProvider.class, RouterFunction.class })
71+
@ConditionalOnMissingBean(McpServerTransportProvider.class)
72+
@Conditional(McpServerStreamableTransportCondition.class)
73+
public class McpWebFluxStreamableServerAutoConfiguration {
74+
75+
@Bean
76+
@ConditionalOnMissingBean
77+
public WebFluxStreamableServerTransportProvider webFluxStreamableTransport(
78+
ObjectProvider<ObjectMapper> objectMapperProvider, McpServerProperties serverProperties) {
79+
80+
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
81+
82+
// 使用builder模式创建WebFlux Streamable传输提供者
83+
return WebFluxStreamableServerTransportProvider.builder()
84+
.messageEndpoint(serverProperties.getSseMessageEndpoint())
85+
.objectMapper(objectMapper)
86+
.build();
87+
}
88+
89+
// Router function for Streamable transport used by Spring WebFlux to start an HTTP
90+
// server.
91+
@Bean
92+
public RouterFunction<?> webfluxMcpStreamableRouterFunction(
93+
WebFluxStreamableServerTransportProvider webFluxStreamableProvider) {
94+
return webFluxStreamableProvider.getRouterFunction();
95+
}
96+
97+
}

0 commit comments

Comments
 (0)