跳到正文

Nacos 与 AI:MCP 与 Agent 注册 ​

一句话:Nacos 3.x 把「服务注册」扩展成「AI 资产注册」:MCP Server/Tool、A2A Agent、Prompt/Skill 都登记到同一个控制面,解决工具和 Agent 的动态发现与治理;它不托管模型、不执行推理,真正调用仍走网关或工具服务。

适用版本:Nacos 3.2.4。MCP Registry 自 3.0(2025-04)引入,A2A Agent Registry 自 3.1,Skill/Prompt Registry 在 3.2 补齐;3.3 起 MCP 发布流程改为 draft/review/publish,接口可能变化。以下 API 形态以 3.2.x 为例,落地前回查对应版本文档。

为什么需要 ​

Agent 调工具时,最常见的第一步不是「模型不够聪明」,而是「工具地址写死」:

  • 工具服务扩缩容、换域名,Agent 的配置要跟着改。
  • 同一个工具多个版本,Agent 不知道该调哪个。
  • 多租户环境下,A 租户不该看到 B 租户的工具。
  • 工具描述和参数 schema 散落在各项目,模型每次都要全量加载,token 浪费且容易选错。

传统注册中心只存「服务名 + ip:port + metadata」,不知道「这个服务暴露了哪些工具、参数是什么、属于哪个版本」。MCP Registry 补的就是这层元数据;A2A Agent Registry 补的是「Agent 之间怎么按名称、描述、能力互相发现」。

核心机制 ​

控制面与数据面 ​

图里 Nacos 只负责「登记与发现」,不转发工具调用;真正的 MCP 请求走 Higress/MCP Router 或直连工具服务。所以 Nacos 挂了,已经拿到地址的 Agent 仍可能调用成功,但发现不了新工具、拿不到新版本;这和注册中心「只发名单」是同一个道理。

注册对象 ​

对象关键字段解决什么版本说明
MCP Server名称、协议(sse/streamable)、版本、endpoint 引用工具服务在哪、用什么协议3.0+
Tool名称、描述、inputSchema、启用开关模型怎么选、怎么传参3.0+
Prompt模板内容、版本、可见性Prompt 集中管理与热更新3.2
A2A AgentAgentCard:名称、描述、能力、端点Agent 之间按能力发现3.1+
Skill技能描述与绑定把可复用能力登记为资产3.2

MCP Server 的 endpoint 通常不是直接写 ip:port,而是引用一个已注册的服务(REF):服务发现负责地址变化,MCP 元数据负责工具语义。这样扩缩容时只动服务注册,不用改工具定义。

命名空间、可见性与鉴权 ​

  • Namespace 隔离环境与租户;MCP Server、Agent、Prompt 都挂在 Namespace 下。
  • 可见性:新发布的 MCP Server 默认 PUBLIC,可通过 Admin API 改为 PRIVATE;跨租户共享要显式设置。
  • 鉴权:3.x 的 Admin/Console API 默认开启鉴权,客户端 OpenAPI 的开关独立;不要把 Registry 暴露到公网。
  • 私有地址:3.2.4 对从私有/本地地址导入 MCP 增加白名单(nacos.console.ai.mcp.import.allowed-private-addresses),防止 SSRF 式导入。

版本、灰度与生命周期 ​

MCP Server 和 Tool 都有版本;更新工具描述或 schema 后,客户端下次发现就能拿到新版本,不需要重启 Agent。3.2 支持灰度/版本管理;3.3 改成 draft → review → publish,旧的一步发布接口仍兼容一段时间,但不要把它当长期契约。

这也带来一个新问题:工具 schema 是运行时契约。改一个必填参数,可能让所有已缓存的 Agent 调用失败;发布前要像改 API 一样做兼容性评估,而不是像改文案一样随手发。

AI 带来的新负载 ​

