Higress REST-to-MCP 实战:一份声明式 YAML 把现成 REST API 变成 3 个 MCP 工具,全程零 Go 代码
【免费下载链接】higress🤖 AI Gateway | AI Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/hi/higress
想让大模型把你手里现成的 REST API 直接当 MCP 工具调用,又不想碰一行 Go 代码?在 Higress 里,REST-to-MCP就是干这个的:你只需要写一份 MCP Server 配置(mcp-server.yaml),声明好工具、参数和请求模板,网关负责把工具注册成 MCP、构造 HTTP 请求、渲染响应。本文拿阿里云云市场的"全国停车场数据实时查询"API 当案例——整个目录里没有一行 Go 源码,就靠一份 OpenAPI 规范和一份 YAML,变成了 3 个可被大模型调用的工具。读完这篇,你可以照同样的套路为任意 REST API 编写声明式 MCP 接入配置。
一、不想写代码接 REST API 当 MCP 工具,怎么办?
先把场景摆出来:你有一个已经能跑的 REST 服务(比如查停车场的接口),现在想把它暴露给 AI 应用。传统做法是在 Higress 的 MCP Server 框架里写 Go 代码、编译成 WASM,流程不短。
但 Higress 的 MCP Server 插件其实内置了一个"声明式模式":REST-to-MCP。它把"写一个工具"这件事降级成"填一张表"——工具叫什么、参数放哪、请求长什么样、响应怎么包装,全部用 YAML 写死,REST Server 源码 里的框架代码负责剩下的事。
本文的落地案例就藏在 mcp-servers 目录 下:mcp-parking-lot-query 这个目录,就是"零代码接 REST"的完整样本。
二、一份 api.json 如何"长"出 3 个 MCP 工具
先看结论:这个 MCP Server 目录里只有 4 个文件,没有.go:
api.json——上游 API 的 OpenAPI 3.0.1 规范,含字段示例值mcp-server.yaml——REST-to-MCP 声明式配置,服务的核心载体README_ZH.md/README.md——从 YAML 转换/翻译出来的文档
它们不是各自手写的,而是一条单向流水线的产物。打开 create_api_directories.sh 就能看到这个链路:
openapi-to-mcp工具读取api.json,套用云市场模板yunmarket-tmpl.yaml,生成mcp-server.yaml;yaml_to_markdown.py再把 YAML 里的工具、参数说明转成中文文档,拼进README_ZH.md;translate_readme.py把中文版翻成README.md。
换句话说,规范是唯一的事实来源:改了api.json重新跑一遍脚本,配置和文档同步刷新。这也是你以后给自己项目做 REST API 转 MCP 时可以照抄的工程结构。
三、先拿凭证:AppCode 怎么来、有什么坑
这个停车场 API 走的是云市场AppCode 认证——凭证本身不复杂,但订阅流程里有几个点得记牢:
- 在云市场 API 详情页订阅服务,优先选免费试用;
- 用阿里云账号登录云市场用户控制台,查看已订阅服务的 AppCode,填进 MCP Server 配置;
- 控制台会实时展示预付费服务的可用额度,免费试用额度用完后可以重新订阅恢复额度。
这里有个省心的约束:订阅多个 API 服务拿到的是同一个 AppCode,一个凭证就能访问你订阅过的全部云市场 API 服务。所以配置里只需要留一个全局占位,给每个工具的请求模板引用:
server: name: parking-lot-query config: appCode: ""server.name是插件路由 MCP 请求时认的"服务器身份",加载配置时必须保持为parking-lot-query;appCode留空即可,部署时再填真实值。
四、解剖一个工具:args、requestTemplate、responseTemplate 三段式
3 个工具里挑最典型的"经纬度查询"(parking-lat-lng-query)完整拆开看,另外两个一句话带过:
parking-city-query:按城市名查列表,必传city,外加page/size分页,打POST /parking/query/city;parking-detail-query:只收一个参数id(就是列表返回的pid),打POST /parking/query/detail。
经纬度查询的完整条目长这样:
- name: parking-lat-lng-query description: 根据经纬度查询停车场信息 args: - name: lat description: 纬度 type: string required: true position: body # lng 同为必填;distance(默认1000米)、page(默认1)、size(默认10)为可选 requestTemplate: url: https://jumparking.market.alicloudapi.com/parking/query/nearby method: POST headers: - key: Content-Type value: application/x-www-form-urlencoded - key: Authorization value: APPCODE {{.config.appCode}} - key: X-Ca-Nonce value: '{{uuidv4}}' responseTemplate: prependBody: |+ # API Response Information ...(字段结构说明,末尾接 ## Original Response 原始响应)三段各自管一件事:
args:告诉 MCP 客户端这个工具收哪些参数、哪些必填。position: body表示参数不进 URL,而是按 form 表单序列化进请求体——正好对上上游要求的application/x-www-form-urlencoded;requestTemplate:声明式地拼出完整 HTTP 请求。url+method定骨架,headers里塞认证和防重放头(下一节细讲);responseTemplate.prependBody:在原始响应前面插一段"字段说明书",这个设计的动机第五节专门说。
三个接口的基地址统一来自api.json的servers字段:https://jumparking.market.alicloudapi.com。
五、模板语法与 AppCode 认证:这几个{{ }}是怎么被渲染的
先给一句大白话:YAML 里所有{{ }}都是模板占位符,框架在真正发请求那一刻才把它们替换成实际值。
渲染引擎是 GJSON Template 库(REST Server 源码 头部 import 的github.com/higress-group/gjson_template),它把 Go 模板语法和 GJSON 路径语法揉在一起,还内置了 70 多个 Sprig 函数。本例实际用到三个语法点:
| 写法 | 含义 |
|---|---|
{{.config.appCode}} | 取server.config下的字段,即你填的云市场凭证 |
{{.args.argName}} | 取某个工具参数(本例参数走 body 自动序列化,模板里没显式引用) |
{{uuidv4}} | Sprig 的 UUID 函数,每次调用生成一个随机值 |
对应到认证细节上,三个请求头各司其职:
Authorization: APPCODE {{.config.appCode}}——把全局凭证注入认证头,完成 AppCode 认证;X-Ca-Nonce: {{uuidv4}}——每次请求换一个一次性随机值,做请求去重/防重放;Content-Type: application/x-www-form-urlencoded——声明表单编码,和position: body配套。
再说序列化。args[].position的合法取值有五种:query、path、header、cookie、body(见 REST Server 源码 中RestToolArg.Position的注释)。而参数具体怎么进请求体,框架还留了argsToFormBody、argsToJsonBody、argsToUrlParam这几个开关(定义在RestToolRequestTemplate结构体上),上游是表单接口就选 form,是 JSON 接口就选 JSON,不用自己拼 body。
prependBody为什么要注入"字段说明书"?这是原文档没讲透、但值得补上的设计动机。看停车场返回的字段:leftnum、canbook、bookprice、taskNo……这些命名对 LLM 来说自解释性很差,靠猜很容易把"剩余车位"和"总车位"弄反。而 REST-to-MCP 场景下响应并没有强 outputSchema 约束兜底,所以云市场系模板(yunmarket-tmpl.yaml)的做法是:在原始 JSON 前面先铺一段"每个字段的类型 + 中文语义",再在## Original Response之后接原始数据。相当于把 schema 提示手写进了 prompt,让大模型在没有结构约束时也能稳定解析返回值。
六、响应长什么样:列表、详情,还有一个坐标系的坑
数据形态分两种,对照 api.json 的示例值一目了然:
- 列表接口(周边/城市查询):
data.list是停车场对象数组,data.total给总数; - 详情接口:
data下直接就是单个停车场对象,没有list包裹层。
外层统一包三个字段:code(返回码,200 表示成功)、msg(返回码描述)、taskNo(本次请求号)。
核心字段挑重点列一下:
| 字段 | 类型 | 含义 | 示例值 |
|---|---|---|---|
pid | integer | 停车场 ID,详情查询的id就传它 | 112 |
name | string | 停车场名称 | 交通银行大厦停车场 |
province/city/area/address | string | 省 / 市 / 区 / 详细地址 | 浙江省 / 杭州市 / 上城区 / 庆春路175号 |
lng/lat | number | 经度 / 纬度(百度坐标系) | 120.173981 / 30.263525 |
price/priceunit/pricedesc | integer / string / string | 价格、价格单位、收费描述 | 10 / 元/首小时 / 首小时10元,后每小时6元 |
num/leftnum | integer | 总车位数 / 剩余车位数 | 20 / 1 |
canbook/bookprice | string | 是否可预订 / 预定价格 | "2" / 空 |
piclist | array of string | 停车场图片列表 | - |
⚠️ 这里有个坑:lng/lat是百度坐标系(api.json里字段描述写得很明确)。如果你的下游业务要做导航跳转、地图标注,而业务侧用的是 GCJ-02 或 WGS-84,记得先做坐标转换——这是接这个数据源时的隐含约束,配置里看不出来。
七、上线与复制:从验证到"照方抓药"
部署三步走
- 版本门槛:MCP Server 插件要求 Higress 2.1.0 或更高版本(见 MCP 服务器实现指南);
- 加载配置:REST-to-MCP 是内置能力,可以直接基于all-in-one插件使用——它把多个 MCP Server 打包进同一个 WASM 二进制,降低网关上挂一堆插件的开销。把
mcp-server.yaml的内容贴进 MCP 插件配置即可,server.name保持parking-lot-query,appCode填入控制台拿到的凭证; - 收敛暴露面:不想全量开放工具时,用
allowTools白名单点名,比如只留parking-city-query。注意它只对挂载了mcp-server插件的路由生效,管不到绕过插件的其他路由。
顺带区分一下两类 MCP Server 的上线方式:Go 代码实现的(如 amap-tools)走 Makefile 里的make SERVER_NAME=xxx build(编译为GOOS=wasip1 GOARCH=wasm的 WASM),还有build-image、build-push等目标;而本例这种纯声明式 Server无需任何编译,配置即上线。
一套可复用的接入方法清单
把案例抽象掉,任何带 OpenAPI 规范的 REST 服务都能照下面五步接入 Higress:
- 备齐契约:拿到(或手写)上游的 OpenAPI 规范,
paths、servers、参数 schema 越全越好,它决定了配置和文档的自动生成质量; - 生成或手声明:用
openapi-to-mcp配一套自己的模板批量产出mcp-server.yaml,或者参照云市场模板手写——工具名、args的position、请求模板三要素是必须想清楚的; - 凭证走全局配置:把 AppCode、Token 之类放进
server.config,请求头里用{{.config.xxx}}引用,敏感值不进模板正文; - 防重放加随机头:上游有 nonce 要求时,
{{uuidv4}}一行解决; - 响应加说明书:字段命名不直观的 API,务必用
responseTemplate.prependBody注入"字段结构 + 原始响应",这是 LLM 稳定解析返回值的保险; - 上线控面:all-in-one 插件加载,
allowTools按需收敛暴露的工具集。
配置即服务、规范即文档——这就是 REST-to-MCP 声明式配置的完整闭环。
【免费下载链接】higress🤖 AI Gateway | AI Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/hi/higress
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考