跳到主要内容

openapi-to-mcp

openapi-to-mcp 插件使网关能够充当 OpenAPI 规范与 MCP (Model Context Protocol) 服务器之间的桥梁。通过此插件,你可以通过 MCP 接口暴露现有的基于 OpenAPI 的服务,使其可供 AI 模型和客户端访问。

该插件的工作原理是将你的 OpenAPI 规范转换为 MCP 格式,并通过 MCP 服务器接口提供服务。来自 AI 客户端的请求随后会被代理到你的上游服务,支持自定义请求头以及两种用于流式响应的传输方法:streamable HTTP 和 Server-Sent Events (SSE),从而实现灵活且可靠的实时通信。

部署前置条件

自 API7 EE 3.9.10 起,OpenAPI-to-MCP 服务不再内置于网关镜像,需要与网关部署在同一网络命名空间下。

  • Kubernetes (Helm):在网关 chart values 中设置 openapiToMcp.enabled: true,以 sidecar 方式运行该服务。
  • Docker / 裸机:运行 api7/openapi-to-mcp 独立容器,并让网关与该容器共享网络命名空间(例如 --network=container:<gateway> 或使用 host 网络),使插件可通过 127.0.0.1:<port>(默认 3000)访问。插件访问目标为硬编码的 127.0.0.1,必须让两者处于同一网络命名空间。

若服务不可达,插件将返回 503。mcp-tools-acl 插件同样受此限制。如需修改服务端口,请参考 静态配置

Sidecar 镜像 tag 与网关版本对应关系

插件与 OpenAPI-to-MCP 服务之间的内部交互协议较为稳定,很少变动,因此同一个 sidecar 镜像 tag 通常可与较大范围的网关版本兼容。下表记录了各网关版本对应的推荐 sidecar tag——只有当二者之间的交互协议发生变化(即下表新增一行)时才需要升级 sidecar。

本表主要面向 Docker / 裸机 部署场景,需要使用者自行选择 sidecar 镜像 tag。Helm chart 已经为对应版本的网关固定了经过验证的 sidecar tag,因此 Helm 用户无需参考此表。

网关版本Sidecar 镜像 tag (api7/openapi-to-mcp)
3.9.10 及之后版本1.0.1

使用 Docker Compose 部署

以下 docker-compose.yaml 将 API7 EE 网关与 OpenAPI-to-MCP 服务一起运行。MCP 服务加入网关的网络命名空间,使插件可通过 127.0.0.1:3000 访问。

在 Dashboard 中添加网关实例时,会自动生成一份可直接使用的 docker-compose.yaml。要启用 openapi-to-mcp 插件,将下方所示的 openapi-to-mcp 服务添加到该生成的文件中:

docker-compose.yaml
services:
gateway:
image: api7/api7-ee-3-gateway:3.9.12
container_name: gateway
hostname: gateway
restart: always
ports:
- "9080:9080"
- "9443:9443"
environment:
API7_DP_MANAGER_ENDPOINTS: '["https://<DP_MANAGER_HOST>:7943"]'
API7_GATEWAY_GROUP_SHORT_ID: "<GATEWAY_GROUP_SHORT_ID>"
API7_DP_MANAGER_CERT: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
API7_DP_MANAGER_KEY: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
API7_CONTROL_PLANE_CA: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

openapi-to-mcp:
image: api7/openapi-to-mcp:1.0.1
network_mode: "service:gateway"
restart: always

❶ DP Manager 端点地址——替换为 Dashboard 中提供的地址。

❷ 网关组 short ID——替换为 Dashboard 中显示的值。

❸ TLS 客户端证书、私钥和 CA 证书。从 Dashboard 复制生成的 compose 文件时,这些字段会自动填充。

network_mode: "service:gateway" 让 MCP 容器共享网关的网络栈,使插件可通过 127.0.0.1:3000 访问 MCP 服务。共享 Docker 桥接网络不能满足要求——插件硬编码 127.0.0.1 作为目标地址。

启动服务:

docker compose up -d

网关启动后,你可以通过 DashboardADC 配置带有 openapi-to-mcp 插件的路由。

下图展示了 MCP 客户端、API7 网关和上游 OpenAPI 服务之间的交互。路径和数据仅为演示用的示例值。


静态配置

插件会从网关的 plugin_attr 配置块中读取以下属性(config.yaml,或 helm chart 的 pluginAttrs):

名称类型必选默认值描述
portinteger3000OpenAPI-to-MCP 服务在 127.0.0.1 上监听的端口,插件会将所有 MCP 流量代理到该端口。该值必须与 OpenAPI-to-MCP 容器实际监听的端口一致。

示例:

config.yaml
plugin_attr:
openapi-to-mcp:
port: 4000

