Kuberwastaken/claurst · error · anyhow::Error

OAuth metadata parse error

Error message

OAuth metadata parse error: {}

What it means

When the well-known OAuth metadata endpoint responds successfully, the body is deserialized into serde_json::Value; a malformed or non-JSON body triggers this parse error. Fields like authorization_endpoint are then read with fallbacks, but the JSON itself must parse. Called by begin_mcp_auth and get_valid_mcp_token.

Solutions

  1. Verify the base URL is the server root so the .well-known path resolves to real metadata
  2. Check the response body manually with curl to see what is actually returned
  3. Confirm the server implements RFC 8414 authorization-server metadata at that URL
  4. Bypass intermediate proxies/gateways that rewrite the response body

Example fix

// before: hitting a path that returns HTML
let url = "https://example.com/mcp";
// after: metadata is discovered from the server root
let url = "https://mcp.example.com"; // fetches {url}/.well-known/oauth-authorization-server
Defensive patterns

Strategy: fallback

Validate before calling

let resp = reqwest::get(format!("{base}/.well-known/oauth-authorization-server", base = server_url)).await?;
let ct = resp.headers().get(reqwest::header::CONTENT_TYPE)
    .and_then(|v| v.to_str().ok()).unwrap_or("");
if !ct.contains("json") {
    anyhow::bail!("metadata endpoint returned non-JSON content-type: {ct}");
}

Try / catch

let meta: serde_json::Value = resp.json().await
    .map_err(|e| e.context("metadata endpoint did not return JSON; check base_url and proxies"))?;

Prevention

When it happens

Trigger: fetch_oauth_metadata() gets a 2xx response from {base_url}/.well-known/oauth-authorization-server whose body is not valid JSON — HTML error pages, empty bodies with 200, or wrong content served by a proxy.

Common situations: Server behind an auth gateway returning an HTML login page with 200; base_url pointing at the wrong path so a generic page is returned; server returning XML or plain text; CDN error page with success status.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/b6ebfd49a469e618. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/mcp/src/oauth.rs:172

        challenge,
    )
}

pub async fn fetch_oauth_metadata(server_url: &str) -> anyhow::Result<McpOAuthMetadata> {
    let base_url = normalized_server_url(server_url);
    let fallback = fallback_oauth_metadata(base_url);
    let metadata_url = format!("{}/.well-known/oauth-authorization-server", base_url);
    let client = reqwest::Client::builder()
        .timeout(Duration::from_secs(10))
        .build()
        .map_err(|e| anyhow::anyhow!("Failed to build HTTP client: {}", e))?;

    match client.get(&metadata_url).send().await {
        Ok(resp) if resp.status().is_success() => {
            let meta: serde_json::Value = resp
                .json()
                .await
                .map_err(|e| anyhow::anyhow!("OAuth metadata parse error: {}", e))?;
            Ok(McpOAuthMetadata {
                authorization_endpoint: meta
                    .get("authorization_endpoint")
                    .and_then(|value| value.as_str())
                    .unwrap_or(fallback.authorization_endpoint.as_str())
                    .to_string(),
                token_endpoint: meta
                    .get("token_endpoint")
                    .and_then(|value| value.as_str())
                    .unwrap_or(fallback.token_endpoint.as_str())
                    .to_string(),
            })
        }
        Ok(_) | Err(_) => Ok(fallback),
    }
}

pub async fn begin_mcp_auth(

View on GitHub (pinned to b0637c97ec)