I am using Spring AI WebMvc MCP Server to build my MCP server, but I now want to start multiple MCP servers within a single project. However, my attempts have failed. Please provide a solution.
Expected Behavior I now want to start multiple MCP servers within a single project.
Current Behavior
The current situation is that only one MCP server can be started, and attempting to start multiple servers results in an error.
Context
version springboot 3.5.0 spring-ai-mcp 1.0.0
package org.example.mcpserverdemo;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
/**
* <p>
*
* </p>
*
* @author kunqi.fc
* @since 2025/6/12
**/
@Configuration
public class McpServerConfig {
@Bean("t1")
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider1() {
return new WebMvcSseServerTransportProvider(new ObjectMapper(), "/mcp/message","/sse");
}
@Bean
public RouterFunction<ServerResponse> mvcMcpRouterFunction1(@Qualifier("t1") WebMvcSseServerTransportProvider transportProvider) {
return transportProvider.getRouterFunction();
}
@Bean("t2")
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider2() {
return new WebMvcSseServerTransportProvider(new ObjectMapper(), "/mcp/message","/sse2");
}
@Bean
public RouterFunction<ServerResponse> mvcMcpRouterFunction2(@Qualifier("t2")WebMvcSseServerTransportProvider transportProvider) {
return transportProvider.getRouterFunction();
}
@Bean("weather-mcp-server_instance1")
public McpSyncServer mcpServer(ToolCallbackProvider provider,@Qualifier("t1") WebMvcSseServerTransportProvider transportProvider) { // @formatter:off
// Configure server capabilities with resource support
var capabilities = McpSchema.ServerCapabilities.builder()
.tools(true) // Tool support with list changes notifications
.logging() // Logging support
.build();
// Create the server with both tool and resource capabilities
// Add @Tools
return McpServer.sync(transportProvider)
.serverInfo("MCP Demo Weather Server1", "1.0.1")
.capabilities(capabilities)
.tools(McpToolUtils.toSyncToolSpecifications(provider.getToolCallbacks())) // Add @Tools
.build(); // @formatter:on
} // @formatter:on
@Bean(name = "weather-mcp-server_instance2")
public McpSyncServer mcpServer2(ToolCallbackProvider provider,@Qualifier("t2") WebMvcSseServerTransportProvider transportProvider) { // @formatter:off
// Configure server capabilities with resource support
var capabilities = McpSchema.ServerCapabilities.builder()
.tools(true) // Tool support with list changes notifications
.logging() // Logging support
.build();
// Create the server with both tool and resource capabilities
// Add @Tools
return McpServer.sync(transportProvider)
.serverInfo("MCP Demo Weather Server2", "1.0.2")
.capabilities(capabilities)
.tools(McpToolUtils.toSyncToolSpecifications(provider.getToolCallbacks())) // Add @Tools
.build(); // @formatter:on
} // @formatter:on
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
}
errormssage: 2025-06-12T11:14:54.713+08:00 INFO 57942 --- [mcp_server_demo] [ main] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2025-06-12T11:14:54.720+08:00 ERROR 57942 --- [mcp_server_demo] [ main] o.s.b.d.LoggingFailureAnalysisReporter :
APPLICATION FAILED TO START
Description:
Parameter 0 of method mcpSyncServer in org.springframework.ai.mcp.server.autoconfigure.McpServerAutoConfiguration required a single bean, but 2 were found: - t1: defined by method 'webMvcSseServerTransportProvider1' in class path resource [org/example/mcpserverdemo/McpServerConfig.class] - t2: defined by method 'webMvcSseServerTransportProvider2' in class path resource [org/example/mcpserverdemo/McpServerConfig.class]
This may be due to missing parameter name information
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
Ensure that your compiler is configured to use the '-parameters' flag. You may need to update both your build tool settings as well as your IDE. (See https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-6.1-Release-Notes#parameter-name-retention)
Comment From: Susuper2019
I have the same requirement. After I configured @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}), both servers can start successfully, but they are unable to load their respective tools.
Comment From: Susuper2019
Using @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) is the correct approach, and I’ve got it running successfully now.
I have the same requirement. After I configured @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}), both servers can start successfully, but they are unable to load their respective tools.我也有同样的要求。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。
Comment From: sunyuhan1998
In fact, your current approach is already equivalent to using the native MCP Java sdk directly, and I don't think you need to introduce Spring AI's MCP Server Starter at all, so that this issue is good to go.
Comment From: quaff
I now want to start multiple MCP servers within a single project.
Curiously, why would you want to do in that way?
Comment From: Susuper2019
I now want to start multiple MCP servers within a single project.现在,我想在一个项目中启动多个 MCP 服务器。
Curiously, why would you want to do in that way?奇怪的是,你为什么要这样做呢?
We have multiple backend services, each paired with its own MCP server and agent. This design offers two main benefits: First, it minimizes resource usage, as a single server application can host multiple MCP servers. Second, the tools defined by each backend service may be similar — for example, tools for starting or stopping rules and business logic. If all tools were placed under a single MCP server, the client might mistakenly invoke the wrong tool.
Comment From: quaff
First, it minimizes resource usage, as a single server application can host multiple MCP servers.
Yes, but it increase complexity, it's a tradeoff.
Second, the tools defined by each backend service may be similar — for example, tools for starting or stopping rules and business logic.
You should extract common code as library for share.
Comment From: gakkiyomi
In fact, your current approach is already equivalent to using the native MCP Java sdk directly, and I don't think you need to introduce Spring AI's MCP Server Starter at all, so that this issue is good to go.
How to bind servlet to embbding webserver when using native SDK? I am not very good at springboot
Comment From: gakkiyomi
Using @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) is the correct approach, and I’ve got it running successfully now.
I have the same requirement. After I configured @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}), both servers can start successfully, but they are unable to load their respective tools.我也有同样的要求。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。
so, how to solve this: 《not load their respective tools.》
Comment From: Susuper2019
Using @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) is the correct approach, and I’ve got it running successfully now.使用 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 是正确的方法,我现在已经成功运行了它。
I have the same requirement. After I configured @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}), both servers can start successfully, but they are unable to load their respective tools.我也有同样的要求。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。我也有同样的要求。在我配置了 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两个服务器都可以成功启动,但它们无法加载各自的工具。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。
so, how to solve this: 《not load their respective tools.》那么,如何解决这个问题: 《不加载各自的工具》
I initially wrote the wrong URL, but after correcting it, the tools were successfully loaded
Comment From: gakkiyomi
Using @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) is the correct approach, and I’ve got it running successfully now.使用 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 是正确的方法,我现在已经成功运行了它。
I have the same requirement. After I configured @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}), both servers can start successfully, but they are unable to load their respective tools.我也有同样的要求。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。我也有同样的要求。在我配置了 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两个服务器都可以成功启动,但它们无法加载各自的工具。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。
so, how to solve this: 《not load their respective tools.》那么,如何解决这个问题: 《不加载各自的工具》
I initially wrote the wrong URL, but after correcting it, the tools were successfully loaded
Can you share your code with me?
Comment From: sunyuhan1998
How to bind servlet to embbding webserver when using native SDK? I am not very good at springboot
I noticed that you are using WebMvcSseServerTransportProvider, so I'm assuming that you expect to use a WebMvc web server, in this scenario you should use spring-boot-starter-web, which can quickly help you build a web server: https://docs.spring.io/spring-boot/tutorial/first-application/index.html
Comment From: FishingDanDan
Using @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) is the correct approach, and I’ve got it running successfully now.使用 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 是正确的方法,我现在已经成功运行了它。
I have the same requirement. After I configured @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}), both servers can start successfully, but they are unable to load their respective tools.我也有同样的要求。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。我也有同样的要求。在我配置了 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两个服务器都可以成功启动,但它们无法加载各自的工具。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。
so, how to solve this: 《not load their respective tools.》那么,如何解决这个问题: 《不加载各自的工具》
I initially wrote the wrong URL, but after correcting it, the tools were successfully loaded
Can you share your code with me?
How did you solve it in the end, buddy
Comment From: FishingDanDan
Using @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) is the correct approach, and I’ve got it running successfully now.使用 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 是正确的方法,我现在已经成功运行了它。
I have the same requirement. After I configured @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}), both servers can start successfully, but they are unable to load their respective tools.我也有同样的要求。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。我也有同样的要求。在我配置了 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两个服务器都可以成功启动,但它们无法加载各自的工具。配置 @SpringBootApplication(exclude = {McpServerAutoConfiguration.class}) 后,两台服务器都可以成功启动,但它们无法加载各自的工具。
so, how to solve this: 《not load their respective tools.》那么,如何解决这个问题: 《不加载各自的工具》
I initially wrote the wrong URL, but after correcting it, the tools were successfully loaded
Can you share your code with me?
How did you solve it in the end, buddy
我解决了,感谢。最重要的还是复写McpWebMvcServerAutoConfiguration,可能由于我之前配置了context-path,你上面的代码没有携带serverProperties.getBaseUrl()。
完整的代码如下:
package com.example.mcpdemo.config;
import com.example.mcpdemo.service.CqmsService;
import com.example.mcpdemo.service.WeatherService;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.mcp.server.autoconfigure.McpServerProperties;
import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
@Configuration
@EnableConfigurationProperties(McpServerProperties.class)
public class McpServerConfig {
@Autowired
private CqmsService cqmsService;
@Autowired
private WeatherService weatherService;
private final ObjectMapper objectMapper = new ObjectMapper();
private McpSyncServer getMcpSyncServer(WebMvcSseServerTransportProvider provider, String name, String version, ToolCallback... toolCallbacks) {
var capabilities = McpSchema.ServerCapabilities.builder()
.tools(true)
.logging()
.build();
return McpServer.sync(provider)
.serverInfo(name, version)
.capabilities(capabilities)
.tools(McpToolUtils.toSyncToolSpecifications(toolCallbacks))
.build();
}
@Bean(name = "cqmsTransportProvider")
public WebMvcSseServerTransportProvider cqmsTransportProvider(McpServerProperties serverProperties) {
return new WebMvcSseServerTransportProvider(objectMapper, serverProperties.getBaseUrl(), "/cqms/mcp/message", "/cqms/sse");
}
@Bean
public RouterFunction<?> cqmsMcpRouterFunction(@Qualifier("cqmsTransportProvider") WebMvcSseServerTransportProvider sseServerTransportProvider) {
return sseServerTransportProvider.getRouterFunction();
}
@Bean("cqms-mcp-server")
public McpSyncServer newsMcpServer(@Qualifier("cqmsTransportProvider") WebMvcSseServerTransportProvider newsTransportProvider) {
return getMcpSyncServer(newsTransportProvider, "MCP Cqms Server", "1.0.0", ToolCallbacks.from(cqmsService));
}
@Bean(name = "weatherTransportProvider")
public WebMvcSseServerTransportProvider weatherTransportProvider(McpServerProperties serverProperties) {
return new WebMvcSseServerTransportProvider(objectMapper, serverProperties.getBaseUrl(), "/weather/mcp/message", "/weather/sse");
}
@Bean
public RouterFunction<ServerResponse> weatherMcpRouterFunction(@Qualifier("weatherTransportProvider") WebMvcSseServerTransportProvider sseServerTransportProvider) {
return sseServerTransportProvider.getRouterFunction();
}
@Bean("weather-mcp-server")
public McpSyncServer weatherMcpServer(@Qualifier("weatherTransportProvider") WebMvcSseServerTransportProvider newsTransportProvider) {
return getMcpSyncServer(newsTransportProvider, "MCP Weather Server", "1.0.0", ToolCallbacks.from(weatherService));
}
}
spring.application.name=mcp-demo
server.port=9090
server.servlet.context-path=/mcp-demo
#
spring.ai.mcp.server.base-url=${server.servlet.context-path}
spring.ai.mcp.server.capabilities.completion=false
spring.ai.mcp.server.capabilities.prompt=false
这样我的两个mcpServer路径分别是: http://localhost:9090/mcp-demo/weather/sse http://localhost:9090/mcp-demo/cqms/sse
Comment From: lihuagang03
In fact, your current approach is already equivalent to using the native MCP Java sdk directly, and I don't think you need to introduce Spring AI's MCP Server Starter at all, so that this issue is good to go.
There is actually such a real demand.
RouterFunction<?> is a Spring-Boot framework-level function.
@sunyuhan1998
Comment From: lihuagang03
I now want to start multiple MCP servers within a single project.
Curiously, why would you want to do in that way?
The demand is wrapping all HTTP REST APIs to MCP Tools, MCP Servers are group by application name. @quaff
Comment From: lihuagang03
最重要的还是复写McpWebMvcServerAutoConfiguration
Good, correct. 正解 @FishingDanDan