修改该值时,必须同步将 OpenAPI-to-MCP 容器的 PORT 环境变量设置为相同端口,否则插件将返回 503。helm chart 在你设置 openapiToMcp.port 时会自动联动容器 PORT,并在模板渲染阶段拒绝两个值不一致的配置。

重载网关以使静态配置变更生效。

示例

以下示例演示了如何在不同场景下配置 openapi-to-mcp 插件。

启用对 Petstore API 的 MCP 访问

以下示例演示了如何通过 MCP 协议暴露 Petstore API,允许 AI 模型和客户端与 Petstore 服务进行交互。

使用 openapi-to-mcp 插件创建一个路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "openapi-to-mcp-route",
"uri": "/mcp",
"methods": ["GET", "POST"],
"plugins": {
"openapi-to-mcp": {
"transport": "streamable_http",
"base_url": "https://petstore3.swagger.io/api/v3",
"headers": {
"Authorization": "special-key"
},
"openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org": 1
}
}
}'

❶ 配置路由允许 GET 和 POST 方法。GET 方法用于工具发现和响应流式传输 (SSE),而 POST 方法用于执行和操作功能 (messages)。

❷ 配置传输方法为 streamable_http(生产环境推荐)。

❸ 配置 Petstore API 地址。

❹ 配置 Petstore API 凭证。

❺ 配置 Petstore OpenAPI 文档 URL。

❻ 为上游节点配置任意值。上游地址不会用于实际的请求转发。

在你的 AI 客户端(如 Cursor)中,使用你的 API7 网关地址更新 MCP 设置,并附加之前创建的路由路径。例如:

mcp.json
{
"mcpServers": {
"api7-petstore-mcp": {
"url": "http://123.123.123.123:9080/mcp"
}
}
}

如果配置成功,你应该能看到可用的工具(通过 MCP 暴露给 AI 客户端的外部函数或服务)。

现在,你可以直接从 AI 客户端的聊天窗口与 API7 企业版 进行交互。例如,试着问:“How many pets are there in the petstore?”

AI client interaction with Petstore

为 MCP 路由配置身份验证

以下示例演示了当路由受到身份验证方法(如 key-auth)保护时,如何通过 MCP 协议暴露 Petstore API。

创建一个消费者 johndoe

curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "johndoe"
}'

johndoe 配置 key-auth 凭证:

curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "cred-john-key-auth",
"plugins": {
"key-auth": {
"key": "john-key"
}
}
}'

创建一个包含 openapi-to-mcpkey-auth 插件的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "openapi-to-mcp-route",
"uri": "/mcp",
"methods": ["GET", "POST"],
"plugins": {
"openapi-to-mcp": {
"transport": "streamable_http",
"base_url": "https://petstore3.swagger.io/api/v3",
"headers": {
"Authorization": "special-key"
},
"openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json"
},
"key-auth": {
"header": "apikey"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org": 1
}
}
}'

当 MCP 服务器需要身份验证时,你可以在 mcp.json 配置中指定请求头。请参阅你的 AI 客户端文档以确认是否支持请求头。

如果支持请求头

例如,在 Cursor 中,你可以使用你的 API7 网关地址更新 MCP 设置,附加之前创建的路由路径,并包含 key-auth 所需的请求头:

mcp.json
{
"mcpServers": {
"api7-petstore-mcp": {
"url": "http://123.123.123.123:9080/mcp",
"headers": {
"apikey": "john-key"
}
}
}
}

配置的请求头将被添加到 GET 和 POST 请求中。

如果配置成功,你应该能看到可用的工具(通过 MCP 暴露给 AI 客户端的外部函数或服务)。然后,你可以直接从 AI 客户端的聊天窗口与 Petstore 进行交互。

如果未在 mcp.json 中配置身份验证头,AI 客户端将无法从 MCP 服务器加载工具。

如果不支持请求头

如果你的 AI 客户端不支持在 mcp.json 中配置请求头,你可以将身份验证凭证包含在 MCP URL 查询参数中,因为 key-auth 支持从 URL 查询中获取凭证。

更新路由上的 key-auth 配置如下:

curl "http://127.0.0.1:9180/apisix/admin/routes/openapi-to-mcp-route" -X PATCH \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"plugins": {
"key-auth": {
"_meta": {
"filter": [
[
"request_method",
"==",
"GET"
]
]
},
"query": "apikey"
}
}
}'

❶ 仅将 key-auth 应用于 GET 请求。这是因为查询参数中配置的 apikey 仅随 GET 请求发送到 SSE 端点,而不会包含在后续的 POST 消息请求中。因此,如果未应用过滤器,消息请求将被 key-auth 插件阻止。

❷ 配置插件从查询参数中获取身份验证密钥。

在你的 AI 客户端中,将凭证包含在 API7 网关地址的查询参数中:

mcp.json
{
"mcpServers": {
"api7-petstore-mcp": {
"url": "http://123.123.123.123:9080/mcp?apikey=john-key"
}
}
}

