一、项目实现目标
项目目标是完成传统SASS服务向MCP服务的转型,并实现MCP服务的负载均衡调用,具体目标如下:
-
服务转型:将现有的传统SASS服务快速、高效地封装并转换为MCP(服务,复用原有业务逻辑,降低转型成本。
-
负载均衡:实现MCP服务的多实例部署与负载均衡调用,通过Nacos服务注册与发现机制,分发请求流量,避免单实例压力过大,提升服务可用性。
文末有源码链接
二、项目运行环境
项目基于Spring生态搭建,依赖以下核心组件,各组件版本及作用明确如下,确保组件间兼容性与功能正常发挥:
-
NACOS 3.1.1:作为服务注册中心与配置中心,支持MCP服务的注册、发现与配置管理,为MCP服务的负载均衡提供基础支撑,确保服务实例可被正常识别与调用。
-
Spring AI Alibaba:提供ReactAgent组件,该组件是对Spring AI ChatClient的更高层次封装,集成了Agent开发常用的工具调用、拦截器、内存存储等能力,简化Agent端的开发流程。
-
千问大模型(qwen-flash):与Spring AI Alibaba深度集成,无需额外复杂配置即可快速调用,为Agent端提供自然语言交互与逻辑处理能力,支撑控制台的对话式服务调用。
三、项目整体架构
项目采用模块化架构设计,分为两个核心项目模块,整体架构如下:
-
sass-to-mcp-agent:作为服务调用端,核心功能是通过命令行控制台与用户进行交互,通过Nacos发现已注册的MCP服务,并根据用户输入的指令,自动调用对应的MCP服务工具,返回处理结果。
-
sass-to-mcp-mcp:作为服务提供端,包含原有SASS服务与封装后的MCP服务,部署时需启动两个实例并注册到Nacos,主要用于模拟MCP服务的多实例部署,配合完成负载均衡测试,验证负载分发效果。
两个模块通过Nacos实现服务注册与发现,Agent端通过MCP客户端调用MCP服务端的工具方法,形成完整的“调用-响应”链路,同时借助Nacos实现负载均衡调度。
四、sass-to-mcp-agent 项目详情
该项目为服务调用端,负责与用户交互、发现MCP服务并发起调用,核心包含依赖配置、配置文件、核心代码三大模块,具体实现如下:
4.1 项目依赖
引入核心依赖,涵盖日志、MCP客户端、Agent框架、千问大模型支持等,确保项目正常运行,依赖配置如下(pom.xml):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
<!-- Spring AI Alibaba Agent Framework:提供Agent核心能力 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
</dependency>
<!-- DashScope ChatModel 支持(千问大模型集成),若更换模型需替换对应starter -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-mcp-distributed</artifactId>
</dependency>
</dependencies>
4.2 配置文件
通过application.yaml配置文件,设置日志、应用名称、MCP客户端、Nacos连接、千问大模型等参数,确保各组件协同工作,配置如下:
logging:
config: classpath:logback-spring.xml # 日志配置文件路径
spring:
application:
name: sass-to-mcp-agent # 应用名称,用于Nacos识别
main:
web-application-type: none # 非Web应用,仅启动命令行交互
ai:
dashscope:
api-key: 你的阿里云百炼key # 千问大模型调用密钥
base-url: https://dashscope.aliyuncs.com # 千问大模型请求地址
chat:
options:
model: qwen-flash # 选用的千问模型版本
alibaba:
mcp:
nacos:
client:
enabled: true # 启用Nacos作为MCP客户端注册中心
sse:
connections:
server1:
service-name: sass-to-mcp-mcp-server # Nacos中MCP服务的名称(需与服务端一致)
version: 1.0.0 # MCP服务版本(需与服务端一致)
configs:
server1:
namespace: public # Nacos命名空间(默认public)
server-addr: 127.0.0.1:8848 # Nacos服务地址
username: nacos # Nacos登录用户名
password: nacos # Nacos登录密码
mcp:
client:
enabled: true # 启用MCP客户端
name: sass-to-mcp-agent # MCP客户端名称
version: 0.0.1 # MCP客户端版本
initialized: true # 启动时初始化MCP客户端
request-timeout: 600s # 请求超时时间
type: sync # 调用类型(同步)
toolcallback:
enabled: true # 启用工具回调
root-change-notification: true # 启用根节点变更通知
4.3 核心代码
4.3.1 配置类
配置ReactAgent实例,集成工具回调、千问大模型、日志拦截器等,用于处理用户请求并调用MCP服务工具,代码如下:
@Configuration(proxyBeanMethods = false)
public class Config {
@Bean
public ReactAgent buildReactAgent(@Qualifier("distributedSyncToolCallback") ToolCallbackProvider tools, ChatModel chatModel) {
// 从nacos获取所有可用的工具回调,打印日志便于调试
ToolCallback[] toolCallbacks = tools.getToolCallbacks();
System.out.println(">>> Available tools: ");
for (int i = 0; i < toolCallbacks.length; i++) {
System.out.println("[" + i + "] " + toolCallbacks[i].getToolDefinition().name());
}
// 构建ReactAgent,集成工具、日志拦截器、模型、内存存储
ReactAgent agent = ReactAgent.builder()
.tools(tools.getToolCallbacks())
.interceptors(new LoggingInterceptor())
.model(chatModel)
.name("test_agent")
.saver(new MemorySaver())
.build();
return agent;
}
}
4.3.2 命令行交互类
实现命令行交互功能,接收用户输入的指令,通过ReactAgent调用MCP服务,返回处理结果,支持“exit”命令退出,代码如下:
@Controller
public class CommandLineRunnerController implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(CommandLineRunnerController.class);
// 注入ReactAgent实例(由配置类创建)
ReactAgent agent;
public CommandLineRunnerController(ReactAgent agent) {
this.agent = agent;
}
@Override
public void run(String... args) throws Exception {
// 生成唯一对话ID,用于标识当前对话上下文
RunnableConfig runnableConfig = RunnableConfig.builder()
.threadId(UUID.randomUUID().toString())
.addMetadata("user_id", "1")
.build();
// 启动控制台扫描器,接收用户输入
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("\n>>> QUESTION: ");
String userInput = scanner.nextLine();
// 输入exit,退出程序
if (userInput.equalsIgnoreCase("exit")) {
break;
}
// 空输入时,默认发送“你好”进行测试
if (userInput.isEmpty()) {
userInput = "你好";
}
String res = "";
try {
// 调用Agent处理用户输入,获取响应结果
AssistantMessage response = agent.call(userInput, runnableConfig);
res = response.getText();
} catch (GraphRunnerException e) {
// 捕获异常,打印日志并返回异常提示
logger.error("", e);
res = "系统异常";
}
// 输出Agent响应结果
System.out.println("\n>>> ASSISTANT: " + res);
}
// 关闭扫描器,释放资源
scanner.close();
}
}
五、sass-to-mcp-mcp 项目详情
该项目为服务提供端,负责将传统SASS服务封装为MCP服务,并注册到Nacos,支持多实例部署,核心包含依赖配置、配置文件、核心代码三大模块,具体实现如下:
5.1 项目依赖
引入核心依赖,涵盖Web服务、日志、MCP服务注册、MCP服务端等,确保SASS服务能正常封装为MCP服务并注册到Nacos,依赖配置如下(pom.xml):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!-- MCP服务注册依赖,用于将MCP服务注册到Nacos -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-mcp-registry</artifactId>
</dependency>
<!-- MCP服务端依赖,支持WebMVC模式 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
</dependencies>
5.2 配置文件
通过application.yaml配置文件,设置端口、应用名称、MCP服务、Nacos注册等参数,支持多实例部署(需手动修改端口),配置如下:
logging:
config: classpath:logback-spring.xml # 日志配置文件路径
server:
port: 8090 # 服务端口(多实例部署时需修改,如8091、8092)
spring:
application:
name: sass-to-mcp-mcp # 应用名称
ai:
mcp:
server:
name: sass-to-mcp-mcp-server # MCP服务名称(需与Agent端配置一致)
version: 1.0.0 # MCP服务版本(需与Agent端配置一致)
type: SYNC # 调用类型(同步)
instructions: "这个mcp提供订单、订单商品相关的工具与资源" # MCP服务描述
alibaba:
mcp:
nacos:
server-addr: localhost:8848 # Nacos服务地址(与Agent端一致)
namespace: public # Nacos命名空间(默认public)
username: nacos # Nacos登录用户名
password: nacos # Nacos登录密码
register:
enabled: true # 启用Nacos服务注册(必须开启,否则Agent端无法发现)
5.3 核心代码
5.3.1 MCP服务类
通过自定义注解@McpTools标记该类为MCP服务类,将SASS服务的方法封装为MCP工具方法,供Agent端调用,代码如下:
@McpTools
public class McpService {
// 注入原有SASS服务的Controller,复用原有业务逻辑
@Autowired
SassController SassController;
/**
* 工具方法:获取订单详情
* @param param 订单详情查询参数(需与SASS服务参数一致)
* @return 订单详情VO
*/
@Tool(description = "获取订单详情")
public OrderDetailVO getOrderDetail(OrderDetailQueryParam param) {
// 直接调用SASS服务的方法,实现快速封装
return SassController.getOrderDetail(param);
}
/**
* 工具方法:获取分页订单列表
* @param param 订单列表查询参数(含分页信息)
* @return 分页订单列表结果
*/
@Tool(description = "获取订单列表(分页)")
public PageResult<OrderVO> getOrderList(OrderListQueryParam param) {
// 直接调用SASS服务的方法,实现快速封装
return SassController.getOrderList(param);
}
}
说明:
-
@McpTools:自定义注解,用于标记MCP服务类,Spring会通过扫描机制,识别该类并注册为MCP服务。
-
@Tool:标记方法为MCP工具方法,description属性用于描述方法功能,供Agent端识别并调用。
-
依赖注入SassController,直接复用原有SASS服务的业务逻辑,无需重复开发,实现传统SASS服务向MCP服务的快速转型。
5.3.2 MCP服务扫描注册类
实现BeanFactoryPostProcessor接口,用于扫描所有带有@McpTools注解的Bean,自动注册为MCP工具回调提供者,确保Agent端能发现并调用MCP服务方法,代码如下:
@Component
public class McpToolRegister implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 判断Bean工厂是否为BeanDefinitionRegistry,用于注册Bean定义
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 获取所有带有@McpTools注解的Bean名称
String[] beanNames = beanFactory.getBeanNamesForAnnotation(McpTools.class);
// 遍历所有@McpTools标记的Bean,注册为MethodToolCallbackProvider
for (String beanName : beanNames) {
BeanDefinition bd = registry.getBeanDefinition(beanName);
try {
// 获取Bean的Class对象
Class<?> beanClass = Class.forName(bd.getBeanClassName());
// 注册MethodToolCallbackProvider,用于提供MCP工具回调
registerMethodToolCallbackProvider(registry, beanName, beanClass);
} catch (ClassNotFoundException e) {
// 捕获类加载异常,打印堆栈信息
e.printStackTrace();
}
}
}
}
/**
* 注册MethodToolCallbackProvider Bean,用于将MCP服务方法封装为工具回调
* @param registry Bean定义注册中心
* @param originalBeanName 原有@McpTools标记的Bean名称
* @param originalBeanClass 原有@McpTools标记的Bean的Class
*/
private void registerMethodToolCallbackProvider(BeanDefinitionRegistry registry, String originalBeanName, Class<?> originalBeanClass) {
// 生成MethodToolCallbackProvider的Bean名称,避免与原有Bean冲突
String providerBeanName = "methodToolCallbackProvider_" + originalBeanName;
// 如果该Bean已注册,则跳过,避免重复注册
if (registry.containsBeanDefinition(providerBeanName)) {
return;
}
// 创建GenericBeanDefinition,设置BeanClass为ClassMethodToolCallbackProvider
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(ClassMethodToolCallbackProvider.class);
// 通过构造函数参数,传递MCP服务类的Class对象
definition.getConstructorArgumentValues().addGenericArgumentValue(Arrays.asList(originalBeanClass));
// 注册Bean定义,完成MCP工具回调提供者的注册
registry.registerBeanDefinition(providerBeanName, definition);
}
}
六、项目运行流程
项目运行需遵循固定流程,确保服务注册、发现与调用正常,具体步骤如下:
-
启动Nacos服务:首先启动本地Nacos服务(版本3.1.1),确保Nacos控制台可正常访问(默认地址:http://127.0.0.1:8848/nacos,用户名/密码:nacos/nacos)。
-
启动sass-to-mcp-mcp项目(多实例):
-
启动第一个实例:使用默认配置,端口为8090,启动命令无需修改端口。
-
启动第二个实例:手动指定端口(避免端口冲突),启动命令示例:
java -jar sass-to-mcp-mcp.jar --server.port=8091(可根据实际情况修改端口号)。 -
启动完成后,可在Nacos控制台“服务管理-服务列表”中,看到sass-to-mcp-mcp-server服务已注册,且包含2个实例。
-
-
启动sass-to-mcp-agent项目:启动Agent项目,启动成功后,控制台会打印可用的MCP工具列表,同时提示“>>> QUESTION: ”,等待用户输入。
-
测试服务调用与负载均衡:在Agent控制台输入相关问题(如“获取订单详情”“获取订单列表”),Agent会自动调用MCP服务,返回处理结果;多次输入相同指令,可通过Nacos控制台或服务日志,观察请求是否在两个MCP实例间均衡分发,验证负载均衡效果。
七、运行截图
nacos mcp服务截图
nacos mcp 方法元数据配置截图
发起请求
服务1收到请求
服务2收到请求
八、总结
结合实战过程中的经验,注意事项如下:
-
若新增/修改了MCP服务的方法定义(如方法参数、返回值、注解信息等),需清除Nacos中的MCP服务,否则将无法生效,甚至可能导致服务调用异常。
-
Agent端(MCP调用方)必须在所有MCP服务实例启动并成功注册到Nacos后,再启动Agent项目;若提前启动Agent,可能会因无法从Nacos发现MCP服务而报错