Registry 的读写比例和传统注册中心不同:服务元数据是「写少读多」,工具元数据则是「写少、读更分散」——每个 Agent 启动/刷新都要拉一批工具描述,描述本身又比 ip:port 大得多。它通常不是高 QPS 系统,但单次响应体积大、发现延迟直接影响 Agent 首轮响应;不要把每次工具调用的结果、会话状态写进 Registry,那里只放「有哪些工具、怎么调」的静态契约。

什么时候真的需要它 ​

只有一两个固定工具、地址不变、单租户,直接写在 Agent 配置里更简单;需要动态扩缩容、多版本并存、多租户隔离、工具数量持续增长时,Registry 才开始产生价值。判断标准不是「用了 Nacos 3.x」,而是「工具地址和 schema 是否会独立于 Agent 变化」。

实战场景 ​

一、工具加了个必填参数,所有 Agent 调用失败 ​

现象:某“订单查询”MCP 工具升级,在 inputSchema 里新增必填字段 tenantId。工具发布后 5 分钟内,接入该工具的三个 Agent 全部报参数校验失败;已运行的 Agent 没有重启,模型侧看不到新字段,仍按旧 schema 传参。

排查:工具 schema 是运行时契约,改动影响面等同于改 API。

定位层取证据判定
schema 变更内容该 Tool 新旧版本的 inputSchema diff新增必填 / 改类型 / 改语义均为破坏性变更
客户端拿到的版本Agent 侧发现结果里的版本号与工具当前版本版本落后 → 缓存未刷新;一致 → 契约本身不兼容
影响范围按工具名统计调用失败率与调用方清单全量失败说明没有灰度隔离
发布方式是直接覆盖同一版本,还是发新版本覆盖式发布让回滚无处可回
模型侧可见性工具描述与参数说明是否让模型知道必填描述没写清,模型不会主动补

对症做法:

json
// 兼容做法:新增字段用可选 + 默认值,语义在描述里写清,旧调用方不受影响
{
  "name": "order.query",
  "inputSchema": {
    "type": "object",
    "properties": {
      "orderId":  { "type": "string", "description": "订单号,必填" },
      "tenantId": { "type": "string", "description": "租户 ID;不传时按调用方凭证推断" }
    },
    "required": ["orderId"]
  }
}
text
1. 破坏性变更必须发新版本,旧版本保留一段时间,让 Agent 渐进迁移;
   覆盖同一版本号会让“回滚”变成一件做不到的事。
2. 走灰度:3.2 支持版本与灰度管理;3.3 的 draft → review → publish 流程落地前,
   不要把一步发布当长期契约。
3. 契约测试进流水线:用旧 schema 的样本参数调用新工具,断言仍然成功。
4. 描述当 API 文档审:输入输出、副作用、幂等性写清楚,
   不要把密钥、内网地址写进描述——描述会进入模型上下文。

复盘要点:判据是“同一工具的调用失败率在发布瞬间不得上升”,并按工具维度而非整体维度看。工具契约的变更评审要纳入接口变更流程:谁在用、能不能双跑、多久下线旧版本;这三个问题答不出来就不该发。

二、默认 PUBLIC 的工具被别的租户发现 ​

现象:多租户 Agent 平台上线两周后,安全 review 发现两个问题:A 租户的 Agent 能列出 B 租户的内部工具(含描述里的业务流程);Registry 的客户端 OpenAPI 可从办公网直接访问。另外有一个工具在导入时填了 127.0.0.1 的 endpoint,被用来探测内网其他服务。

排查:Registry 存的是工具元数据,被篡改或越权可见的后果不只是“看到不该看的”。

定位层取证据判定
默认可见性新发布 MCP Server 的 visibility默认 PUBLIC,生产未逐项收紧
租户隔离工具所在 Namespace 与调用方 Namespace同 Namespace 承载多租户即为越权入口
API 暴露面客户端 OpenAPI 与 Admin/Console API 的鉴权开关、访问来源3.x Admin/Console 默认鉴权,客户端 OpenAPI 开关独立
私有地址导入nacos.console.ai.mcp.import.allowed-private-addresses 配置未设白名单时,导入功能可指向内网地址
endpoint 形态是直接写 ip:port 还是 REF 引用已注册服务写死地址既难维护又绕过服务发现的治理
版本风险服务端版本3.0.0–3.2.3 存在鉴权作用域问题,要锁 3.2.4 及以上补丁