如果配置成功,你应该能看到可用的工具(通过 MCP 暴露给 AI 客户端的外部函数或服务)。然后,你可以直接从 AI 客户端的聊天窗口与 Petstore 进行交互。

如果未在 MCP 服务器 URL 查询中配置身份验证凭证,AI 客户端将无法从 MCP 服务器加载工具。

向上游透传动态请求头

当上游 API 需要按请求传递的凭证或上下文信息,且不同 MCP 客户端之间存在差异时(例如用户特定的 API 令牌、租户标识符或会话 ID),你可以使用 x-openapi2mcp-header-* 约定将这些信息从 MCP 客户端动态传递到上游。

任何发送到网关且匹配 x-openapi2mcp-header-{name} 模式的 HTTP 请求头,都会被 OpenAPI-to-MCP sidecar 提取,去除前缀后作为 {name} 请求头转发给上游 API。

例如,客户端请求中的 x-openapi2mcp-header-my-token: abc123 请求头会变为上游 API 请求中的 my-token: abc123

工作原理

网关插件和 OpenAPI-to-MCP sidecar 协同工作来转发请求头:

  1. 插件级请求头:在插件 headers 字段中配置的请求头会在网关层解析,并以 x-openapi2mcp-header-{name} 的形式转发到 sidecar。这些请求头在所有客户端间共享,但使用内置变量时,其值可以随请求变化。
  2. 客户端级请求头(动态):MCP 客户端在 mcp.json 中使用 x-openapi2mcp-header-* 前缀设置的请求头,会通过网关传递到 sidecar,然后转发给上游。这些请求头可以因客户端不同而不同。

当插件级请求头和客户端级动态请求头同时存在时,两者会被合并。如果客户端请求头与插件请求头同名,插件请求头优先,客户端的值会被忽略。

传输方式的行为差异

动态请求头的行为取决于插件中配置的传输方式:

  • streamable_http(推荐):每个 MCP 请求都是独立且无状态的。sidecar 在每次请求时都会读取 x-openapi2mcp-header-* 请求头,因此动态请求头是真正的按请求传递。这是动态请求头透传的推荐传输方式。
  • ssex-openapi2mcp-header-* 请求头仅在建立 SSE 连接的初始 GET 请求时读取。同一会话中后续的 POST 请求不会重新读取这些请求头。因此,动态请求头在整个会话期间是固定的,无法在会话中途更改。

如果你的使用场景需要在不同请求之间使用不同的请求头值(例如按用户变化的令牌),请使用 streamable_http 传输方式。

配置客户端请求头

如果你的 MCP 客户端支持自定义请求头(如 Cursor 或 Claude Desktop),在 mcp.jsonheaders 字段中添加 x-openapi2mcp-header-* 条目:

mcp.json
{
"mcpServers": {
"my-api-mcp": {
"url": "http://123.123.123.123:9080/mcp",
"headers": {
"x-openapi2mcp-header-authorization": "Bearer <user-token>",
"x-openapi2mcp-header-x-tenant-id": "tenant-42"
}
}
}
}

当 MCP 客户端发送 tools/call 请求时,sidecar 会提取这些请求头并以如下形式转发给上游 API:

authorization: Bearer <user-token>
x-tenant-id: tenant-42

请求头名称映射

HTTP 基础设施(如 Nginx 和 Fastify)会将请求头名称规范化为小写。因此,在 x-openapi2mcp-header- 前缀之后提取的请求头名称在上游请求中始终为小写。下表总结了映射关系:

客户端请求头上游请求头
x-openapi2mcp-header-authorizationauthorization
x-openapi2mcp-header-x-api-keyx-api-key
x-openapi2mcp-header-my-tokenmy-token
备注

x-openapi2mcp-header-* 请求头会被 sidecar 消费,不会原样转发给上游。只有提取后的请求头名称和值会被发送到上游。

安全注意事项

MCP 客户端发送的任何 x-openapi2mcp-header-* 请求头在去除前缀后都会被转发到上游 API。这意味着客户端可以向上游请求注入任意请求头。为降低风险:

  • 使用网关级认证插件(如 key-authjwt-auth)限制对 MCP 路由的访问,确保只有经过授权的客户端才能发送请求。
  • 如果上游 API 依赖特定请求头进行认证或授权,请在插件级 headers 配置中设置这些请求头,而不是依赖客户端提供的值,因为插件级请求头优先于客户端级请求头。

扁平化工具架构参数

以下示例演示了 flatten_parameters 如何影响生成的 MCP 工具输入架构中查询和路径参数的结构。

