跳到主要内容

MCP 服务注册

Nacos 3.0 引入了革命性的 AI 服务注册能力,支持 MCP(Model Context Protocol)服务的自动注册与发现。本章介绍如何使用 Nacos 作为 MCP Registry 实现 AI 服务的管理。

概述

什么是 MCP

MCP(Model Context Protocol)是一个开放协议,用于连接 AI 模型与外部工具和数据源。通过 MCP,AI 应用可以:

  • 调用外部工具获取实时数据
  • 执行特定操作
  • 访问各种数据源

Nacos MCP Registry 的优势

Nacos 3.0 作为 MCP Registry 提供:

  • 动态服务管理:通过控制台添加、删除、更新 MCP 服务
  • 动态描述更新:工具描述和参数定义支持运行时热更新
  • 动态开关控制:运行时启用或禁用工具,无需重启服务
  • 全栈集成:与 Spring AI Alibaba、Nacos MCP Router、Higress 网关无缝集成

架构图

┌─────────────────────────────────────────────────────────────────┐
│ Nacos MCP Registry 架构 │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ AI Agent 应用 │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Spring AI │ │ LangChain │ │ 其他框架 │ │ │
│ │ │ Alibaba │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 发现 MCP 服务 │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Nacos MCP Registry │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ MCP 服务列表 │ │ │
│ │ │ • 服务名称、版本、描述 │ │ │
│ │ │ • 工具定义(Tool Definitions) │ │ │
│ │ │ • 服务地址、协议类型 │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 注册 MCP 服务 │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ MCP Server 服务 │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Java 服务 │ │ Python 服务 │ │ 其他服务 │ │ │
│ │ │ (Spring AI) │ │ (FastMCP) │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

快速开始

环境准备

  • Nacos 3.0+ 服务已启动
  • JDK 17+(Java 方式)
  • Python 3.8+(Python 方式)

使用 Spring AI Alibaba 注册 MCP 服务

1. 添加依赖

<!-- Nacos MCP Registry Starter -->
<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>

2. 定义工具服务

使用 @Tool 注解定义 MCP 工具:

@Service
public class WeatherService {

/**
* 根据城市名称获取天气信息
*
* @param cityName 城市名称
* @return 天气信息
*/
@Tool(description = "根据城市名称获取天气信息")
public String getWeather(
@ToolParam(description = "城市名称,如:北京、上海") String cityName) {
// 实际应用中调用天气 API
return String.format("%s 今天天气晴朗,气温 25°C", cityName);
}

/**
* 获取空气质量指数
*
* @param cityName 城市名称
* @return 空气质量指数
*/
@Tool(description = "获取指定城市的空气质量指数")
public String getAirQuality(
@ToolParam(description = "城市名称") String cityName) {
// 模拟返回空气质量数据
return String.format("%s 空气质量指数:优(AQI 50)", cityName);
}
}

3. 配置自动注册

application.yml 中配置:

spring:
application:
name: weather-mcp-server
ai:
mcp:
server:
name: weather-service # MCP 服务名称
version: 1.0.0 # 服务版本
type: SYNC # 调用类型:SYNC(同步)或 ASYNC(异步)
instructions: 提供天气查询和空气质量查询服务

# Nacos 配置
alibaba:
cloud:
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
namespace: public
group: MCP_GROUP

4. 注册工具 Bean

在启动类中注册工具:

@SpringBootApplication
public class McpServerApplication {

public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}

@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}

启动服务后,MCP 服务会自动注册到 Nacos。

使用 Python 注册 MCP 服务

1. 安装依赖

pip install nacos-mcp-wrapper-python

2. 创建 MCP 服务

使用 NacosMCP 类创建 MCP 服务,通过 @mcp.tool() 装饰器定义工具:

from nacos_mcp_wrapper.server.nacos_mcp import NacosMCP
from nacos_mcp_wrapper.server.nacos_settings import NacosSettings

