MCP 与 Nacos 集成
设计 Pixiu MCP Server 与 Nacos 集成后的配置发现、转换和动态更新流程。
MCP 与 Nacos 集成
概述
本文档描述了 Dubbo-go-pixiu 中 MCP Server 与 Nacos 集成的设计方案,实现动态配置管理和服务发现能力。该方案通过扩展 pkg/remote 模块提供统一的远程通信抽象,并在 pkg/adapter 层创建 mcpserver 适配器来实现配置的动态转换和生命周期管理。
架构背景
现有架构问题
当前dubbo-go-pixiu存在以下架构不一致性:
- 分散的Nacos客户端实现
-
pkg/remote/nacos/client.go: 服务发现客户端(naming_client.INamingClient) -
configcenter/nacos_load.go: 配置管理客户端(config_client.IConfigClient) -
两套独立实现导致代码重复和维护困难
- 静态MCP Server配置
-
当前
pkg/filter/mcp/mcpserver只支持静态配置的工具 -
无法动态响应Nacos配置变化
-
缺乏与远程配置中心的集成能力
- 缺乏统一抽象
-
pkg/remote缺乏配置管理能力 -
configcenter与pkg/remote职责重叠 -
没有统一的远程存储通信抽象
目标架构
建立清晰的三层架构:
-
pkg/remote: 统一远程存储通信抽象层
-
pkg/adapter: 配置转换和生命周期管理层
-
pkg/filter: 协议处理层
动态配置发现流程
本节流程图聚焦于配置发现的逻辑,阐述了网关如何从 Nacos 中一步步发现并聚合一个完整 MCP 服务所需的全部信息。
sequenceDiagram
participant Pixiu as Pixiu 网关
participant Nacos
loop 定期服务发现
Pixiu->>Nacos: 1. 搜索所有 MCP 服务<br>(查询 group='mcp-server-versions' 的配置)
Nacos-->>Pixiu: 返回服务ID列表
end
Note over Pixiu: 为每个新发现的服务启动一个独立的监听流程
Pixiu->>Nacos: 2. 监听服务版本<br>(Listen to `{serviceId}-mcp-versions.json`)
Nacos-->>Pixiu: 推送版本更新 (例如: `latest: v1.0.0`)
Note over Pixiu: 收到新版本后,开始监听该版本的具体规格文件
Pixiu->>Nacos: 3. 监听服务与工具规格<br>(Listen to `...-v1.0.0-mcp-server.json`)<br>(Listen to `...-v1.0.0-mcp-tools.json`)
Nacos-->>Pixiu: 推送服务规格 (Service Spec)
Note over Pixiu: 从服务规格中解析出后端服务名 (`serviceRef`)
Pixiu->>Nacos: 4. 订阅后端服务实例<br>(Subscribe to `serviceRef`)
Nacos-->>Pixiu: 推送实例列表 (e.g., [10.0.0.1, 10.0.0.2])
Nacos-->>Pixiu: 推送工具规格 (Tool Spec)
Note over Pixiu: 当版本、服务规格、工具规格、实例列表<br>这四份信息都就绪后...
Pixiu->>Pixiu: 5. 聚合生成完整的配置快照
Note over Pixiu: 快照已生成,准备在网关内部应用
设计方案
1. 统一远程客户端抽象 (pkg/remote)
本层负责屏蔽底层具体远程存储(如 Nacos、etcd)的实现差异,为上层提供一个稳定、统一的交互接口。
1.1 核心接口定义
我们将定义一个核心的 RemoteClient 接口,它通过组合另外两个接口来获得所需的能力。
-
RemoteClient接口: -
组合能力: 它将内嵌
ServiceDiscovery和ConfigManagement两个接口。 -
生命周期管理:
-
Start() error: 启动客户端,建立与远程服务的连接。 -
Stop() error: 关闭客户端,释放所有资源。 -
ServiceDiscovery接口: 负责服务发现相关的所有操作。 -
方法:
-
SelectInstances(serviceName string) ([]Instance, error): 根据服务名查询所有健康的服务实例列表。Instance是一个我们自己定义的、与具体实现无关的结构体,包含 IP、端口、元数据等信息。 -
Subscribe(serviceName string, callback func([]Instance)) error: 订阅一个服务的实例变化。当实例列表发生变更时,将异步调用传入的callback函数。 -
Unsubscribe(serviceName string) error: 取消对某个服务的订阅。 -
ConfigManagement接口: 负责配置管理相关的所有操作。 -
方法:
-
GetConfig(dataId, group string) (string, error): 根据dataId和group获取一份配置,返回其原始内容(字符串)。 -
ListenConfig(dataId, group string, callback func(string)) error: 监听一份配置的变更。当内容发生变化时,将异步调用传入的callback函数。 -
StopListen(dataId, group string) error: 取消对某份配置的监听。
1.2 Nacos 客户端实现 (UnifiedNacosClient)
这是 RemoteClient 接口在 Nacos 场景下的具体实现。
-
结构:
UnifiedNacosClient是一个结构体 (struct)。 -
核心字段:
-
namingClient: 持有一个 Nacos 官方的naming_client.INamingClient实例。 -
configClient: 持有一个 Nacos 官方的config_client.IConfigClient实例。 -
config: 保存用于初始化这两个客户端的配置信息。 -
职责:
-
实现
RemoteClient接口的所有方法。 -
其方法的内部逻辑非常简单:将调用代理 (delegate) 给内部持有的
namingClient或configClient去完成实际工作,并对返回结果进行必要的格式转换,使其符合我们自己定义的Instance等中立结构。
2. MCP Server 适配器 (pkg/adapter/mcpserver)
本层是连接远程数据源和网关核心业务逻辑的桥梁,负责将从 pkg/remote 获取的原始配置数据,转化为网关内部可以消费的业务对象。
2.1 适配器核心 (MCPServerAdapter)
- 结构:
MCPServerAdapter是一个结构体。
核心字段:
-
remoteClient: 持有一个remote.RemoteClient接口实例,它不知道底层是 Nacos 还是其他实现。 -
dynamicRegistry: 持有一个指向DynamicToolRegistry的指针,用于将最终的工具配置更新到 MCP 过滤器中。 -
listeners: 一个map,用于管理所有ConfigListener实例,方便启停。
职责:
-
初始化: 通过
Apply(config)方法接收配置,并创建出remoteClient实例。 -
启动:
Start()方法会启动一个发现循环,扫描 Nacos 中所有需要处理的 MCP 服务,并为每个服务创建一个ConfigListener。 -
停止:
Stop()方法会优雅地停止所有的ConfigListener,并关闭remoteClient。 -
处理变更: 提供一个内部方法(如
onConfigUpdate),供ConfigListener回调。当收到更新时,它会调用ConfigConverter进行转换,并将转换后的结果应用到DynamicToolRegistry、RouterManager和ClusterManager。
2.2 配置监听器 (ConfigListener)
- 结构:
ConfigListener是一个结构体,每个实例负责一个 MCP 服务的完整监听。
核心字段:
-
serviceId: 标识自己正在监听哪个 MCP 服务。 -
adapter: 一个指回MCPServerAdapter的指针,用于回调。 -
stopChan: 一个 channel,用于接收停止信号。
职责:
- 封装级联监听的复杂业务流程。在其
watch()方法中,它会:
-
首先使用
remoteClient.ListenConfig监听“版本文件”。 -
在版本的回调中,取消旧监听,并创建对新的“服务规格”和“工具规格”文件的监听。
-
在服务规格的回调中,使用
remoteClient.Subscribe订阅后端服务的实例列表。
- 当所有相关配置(版本、规格、实例)都获取到或发生变化时,将这些零散的信息聚合成一个完整的配置快照,然后调用
adapter.onConfigUpdate方法,将快照传递出去。
2.3 配置转换器 (ConfigConverter)
- 结构:
ConfigConverter是一个无状态的工具类结构体,只包含方法。
职责:
负责数据模型的转换。
核心方法:
-
ConvertToInternalModels(snapshot AggregatedConfig) (*InternalModels, error): -
接收
ConfigListener聚合好的配置快照AggregatedConfig。 -
将快照中的各类 JSON/YAML 字符串,解析并转换为网关内部的强类型业务对象,如
[]mcpserver.Tool、model.Router、model.Cluster。 -
将这些转换好的对象打包成
InternalModels结构体返回。
3. 集成点设计
3.1 与 MCP Filter 集成 (DynamicToolRegistry)
目标: 让 MCP 过滤器能动态地感知工具的变化。
实现:
-
我们将改造现有的
ToolRegistry,使其成为DynamicToolRegistry。 -
它将提供一个线程安全的方法:
UpdateTools(adapterId string, tools []mcpserver.Tool)。 -
MCPServerAdapter在转换出新的工具列表后,会调用此方法。DynamicToolRegistry内部会使用读写锁 (sync.RWMutex) 来安全地替换内存中的工具列表。
3.2 路由与集群管理集成
目标: 动态地创建、更新或删除与 MCP 工具相关的路由和后端集群。
实现:
-
MCPServerAdapter在调用ConfigConverter得到model.Router和model.Cluster对象后,将直接调用网关全局的管理器: -
server.GetRouterManager().AddRouter(router) -
server.GetClusterManager().SetCluster(cluster) -
这些管理器已经具备了处理动态更新的能力,我们只需将正确的配置喂给它们即可。