完成[上一个示例](#启用对 Petstore API 的 MCP 访问)以设置对 Petstore API 的 MCP 访问。虽然配置没有显式设置 flatten_parameters,但该参数默认为 false

在你的 AI 客户端(如 Cursor)中,检查工具输入架构。你应该看到参数嵌套在 pathParametersqueryParameters 下:

{
"operations": {
...,
"getPetById": {
"method": "GET",
"path": "/pet/{petId}",
"pathParameters": {
"type": "object",
"required": ["petId"],
"properties": {
"petId": {
"type": "integer",
"description": "ID of pet to return"
}
},
"additionalProperties": false
}
},
"findPetsByStatus": {
"method": "GET",
"path": "/pet/findByStatus",
"queryParameters": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["available", "pending", "sold"],
"description": "Status values that need to be considered for filter",
"default": "available"
}
},
"additionalProperties": false
}
}
}
}

更新插件以扁平化查询和路径参数:

curl "http://127.0.0.1:9180/apisix/admin/routes/openapi-to-mcp-route" -X PATCH \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"plugins": {
"openapi-to-mcp": {
"flatten_parameters": true
}
}
}'

在你的 AI 客户端(如 Cursor)中,检查工具输入架构。你应该看到像 status 这样的参数不再嵌套在 pathParametersqueryParameters 下:

{
"operations": {
...,
"getPetById": {
"parameters": {
"type": "object",
"required": ["petId"],
"properties": {
"petId": {
"type": "integer",
"description": "ID of pet to return"
}
},
"additionalProperties": false
}
},
"findPetsByStatus": {
"parameters": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["available", "pending", "sold"],
"description": "Status values that need to be considered for filter",
"default": "available"
}
},
"additionalProperties": false
}
}
}
}

自定义 MCP 工具注解

可用性

MCP 工具注解自 API7 企业版 3.9.7 版本起可用。

以下示例演示了如何为 openapi-to-mcp 插件暴露的 OpenAPI 操作添加 MCP 工具注解。

如果不配置注解,AI 客户端只能获取到生成的工具名称、描述和输入模式,无法可靠地判断某个工具是只读的、具有破坏性的还是幂等的,这使得正确排序和安全使用工具变得更加困难。

内置的 OpenAPI-to-MCP 转换器通过两种方式实现工具注解:

  1. 根据 HTTP 方法推断默认的工具行为。
  2. 读取 OpenAPI vendor 扩展 x-mcp-annotations 中的显式操作级配置。

当两者同时存在时,显式的 x-mcp-annotations 值会覆盖推断的默认值。

完成上一个示例以通过 openapi-to-mcp 插件暴露 OpenAPI 文档,然后为 OpenAPI 操作添加注解:

上一个 Petstore 示例使用的是公共 OpenAPI 文档,你无法直接编辑。要使用 x-mcp-annotations,请托管你自己的 OpenAPI 文档,并更新 openapi-to-mcp 插件配置中的 openapi_url 字段,指向该托管文档。

openapi.yaml
paths:
/users/{id}:
get:
operationId: getUser
summary: Get user information
x-mcp-annotations:
title: Get User
readOnlyHint: true
openWorldHint: false
delete:
operationId: deleteUser
summary: Delete a user
x-mcp-annotations:
title: Delete User
destructiveHint: true

支持的注解字段:

  • title
  • readOnlyHint
  • destructiveHint
  • idempotentHint
  • openWorldHint

如果未配置 x-mcp-annotations,转换器仍会应用默认推断规则:

  • GETHEADOPTIONS 映射为 readOnlyHint: true
  • DELETE 映射为 destructiveHint: trueidempotentHint: true
  • PUT 映射为 idempotentHint: true

更新托管的 OpenAPI 文档后,让 MCP 客户端列出工具。以下代码片段展示了 tools/list 响应中的 result.tools 部分:

{
"tools": [
{
"name": "getUser",
"annotations": {
"title": "Get User",
"readOnlyHint": true,
"openWorldHint": false
}
},
{
"name": "deleteUser",
"annotations": {
"title": "Delete User",
"destructiveHint": true,
"idempotentHint": true
}
}
]
}

注意事项:

  • 仅支持操作级别的 x-mcp-annotations
  • 无效值和不支持的字段会被忽略。
  • summarydescription 仍然控制生成的工具描述。
  • title 仅从 x-mcp-annotations.title 读取。

故障排除

要诊断问题,请检查网关容器或 Pod 中 /usr/local/openapi2mcp/error.log 处的 openapi-to-mcp 错误日志。请注意,此日志与网关的错误日志是分开的。

已知问题

  1. 错误 Cannot use 'in' operator to search for '$ref' in undefined 通常发生在 openapi_url 中使用 OpenAPI v2 文档时。该插件仅支持 openapi_url 中的 OpenAPI v3 文档。

  2. 该插件在处理从 openapi_url 获取的 OpenAPI v3 文档中的 oneOf 架构时存在已知的解析问题。在这种情况下,MCP 客户端将在加载工具时卡住。