# 创建 Nacos 配置
nacos_settings = NacosSettings()
nacos_settings.SERVER_ADDR = "127.0.0.1:8848" # Nacos 服务地址
nacos_settings.NAMESPACE = "public" # 命名空间
nacos_settings.USERNAME = "nacos" # 用户名
nacos_settings.PASSWORD = "nacos" # 密码

# 创建 MCP 服务实例
mcp = NacosMCP(
"finance-tools", # MCP 服务名称
nacos_settings=nacos_settings,
version="1.0.0", # 服务版本
port=18001 # 服务端口
)

# 注册工具:获取股票价格
@mcp.tool()
def get_stock_price(symbol: str) -> str:
"""
获取指定股票代码的当前价格

Args:
symbol: 股票代码,如 AAPL、GOOGL、MSFT
"""
# 模拟返回股票价格
prices = {
"AAPL": "178.50",
"GOOGL": "141.25",
"MSFT": "378.90"
}
price = prices.get(symbol, "未知股票")
return f"{symbol} 当前价格: ${price}"

# 注册工具:加法运算
@mcp.tool()
def add_numbers(a: int, b: int) -> int:
"""
计算两个整数的和

Args:
a: 第一个整数
b: 第二个整数
"""
return a + b

# 注册工具:减法运算
@mcp.tool()
def minus_numbers(a: int, b: int) -> int:
"""
计算两个整数的差

Args:
a: 被减数
b: 减数
"""
return a - b

if __name__ == "__main__":
try:
# 启动 MCP 服务,支持三种传输类型
mcp.run(transport="sse")
# mcp.run(transport="stdio")
# mcp.run(transport="streamable-http")
except Exception as e:
print(f"运行时错误: {e}")

3. 传输类型说明

Nacos MCP Python 支持三种传输类型:

传输类型说明适用场景
sseServer-Sent Events远程服务,支持流式响应
stdio标准输入输出本地进程通信
streamable-http可流式 HTTP远程服务,兼容性好

选择建议:

  • 远程服务:推荐使用 ssestreamable-http
  • 本地开发测试:可使用 stdio
  • 需要穿越防火墙:推荐 streamable-http

在控制台管理 MCP 服务

查看 MCP 服务列表

  1. 登录 Nacos 控制台
  2. 进入「AI 服务」->「MCP 服务」
  3. 可以看到所有已注册的 MCP 服务

查看服务详情

点击服务名称可以查看:

  • 服务基本信息(名称、版本、描述)
  • 工具列表(Tool Definitions)
  • 服务地址和协议类型
  • 元数据信息

动态管理工具

Nacos 3.0 支持运行时动态管理 MCP 工具:

启用/禁用工具

  1. 进入服务详情页
  2. 在工具列表中找到目标工具
  3. 点击开关按钮启用或禁用

修改工具描述

  1. 点击工具的「编辑」按钮
  2. 修改工具描述或参数说明
  3. 保存后立即生效

AI Agent 调用 MCP 服务

使用 Spring AI Alibaba 调用

1. 添加依赖

<!-- OpenAI 模型配置(用于连接大模型) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-openai</artifactId>
<version>1.0.0</version>
</dependency>

<!-- Chat Client 配置 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>
<version>1.0.0</version>
</dependency>

<!-- Nacos MCP Registry -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-mcp-registry</artifactId>
<version>1.0.0.3</version>
</dependency>

<!-- MCP Client (WebFlux) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
<version>1.0.0</version>
</dependency>

2. 创建 MCP 客户端

Spring AI Alibaba 提供两种客户端类型:

同步客户端

@Autowired
private List<LoadbalancedMcpSyncClient> loadbalancedMcpSyncClients;

异步客户端

@Autowired
private List<LoadbalancedMcpAsyncClient> loadbalancedMcpAsyncClients;

使用 ToolCallbackProvider(推荐):

// 同步工具回调
@Autowired
@Qualifier("loadbalancedSyncMcpToolCallbacks")
private ToolCallbackProvider syncToolCallbacks;