对症做法:

properties
# 1. 私有地址导入必须白名单,只允许登记的入口网段
nacos.console.ai.mcp.import.allowed-private-addresses=10.30.0.0/16
text
2. 默认私有:新 MCP Server 发布后置为 PRIVATE,逐一授权共享;
   跨租户共享用显式可见性设置,不靠约定。
3. Namespace 按租户 + 环境切分:测试工具与生产工具不同 Namespace。
4. endpoint 用 REF 引用服务发现里已注册的服务,
   扩缩容只改服务注册,不改工具定义,也不暴露裸地址。
5. Registry 不暴露公网:只在内网/办公网经 VPN 到达,数据面调用统一走 Higress/MCP Router,
   鉴权、限流、审计都在网关做。

复盘要点:可见性属于“默认值不安全”的配置,判据只能是扫描:定期导出全部 MCP Server 的 visibility 与 Namespace 分布,出现生产 Namespace 下的 PUBLIC 即告警。同时把版本锁进部署模板(3.2.4+),并把“Registry 只做发现、数据面走网关”写进架构约束——Nacos 不代理工具流量,指望它做工具级鉴权会落空。

三、把调用结果写进 Registry:Agent 首轮响应被拖慢 ​

现象:某团队在工具调用链路里把每次调用的返回样例、会话状态和统计计数写回 Registry 的 Tool 元数据。两周后出现两个症状:Agent 启动时拉取工具列表的响应体从 40KB 涨到 3.2MB,首轮响应延迟增加 8 秒(经验值,需校准);模型侧报告“工具太多不知道选哪个”,任务成功率下降。

排查:Registry 的负载形态与传统注册中心不同,读分散、单响应体积大。

定位层取证据判定
写入内容与频率Registry 的变更记录来源(是否为运行时进程)运行时回写 = 把 Registry 当业务库
单次发现响应体积Agent 拉取工具列表的响应字节数分布体积随调用量增长即说明混入了动态数据
发现延迟从 Agent 启动到拿到工具列表的时间直接影响首轮响应
工具数量每个 Agent 可见的工具数与描述长度数量膨胀会稀释模型注意力
是否影响服务发现链路Registry 与 Naming 同进程时的资源占用大量元数据读写会争抢同一份 CPU/连接

对症做法:

text
1. 边界收紧:Registry 只放“有哪些工具、怎么调”的静态契约。
   调用结果、会话状态、用量统计进时序库或对象存储,不进 Registry。
2. 动态统计单独走指标:调用次数、失败率、耗时分位,
   用 Prometheus 暴露,不写回 Tool 元数据。
3. 工具集按 Agent 角色分组(Namespace + 可见性),
   每个 Agent 只看到自己需要的工具,控制单次发现的体积与模型选择负担。
4. 描述做“可路由”而非“可枚举”:名称、用途、输入输出、副作用四段,
   删掉重复的背景介绍;长描述进文档链接,不进上下文。
java
// 5. 是否需要 Registry 的判据:工具地址与 schema 是否会独立于 Agent 变化
//    只有一两个固定工具、地址不变、单租户 → 直接写在 Agent 配置里更简单

复盘要点:判据是量化的:单次发现响应体积、发现延迟、每 Agent 可见工具数三条都要设预算并告警(例如响应 ≤ 200KB、发现延迟 ≤ 1s、可见工具 ≤ 30 个,经验值,需校准)。注册不等于可用——Registry 里有元数据不代表工具服务健康,健康检查仍要在服务发现和网关两层做,见 监控指标与告警。

最小上手 ​

沿用 01 的 3.2.4 单机实例,先注册一个普通服务,再把 MCP Server 的 endpoint 指向它:

