用Nacos管理Agent和MCP
本文介绍了如何利用Nacos 3.0+版本管理AI系统中的Agent和MCP服务。通过Docker部署Nacos服务,并演示了SpringBoot应用集成Nacos实现服务注册与发现的完整流程。主要内容包括:1)使用Docker-compose部署Nacos和MySQL;2)开发MCP Server实现时间服务并通过Nacos自动注册;3)构建MCP Client调用注册的MCP服务;4)开发自定
1.背景说明
随着 Ai越来越强大,越来越多的系统,需要引入 Agent和MCP(模型上下文协议);但是总不能每引入一个Agent或者MCP,都要在系统中写对应的Agent或者MCP,这样系统会臃肿,不便于维护和管理。Nacos3.0版本之后,就开始引入了Agent管理、MCP管理的功能,能够让系统在调用Agent或者MCP时,类似调用其他微服务一样。 本文的项目演示,是以SpringBoot 3版本以上
2.Nacos安装和部署
本文的案例,是以docker 容器运营Nacos的,具体的docker-compose.yml文本如下:
version: '3.8' # docker 18
services:
mysql:
image: mysql:8.0.39
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: 12345678 # 数据库登录密码
ports:
- "3306:3306"
volumes:
- /opt/mysql/conf:/etc/mysql/conf.d # 挂载数据库配置
- /opt/mysql/data:/var/lib/mysql # 挂载数据库的数据
- /opt/mysql/logs:/var/log/mysql # 挂载数据库的日志信息
restart: always
networks:
- asurada-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p12345678"]
interval: 10s
timeout: 5s
retries: 5
nacos:
image: nacos/nacos-server:v3.1.0
container_name: nacos
restart: always
ports:
- "8080:8080"
- "8848:8848"
- "9848:9848"
- "9849:9849"
volumes:
- /opt/nacos/conf:/home/nacos/conf # 挂载 Nacos的配置文件
- /opt/nacos/logs:/home/nacos/logs # 挂载 Nacos的日志文件
- /opt/nacos/data:/home/nacos/data # 挂载 Nacos的相关数据
environment:
MODE: standalone # 单机启动
SPRING_DATASOURCE_PLATFORM: mysql # 指定nacos 使用的数据库平台
MYSQL_SERVICE_HOST: mysql
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_DB_NAME: nacos # nacos使用的database
MYSQL_SERVICE_USER: root # 数据库的账号
MYSQL_SERVICE_PASSWORD: 12345678 # 数据库密码
NACOS_AUTH_ENABLE: true # 是否开启安全验证
NACOS_AUTH_TOKEN: LMCoybqtzdX969JnWM8392iuCFdsviI9OkOjEtSEQSs= # 安全验证的Token
NACOS_AUTH_IDENTITY_KEY: nacos # 安全验证key 建议使用nacos的登录账号
NACOS_AUTH_IDENTITY_VALUE: 123456 # 安全验证value 建议使用nacos登录账号的密码
depends_on:
mysql:
condition: service_healthy
networks:
- asurada-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080"] # nacos 3.0以上版本之后,console控制台端口默认是8080,访问路径/
interval: 10s
timeout: 5s
retries: 15
注意
- NACOS_AUTH_TOKEN: Nacos 用于生成JWT Token的密钥,使用长度大于32字符的字符串,再经过Base64编码。可以使用网上的JWT Token生成器,生成这个密钥。
- NACOS_AUTH_IDENTITY_KEY: Nacos Server端之间 Inner API的身份标识的Key,必填。
- NACOS_AUTH_IDENTITY_VALUE: Nacos Server端之间 Inner API的身份标识的Value,必填。
- nacos console控制台的访问端口,默认是8080,因此部署Nacos的镜像时,必须映射这个端口,为了Nacos在启动时报端口冲突的错误,其他应用不要使用8080端口了。
根据自身的情况,修改完docker-comose.yml之后,执行以下命令:
docker compose up -d
生成 mysql、nacos的容器。
3.验证Nacos服务是否启动成功
在浏览器页面,访问http://宿主机ip:8080(nacos console控制台的访问端口,默认是8080)
4.Nacos管理MCP Server
使用 Spring AI Alibaba Nacos MCP 框架开发 MCP Server
4.1 MCP Server 自动注册
4.1.1 引入依赖
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-mcp-registry</artifactId>
<version>{1.0.0.3及以上版本}</version>
</dependency>
<!-- MCP Server (WebMVC) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>{1.0.0及以上版本}</version>
</dependency>
4.1.2 服务案例
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author yingzi
* @date 2025/5/28 08:55
*/
@Service
public class TimeService {
private static final Logger logger = LoggerFactory.getLogger(TimeService.class);
@Tool(description = "Get the time of a specified city.")
public String getCityTimeMethod(@ToolParam(description = "Time zone id, such as Asia/Shanghai") String timeZoneId) {
logger.info("The current time zone is {}", timeZoneId);
return String.format("The current time zone is %s and the current time is " + "%s", timeZoneId,
getTimeByZoneId(timeZoneId));
}
private String getTimeByZoneId(String zoneId) {
// Get the time zone using ZoneId
ZoneId zid = ZoneId.of(zoneId);
// Get the current time in this time zone
ZonedDateTime zonedDateTime = ZonedDateTime.now(zid);
// Defining a formatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
// Format ZonedDateTime as a string
String formattedDateTime = zonedDateTime.format(formatter);
return formattedDateTime;
}
}
4.1.3自动注册参数配置
server:
port: 8081
spring:
application:
name: mcp-nacos-register # 替换为你的服务名称
ai:
mcp:
server:
name: webflux-mcp-server
version: 1.0.0
type: ASYNC # Recommended for reactive applications
instructions: "This reactive server provides time information tools and resources"
sse-message-endpoint: /mcp/messages
capabilities:
tool: true
resource: true
prompt: true
completion: true
alibaba:
mcp:
nacos:
namespace: dev # nacos中的命名空间
server-addr: # 替换为你的 Nacos 地址
username: ${NACOS_USERNAME} # 替换为你的nacos登录名
password: ${NACOS_PASSWORD} # 替换为你的nacos密码
register:
enabled: true
service-group: mcp-server #替换为你的MCP server服务群
service-name: webflux-mcp-server #替换为你的MCP server服务名
4.1.4服务启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class McpRegisterApplication {
public static void main(String[] args) {
SpringApplication.run(McpRegisterApplication.class, args);
}
4.2 MCP Server 发现和调用
4.2.1 引入依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-openai</artifactId>
<version>{1.0.0及以上版本}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>
<version>{1.0.0及以上版本}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-mcp-registry</artifactId>
<version>{1.0.0.3及以上版本}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
<version>{1.0.0及以上版本}</version>
</dependency>
4.2.2 application.yml配置
server:
port: 8085
spring:
main:
web-application-type: none
application:
name: mcp-nacos-discovery-example
ai:
openai:
api-key: # 大模型的api-key
base-url: https://dashscope.aliyuncs.com/compatible-mode
chat:
options:
model: qwen-plus
mcp:
client:
enabled: true
name: my-mcp-client
version: 1.0.0
request-timeout: 30s
type: ASYNC # or ASYNC for reactive applications
alibaba:
mcp:
nacos:
namespace: dev # nacos 命名空间
server-addr: ${TENCENT_CLOUD_IP}:8848 #你的Nacos服务地址
username: ${NACOS_USERNAME} # Nacos用户
password: ${NACOS_PASSWORD} # Nacos 密码
client:
enabled: true
sse:
connections:
server1:
serviceName: webflux-mcp-server
version: 1.0.0
# 调试日志
logging:
level:
io:
modelcontextprotocol:
client: DEBUG
spec: DEBUG
4.2.3 创建 Async 或者 Sync MCP Client
@Autowired
private List<LoadbalancedMcpSyncClient> loadbalancedMcpSyncClients;
or
@Autowired
private List<LoadbalancedMcpAsyncClient> loadbalancedMcpAsyncClients;
或者使用 提供的ToolCallbackProvider
@Qualifier("loadbalancedSyncMcpToolCallbacks") ToolCallbackProvider tools
or
@Qualifier("loadbalancedMcpAsyncToolCallbacks") ToolCallbackProvider tools
4.2.4 参考示例代码
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Scanner;
@SpringBootApplication
public class NacosMcpDiscoveryClientApplication {
public static void main(String[] args) {
SpringApplication.run(NacosMcpDiscoveryClientApplication.class, args);
}
@Bean
public CommandLineRunner predefinedQuestionsDemo(ChatClient.Builder chatClientBuilder, @Qualifier("loadbalancedMcpAsyncToolCallbacks") ToolCallbackProvider tools,
ConfigurableApplicationContext context) {
return args -> {
ChatClient chatClient = chatClientBuilder
.defaultToolCallbacks(tools.getToolCallbacks())
.build();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("\n>>> QUESTION: ");
String userInput = scanner.nextLine();
if (userInput.equalsIgnoreCase("exit")) {
break;
}
if (userInput.isEmpty()) {
userInput = "北京时间现在几点钟";
}
System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput).call().content());
}
scanner.close();
context.close();
};
}
}
5.Nacos管理Agent
5.1 Agent自动注册
5.1.1 发布通过Spring AI Alibaba开发的Agent
5.1.1.1 引入依赖
<!-- Spring AI Alibaba版本1.0.0.4及以上 -->
<properties>
<spring-ai-alibaba.version>1.0.0.4</spring-ai-alibaba.version>
</properties>
<dependencies>
<!-- Spring Boot Web Starter - A2A 服务器需要 Web 环境 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入A2A Server starter -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-a2a-server</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- 引入A2A Nacos 注册中心 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-a2a-registry</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- 引入A2A 百炼大模型客户端,可以用其他的spring ai大模型客户端代替,如openai -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- Spring Boot Actuator for monitoring and management -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
5.1.1.2 构建agent
通过Spring AI Alibaba中定义的Agentic API快速定义及构建Agent运行逻辑,如下代码,创建了一个直接访问大模型Agent
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.cloud.ai.graph.agent.BaseAgent;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
@Configuration
public class RootAgentConfiguration {
@Bean
@Primary
public BaseAgent rootAgent(ChatModel chatModel) throws GraphStateException {
return ReactAgent.builder().name("rootAgent").description("A simple agent that responds with 'Hello, World!'").model(chatModel)
.instruction("You are a helpful assistant that responds with 'Hello, World!'").build();
}
}
5.1.1.3 application.yml配置
server:
port: 9999
spring:
application:
name: a2s-server-example
ai:
# 配置大模型的参数,如API-KEY,模型名称等
dashscope:
api-key: ${TONGYI_AI_KEY}
base-url: https://dashscope.aliyuncs.com/compatible-mode
chat:
options:
model: qwen-plus-latest
alibaba:
a2a:
# 配置Nacos的地址和用户名密码
nacos:
namespace: dev
server-addr: ${TENCENT_CLOUD_IP}:8848
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD}
# 配置A2A server的额外信息,如版本号,agentCard中的Skills等
server:
version: 1.0.2
card:
# 配置Agent(AgentCard)的URL,若当前版本无可用端点,会使用此 URL
url: http://localhost:9999/a2a
skills:
- id: nacos-question-answer
name: Nacos Question Answer
description: Answer questions about Nacos.
tags:
- Nacos
examples:
- What is Nacos?
icon-url: https://img.alicdn.com/imgextra/i4/O1CN01rW3vAB1FDWKSOiFf0_!!6000000000453-2-tps-204-40.png
documentation-url: https://nacos.io
provider:
organization: Alibaba
url: https://www.alibaba.com
5.1.1.4启动A2A Server Agent
启动服务,通过Nacos控制台查看注册的Agent(AgentCard)信息
5.1.2 发布其他的自定义Agent
自定义Agent程序的自动发布需要使用SDK(Nacos-Client)进行发布
5.1.2.1 引入依赖
<!-- Nacos Client 3.1.0及以上版本 -->
<properties>
<nacos.client.version>3.1.0</nacos.client.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入Nacos Client-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>${nacos.client.version}</version>
</dependency>
</dependencies>
5.1.2.2 application.yml配置
spring:
application:
name: agent-card-register
ai:
# 配置大模型的参数,如API-KEY,模型名称等
dashscope:
api-key: ${TONGYI_AI_KEY}
base-url: https://dashscope.aliyuncs.com/compatible-mode
chat:
options:
model: qwen-plus-latest
alibaba:
a2a: # 这里的配置只适用于项目读取配置,不是真的引入 spring alibaba a2a
# 配置Nacos的地址和用户名密码
nacos:
namespace: dev
server-addr: ${TENCENT_CLOUD_IP}:8848
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD}
5.1.2.3 发布AgentCard及注册Agent的endpoint
import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.ai.AiFactory;
import com.alibaba.nacos.api.ai.AiService;
import com.alibaba.nacos.api.ai.model.a2a.AgentCard;
import com.alibaba.nacos.api.exception.NacosException;
@Configuration
public class AgentCardConfig {
@Value("${spring.ai.alibaba.a2a.nacos.server-addr}")
private String serverAddr ;
@Value("${spring.ai.alibaba.a2a.nacos.username}")
private String username;
@Value("${spring.ai.alibaba.a2a.nacos.password}")
private String password ;
@Value("${spring.ai.alibaba.a2a.nacos.namespace}")
private String namespace;
@Bean
public AgentCard agentCard() {
AgentCard agentCard = new AgentCard();
agentCard.setName("asurada");
agentCard.setDescription("asurada is a smart agent");
agentCard.setUrl("http://localhost:9093");
agentCard.setVersion("1.0.0");
agentCard.setProtocolVersion("0.0.1");
agentCard.setPreferredTransport("http");
return agentCard;
}
@Bean
public AiService aiService() throws NacosException{
Properties properties = new Properties() ;
properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddr) ;
properties.setProperty(PropertyKeyConst.USERNAME, username) ;
properties.setProperty(PropertyKeyConst.PASSWORD, password) ;
properties.setProperty(PropertyKeyConst.NAMESPACE, namespace) ;
return AiFactory.createAiService(properties);
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import com.alibaba.nacos.api.ai.AiService;
import com.alibaba.nacos.api.ai.model.a2a.AgentCard;
import jakarta.annotation.Resource;
@Component
public class AgentCardResisterRunner implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(AgentCardResisterRunner.class);
@Resource
private AiService aiService;
@Resource
private AgentCard agentCard;
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("开始注册 Agent Card 到 Nacos...");
logger.info("Agent 信息: name={}, version={}, url={}",
agentCard.getName(), agentCard.getVersion(), agentCard.getUrl());
try {
// 发布Agent的身份信息
logger.info("正在发布 Agent 身份信息...");
aiService.releaseAgentCard(agentCard);
logger.info("Agent 身份信息发布成功");
// 发布Agent的具体的服务调用方式
logger.info("正在注册 Agent 端点...");
aiService.registerAgentEndpoint(agentCard.getName(), agentCard.getVersion(), "http://localhost", 9093);
logger.info("Agent 端点注册成功");
logger.info("Agent Card 注册完成!应用将继续运行以保持服务可用性...");
logger.info("可以通过以下端点查看应用状态:");
logger.info(" - 健康检查: http://localhost:9093/api/health");
logger.info(" - Agent 信息: http://localhost:9093/api/agent/info");
} catch (Exception e) {
logger.error("Agent Card 注册失败: {}", e.getMessage(), e);
throw new RuntimeException("Agent Card 注册失败", e);
}
}
}
5.1.2.4 启动服务
启动项目,登录Nacos的控制台,在Agent列表中,查看相关的agent
5.2 查询/获取Agent
5.2.1 通过Spring AI Alibaba查询/订阅/调用 Agent
5.2.1.1 引入依赖
<!-- Spring AI Alibaba版本1.0.0.4及以上 -->
<properties>
<spring-ai-alibaba.version>1.0.0.4</spring-ai-alibaba.version>
</properties>
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入A2A Server starter -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-a2a-client</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- 引入A2A Nacos 注册中心 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-a2a-registry</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- Spring Boot Actuator for health checks -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
5.2.1.2 构建A2A Agent Client
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.cloud.ai.graph.agent.BaseAgent;
import com.alibaba.cloud.ai.graph.agent.a2a.A2aRemoteAgent;
import com.alibaba.cloud.ai.graph.agent.a2a.AgentCardProvider;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
@Configuration
public class AgentConfig {
@Bean
public BaseAgent rootAgent(AgentCardProvider agentCardProvider) throws GraphStateException {
return A2aRemoteAgent.builder()
// 传入自动构建的Nacos AgentCard Provider
.agentCardProvider(agentCardProvider)
// 设置需要的Agent的名称,Nacos AgentCard Provider会根据此名称自动订阅AgentCard和Agent的可访问端点
.name("rootAgent").description("A simple agent that returns 'Hello World!'.")
.build();
}
}
5.2.1.3 application.yml配置
server:
port: 9091
spring:
application:
name: a2s-discover-example
ai:
alibaba:
a2a:
# 配置Nacos的地址和用户名密码
nacos:
namespace: dev
server-addr: ${TENCENT_CLOUD_IP}:8848
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD}
5.2.1.4 调用A2A Agent Client
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.cloud.ai.graph.agent.BaseAgent;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
import jakarta.annotation.Resource;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
@RequestMapping("/root")
@RestController
public class RootAgentController {
@Resource
private BaseAgent rootAgent;
private static final Logger logger = LoggerFactory.getLogger(RootAgentController.class);
/**
* 调用根代理
* @param message 消息
* @return 响应流
* @throws GraphStateException 图状态异常
* @throws GraphRunnerException 图运行异常
*/
@GetMapping("/stream")
public Flux<String> rootAgent(@RequestParam("message") String message)
throws GraphStateException,GraphRunnerException{
return rootAgent.stream(Map.of("input", message, "messages", List.of(new UserMessage(message)))).mapNotNull(output -> {
logger.debug("stream agent invoke : `{}`", output.toString());
if (output.isSTART() || output.isEND()) {
return null;
}
if (output instanceof StreamingOutput) {
return ((StreamingOutput) output).chunk();
}
return null;
}).publishOn(Schedulers.parallel());
}
}
6.本文项目源码地址
https://gitee.com/sgjingwen/spring-ai-alibaba-example
更多推荐

所有评论(0)