// 异步工具回调
@Autowired
@Qualifier("loadbalancedMcpAsyncToolCallbacks")
private ToolCallbackProvider asyncToolCallbacks;

3. 配置文件

spring:
application:
name: mcp-client-app
ai:
openai:
api-key: ${DASHSCOPE_API_KEY}
base-url: https://dashscope.aliyuncs.com/compatible-mode
chat:
options:
model: qwen-max
alibaba:
mcp:
nacos:
server-addr: 127.0.0.1:8848
namespace: public
group: MCP_GROUP
username: nacos
password: nacos

4. 完整调用示例

@SpringBootApplication
public class McpClientApplication {

public static void main(String[] args) {
SpringApplication.run(McpClientApplication.class, args);
}

@Bean
public CommandLineRunner chatDemo(
ChatClient.Builder chatClientBuilder,
@Qualifier("loadbalancedSyncMcpToolCallbacks") ToolCallbackProvider tools) {

return args -> {
// 构建 ChatClient,注入 MCP 工具
ChatClient chatClient = chatClientBuilder
.defaultToolCallbacks(tools.getToolCallbacks())
.build();

Scanner scanner = new Scanner(System.in);
System.out.println("MCP Client 已启动,输入问题进行对话(输入 'exit' 退出)");

while (true) {
System.out.print("\n>>> 问题: ");
String userInput = scanner.nextLine();

if ("exit".equalsIgnoreCase(userInput)) {
break;
}

if (userInput.isEmpty()) {
continue;
}

// 调用 AI 并自动使用 MCP 工具
String response = chatClient.prompt()
.user(userInput)
.call()
.content();

System.out.println("\n>>> 回答: " + response);
}
};
}
}

调用示例

用户输入:

请帮我查询北京今天的天气情况

AI Agent 会自动:

  1. 从 Nacos 发现 weather-service MCP 服务
  2. 调用 getWeather 工具
  3. 返回天气信息

响应:

北京今天天气晴朗,气温 25°C

与 Higress 网关集成

Nacos MCP Registry 可以与 Higress 网关集成,实现统一的流量管理和安全控制。

配置 Higress

# Higress 配置
mcp:
registry:
nacos:
server-addr: 127.0.0.1:8848
namespace: public
group: MCP_GROUP

架构示例

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ AI Agent │ ──> │ Higress │ ──> │ MCP Server │
│ │ │ Gateway │ │ │
└─────────────┘ └─────────────┘ └─────────────┘

│ 服务发现

┌─────────────┐
│ Nacos │
│ MCP Registry
└─────────────┘

通过网关可以实现:

  • 统一的认证授权
  • 流量控制和限流
  • 请求日志和审计
  • 服务路由和负载均衡

最佳实践

1. 服务命名规范

建议使用清晰、有意义的命名规范,便于管理和识别:

{业务域}-{功能描述}-{版本}

示例:
- weather-query-v1 # 天气查询服务 v1
- finance-stock-v1 # 金融股票服务 v1
- travel-booking-v1 # 旅行预订服务 v1
- order-process-async-v1 # 订单处理服务(异步)v1

命名约定:

  • 使用小写字母和连字符
  • 业务域在前,功能描述在后
  • 版本号使用 v1、v2 等格式
  • 异步服务添加 async 后缀

2. 版本管理策略

版本兼容性原则

同一版本号下的工具定义必须保持兼容:

  • 可以添加新工具
  • 可以修改工具描述和参数描述
  • 不能修改工具名称
  • 不能修改参数名称和类型
  • 不能删除工具或参数

版本升级流程

1. 开发新版本服务(如 weather-query-v2)
2. 在 Nacos 中注册新版本
3. 更新 AI Agent 配置,逐步切换到新版本
4. 观察运行稳定后,下线旧版本

版本共存示例

# AI Agent 可以同时使用多个版本
spring:
ai:
alibaba:
mcp:
clients:
- name: weather-query-v1 # 稳定版本
- name: weather-query-v2 # 新版本,灰度测试

3. 工具描述编写规范

清晰、详细的描述有助于 AI 正确理解和使用工具:

