Spring AI Alibaba 企业级 MCP 分布式部署方案:基于 Nacos 的服务注册与发现实践

发布时间:2026/10/3 6:29:19
Spring AI Alibaba 企业级 MCP 分布式部署方案:基于 Nacos 的服务注册与发现实践 1. 为什么单机 MCP Server 撑不住企业场景很多同学第一次接触 Spring AI Alibaba 的 MCP 能力时都是在本机跑一个 MCP Server然后在 Agent 里配一个固定地址就完事了。这种玩法在 Demo 阶段没问题但一旦放到企业内网问题马上就暴露出来MCP Server 只部署了一个实例Agent 的所有工具调用都压在这一个进程上某天运维要做滚动发布Server 重启的几十秒里 Agent 直接报连接失败再往后业务方想给订票工具加一个新参数你得挨个通知所有 Agent 团队改配置重启。这些问题的本质是 MCP 协议本身只解决了「Agent 和工具之间怎么通信」它没有规定「Agent 怎么找到工具」。而企业级部署里服务实例是动态的、地址是会变的、工具元数据是会演进的这就需要一个注册中心来兜底。Spring AI Alibaba 给出的答案是把 Nacos 拉进来MCP Server 启动时把自己的 IP、端口、工具列表注册到 NacosMCP Client 订阅这个服务实例上下线、工具增删都能实时感知调用时再叠加负载均衡。这套方案适合谁如果你正在用 Spring Boot 做企业内部系统想把已有的订单、库存、工单这类业务能力包装成 AI Agent 能调用的工具或者你已经在写 Agent 但被多实例部署和动态更新卡住了那这篇内容就是给你准备的。下面我会从 Nacos 准备开始把 MCP Server 注册、MCP Client 发现、多实例验证、常见报错排查整条链路走一遍配置和代码都可以直接复制。需要说明的是MCP 分布式部署解决的是「服务发现 负载均衡 元数据动态更新」这三件事它不改变 MCP 工具本身的编写方式你原来用Tool注解写的业务方法注册到 Nacos 之后照样能用只是调用方从「直连某个 IP」变成了「通过服务名调用」。2. Nacos 与依赖准备命名空间和 starter 怎么选在动手写代码之前先把注册中心这一层准备好。Nacos 的安装这里不展开假设你已经有一个可访问的 Nacos Server版本上 Spring AI Alibaba 目前同时兼容 Nacos 2 和 Nacos 3我用的是本地127.0.0.1:8848。第一步是给 MCP 服务单独建一个命名空间。为什么要单独建因为企业里 Nacos 往往还注册着大量微服务MCP 的工具实例和普通微服务混在一起排查问题时列表会非常乱而且命名空间隔离后Agent 订阅的范围也更干净。登录 Nacos 控制台在「命名空间」里新建一个名字可以叫nacos-default-mcp创建完成后记下它的命名空间 ID类似9ba5f1aa-b37d-493b-9057-72918a40ef35这样一串 UUID后面 Server 和 Client 的配置都要填这个 ID。接下来是依赖选择这是很多人第一次踩坑的地方。Spring AI Alibaba 把 MCP 的 Nacos 能力拆成了两个 starterstarter 名称作用用在哪个应用spring-ai-alibaba-starter-nacos-mcp-server把 MCP 工具注册到 NacosMCP Server 应用spring-ai-alibaba-starter-nacos-mcp-client从 Nacos 发现并负载均衡调用 MCPAgent / MCP Client 应用注意这两个 starter 不要同时往一个应用里塞除非你确实要做一个既提供工具又消费工具的混合应用那种情况要额外注意 Bean 冲突。版本方面我用的组合是 Spring AI1.0.0-M8配 Spring AI Alibaba1.0.0-M8.1-SNAPSHOT这两个版本号要对齐M 系列版本之间 API 变动比较频繁混用很容易出现类找不到的问题。Server 端的pom.xml关键依赖如下注意properties里把两个版本号抽出来方便统一管理properties spring-ai.version1.0.0-M8/spring-ai.version ai-alibaba.version1.0.0-M8.1-SNAPSHOT/ai-alibaba.version /properties dependencies dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-nacos-mcp-server/artifactId version${ai-alibaba.version}/version /dependency /dependenciesClient 端只需要把 artifactId 换成spring-ai-alibaba-starter-nacos-mcp-client其余结构一致。这里有个细节SNAPSHOT 版本需要你的 Maven 配置里能拉到 Spring 的 snapshot 仓库如果公司内网有私服记得让运维把对应仓库代理配上否则会卡在依赖下载。提示命名空间 ID 一定要复制完整Nacos 控制台上显示的是名称但配置里填的是 ID填错名称会导致注册到一个不存在的命名空间表现为「Server 启动没报错但控制台看不到实例」。3. MCP Server 配置把工具实例注册进 Nacos这一节是整篇的核心配置写对了后面基本就顺了。先看 Server 端的application.yml我把它拆成三段来理解基础服务信息、MCP 元数据、Nacos 注册。server: port: ${SERVER_PORT:19000} spring: application: name: mcp-server-provider main: banner-mode: off ai: mcp: server: name: mcp-server-provider version: 1.0.1 sse-message-endpoint: /mcp/messages_ type: SYNC alibaba: mcp: nacos: enabled: true server-addr: 127.0.0.1:8848 username: nacos password: nacos registry: service-namespace: 9ba5f1aa-b37d-493b-9057-72918a40ef35 logging: level: io: modelcontextprotocol: client: DEBUG spec: DEBUG server: DEBUG逐项说明几个关键点。server.port用了${SERVER_PORT:19000}这种写法是为了后面演示多实例时能通过环境变量改端口本地默认 19000。spring.ai.mcp.server.name和spring.application.name建议保持一致都叫mcp-server-provider这个名称就是 Client 端订阅时用的服务名两边必须对得上。type: SYNC表示这是一个同步类型的 MCP Server对应 Client 端也要用同步的LoadbalancedMcpSyncClient同步异步不能混。sse-message-endpoint是 SSE 传输的消息端点保持默认即可。Nacos 部分enabled: true打开注册开关server-addr指向你的 Nacos 地址service-namespace填前面记下的命名空间 ID。用户名密码按你 Nacos 的实际配置填如果没开鉴权可以留空但生产环境强烈建议开启。工具的定义方式和你平时写 MCP 工具完全一样用Tool和ToolParam注解即可。比如一个查询订单的工具Tool(description 获取指定订单号的订单详情) public Order getOrder(ToolParam(description 订单号) String orderId) { return restTemplate.getForObject(http://order-service/order?id orderId, Order.class); }这里restTemplate如果加了LoadBalanced它自己也能通过 Spring Cloud Alibaba 的服务发现去调后端微服务这样 MCP Server 就变成了一个「代理层」对 Agent 暴露 MCP 工具对后端转发 HTTP 或 Dubbo 请求。这种模式特别适合存量系统改造你不需要动原来的订单服务只要新写一个 MCP Server 应用做适配就行。启动这个 Server如果日志里出现向 Nacos 注册成功的记录并且没有抛异常就说明注册这一步通了。接下来去 Nacos 控制台切到nacos-default-mcp命名空间在「服务列表」里应该能看到mcp-server-provider点进去能看到实例的 IP 和端口。再切到 MCP 相关的元数据视图能看到这个 Server 注册上来的工具列表包括工具名、参数、描述。这一步验证通过Server 端就算完成了。4. MCP Client 配置订阅服务并负载均衡调用Server 注册好了现在写 Agent 端。Client 的application.yml比 Server 多了一块「要订阅哪个服务」的配置这是分布式调用的关键。server: port: 8080 spring: application: name: mcp-client-webflux ai: openai: api-key: ${DASHSCOPE_API_KEY} base-url: https://dashscope.aliyuncs.com/compatible-mode chat: options: model: qwen-max alibaba: mcp: nacos: enabled: true service-namespace: 9ba5f1aa-b37d-493b-9057-72918a40ef35 server-addr: 127.0.0.1:8848 username: nacos password: nacos client: sse: connections: server1: mcp-server-provider mcp: client: enabled: true name: mcp-client-webflux version: 0.0.1 initialized: true request-timeout: 600s nacos-enabled: true type: sync toolcallback: enabled: true root-change-notification: true logging: level: io: modelcontextprotocol: client: DEBUG spec: DEBUG重点看spring.ai.alibaba.mcp.nacos.client.sse.connections这一段server1是你给这个连接起的别名mcp-server-provider是要订阅的 MCP Server 服务名。Client 只会订阅这里列出的服务没列的服务即使注册在同一个命名空间也不会被拉进来这样能避免 Agent 误调用到不相关的工具。root-change-notification: true这个开关建议打开它让 Client 能感知到工具元数据的变化比如 Server 端新增了一个工具、改了参数描述Client 不用重启就能拿到最新的工具定义。request-timeout: 600s是调用超时AI Agent 场景下工具执行可能比较慢设长一点避免误超时。Client 端拿到工具的方式有两种。第一种是直接注入负载均衡的 MCP ClientAutowired private ListLoadbalancedMcpSyncClient mcpClients;第二种也是更常用的是注入ToolCallbackProvider把工具回调直接喂给 ChatClientAutowired private LoadbalancedSyncMcpToolCallbackProvider toolCallbackProvider; ToolCallback[] toolCallbacks toolCallbackProvider.getToolCallbacks();拿到toolCallbacks之后挂到 ChatClient 上Agent 就能在对话中自动决定调用哪个工具。整个链路是这样的Agent 收到用户问题 → 模型判断需要调用工具 → 通过LoadbalancedMcpSyncClient发起调用 → Client 从 Nacos 拿到mcp-server-provider的实例列表 → 按负载均衡策略选一个实例 → 通过 SSE 把请求发过去 → Server 执行Tool方法返回结果。这里有个容易忽略的点LoadbalancedSyncMcpToolCallbackProvider的 Bean 名称是固定的loadbalancedSyncMcpToolCallbacks如果你用Qualifier注入名字别写错。异步版本对应LoadbalancedAsyncMcpToolCallbackProviderBean 名是loadbalancedMcpAsyncToolCallbacks。5. 多实例验证与常见报错排查配置写完了怎么确认分布式真的生效了最直接的办法是起两个 Server 实例。用环境变量改端口SERVER_PORT19000 java -jar mcp-server-provider.jar SERVER_PORT19001 java -jar mcp-server-provider.jar两个实例都起来后去 Nacos 控制台看mcp-server-provider的服务列表应该能看到两个健康实例IP 相同但端口不同。这时候在 Agent 端连续发起几次工具调用观察两个实例的日志正常情况下请求会分散到两个实例上这就是负载均衡在起作用。再验证动态感知手动停掉 19001 这个实例等几秒让 Nacos 把它标记为不健康并摘除然后继续调 Agent你会发现请求全部落到 19000 上Agent 端不需要重启也不会报错。这就是「节点变更动态感知」的价值。实际跑的时候下面这几个报错很常见我按现象和原因列一下报错现象可能原因排查方向401 UnauthorizedNacos 用户名密码错误或命名空间 ID 填错核对username/password确认service-namespace是 ID 不是名称local proxy failed / 连接被拒Server 没起来或端口被占用检查 Server 进程和端口确认 Nacos 里实例是健康状态reading choices 相关异常模型返回格式解析失败通常是工具定义有问题检查Tool描述是否为空参数类型是否被模型支持OAuth / 鉴权失败模型 API Key 无效或过期检查DASHSCOPE_API_KEY环境变量是否注入成功Client 启动后工具列表为空订阅的服务名写错或 Server 没注册成功对比connections里的服务名和 Nacos 里的服务名还有一个隐蔽的坑如果你在同一个应用里既引了 server starter 又引了 client starter可能会出现ToolCallbackProvider注入到错误的 Bean。这种情况建议拆成两个独立应用Server 只负责提供工具Client 只负责消费职责清晰排查也简单。另外request-timeout设得太短也会导致偶发失败尤其是工具内部要调外部 HTTP 接口的时候。我一般设 600s如果业务确实很快可以适当调小但不建议低于 30s。6. 从注册发现到稳定调用把链路跑通之后把上面几步走完你手上就有了一套能自动注册、自动发现、带负载均衡的 MCP 分布式调用链路。回过头看Spring AI Alibaba 这套方案真正解决的问题是把 MCP 从「点对点直连」升级成了「面向注册中心调用」Agent 不再关心工具部署在哪台机器上Server 扩容缩容对 Agent 透明工具元数据更新也能实时同步。如果你后续要把它用到生产有几个方向可以继续打磨。一是把 Nacos 的鉴权和命名空间规划做细不同业务线的 MCP 服务分命名空间隔离二是给 MCP Server 加上健康检查接口让 Nacos 能更准确地判断实例状态三是关注 Nacos 3 的 mcp-registry 和 mcp-router 能力Spring AI Alibaba 后续会基于这些做更灵活的路由策略。调试阶段如果遇到模型侧的问题比如工具调用返回的结果模型理解不了可以先用模型对话能力单独验证一下模型是否正常把模型问题和 MCP 链路问题分开排查效率会高很多。工具调用的 Key 和接入配置可以在 API Keys 页面统一管理接入文档里有各语言 SDK 的完整示例照着改就行。如果是要长期跑编码类或 Agent 类的任务Coding Plan 里的额度模型更适合持续调用不用每次单独申请。最后留一个实操建议第一次搭这套链路时先不要接真实的业务系统用一个返回固定值的假工具把 Server 注册、Client 发现、多实例负载均衡这三步验证通过再替换成真实业务逻辑。这样出问题时你能快速定位是链路问题还是业务代码问题省掉大量来回排查的时间。