bash
# 1. 业务服务先注册(略,见 02)
# 2. 注册 MCP Server + Tool,endpoint 用 REF 引用服务
curl -s -X POST "http://127.0.0.1:8848/nacos/v3/admin/ai/mcp?mcpName=order-tools&namespaceId=public" \
  -H "accessToken: $ACCESS_TOKEN" \
  --data-urlencode 'serverSpecification={"name":"order-tools","protocol":"mcp-streamable","frontProtocol":"mcp-streamable","description":"订单查询工具","versionDetail":{"version":"1.0.0"},"remoteServerConfig":{"exportPath":"/mcp"}}' \
  --data-urlencode 'toolSpecification={"tools":[{"name":"getOrder","description":"按订单号查询订单","inputSchema":{"type":"object","properties":{"orderId":{"type":"string"}},"required":["orderId"]}}]}' \
  --data-urlencode 'endpointSpecification={"type":"REF","data":{"serviceName":"order-service","groupName":"DEFAULT_GROUP","namespaceId":"public"}}'

打开 http://127.0.0.1:8080/ 的 MCP Registry 页面,能看到 order-tools 和 getOrder 即注册成功。Agent 侧通过 MCP Router 或 Spring AI Alibaba 发现并调用;具体发现 API 随版本变化,以官方文档为准。3.2.4 起旧的 MCP 导入 API 默认返回 410,需要兼容开关才能恢复。

生产动作 ​

  • 锁 3.2.4 或更高补丁:3.0.0–3.2.3 有鉴权作用域问题;Registry 存的是工具元数据,被篡改的后果不只是「看到不该看的」。
  • Namespace 按租户/环境切:不要把测试工具和生产线工具放同一个 Namespace;跨租户共享用显式可见性,不靠约定。
  • 默认私有:新 MCP Server 默认 PUBLIC,生产要改成 PRIVATE 并逐一授权。
  • 网关做数据面:Nacos 只做 Registry;MCP 调用走 Higress/MCP Router,鉴权、限流、审计在网关做。
  • 工具描述当 API 文档审:描述会进入模型上下文,写清楚输入输出、副作用、幂等性;不要把密钥、内网地址写进描述。
  • 版本兼容:新增可选参数可以,改必填/改语义要发新版本;保留旧版本一段时间,让 Agent 渐进迁移。
  • 监控:MCP Server 注册数、工具调用失败率、发现延迟、私有地址导入拦截数、Registry 的鉴权失败数。

边界 ​

  • 不是模型平台:Nacos 不托管模型、不做推理、不做上下文管理;它只登记「有哪些工具/Agent」。
  • 不是完整 MCP 网关:协议转换、SSE 会话、限流在 Higress/MCP Router;Nacos 不代理工具流量。
  • MCP/A2A 仍在演进:3.3 的 draft/review/publish、A2A 规范版本都会变;接口和字段以对应版本文档为准。
  • 注册不等于可用:Registry 里有元数据,不代表工具服务健康;健康检查仍要在服务发现和网关上做。
  • 工具多了会稀释模型注意力:Registry 解决「找得到」,不解决「选得对」;工具数量和描述质量仍要治理。

讲清楚 ​

Nacos 3.x 在注册中心之上加了一层 AI 资产注册:MCP Server 和 Tool 登记工具语义,A2A Agent 登记 AgentCard,Prompt/Skill 登记可复用能力;Namespace 做租户隔离,鉴权和可见性控制谁能看到什么。它的角色是控制面,只负责「登记与发现」,真正的 MCP 调用走 Higress/MCP Router 或直连工具服务,所以 Nacos 挂了已拿到地址的 Agent 还能调,但发现不了新工具。它解决的是工具地址写死、版本混乱、多租户可见性和模型每次全量加载工具描述的问题;代价是工具 schema 变成运行时契约,改参数可能让已缓存的 Agent 调用失败,必须像改 API 一样做版本兼容。边界是:它不托管模型、不执行推理、不是完整网关,MCP/A2A 规范仍在演进,落地要锁版本并回查官方文档。

把一门技术讲透到既懂也会用