@Tool(description = """
根据城市名称获取天气预报信息。

支持的城市:
- 一线城市:北京、上海、广州、深圳
- 省会城市:杭州、南京、成都、武汉等

返回信息包括:
- 天气状况(晴、雨、阴等)
- 温度范围(最高/最低温度)
- 湿度百分比
- 风力等级和风向
- 空气质量指数(AQI)
""")
public String getWeatherForecast(
@ToolParam(description = "城市名称,必须是支持的中文城市名,如:北京、上海") String cityName,
@ToolParam(description = "预报天数,范围 1-7,默认 3 天", required = false) Integer days) {
// 实现
}

描述编写建议:

  • 第一句话概括工具功能
  • 列出重要的使用限制
  • 说明返回内容的格式和含义
  • 参数描述要包含格式要求和默认值

4. 错误处理与用户友好提示

合理的错误处理可以提升用户体验,帮助 AI 理解问题并做出合适的响应:

@Tool(description = "查询银行账户余额")
public String getAccountBalance(
@ToolParam(description = "银行账户号码") String accountNumber) {
try {
// 验证账户格式
if (!accountNumber.matches("\\d{16,19}")) {
return "错误:账户号码格式不正确,应为 16-19 位数字";
}

// 查询余额
BigDecimal balance = accountService.getBalance(accountNumber);

if (balance == null) {
return "错误:账户不存在或无权限访问";
}

return String.format("账户 %s 余额:¥%.2f",
maskAccountNumber(accountNumber), balance);

} catch (ServiceUnavailableException e) {
return "系统繁忙,请稍后重试";
} catch (Exception e) {
log.error("查询余额失败", e);
return "查询失败,请联系客服";
}
}

5. 敏感信息保护

对敏感数据进行脱敏处理,防止信息泄露:

private String maskAccountNumber(String accountNumber) {
if (accountNumber == null || accountNumber.length() < 8) {
return accountNumber;
}
// 保留前4位和后4位
return accountNumber.substring(0, 4) + "****" +
accountNumber.substring(accountNumber.length() - 4);
}

private String maskPhone(String phone) {
if (phone == null || phone.length() < 7) {
return phone;
}
// 保留前3位和后4位
return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
}

6. 多环境管理

使用命名空间隔离不同环境,确保开发、测试、生产环境互不影响:

命名空间配置:
- mcp-dev :开发环境
- mcp-test :测试环境
- mcp-staging :预发布环境
- mcp-prod :生产环境

不同环境的配置示例:

# application-dev.yml
spring:
ai:
alibaba:
mcp:
nacos:
namespace: mcp-dev

# application-prod.yml
spring:
ai:
alibaba:
mcp:
nacos:
namespace: mcp-prod

7. 监控与日志

添加监控指标

@Service
public class MonitoredWeatherService {

private final MeterRegistry meterRegistry;

@Tool(description = "获取天气信息")
public String getWeather(String cityName) {
Timer.Sample sample = Timer.start(meterRegistry);

try {
String result = doGetWeather(cityName);
meterRegistry.counter("mcp.tool.success", "tool", "getWeather").increment();
return result;
} catch (Exception e) {
meterRegistry.counter("mcp.tool.error", "tool", "getWeather").increment();
throw e;
} finally {
sample.stop(meterRegistry.timer("mcp.tool.duration", "tool", "getWeather"));
}
}
}

日志记录最佳实践

@Slf4j
@Service
public class LoggingWeatherService {

@Tool(description = "获取天气信息")
public String getWeather(String cityName) {
log.info("MCP工具调用开始: getWeather, 参数: cityName={}", cityName);

try {
String result = weatherApi.query(cityName);
log.info("MCP工具调用成功: getWeather, 结果长度: {}", result.length());
return result;
} catch (Exception e) {
log.error("MCP工具调用失败: getWeather, 错误: {}", e.getMessage(), e);
throw e;
}
}
}

8. 性能优化建议

工具实现优化

