场景
SpringAIAlibaba整合 Streamable HTTP 调用免费 MCP Server 实战全解:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/160973965
上面调用单个MCP Server的基础上,如何集成调用多个Mcp server实现同时都调用,和限制调用某个的效果。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
pom文件保持不变
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version>
</parent>
<groupId>com.badao</groupId>
<artifactId>spring-ai-alibaba-mcp-third-multipart</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
<maven.compiler.release>17</maven.compiler.release>
<spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
<spring-ai.version>1.1.2</spring-ai.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Alibaba DashScope 大模型 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- Spring AI MCP 客户端 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>aliyun</id>
<name>Aliyun Maven</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
yml文件指定两个Mcp Server
server:
port: 8082
spring:
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY}
mcp:
client:
enabled: true
request-timeout: 30s
toolcallback:
enabled: true
streamable-http:
connections:
# 第一个 MCP Server:Hacker News
hacker-news:
url: https://hn.caseyjhand.com
# 第二个 MCP Server
nws-weather:
url: https://nws.caseyjhand.com/mcp
logging:
level:
org.springframework.ai.mcp: DEBUG
org.springframework.ai.chat.client: DEBUG
修改配置类 -- 创建多个 ChatClient Bean
package com.badao.ai.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Configuration
public class AiConfig {
/**
* 通用 ChatClient:注入所有 MCP 工具
*/
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder,
ToolCallbackProvider toolCallbackProvider) {
return chatClientBuilder
.defaultToolCallbacks(toolCallbackProvider.getToolCallbacks())
.build();
}
/**
* 限定为 Hacker News 工具的 ChatClient
* 过滤条件:工具名以 "hn_" 开头(可根据实际工具名调整)
*/
@Bean
public ChatClient hnChatClient(ChatClient.Builder chatClientBuilder,
ToolCallbackProvider toolCallbackProvider) {
List<ToolCallback> hnTools = Arrays.stream(toolCallbackProvider.getToolCallbacks())
.filter(tc -> tc.getToolDefinition().name().startsWith("hn_"))
.collect(Collectors.toList());
return chatClientBuilder
.defaultToolCallbacks(hnTools)
.build();
}
/**
* 限定为 NWS Weather 工具的 ChatClient
* 过滤条件:工具名包含 "weather" 或 "nws",可根据实际工具名调整
*/
@Bean
public ChatClient nwsChatClient(ChatClient.Builder chatClientBuilder,
ToolCallbackProvider toolCallbackProvider) {
List<ToolCallback> nwsTools = Arrays.stream(toolCallbackProvider.getToolCallbacks())
.filter(tc -> {
String name = tc.getToolDefinition().name().toLowerCase();
return name.contains("weather") || name.contains("nws");
})
.collect(Collectors.toList());
return chatClientBuilder
.defaultToolCallbacks(nwsTools)
.build();
}
}
通用服务类保持不变
package com.badao.ai.service;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class AiService {
private final ChatClient chatClient;
public AiService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String chatWithAllTools(String userMessage) {
return chatClient.prompt()
.user(userMessage)
.call()
.content();
}
}
新增限定工具服务类
package com.badao.ai.service;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class SelectiveAiService {
private final ChatClient hnChatClient;
private final ChatClient nwsChatClient;
public SelectiveAiService(@Qualifier("hnChatClient") ChatClient hnChatClient,
@Qualifier("nwsChatClient") ChatClient nwsChatClient) {
this.hnChatClient = hnChatClient;
this.nwsChatClient = nwsChatClient;
}
/** 仅使用 Hacker News 工具 */
public String chatWithHackerNewsOnly(String userMessage) {
return hnChatClient.prompt()
.user(userMessage)
.call()
.content();
}
/** 仅使用 NWS Weather 工具 */
public String chatWithNwsOnly(String userMessage) {
return nwsChatClient.prompt()
.user(userMessage)
.call()
.content();
}
}
修改控制器
package com.badao.ai.controller;
import com.badao.ai.service.AiService;
import com.badao.ai.service.SelectiveAiService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class AiController {
private final AiService aiService;
private final SelectiveAiService selectiveAiService;
public AiController(AiService aiService, SelectiveAiService selectiveAiService) {
this.aiService = aiService;
this.selectiveAiService = selectiveAiService;
}
/** 通用对话:自动调用所有 MCP 工具 */
@PostMapping("/chat")
public ChatResponse chat(@RequestBody ChatRequest request) {
String result = aiService.chatWithAllTools(request.message());
return new ChatResponse(200, "success", result);
}
/** 仅 Hacker News 工具 */
@PostMapping("/chat/hn")
public ChatResponse chatHn(@RequestBody ChatRequest request) {
String result = selectiveAiService.chatWithHackerNewsOnly(request.message());
return new ChatResponse(200, "success", result);
}
/** 仅 NWS Weather 工具 */
@PostMapping("/chat/weather")
public ChatResponse chatWeather(@RequestBody ChatRequest request) {
String result = selectiveAiService.chatWithNwsOnly(request.message());
return new ChatResponse(200, "success", result);
}
public record ChatRequest(String message) {}
public record ChatResponse(int code, String msg, String data) {}
}
测试验证
# 通用接口:同时问新闻和天气
curl -X POST http://localhost:8083/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "Hacker News 最热文章是什么?纽约明天天气如何?"}'

# 仅 Hacker News
curl -X POST http://localhost:8083/api/chat/hn \
-H "Content-Type: application/json" \
-d '{"message": "显示当前最热门的 3 篇文章"}'
