AlinsRan opened a new pull request, #13942:
URL: https://github.com/apache/apisix/pull/13942

   ### Description
   
   Adds the `openapi-to-mcp` plugin. It exposes an existing HTTP API to [Model 
Context Protocol](https://modelcontextprotocol.io/) clients without changing 
the API: the plugin fetches the API's OpenAPI document, generates one MCP tool 
per operation, and answers the MCP protocol on the route itself. A `tools/call` 
becomes an ordinary HTTP request to the API, and the response comes back as the 
tool result.
   
   Everything runs inside APISIX, in Lua. Where `mcp-bridge` relays to a stdio 
MCP server process, this plugin *is* the MCP server, generated from a document 
the API already publishes.
   
   #### What it supports
   
   - Transports: Streamable HTTP (stateless) and HTTP+SSE.
   - Protocol versions `2024-10-07` through `2025-11-25`, negotiated in 
`initialize`; an unknown version falls back to the latest.
   - Methods: `initialize`, `ping`, `tools/list`, `tools/call`. Anything else 
is a JSON-RPC `-32601`.
   - Documents: OpenAPI 3.x in JSON or YAML; Swagger 2.0 on a best-effort basis 
(`in: body` / `in: formData` are not turned into inputs). Internal and 
`http(s)` `$ref`s are resolved, with a cap on how many documents and how much 
time one resolution may use, since it runs in the request path.
   - Arguments are validated against the generated input schema before the API 
is called; an unknown tool or invalid arguments give a result with `isError: 
true`.
   - `base_url` and `headers` accept APISIX/NGINX variables, so a caller's 
credentials can be passed through.
   
   #### How it is put together
   
   | Module | Responsibility |
   |---|---|
   | `openapi/loader.lua` | fetch and parse the document, keep path order |
   | `openapi/ref.lua` | resolve `$ref`s, internal and `http(s)` |
   | `openapi/schema.lua`, `openapi/endpoints.lua` | OpenAPI schema to JSON 
Schema; walk paths x methods in a stable order |
   | `tools/generator.lua` | build tool definitions |
   | `tools/handler.lua` | turn a `tools/call` into the HTTP request to the API 
|
   | `cache.lua` | cache the generated tool list per route (1h, 100 entries) |
   | `jsonrpc.lua`, `protocol.lua`, `server.lua` | the MCP protocol layer |
   | `transport/streamable_http.lua`, `transport/sse.lua` | the two transports |
   | `session.lua` | SSE session queues in the `mcp-session` shared dict |
   
   The response is produced in `before_proxy`, not `access`, so every plugin on 
the route (authentication, rate limiting, ...) has run before the MCP request 
is answered, and response filters still see the response. SSE sessions are kept 
in `mcp-session`, the shared dict `mcp-bridge` already declares, so the stream 
and the message requests of a session can land on different workers; 
`ngx_tpl.lua` now declares the dict when either plugin is enabled. Session ids 
are UUIDs, so the two plugins do not collide in it.
   
   The modules live under `apisix/plugins/openapi-to-mcp/`, named after the 
plugin like `ai-proxy/`, and separate from `mcp-bridge`'s `apisix/plugins/mcp/`.
   
   #### Tests
   
   - Unit: document loading, `$ref` resolution, schema conversion, endpoint 
walking, tool generation, JSON-RPC validation, cache and session behaviour.
   - End to end over both transports, including variables, flattened and nested 
parameters, header parameters, Swagger 2.0, and a route with no upstream.
   - `openapi-to-mcp-interop.t` drives both transports with the official 
`@modelcontextprotocol/sdk` client (already a dev dependency for `mcp-bridge`): 
handshake, `tools/list`, `tools/call`, `ping`, several requests in flight at 
once on one connection, and reconnecting after `close()`.
   - `openapi-to-mcp-e2e-sse-multiworker.t` and `openapi-to-mcp-concurrent.t` 
run under `workers(4)`, since a single worker cannot tell a shared dict from a 
worker-local table.
   - `openapi-to-mcp-plugin-stack.t` checks `key-auth`, `limit-count` and 
`response-rewrite` alongside the plugin.
   - `t/cli/test_openapi_to_mcp.sh` checks the shared dict is declared.
   
   Documents are served by a local fixture 
(`t/lib/openapi_to_mcp_fixture.lua`), including the petstore spec 
`oas-validator` already uses, so no external service is needed.
   
   #### Which issue(s) this PR fixes:
   
   N/A
   
   ### Checklist
   
   - [x] I have explained the need for this PR and the problem it solves
   - [x] I have explained the changes or the new features added to this PR
   - [x] I have added tests corresponding to this change
   - [x] I have updated the documentation to reflect this change
   - [x] I have verified that this change is backward compatible (If not, 
please discuss on the [APISIX mailing 
list](https://github.com/apache/apisix/tree/master#community) first)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to