What happened
In the official MCP TypeScript SDK, the OAuth client let the MCP server a client connected to decide which authorization server received the client's OAuth credentials; stored/pre-provisioned credentials were not bound to the authorization server they belong to (CWE-345/CWE-522). A malicious or compromised MCP server can name its own authorization server in protected-resource metadata and, with no user interaction, receive the victim's saved `refresh_token` and `client_secret` (1.x) or the configured `client_secret`/signed assertion of a bundled non-interactive provider (1.x and 2.x). Fixed in SDK 1.31.0 and @modelcontextprotocol/client 2.2.0.
Why it matters
This is the reference SDK for the Model Context Protocol — the connective tissue of agentic AI. Because it is automatable (no UI, no user confirmation), any agent that connects to an untrusted or compromised MCP server while holding OAuth credentials for a legitimate provider silently hands over long-lived tokens, enabling account/API takeover across the agent stack. The trusted-issuer binding gap is a structural OAuth-integration flaw in the ecosystem's core client library.
Attack vector
Agent connects to an attacker-controlled/compromised MCP server; server advertises its own authorization server in resource metadata; SDK sends stored refresh_token + client_secret to that endpoint on the next token refresh.
Affected systems
@modelcontextprotocol/sdk >= 1.12.0, < 1.31.0 (1.x); @modelcontextprotocol/client >= 2.0.0, < 2.2.0 (2.x)
Mitigation
Upgrade to @modelcontextprotocol/sdk 1.31.0 or @modelcontextprotocol/client 2.2.0; on 1.x only connect OAuth-enabled clients to trusted MCP servers (workaround).