// 推荐:异步处理耗时操作
@Tool(description = "批量查询天气")
public CompletableFuture<String> batchGetWeather(List<String> cities) {
return CompletableFuture.supplyAsync(() -> {
return cities.parallelStream()
.map(this::getWeather)
.collect(Collectors.joining("\n"));
});
}

// 推荐:添加缓存
@Cacheable(value = "weather", key = "#cityName", unless = "#result == null")
@Tool(description = "获取天气信息")
public String getWeather(String cityName) {
return weatherApi.query(cityName);
}

// 推荐:设置合理的超时
@Tool(description = "获取天气信息")
public String getWeather(String cityName) {
return weatherApi.queryWithTimeout(cityName, Duration.ofSeconds(5));
}

常见问题

1. 服务注册失败

错误信息:服务启动后未在 Nacos 控制台看到 MCP 服务

排查步骤

  1. 检查 Nacos 服务是否正常运行
  2. 确认网络连通性,能够访问 Nacos 服务地址
  3. 验证认证信息(用户名、密码)是否正确
  4. 检查命名空间和分组配置是否正确

解决方案

# 检查配置是否正确
alibaba:
cloud:
nacos:
server-addr: 127.0.0.1:8848 # 确保地址正确
namespace: public # 确保命名空间存在
group: MCP_GROUP # 确保分组正确
username: nacos # 确保用户名正确
password: nacos # 确保密码正确

2. MCP Server 数据不兼容

错误信息mcp server info is not compatiblecheck mcp server compatible false

原因:Nacos 中已存在相同版本的 MCP Server 数据,但新注册的数据与原有数据不兼容。

兼容性检查项

  • MCP Server 协议(stdio、sse、streamable-http)是否一致
  • 服务引用是否一致
  • Tools 数据是否一致:
    • Tools 名称
    • Tools 数量
    • 参数类型
    • 参数是否可选
    • 注意:Tools 描述和参数描述不要求一致

解决方案

  1. 如果是有意更新工具定义,先在 Nacos 控制台删除旧版本的 MCP 服务
  2. 如果是启动了多个相同服务实例,确保它们的工具定义完全一致

3. AI Agent 无法发现服务

现象:AI Agent 无法找到已注册的 MCP 服务

排查步骤

  1. 确认服务是否已在 Nacos 控制台正确显示
  2. 检查客户端配置的命名空间和分组是否与服务注册时一致
  3. 确认工具是否已启用(在控制台检查工具状态)

解决方案

# 确保客户端和服务端配置一致
spring:
ai:
alibaba:
mcp:
nacos:
server-addr: 127.0.0.1:8848
namespace: public # 必须与服务注册时一致
group: MCP_GROUP # 必须与服务注册时一致

4. 工具调用超时

现象:调用 MCP 工具时返回超时错误

解决方案

spring:
ai:
mcp:
client:
request-timeout: 30000 # 增加请求超时时间(毫秒)
connect-timeout: 10000 # 增加连接超时时间(毫秒)

同时优化工具实现,减少处理时间:

  • 避免在工具中进行耗时的 I/O 操作
  • 对于必须的耗时操作,考虑使用异步方式
  • 设置合理的超时时间并优雅处理超时

5. 多实例负载均衡问题

现象:部署多个 MCP Server 实例时,请求未正确分发

解决方案

spring:
ai:
alibaba:
mcp:
nacos:
# 启用负载均衡
loadbalancer:
enabled: true
# 负载均衡策略:random(随机)、round-robin(轮询)
strategy: round-robin

6. 工具描述中文乱码

现象:在 Nacos 控制台或 AI 响应中看到工具描述出现乱码

解决方案

  1. 确保源代码文件使用 UTF-8 编码保存
  2. 确保数据库和 Nacos 的字符编码配置正确
  3. 在 Java 代码中明确指定编码:
@Tool(description = "根据城市名称获取天气信息")
public String getWeather(
@ToolParam(description = "城市名称,如:北京、上海") String cityName) {
// 实现
}

下一步

参考资源