Skip to main content
Version: Next

openapi-to-mcp

描述#

openapi-to-mcp 插件无需修改已有的 HTTP API,即可将其提供给 Model Context Protocol(MCP)客户端(例如 LLM Agent)使用。插件会获取 API 的 OpenAPI 文档,为每个操作生成一个 MCP 工具,并由插件自身应答 MCP 协议。客户端调用工具时,插件向 API 发送对应的 HTTP 请求,并将响应作为工具结果返回。

MCP 服务运行在 APISIX 内部,不需要额外的进程或服务。

插件支持:

  • Streamable HTTP 传输(无状态)和 HTTP+SSE 传输。
  • MCP 协议版本 2024-10-072024-11-052025-03-262025-06-182025-11-25,在 initialize 时协商。
  • initializepingtools/listtools/call 方法。
  • JSON 或 YAML 格式的 OpenAPI 3.x 文档,支持解析内部引用和 http(s) 形式的 $ref。Swagger 2.0 文档尽力兼容:in: bodyin: formData 参数不会转换为工具输入。

属性#

名称类型必选项默认值有效值描述
transportstringsse[sse, streamable_http]路由上提供的 MCP 传输方式。
openapi_urlstringOpenAPI 文档的 URL。文档在首次请求时获取,生成的工具缓存一小时。
base_urlstring工具调用的 API 基础地址,每个操作的路径拼接在其后。支持 APISIX 变量NGINX 变量,例如 http://${http_x_backend}
headersobject发往 API 的每个请求都会携带的请求头。值支持变量,例如 "Authorization": "Bearer ${http_x_api_token}"
flatten_parametersbooleanfalsefalse 时,工具输入中的参数分别嵌套在 pathParametersqueryParametersheaderParameters 下;为 true 时,参数直接放在输入对象的顶层。

调用 API 之前,插件会按生成的输入 Schema 校验工具参数。调用不存在的工具或参数不合法时,返回 isErrortrue 的结果。

调用工具时,插件根据操作定义构造请求:

  • 声明在 Path Item 上的参数适用于该路径下的所有操作;操作中同名且位置相同的参数会覆盖它。
  • 查询参数按其 styleexplode 序列化,规则见 OpenAPI Parameter Object。使用默认值(form,展开)时,tags: ["a", "b"] 发送为 tags=a&tags=b。同时支持 spaceDelimitedpipeDelimiteddeepObject
  • 请求体使用操作中声明的媒体类型发送,除非 headers 中已设置 Content-Type

使用 SSE 传输时,会话保存在共享字典 mcp-session 中,因此同一会话的事件流请求和消息请求可以由不同的 worker 进程处理。会话只在单个 APISIX 实例内有效:多个实例部署在负载均衡之后时,同一 SSE 会话的请求必须到达同一实例。Streamable HTTP 传输是无状态的,没有这一限制。

使用示例#

以下示例使用 ID 为 mcp 的路由。调用 Admin API 需要 admin key

admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')

通过 Streamable HTTP 提供 API#

创建一个路由,提供 Swagger Petstore API 的工具:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "mcp",
"uri": "/mcp",
"plugins": {
"openapi-to-mcp": {
"transport": "streamable_http",
"openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json",
"base_url": "https://petstore3.swagger.io/api/v3"
}
}
}'

列出工具:

curl "http://127.0.0.1:9080/mcp" -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

响应是一个携带 JSON-RPC 结果的 SSE 事件:

event: message
data: {"result":{"tools":[{"name":"updatePet","description":"Update an existing pet by Id", ...}]},"jsonrpc":"2.0","id":1}

调用工具:

curl "http://127.0.0.1:9080/mcp" -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "findPetsByStatus",
"arguments": { "queryParameters": { "status": "sold" } }
}
}'

工具结果以 JSON 文本的形式包含 API 返回的状态码、状态文本、响应头和响应体:

event: message
data: {"result":{"content":[{"type":"text","text":"{\n \"status\": 200,\n \"statusText\": \"OK\", ..."}]},"jsonrpc":"2.0","id":2}

MCP 客户端使用其 Streamable HTTP 传输连接 http://127.0.0.1:9080/mcp 即可。

通过 SSE 提供 API#

transport 设置为 sse 或不设置时,客户端通过 GET 请求建立事件流。第一个事件告诉客户端消息应发往哪里:

curl -N "http://127.0.0.1:9080/mcp"
event: endpoint
data: /mcp?sessionId=4c9b0a4e-1bb0-4f4d-9b0b-2f3c3e0f7a51

之后客户端将每条 JSON-RPC 消息 POST 到该地址,收到 202 Accepted,并从事件流中读取应答。

将凭证透传给 API#

从请求头中读取调用方的令牌并透传给 API:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "mcp",
"uri": "/mcp",
"plugins": {
"openapi-to-mcp": {
"transport": "streamable_http",
"openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json",
"base_url": "https://petstore3.swagger.io/api/v3",
"headers": {
"Authorization": "Bearer ${http_x_api_token}"
}
}
}
}'

路由上的其他插件照常生效。例如 key-authlimit-count 会在 MCP 请求被应答之前执行,被它们拒绝的请求不会到达工具。

删除插件#

如需删除 openapi-to-mcp 插件,从路由配置中移除即可,APISIX 会自动重新加载配置:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "mcp",
"uri": "/mcp",
"plugins": {},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'