Safeguard
AI Security•

MCP SDK OAuth Flaws: CVE-2026-104850 and rmcp Let Servers Grab Tokens

The official TypeScript and Rust MCP SDKs trusted the MCP server to say where OAuth credentials should go. Upgrading fixes new sign-ins, but stored tokens need work too.

Safeguard Security Team
5 min read

On 6 October 2026, GitHub's Advisory Database added CVE-2026-104850 (GHSA-6qxp-vccf-f47h) for the official MCP TypeScript SDK. In affected versions, the SDK's OAuth client let the MCP server pick which authorization server received the client's credentials, so a malicious or compromised MCP server could name its own and the client would send it stored refresh tokens and client secrets with no user interaction. The official Rust SDK, rmcp, had related advisories added on 16 September and 2 October.

What's affected

TypeScript SDK, CVE-2026-104850 (High, CVSS 3.1 7.5):

  • @modelcontextprotocol/sdk 1.12.0 through 1.30.1. Fixed in 1.31.0.
  • @modelcontextprotocol/client 2.0.0 through 2.1.0, in specific cases: bundled providers without expectedIssuer, credentials stored without issuer, direct fetchToken() calls, and providers that read storage back through the token or client-information schemas. Fixed in 2.2.0.

What could leak, per the advisory: the stored refresh_token and client_secret from an earlier sign-in, and the client secret or signed assertion configured on a bundled provider. You're affected if your app uses the SDK's OAuth client over HTTP (an authProvider on a transport, the withOAuth() middleware, or direct auth() or fetchToken() calls) and may connect to an MCP server you don't fully trust. MCP servers built with the SDK and stdio clients are not affected.

Rust SDK, rmcp (all fixed in 2.0.0 unless noted):

  • CVE-2026-63127 (High, 8.2): rmcp didn't check the resource field in OAuth Protected Resource Metadata, which RFC 9728 requires. A malicious server could declare a legitimate server as the resource, send the victim through that server's real authorization prompt, and receive a token valid for the real server. Credited to researchers at the University of Illinois Urbana-Champaign and UC Irvine.
  • GHSA-c9xm-49cp-xcr9 (Moderate, no CVE): the OAuth client fetched a resource_metadata URL from the server's WWW-Authenticate header without origin or private-network checks, which is SSRF from the client's network.
  • CVE-2026-64684: custom HTTP headers leaked to cross-origin redirect targets in the Streamable HTTP client. Fixed in 2.1.0.

How to check if you're exposed

Find the SDK versions in your projects:

npm ls @modelcontextprotocol/sdk @modelcontextprotocol/client @modelcontextprotocol/core
cargo tree -i rmcp 2>/dev/null | head -3

Then check whether you use the OAuth client at all. A version match without OAuth use is low priority:

grep -rnE "authProvider|withOAuth\(|fetchToken\(|ClientCredentialsProvider|PrivateKeyJwtProvider|CrossAppAccessProvider" src/

If you'd rather not run tooling, the free scan at gold.safeguard.sh/scan reads a package.json in the browser, and the file never leaves your machine.

What to do now

  1. Upgrade. TypeScript 1.x to @modelcontextprotocol/sdk 1.31.0 or later. TypeScript 2.x to @modelcontextprotocol/client 2.2.0 or later, plus @modelcontextprotocol/core 2.2.0 if you import it directly. Rust to rmcp 2.1.0 or later to cover all three Rust issues.

  2. Set expectedIssuer on bundled providers. The advisory says the client-credentials, private-key JWT and cross-app access providers still follow the server's choice without it:

    // add to the options of ClientCredentialsProvider, PrivateKeyJwtProvider,
    // StaticPrivateKeyJwtProvider or CrossAppAccessProvider
    expectedIssuer: "https://auth.example.com",
    
  3. Deal with stored credentials. Tokens and client information saved before the upgrade have no issuer recorded and "still go to whichever server is named at first use." Add issuer to them, or clear them so users sign in again.

  4. Fix custom providers. If you implemented OAuthClientProvider, save exactly what saveTokens() and saveClientInformation() receive, including issuer.

  5. Rotate if in doubt. The advisory recommends that if an affected client may have connected to an untrusted MCP server, you rotate its client secret or signing key and revoke its tokens.

  6. Know what the fix doesn't cover. A new interactive sign-in still goes to whatever authorization server the MCP server names, and direct calls to refreshAuthorization() and exchangeAuthorization() are outside the fix. Only complete sign-ins for servers you trust.

Why it matters

MCP clients increasingly hold real credentials for many servers at once. The protocol's OAuth discovery lets a server tell the client where to authenticate, which is convenient and also means the server is an input to a security decision. Both SDKs trusted that input more than they should have.

The interesting part is that upgrading isn't enough. Credentials saved under the old code carry no record of which authorization server they belong to, so the new binding check has nothing to compare against. Any fix that adds binding to stored secrets has this migration problem, and it's easy to miss in a changelog.

For teams building agents, the takeaway is that "connect to this MCP server" is a trust decision that can expose credentials for other services.

Try it yourself

Search one of your MCP client codebases for the OAuth entry points listed above. If none appear, the SDK version alone doesn't put you at risk from CVE-2026-104850. That is the same question reachability analysis answers automatically: does your code call the vulnerable part?

Click through it at your own pace, or open it full screen.

Quick check

I build an MCP server with the TypeScript SDK. Am I affected by CVE-2026-104850?

No. The advisory says MCP servers built with the SDK and stdio clients are not affected. It's a client-side OAuth issue.

Why do stored tokens still need attention after upgrading?

The fix records the authorization server as issuer on saved credentials and refuses to send them elsewhere. Credentials saved before the upgrade have no issuer, so they still follow whichever server is named first.

What does CVE-2026-63127 let a malicious rmcp server do?

Send the user through a legitimate server's real sign-in, then receive the resulting token. The attacker can then act as the user on the real server.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.