先说结论:用 Nacos 3 做存量服务的注册与发现,用 Higress 在网关层把现有 REST API 快速包装成 MCP Service,是我目前接过的 AI 集成项目里性价比最高的一条路。
2025 年接 AI 应用集成需求的项目越来越多,业务方开口就是“让 Agent 能查订单、能改配置、能调内部系统”。但大多数公司的现状是:系统里攒了上千个 REST API,业务逻辑跑得好好的,不可能为了 AI 重写一遍。强行让 Agent 直连这些接口,安全、鉴权、限流全要重新处理,还得处理接口语义五花八门的问题。Nacos 3 + Higress 这套组合解决的就是这个矛盾——业务接口原封不动,只在流量入口加一层 MCP 网关,把存量 API 变成 Agent 认识的工具。
这篇东西适合正在做 AI 应用集成的后端工程师、API 平台负责人,以及被“所有系统都要接入 MCP”这个需求压得喘不过气的同学。我会把方案选型、协议转换原理、完整实操步骤、生产化要点和踩坑记录都讲一遍,基本是你直接照着搭就能跑通的程度。
1. 先想清楚:为什么要用网关方案做 MCP 转换
1.1 手动写 MCP 桥接层的成本陷阱
最早接到类似需求时,我第一反应是写一个独立的 MCP Server,内部通过 HTTP Client 去调存量 API。思路很简单:起一个 Python 或 TypeScript 的 MCP 服务,里面定义工具函数,每个函数对应一个或几个 REST 接口,收到 MCP 请求后拼 HTTP 请求打到下游。
这方案听起来直接,但做一两个接口没问题,接口一多就失控了。假设你有 20 个系统、每个系统 30 个核心接口,就是 600 个工具函数要写。每个接口的参数映射、错误处理、鉴权逻辑都要单独维护,而且新增接口要改代码、发版、重启服务。我实测过,接第 50 个接口的时候,这个桥接层已经变成谁都不敢动的屎山了。
更麻烦的是,桥接层本身变成了新的单点。Agent 调用链路从“Agent → 存量服务”变成了“Agent → MCP Server → 存量服务”,中间多出来这层只要出问题,整个 AI 功能就挂了,但业务系统本身是好的。
1.2 三条路对比之后选了网关层
我在方案评审时列了三种路线:
| 路线 | 实现方式 | 优点 | 痛点 |
|---|---|---|---|
| 手动桥接层 | 独立服务做协议转换 | 灵活,可做复杂逻辑 | 每接口都要写代码,维护成本高,接口多就失控 |
| 改造存量 API | 在业务代码里加 MCP 端点 | 无额外组件 | 侵入业务系统,所有团队都要配合,根本不现实 |
| 网关层转换 | Higress 在流量入口做协议转换 | 业务零改动,统一鉴权限流,自动发现服务 | 需要引入网关组件,配置文件有学习成本 |
最后选了网关层。原因很朴素:存量 API 是资产不是负债,能让它不动就不动。网关层转换的思路类似于“翻译官”——MCP Client 发来 JSON-RPC 格式的请求,网关翻译成 REST 请求打到后端,再把后端的响应翻译回 MCP 格式。业务团队不用知道 MCP 是什么,他们只管把 OpenAPI 文档维护好就行。
1.3 Nacos 3 在这个架构里的角色
Nacos 在这里负责两件事:服务注册与发现、配置管理。存量服务通过 Nacos Client 注册上去之后,Higress 路由时不需要写死 IP,直接按服务名去 Nacos 查实例列表就行,服务扩缩容、实例挂了自动摘除,这些都不用你操心。
选 Nacos 3 而不是旧版本,主要是两个考虑。第一,Nacos 3 在服务发现性能和大规模实例场景下提升明显,我们有一个命名空间里注册了上千个实例,老版本在频繁变更时推送有延迟,3.x 实测稳定很多。第二,Nacos 3 对异构语言 SDK 的支持更完整,存量系统里有 Java、Go、Node.js 各种语言写的服务,都能轻松接入。
2. 搞懂 MCP 转换到底在转什么
2.1 MCP 协议的本质是 JSON-RPC 调用
很多人一听 MCP 就觉得很神秘,其实剥开看就是一套基于 JSON-RPC 2.0 的通信协议。MCP 定义了三种核心能力:工具(Tools)、资源(Resources)、提示词(Prompts)。实际接入存量 API 时,用到最多的是工具——Agent 通过tools/list拿到有哪些能力可用,通过tools/call发起一次具体调用。
这套标准的好处是生态统一。任何支持 MCP 的客户端——比如各种 AI 开发框架、Agent 运行时——都能用同一套方式发现和调用工具,不需要为每个 AI 平台单独适配。转换层的目标就是让存量 REST API 在这个协议框架里“看起来”天生就是 MCP 工具。
2.2 Higress 的转换逻辑:从 OpenAPI 到 MCP Tools
Higress 做转换的核心机制是读取存量服务的OpenAPI(Swagger)文档,把它映射成 MCP Tools 的定义。这里有三个关键映射关系:
- 接口路径和方法 → 工具的唯一名称。比如
GET /api/v1/orders/{orderId}会被转成类似get_order这样的工具名,Agent 调用这个工具时,实际发送的是tools/call请求,里面携带工具名和参数。 - OpenAPI 参数 → 工具的输入参数。路径参数、查询参数、请求体,全都会转成 JSON Schema 格式的工具入参定义,这样 Agent 才能知道该传什么。
- 响应 schema → 工具的输出结构。MCP 的响应虽然包了一层协议外壳,但核心数据部分基本就是原接口响应的 JSON。
这个设计最大的好处是配置驱动。你要新暴露一个接口,不用写代码,只需要把上游服务的 OpenAPI 文档地址配好,或者手动把接口定义填充进配置。整个转换过程对上游服务完全透明——它们根本不知道自己被“变成”了 MCP Service。
2.3 传输方式的选型:SSE 与 Streamable HTTP
MCP 早期版本主推 SSE(Server-Sent Events)传输,优点是兼容性好,简单可靠;缺点是单向推送,Agent 端处理起来有点绕,而且长连接在网关层容易超时。2025 年之后主流客户端都在往Streamable HTTP迁移,这种传输方式更接近普通的 HTTP 请求-响应模型,Agent 端开发体验更好。
我测试下来的经验是:如果你的客户端是近期版本,优先用 Streamable HTTP,在网关配置里把传输协议选对就行。如果遇到老客户端只支持 SSE,Higress 也能兼容,但需要额外关注连接超时参数——后面排查章节会详细讲。
3. 完整实操:从部署到第一个工具调通
3.1 环境准备与 Nacos 3 部署
我这次操作的模拟环境是某公司内部测试集群,Kubernetes 版本 1.28,准备了三个命名空间:nacos、higress、business。Nacos 3 部署用的是 Helm 方式,存储用的 MySQL 8.0,生产环境建议至少三节点集群模式,测试环境单节点跑通流程就够了。
Nacos 部署完成后,第一件事是创建一个独立的命名空间(Nacos 概念里的命名空间,不是 K8s 命名空间)给存量服务用,ID 设为ai-integration。这一步很多人会忽略,但强烈建议做——命名空间隔离能让 AI 相关的服务注册信息跟其他业务隔开,排查问题的时候一眼就能定位。
存量服务接入 Nacos 的代码各家语言 SDK 不一样,但核心动作是一样的:引入 Nacos Client 依赖,配置服务地址和命名空间,然后在服务启动时注册。我们的模拟服务是一个订单查询服务,注册名定为order-service,注册完后在 Nacos 控制台的“服务列表”里能看到实例状态为“上线”。
3.2 Higress 网关部署
Higress 本身也是一套 Helm 包,装好之后它会以网关 Pod 的形式跑在集群里,同时提供一个 CRD(自定义资源)叫McpBridge,专门用来定义 MCP 转换规则。
部署完后先做一个健康检查:确认 Higress 的 Control Plane 和 Data Plane 都正常运行,然后用kubectl get mcpbridge看看 CRD 是否可用。如果这里正常,就可以开始配置转换规则了。
3.3 核心配置:McpBridge 之一次就把存量 API 暴露成 MCP Service
这是整个项目最关键的一步。McpBridge 的配置逻辑是:定义一个 MCP 服务端入口,告诉它哪些上游服务要暴露、它们的 OpenAPI 文档在哪、通过哪个注册中心找实例。核心字段大致长这样(具体字段名以你安装版本为准):
apiVersion: extensions.higress.io/v1alpha1 kind: McpBridge metadata: name: ai-mcp-bridge namespace: higress spec: type: higress-mcp-http meta: serverName: ai-integration-server schemas: - name: order-service type: openapi url: http://order-service.business:8080/v3/api-docs upstreamParameters: - name: order-upstream type: nacos serviceName: order-service namespaceId: ai-integration groupName: DEFAULT_GROUP这段配置干了三件事:
schemas里声明了要暴露哪个服务的 OpenAPI 文档,Higress 从这里解析出所有可用的工具定义。upstreamParameters告诉 Higress 这个服务的实例去哪找——通过 Nacos,命名空间ai-integration,服务名order-service。type: higress-mcp-http指定了 MCP 传输走 HTTP 协议。
配置 apply 之后,Higress 会启动一个 MCP Server 端点。你可以通过 Higress 暴露的地址访问到类似这样的路径:http://<gateway-address>/mcp/ai-integration-server,这就是 Agent 要连接的 MCP 端点。
3.4 用 MCP Inspector 验证工具发现和调用
配置完成后必须做的验证步骤,我强烈建议先用官方 MCP Inspector 工具测一遍,不要直接上 Agent 调试。把 MCP 端点地址填进 Inspector,连接成功后先执行tools/list,看能不能列出从 OpenAPI 解析出来的工具列表。
我在模拟项目里预期看到类似order_service_get_order这样的工具名,实测确实解析出来了。然后执行tools/call,工具名填刚才查到的,参数传一个真实的订单 ID,如果返回了和直接调 REST 接口一样的数据结构,说明转换链路完全通了。
这一步能过,Agent 接入基本就稳了。之后你在任意支持 MCP 的客户端里配置同一个端点,Agent 就能自动发现这些工具并调用。
3.5 一个调用全链路的字节级追踪
我习惯在验证时把整条链路的日志串起来看,这样出问题能快速定位。一次完整的工具调用大概是:
- Agent 向 Higress 的 MCP 端点发送
tools/call请求。 - Higress 解析请求,识别出要调用的是
order_service_get_order。 - Higress 根据配置,去 Nacos 查询
order-service的可用实例。 - 选出一个实例,把 MCP 请求翻译成
GET /api/v1/orders/{orderId},带上原始参数打过去。 - 服务返回订单 JSON,Higress 包装成 MCP 响应格式返回给 Agent。
这个链路里任何一环断了,日志里都能看出来。我排查问题时的习惯是先看 Nacos 服务列表有没有实例,再看 Higress 网关日志有没有收到请求,最后看上游服务日志有没有被调用——三步定位法基本覆盖了绝大多数异常场景。
4. 生产化改造:不能只“能跑”就上线
4.1 鉴权与安全:MCP 端点不能裸奔
MCP 端点一旦暴露出去,等于把内部接口的能力开放给了所有能连到这个地址的客户端,安全生产必须第一件事处理鉴权。
我常用的方案是在网关层加一个 API Token 校验,Agent 端连接 MCP 时在请求头里带上 token,网关校验通过才放行,不通过直接 401。如果公司有统一认证体系,也可以接 OIDC / JWT,Higress 都支持。
这里有一个容易踩的坑:上游业务系统本身可能也有鉴权,MCP 转换层不能把上游的鉴权丢掉。正确做法是网关用 AI 调用的独立凭证去访问上游,比如网关持有上游系统的服务账号,每次转发时带上这个凭证。简单说就是“Agent → 网关(校验 AI Token)→ 上游(携带服务账号凭证)”,两层鉴权各管各的,互不干扰。
4.2 超时、重试与限流:AI 生态下的稳定性治理
AI 调用和普通用户调用有一个很大的不同——Agent 可能会并发发起多个工具调用,而且单个 Agent 任务失败后会自动重试,这意味着洪峰来得比人肉流量更猛、更没规律。
超时参数必须单独调。内部接口一般要求 3 秒内响应,但放到 AI 场景,Agent 有时候会在思考过程中反复调用同一个工具,单个工具慢一点会导致整个任务链断裂。我一般把 MCP 网关到上游的超时设为 10 秒,超过就报错返回,让 Agent 知道这次调用失败了,而不是无限等下去。
限流也要按调用方维度做隔离。比如给某个 Agent 客户端单独设一个每秒调用上限,防止一个失控的 Agent 把整个上游系统打爆。这个在 Higress 配置限流策略就行,按请求头里的客户端标识来做维度。
4.3 可观测性:没有日志就是盲人摸象
MCP 调用链路比普通 API 多了一层协议翻译,如果日志里没有记录原始 MCP 请求和翻译后的 HTTP 请求,出了问题基本靠猜。
我建议至少记录三个维度:连接维度(哪个 Agent 什么时候建立了 MCP 连接)、工具调用维度(调了哪个工具、参数是什么、返回码是多少)、上游维度(实际打到哪个实例、上游响应时间)。Higress 网关日志天然有这些信息,把日志接入公司统一日志平台就行。
另外一个实用小技巧:给每个转发的上游请求加一个全局唯一的追踪 ID,这样 Agent 端报错时,能顺着这个 ID 在网关日志和上游日志里串出完整链路。
4.4 灰度与发布:别一下子暴露全部 600 个 API
首次上线时千万不要把系统的所有接口一次性全暴露给 MCP。正确做法是挑 3 到 5 个只读、低风险、语义清晰的接口先跑通流程,比如查询类接口。等 Agent 和网关配合稳定了,再逐步扩大范围。
我操作时是先暴露了订单查询和配置读取两个接口,跑了一周,确认稳定性后再加了更新类的接口。这个习惯帮我避免过一次事故——有个接口的 OpenAPI 文档写错了参数类型,如果一次性暴露几百个接口,根本发现不了哪个解析错误。
5. 实操中踩过的坑与排查速查表
5.1 客户端报“Unknown tool”但工具列表里明明有
这个问题的根源一般是 Tool 名称不一致。OpenAPI 里同一个操作,如果不同服务版本把operationId改了,生成的工具名就变了,Agent 端缓存了旧名字就会报 Unknown tool。
排查方法:重新执行tools/list看真实返回的工具名,跟 Agent 代码里调用的名字对比。这类问题不是网关故障,是命名规范问题。解决思路是统一 OpenAPI 文档规范,在 CI 里加一道检查,operationId 不允许随意改动。
5.2 OpenAPI 文档不规范导致工具解析失败
我遇到过一次上游文档里某个接口没有定义operationId,Higress 解析时自动生成了一个冗长且不可读的工具名,Agent 端每次调用都很别扭。更严重的是一次把responseschema 写错了类型,导致返回数据在 MCP 包装时格式错乱。
这类问题没有银弹,只能在接入时逐个接口验证。我后来总结出一个流程:对每个要暴露的接口,先在 Inspector 里看一眼解析出来的工具定义和参数结构,没问题再接。看起来多花几分钟,实际上省了后面排查的大量时间。
5.3 Nacos 命名空间不一致导致路由找不到服务
存量服务注册在 Nacos 的public命名空间,但 McpBridge 配置里写的是ai-integration,结果网关上游实例怎么查都是 0,排除了半天才发现是命名空间不匹配。
这个坑特别隐蔽,因为配置语法没问题、网关日志也没报错,就是没实例。后来我把所有命名空间 ID 统一收口到一个配置中心管理,部署时自动注入,从根上避免了手写不一致的问题。
5.4 长连接闲置断开与客户端重连风暴
SSE 模式下,如果网关和上游之间长时间没有消息,连接可能被中间的网络设备断开。某个 Agent 客户端一旦重连,会重新执行tools/list,多个 Agent 同时重连就形成了重连风暴。
处理办法有两个层面:网关层把空闲超时适当调大(比如 300 秒),客户端层加指数退避重连策略,不要断线后立刻猛连。升级到 Streamable HTTP 之后,这个问题基本消失,所以我建议新项目直接用 Streamable HTTP。
| 现象 | 可能原因 | 排查路径 |
|---|---|---|
| tools/list 为空 | OpenAPI 文档地址不可达或解析失败 | 先 curl 文档地址,确认返回合法 |
| 工具能列出但调用超时 | 上游实例异常或超时设置太小 | 查 Nacos 实例状态,看上游日志 |
| 调用返回 401 | 网关 Token 校验失败 | 检查 Agent 连接时是否带了正确请求头 |
| 上游没收到请求 | 服务发现失败 | 核对命名空间、服务名、分组 |
| 返回数据乱码 | OpenAPI response schema 错误 | 在 Inspector 里查看原始返回 |
5.5 关于配置管理的一个建议
网关的 McpBridge 配置和 Nacos 连接信息,建议通过 Nacos 3 的配置中心统一管理,而不是散落在各环境的 Helm values 里。因为不同环境(开发、测试、生产)的 Nacos 地址、命名空间可能不一样,集中管理能避免把测试配置带到生产环境这种低级事故。
我在实际操作中的体会是,这套方案最大的价值不是“省了写桥接层的代码”,而是建立了一条可持续扩展的接口暴露通道。今天加一个订单服务,明天加一个用户服务,都只是改一段 McpBridge 配置的功夫,不用写一行业务代码。
最后再分享一个小技巧:在做大规模暴露之前,先花一天时间把公司内部的 OpenAPI 文档规范统一一下,重点检查每个操作有没有唯一的 operationId、参数类型是否完整、响应 schema 是否正确。这一步做扎实了,后面的 MCP 转换基本就是一路绿灯。如果未来接了更多外部 Agent 平台,这套网关机制也能直接复用,不需要为每个平台单独适配——协议转换的活,全在网关这层干完了。