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

   Three defects in `openapi-to-mcp`, found by driving the plugin with the 
official MCP SDK client (TypeScript, `@modelcontextprotocol/sdk`) over both 
transports and comparing what the upstream API actually received. Each one 
makes a call that a client is entitled to make fail or silently do the wrong 
thing.
   
   ### 1. `default` is applied after validation, so a required member that has 
one is rejected
   
   An operation may declare a parameter `required: true` together with a 
`default`; the same holds for a required request-body property. The generated 
input schema reproduces both, `core.schema.check()` runs first and reports the 
member as missing, and the call never reaches the API — even though the 
document says what the value should be when the client does not send it.
   
   Defaults are now filled into the arguments before validation, recursively, 
and only into objects the caller actually sent, so an absent required container 
is still an error rather than one this pass invents. An argument the client did 
send is never replaced.
   
   ### 2. SSE: `${...}` in `base_url` and `headers` is resolved again on every 
message
   
   With `transport: sse`, the endpoint the server hands the client is 
`<path>?sessionId=<uuid>`. Every subsequent message is a POST to that endpoint, 
and it carries none of the request state the variables were read from. 
Resolving them there produced empty values:
   
   - `"Authorization": "Bearer ${http_x_api_token}"` reached the API as `Bearer 
`.
   - `base_url: "http://${http_x_backend}"` failed to resolve at all.
   
   The resolved `base_url` and `headers` are now captured when the stream is 
opened, stored with the session record in the `mcp-session` shared dict, and 
used for every message on that session. A session created before this change 
keeps the previous behaviour instead of failing.
   
   ### 3. A document that is not an OpenAPI document produces an empty tool list
   
   Any JSON or YAML parses, so a route pointed at an error page, a JSON index 
or a spec that failed to render came up as a healthy MCP server with zero 
tools, and nothing anywhere said why.
   
   The route's own document must now declare an `openapi` or a `swagger` 
version and a `paths` object. The check is deliberately not in 
`loader.parse()`: a document pulled in by an external `$ref` is normally a 
fragment — components, a single schema — and has neither key, so it is still 
accepted as before. The error surfaces through the `-32603` envelope both 
transports already use.
   
   ### Tests
   
   - `t/plugin/openapi-to-mcp-openapi-loader.t`: `validate()` accepts OpenAPI 
and Swagger documents, rejects a document with no version and one with no 
`paths`, and `parse()` still accepts a `$ref` fragment.
   - `t/plugin/openapi-to-mcp.t`: a required query parameter and a required 
body property, each with a default, may be omitted; an argument that is sent 
wins over the default; a route pointed at a non-OpenAPI document reports an 
error instead of an empty tool list.
   - `t/plugin/openapi-to-mcp-e2e-sse.t` with a new driver: the header value 
resolved when the stream opened is the one the API receives on a later message.
   
   All four new behaviours were confirmed to fail on `master` before the change.
   
   ### Docs
   
   `docs/en/latest/plugins/openapi-to-mcp.md` and the Chinese page document the 
default handling, the point at which SSE resolves variables, and — since it is 
easy to assume otherwise — the exact query-string form arrays take: 
`tags=a&tags=b` for the `form`/exploded default and `tags=a,b` for `explode: 
false`, never `tags[]=a&tags[]=b`.
   


-- 
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