Skip to content

Commit e71da4e

Browse files
tzolovFottas
andcommitted
refactor(mcp): consolidate MCP server auto-configuration modules and introduce protocol-based configuration (#4211)
- Consolidate multiple MCP server modules into unified webmvc/webflux modules - Move properties classes from separate module into mcp-server-common - Merge stateless auto-configuration into common module - Replace transport-specific modules with protocol-based configuration - Add ServerProtocol enum (SSE, STREAMABLE, STATELESS) for protocol selection - Rename ServerType to ApiType for clarity - Update conditional configuration to use protocol-based conditions - Remove separate streamable/stateless starter modules - Update documentation to reflect unified protocol-based approach - Simplify module structure while maintaining backward compatibility through configuration This refactoring reduces complexity by consolidating 7+ modules into 3 main modules (common, webmvc, webflux) while providing the same functionality through the spring.ai.mcp.server.protocol configuration property. Signed-off-by: Christian Tzolov <[email protected]> Co-authored-by: yinh <[email protected]>
1 parent d67a3cd commit e71da4e

File tree

54 files changed

+567
-968
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+567
-968
lines changed

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@
2323

2424
<dependencies>
2525

26-
<dependency>
27-
<groupId>org.springframework.ai</groupId>
28-
<artifactId>spring-ai-autoconfigure-mcp-server-properties</artifactId>
29-
<version>${project.parent.version}</version>
30-
</dependency>
31-
3226
<dependency>
3327
<groupId>org.springframework.boot</groupId>
3428
<artifactId>spring-boot-starter</artifactId>

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

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323

2424
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerChangeNotificationProperties;
2525
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties;
26-
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerStreamableHttpProperties;
2726
import org.springframework.beans.factory.ObjectProvider;
2827
import org.springframework.boot.autoconfigure.AutoConfiguration;
2928
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
3029
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
30+
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
3131
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3232
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3333
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -71,23 +71,64 @@
7171
* @since 1.0.0
7272
* @see McpServerProperties
7373
*/
74-
@AutoConfiguration(afterName = {
75-
"org.springframework.ai.mcp.server.common.autoconfigure.ToolCallbackConverterAutoConfiguration",
76-
"org.springframework.ai.mcp.server.autoconfigure.McpServerSseWebFluxAutoConfiguration",
77-
"org.springframework.ai.mcp.server.autoconfigure.McpServerSseWebMvcAutoConfiguration",
78-
"org.springframework.ai.mcp.server.streamable.webflux.autoconfigure.McpServerStreamableHttpWebMvcAutoConfiguration",
79-
"org.springframework.ai.mcp.server.streamable.webflux.autoconfigure.McpServerStreamableHttpWebFluxAutoConfiguration" })
80-
@ConditionalOnClass({ McpSchema.class, McpSyncServer.class })
74+
@AutoConfiguration(
75+
afterName = { "org.springframework.ai.mcp.server.common.autoconfigure.ToolCallbackConverterAutoConfiguration",
76+
"org.springframework.ai.mcp.server.autoconfigure.McpServerSseWebFluxAutoConfiguration",
77+
"org.springframework.ai.mcp.server.autoconfigure.McpServerSseWebMvcAutoConfiguration",
78+
"org.springframework.ai.mcp.server.autoconfigure.McpServerStreamableHttpWebMvcAutoConfiguration",
79+
"org.springframework.ai.mcp.server.autoconfigure.McpServerStreamableHttpWebFluxAutoConfiguration" })
80+
@ConditionalOnClass({ McpSchema.class })
8181
@EnableConfigurationProperties({ McpServerProperties.class, McpServerChangeNotificationProperties.class })
8282
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
8383
matchIfMissing = true)
84+
@Conditional(McpServerAutoConfiguration.NonStatlessServerCondition.class)
8485
public class McpServerAutoConfiguration {
8586

8687
private static final LogAccessor logger = new LogAccessor(McpServerAutoConfiguration.class);
8788

88-
public static class EnabledNonStatlessServerCondition extends AllNestedConditions {
89+
public static class NonStatlessServerCondition extends AnyNestedCondition {
8990

90-
public EnabledNonStatlessServerCondition() {
91+
public NonStatlessServerCondition() {
92+
super(ConfigurationPhase.PARSE_CONFIGURATION);
93+
}
94+
95+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol", havingValue = "SSE",
96+
matchIfMissing = true)
97+
static class SseEnabledCondition {
98+
99+
}
100+
101+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol",
102+
havingValue = "STREAMABLE", matchIfMissing = false)
103+
static class StreamableEnabledCondition {
104+
105+
}
106+
107+
}
108+
109+
public static class EnabledSseServerCondition extends AllNestedConditions {
110+
111+
public EnabledSseServerCondition() {
112+
super(ConfigurationPhase.PARSE_CONFIGURATION);
113+
}
114+
115+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
116+
matchIfMissing = true)
117+
static class McpServerEnabledCondition {
118+
119+
}
120+
121+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol", havingValue = "SSE",
122+
matchIfMissing = true)
123+
static class SseEnabledCondition {
124+
125+
}
126+
127+
}
128+
129+
public static class EnabledStreamableServerCondition extends AllNestedConditions {
130+
131+
public EnabledStreamableServerCondition() {
91132
super(ConfigurationPhase.PARSE_CONFIGURATION);
92133
}
93134

@@ -97,9 +138,9 @@ static class McpServerEnabledCondition {
97138

98139
}
99140

100-
@ConditionalOnProperty(prefix = McpServerStreamableHttpProperties.CONFIG_PREFIX, name = "stateless",
101-
havingValue = "false", matchIfMissing = true)
102-
static class StatelessEnabledCondition {
141+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol",
142+
havingValue = "STREAMABLE", matchIfMissing = false)
143+
static class StreamableEnabledCondition {
103144

104145
}
105146

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,21 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.mcp.server.stateless.autoconfigure;
17+
package org.springframework.ai.mcp.server.common.autoconfigure;
1818

1919
import java.util.ArrayList;
2020
import java.util.List;
2121

2222
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties;
2323
import org.springframework.beans.factory.ObjectProvider;
2424
import org.springframework.boot.autoconfigure.AutoConfiguration;
25+
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
2526
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2627
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2728
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2829
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2930
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.Conditional;
3032
import org.springframework.core.env.Environment;
3133
import org.springframework.core.log.LogAccessor;
3234
import org.springframework.util.CollectionUtils;
@@ -53,17 +55,37 @@
5355
* @author Christian Tzolov
5456
*/
5557
@AutoConfiguration(afterName = {
56-
"org.springframework.ai.mcp.server.stateless.autoconfigure.ToolCallbackConverterAutoConfiguration",
57-
"org.springframework.ai.mcp.server.stateless.webflux.autoconfigure.McpServerStatelessWebFluxAutoConfiguration",
58-
"org.springframework.ai.mcp.server.stateless.webmvc.autoconfigure.McpServerStatelessWebMvcAutoConfiguration" })
58+
"org.springframework.ai.mcp.server.common.autoconfigure.StatelessToolCallbackConverterAutoConfiguration",
59+
"org.springframework.ai.mcp.server.autoconfigure.McpServerStatelessWebFluxAutoConfiguration",
60+
"org.springframework.ai.mcp.server.autoconfigure.McpServerStatelessWebMvcAutoConfiguration" })
5961
@ConditionalOnClass({ McpSchema.class })
6062
@EnableConfigurationProperties(McpServerProperties.class)
61-
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
62-
matchIfMissing = true)
63+
@Conditional({ McpServerStdioDisabledCondition.class,
64+
McpServerStatelessAutoConfiguration.EnabledStatelessServerCondition.class })
6365
public class McpServerStatelessAutoConfiguration {
6466

6567
private static final LogAccessor logger = new LogAccessor(McpServerStatelessAutoConfiguration.class);
6668

69+
public static class EnabledStatelessServerCondition extends AllNestedConditions {
70+
71+
public EnabledStatelessServerCondition() {
72+
super(ConfigurationPhase.PARSE_CONFIGURATION);
73+
}
74+
75+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
76+
matchIfMissing = true)
77+
static class McpServerEnabledCondition {
78+
79+
}
80+
81+
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol", havingValue = "STATELESS",
82+
matchIfMissing = false)
83+
static class StatelessEnabledCondition {
84+
85+
}
86+
87+
}
88+
6789
@Bean
6890
@ConditionalOnMissingBean
6991
public McpSchema.ServerCapabilities.Builder capabilitiesBuilder() {
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.mcp.server.stateless.autoconfigure;
17+
package org.springframework.ai.mcp.server.common.autoconfigure;
1818

1919
import java.util.ArrayList;
2020
import java.util.List;
@@ -41,8 +41,10 @@
4141
*/
4242
@AutoConfiguration
4343
@EnableConfigurationProperties(McpServerProperties.class)
44-
@Conditional(ToolCallbackConverterAutoConfiguration.ToolCallbackConverterCondition.class)
45-
public class ToolCallbackConverterAutoConfiguration {
44+
@Conditional({ McpServerStdioDisabledCondition.class,
45+
McpServerStatelessAutoConfiguration.EnabledStatelessServerCondition.class,
46+
StatelessToolCallbackConverterAutoConfiguration.ToolCallbackConverterCondition.class })
47+
public class StatelessToolCallbackConverterAutoConfiguration {
4648

4749
public static class ToolCallbackConverterCondition extends AllNestedConditions {
4850

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3232
import org.springframework.context.annotation.Bean;
3333
import org.springframework.context.annotation.Conditional;
34-
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
3534
import org.springframework.util.CollectionUtils;
3635
import org.springframework.util.MimeType;
3736

@@ -42,7 +41,8 @@
4241
*/
4342
@AutoConfiguration
4443
@EnableConfigurationProperties(McpServerProperties.class)
45-
@Conditional(ToolCallbackConverterAutoConfiguration.ToolCallbackConverterCondition.class)
44+
@Conditional({ ToolCallbackConverterAutoConfiguration.ToolCallbackConverterCondition.class,
45+
McpServerAutoConfiguration.NonStatlessServerCondition.class })
4646
public class ToolCallbackConverterAutoConfiguration {
4747

4848
public static class ToolCallbackConverterCondition extends AllNestedConditions {
Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,12 @@ public class McpServerProperties {
8888
* <li>ASYNC - Asynchronous server</li>
8989
* </ul>
9090
*/
91-
private ServerType type = ServerType.SYNC;
91+
private ApiType type = ApiType.SYNC;
9292

9393
private Capabilities capabilities = new Capabilities();
9494

95+
private ServerProtocol protocol = ServerProtocol.SSE;
96+
9597
/**
9698
* Sets the duration to wait for server responses before timing out requests. This
9799
* timeout applies to all requests made through the client, including tool calls,
@@ -112,10 +114,16 @@ public Capabilities getCapabilities() {
112114
return this.capabilities;
113115
}
114116

117+
public enum ServerProtocol {
118+
119+
SSE, STREAMABLE, STATELESS
120+
121+
}
122+
115123
/**
116-
* Server types supported by the MCP server.
124+
* API types supported by the MCP server.
117125
*/
118-
public enum ServerType {
126+
public enum ApiType {
119127

120128
/**
121129
* Synchronous (McpSyncServer) server
@@ -176,11 +184,11 @@ public void setInstructions(String instructions) {
176184
this.instructions = instructions;
177185
}
178186

179-
public ServerType getType() {
187+
public ApiType getType() {
180188
return this.type;
181189
}
182190

183-
public void setType(ServerType serverType) {
191+
public void setType(ApiType serverType) {
184192
Assert.notNull(serverType, "Server type must not be null");
185193
this.type = serverType;
186194
}
@@ -189,6 +197,15 @@ public Map<String, String> getToolResponseMimeType() {
189197
return this.toolResponseMimeType;
190198
}
191199

200+
public ServerProtocol getProtocol() {
201+
return this.protocol;
202+
}
203+
204+
public void setProtocol(ServerProtocol serverMode) {
205+
Assert.notNull(serverMode, "Server mode must not be null");
206+
this.protocol = serverMode;
207+
}
208+
192209
public static class Capabilities {
193210

194211
private boolean resource = true;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.mcp.server.autoconfigure;
17+
package org.springframework.ai.mcp.server.common.autoconfigure.properties;
1818

1919
import java.time.Duration;
2020

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@
1515
#
1616
org.springframework.ai.mcp.server.common.autoconfigure.McpServerAutoConfiguration
1717
org.springframework.ai.mcp.server.common.autoconfigure.ToolCallbackConverterAutoConfiguration
18+
org.springframework.ai.mcp.server.common.autoconfigure.McpServerStatelessAutoConfiguration
19+
org.springframework.ai.mcp.server.common.autoconfigure.StatelessToolCallbackConverterAutoConfiguration

0 commit comments

Comments
 (0)