Skip to content

fix: treat resource metadata JSON parse failure as soft error#810

Open
jh-block wants to merge 1 commit intomodelcontextprotocol:mainfrom
jh-block:fix/resource-metadata-parse-fallback
Open

fix: treat resource metadata JSON parse failure as soft error#810
jh-block wants to merge 1 commit intomodelcontextprotocol:mainfrom
jh-block:fix/resource-metadata-parse-fallback

Conversation

@jh-block
Copy link
Copy Markdown

Summary

fetch_resource_metadata_from_url returns a fatal AuthError::MetadataError when the resource metadata response body cannot be parsed as JSON. This prevents discover_metadata() from falling through to direct .well-known/oauth-authorization-server discovery (Strategy B).

Problem

MCP servers that return HTTP 200 with non-JSON content (e.g. HTML) at their base URL cause the resource metadata probe to "succeed" at the HTTP level (200 = "this is the metadata URL"), but then the JSON deserialization fails fatally. The OAuth flow aborts entirely even though the server has a valid .well-known/oauth-authorization-server endpoint.

Users see "Auth required" with no OAuth browser flow ever opening.

Fix

Treat JSON parse errors as a soft failure — return Ok(None) with a debug! log — consistent with how HTTP errors (non-200 status codes) are already handled in the same function. This allows discover_metadata() to continue and attempt direct authorization server metadata discovery.

Test plan

  • Verified the project compiles with cargo check -p rmcp --features auth
  • Existing tests continue to pass
  • Servers returning valid resource metadata JSON are unaffected (happy path unchanged)
  • Servers returning non-JSON at their base URL now correctly fall through to .well-known/oauth-authorization-server discovery instead of aborting

In fetch_resource_metadata_from_url, a JSON parse failure on the
response body caused a fatal AuthError::MetadataError, preventing
discover_metadata() from falling through to direct
.well-known/oauth-authorization-server discovery (Strategy B).

MCP servers that return HTTP 200 with non-JSON content (e.g. HTML)
at their base URL caused the OAuth flow to abort entirely, even
when the server had a valid .well-known/oauth-authorization-server
endpoint.

Return Ok(None) on parse failure, consistent with how HTTP errors
are already handled in the same function.
@jh-block jh-block requested a review from a team as a code owner April 16, 2026 09:38
@github-actions github-actions bot added T-core Core library changes T-transport Transport layer changes labels Apr 16, 2026
@jh-block
Copy link
Copy Markdown
Author

jh-block commented Apr 16, 2026

Note that an alternative here would be to not try to parse this response as metadata at all, since fetching the MCP server URL itself and treating it as the metadata isn't part of the discovery flow in the spec. But I am assuming this variation from the spec is intentional for some reason (compatibility with non-compliant servers maybe?) so kept this change minimal.

jh-block added a commit to aaif-goose/goose that referenced this pull request Apr 16, 2026
…vers

When a remote MCP server returns HTTP 200 with non-JSON content at its
base URL, rmcp's resource metadata discovery fatally errors instead of
falling through to .well-known/oauth-authorization-server discovery.
This prevents the OAuth browser flow from ever opening.

Add a fallback in oauth_flow that catches the discovery failure and
retries by fetching .well-known/oauth-authorization-server directly.
Also log the actual error when oauth_flow fails instead of silently
swallowing it.

Upstream fix: modelcontextprotocol/rust-sdk#810
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant