Skip to content

Build Not yet confirmed elsewhere1 publisher 3 min read Published

Metadata discovery turns an MCP client into an unauthenticated fetch primitive

A researcher found the SSRF guard sitting on the credential-submission side of an MCP auth SDK while the .well-known discovery fetch ran unchecked from the public entry point, which is the wrong end of the flow to protect.

The Engineer · Build desk

Send a correction

Illustration accompanying Metadata discovery turns an MCP client into an unauthenticated fetch primitive
Generated illustration

What happened

  • A researcher reviewing AI tooling reported a server-side request forgery in the OAuth metadata discovery path of an SDK that handles authentication for MCP servers.
  • The same codebase already carried a URL guard, assertSafeOAuthEndpoint delegating to validateDownloadUrl, but it had been applied only where credentials are submitted.
  • From the public auth() API down through discoverOAuthProtectedResourceMetadata, discoverMetadataWithFallback and tryMetadataDiscovery to fetch, the researcher found no validation at any step.

Why it matters

  • What it limits constraint Any client that resolves a remote name before authentication finishes has to be treated as an unauthenticated fetch primitive, which pushes the real control to network egress policy rather than a library helper.
  • Who has to decide decision Teams whose MCP server URLs arrive from config, a database row or an end user now have to decide whether those connections egress through a resolving proxy, because the SDK's own guard does not sit on the discovery path.
  • Who's exposed exposure The blast radius is set by where the agent process runs, not by what the agent was asked to do, so an app with an attached IAM role inherits the risk of every URL it was ever configured to discover.
  • What doesn't add up contradiction The demonstration ends at a request arriving on localhost while the impact section reaches instance-metadata credential theft, and that gap is the difference between a blind forgery and an exfiltration you have to disclose.

Discovery runs before trust exists. That is what makes this a structural bug rather than a careless one. An OAuth client has to ask a server where its endpoints live before any token exists and before any issuer has been verified, so the only authority behind that first request is the network position of the process making it. The guard this SDK does have was applied to the part of the flow where credentials are submitted [7]. By then the requests have already gone out.

The chain is short enough to read in one sitting: `auth()` calls `discoverOAuthProtectedResourceMetadata()`, which calls `discoverMetadataWithFallback()`, which calls `tryMetadataDiscovery()`, which calls `fetch()` [4]. Four call boundaries, none of them validating [13]. The check that exists elsewhere is `assertSafeOAuthEndpoint()`, which delegates to `validateDownloadUrl()` [2]. That name is the interesting part: a URL guard written for downloads and later borrowed for OAuth endpoints. Borrowed validation gets applied where it was pasted, not where the data flows, which is the researcher's stated reason for reviewing this class of tooling in the first place [12].

On evidence strength, be precise about what was shown. A mock server on localhost received the SDK's standard OAuth discovery requests, headers and all, after the SDK was pointed at it through its public API [6]. That demonstrates request delivery to `127.0.0.1`. The writeup's other targets, the AWS metadata service at `169.254.169.254`, Redis on 6379, Elasticsearch on 9200 [8], and the credential-theft outcome it describes for instances with an attached IAM role [15], are consequences argued from that primitive rather than captured artifacts [14]. For instance metadata to become stolen credentials, the response body or an error carrying it has to be observable by whoever chose the URL, and that step is not in the writeup [14]. Blind SSRF is still a forged request originating inside your network; the distinction only changes which severity you file it under.

Three things have to hold for the finding to transfer to your deployment. The `serverUrl` has to be influenced by data you do not control, which the writeup notes can be a config file, a database row, or a user-supplied value [11]. The process has to sit somewhere with internal services worth reaching [15]. And nothing between the process and those services can be filtering egress. If your MCP client only ever dials a hardcoded host through a proxy that resolves and allowlists, this is a library defect and not your incident.

The awkward part is that the writeup does not name the SDK, describing it only as a fairly popular AI SDK for MCP authentication [10], so there is no version to diff and no advisory to read. The portable check is a shape rather than a version number: a public API that accepts a URL, a `fetch()` reachable from it, and a guard applied at some of the call sites downstream. The attacker in this model needs no credentials and no privileges [9], which means the interesting question about any agent framework you run is not whether it validates URLs, but whether it validates them at every point where a remote name turns into a socket.

What to watch

  • Whether the SDK gets named and a patched version published, since the writeup supplies neither.
  • Whether the fix lands as a guard at every fetch site or a single wrapper around the whole discovery chain.
  • Whether MCP client guidance starts requiring pre-auth discovery fetches to pass an allowlist before the socket opens.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence45
Adoption
Not scored
Hype gap+28
Incentives55
Confidence42
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    A researcher doing security reviews of AI-related tooling found a server-side request forgery in the OAuth metadata discovery path of an SDK that handles authentication for MCP servers.

    Reported Supported Source: researcher writeup published on dev.toView cited source
  2. [2]

    The SDK codebase contains a function assertSafeOAuthEndpoint(endpointUrl: URL) that calls validateDownloadUrl(endpointUrl.href).

    Reported SupportedView cited source
  3. [3]

    A separate function responsible for discovering OAuth metadata also fetches URLs during the OAuth flow, and it does not call validateDownloadUrl.

    Reported SupportedView cited source

Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.

Add to ChatGPTAdd to Claude

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · August 29, 2026

    How I Found an SSRF in an AI SDK's OAuth Metadata Discovery

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Loading related stories