Skip to content

fix: preserve upstream tool schemas verbatim through the stdio→HTTP relay#179

Open
avargaskun wants to merge 1 commit into
sigbit:mainfrom
avargaskun:main
Open

fix: preserve upstream tool schemas verbatim through the stdio→HTTP relay#179
avargaskun wants to merge 1 commit into
sigbit:mainfrom
avargaskun:main

Conversation

@avargaskun

Copy link
Copy Markdown

What

When mcp-auth-proxy relays tools/list from a stdio MCP server over the HTTP /mcp endpoint, the tool inputSchema/outputSchema now reach the downstream client byte-for-byte identical to what the upstream emitted.

Why

The relay round-tripped each schema through mcp-go's typed ToolArgumentsSchema, which:

  • rewrote JSON-Schema definitions$defs without rewriting the matching #/definitions/... $ref targets, producing dangling refs that break JSON-Schema-validating clients (ajv, etc.); and
  • silently dropped any root-level keyword it doesn't model ($schema, title, oneOf, …).

Both classes apply to outputSchema as well, which uses the same type.

How

New helper listToolsPreservingSchema (pkg/backend/toolschema.go) issues tools/list directly over the raw transport, parses the same response bytes twice (typed mcp.ListToolsResult for metadata, a raw struct for the schema bytes), and carries the original schema bytes via RawInputSchema/RawOutputSchema so the proxy server re-emits them unchanged. setupProxy calls it in place of c.ListTools, with a graceful fallback to the typed path on error (tool discovery never regresses to unavailable). No $ref rewriting or schema normalization is done on our side. Prompts/resources handling and the NewStreamableHTTPServer construction are unchanged.

Testing

  • Unit (fake transport): pagination, omitted schema, graceful fallback (asserts the tool is still relayed via the fallback).
  • In-process HTTP integration — reads the proxy's raw wire bytes (not a lossy client.ListTools round-trip, which would re-normalize and mask the fix): draft-07 definitions/$ref preserved, unmodeled root keywords preserved, native $defs back-compat, plain-schema back-compat, outputSchema preserved, and multi-tool index alignment.
  • Real subprocess/stdio integration test over go run ./testserver.
  • gofmt -s, go vet, go build, go test ./... all green locally. (staticcheck runs here in CI — the current release can't analyze go1.26 locally.)

Closes #178

🤖 Generated with Claude Code

…elay

Relay tools/list over the raw transport and carry the upstream inputSchema/
outputSchema bytes through RawInputSchema/RawOutputSchema so the proxy server
re-emits them verbatim. Fixes dangling $ref (draft-07 definitions were rewritten
to $defs without rewriting the matching #/definitions/... refs) and silently
dropped root-level keywords ($schema, title, oneOf, ...). Graceful fallback to
the typed ListTools path on error so tool discovery never regresses.

Covered by fake-transport unit tests, in-process HTTP integration tests that
read the proxy's raw wire bytes, and a real subprocess/stdio integration test.

Tracking: sigbit#178

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@avargaskun avargaskun requested a review from a team as a code owner June 24, 2026 20:15
@avargaskun avargaskun requested review from hrntknr and removed request for a team June 24, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tools/list over /mcp: JSON Schema "definitions" renamed to "$defs" but "$ref" targets left as "#/definitions/..." (unresolvable)

1 participant