Pin the Issuer: MCP OAuth Discovery, RFC 9728, RFC 9207, and the Python and TypeScript SDK Advisories
Two MCP SDK advisories showed OAuth clients sending credentials to a server-chosen authorization server. How RFC 9728 discovery works and what clients must pin.
An MCP client connects to a server, gets a 401, and is told which authorization server to use. Where does that "which one" come from? From the MCP server itself.
That is by design. It is also the root of two recent security advisories: the OAuth clients in the official MCP Python and TypeScript SDKs could be steered by a malicious or compromised MCP server into sending credentials to an authorization server the server chose. This post explains the discovery flow, what the advisories say, and the checks a client has to make.
What the advisories say
Both are rated High, CVSS 7.5, and both are classified as insufficient verification of data authenticity and insufficiently protected credentials.
| Python SDK | TypeScript SDK | |
|---|---|---|
| Advisory | GHSA-qx49-fqc8-xw99 | GHSA-6qxp-vccf-f47h |
| CVE | CVE-2026-115563 | CVE-2026-104850 |
| Published | Sep 28, 2026 | Sep 30, 2026 |
| Affected | mcp 1.9.1 to before 1.30.0; 2.0.0a1 to before 2.2.0 | @modelcontextprotocol/sdk 1.12.0 to 1.30.1; @modelcontextprotocol/client 2.0.0 to 2.1.0 under narrower conditions |
| Fixed in | 1.30.0, 2.2.0 | 1.31.0, 2.2.0 |
In the Python advisory's words, the authorization server issuer was not validated on every discovery path, and stored or pre-provisioned credentials were not bound to the authorization server they belong to. A malicious server could redirect the client to a token endpoint it controls, exposing material such as the client secret, authorization code, PKCE code verifier or a signed client assertion. The TypeScript advisory describes the same class of problem: a stored refresh_token and client_secret could go to the server-chosen authorization server.
Not affected, per the advisories: MCP servers built with the SDKs, stdio clients, and clients that attach their own tokens or headers.
How MCP OAuth discovery works
The current draft of the MCP authorization specification builds on OAuth 2.1, RFC 9728 (protected resource metadata), RFC 8414 and OpenID Connect Discovery (authorization server metadata), RFC 8707 (resource indicators) and RFC 9207 (issuer identification). The flow:
- The client sends an MCP request without a token.
- The server answers
401with aWWW-Authenticateheader carrying aresource_metadataURL. - The client fetches that Protected Resource Metadata document. It lists the
authorization_serversfor this resource. - The client picks one and fetches its authorization server metadata.
- The client registers or uses an existing client ID, runs the authorization request with PKCE and a
resourceparameter, and exchanges the code for a token.
Per the draft, MCP servers MUST implement RFC 9728, and clients MUST use it for authorization server discovery. Clients MUST support both RFC 8414 and OpenID Connect discovery.
Step 3 is the part to notice. The resource server tells the client who the authorization server is. A client that trusts that answer without further checks lets any server redirect its credentials.
What the specification asks a client to pin
The draft spells out several requirements that close this gap:
- Record the issuer first. Before redirecting the user, the client MUST record the
issuerfrom the selected authorization server's validated metadata, and tie it to the same per-request record as the PKCE verifier. The spec notes the protection fails if the expected issuer came from an unvalidated source. - Validate
isson the way back. Before sending the authorization code to any token endpoint, the client MUST apply the RFC 9207 check and compare a presentissto the recorded issuer by simple string comparison, with no case folding or normalization. This applies to error responses too. - Send the resource parameter. Clients MUST include
resource(RFC 8707) in authorization and token requests, identifying the MCP server the token is for. - Never forward foreign tokens. Clients MUST NOT send tokens to the MCP server other than ones issued by that server's authorization server, and servers MUST validate that a token was issued for them as audience.
The advisories read as the same lesson from the implementation side: credentials have to be bound to the issuer they were issued by, not to whichever server happens to ask.
What to do
For client operators:
- Upgrade to a fixed SDK version from the table above.
- Set the expected issuer. In Python, pass
issuer=toClientCredentialsOAuthProviderandPrivateKeyJWTOAuthProvider; upgrading alone does not fix those providers, and omitting it only raises a deprecation warning that Python hides by default. In TypeScript, passexpectedIssuerto the bundled providers. - Clear unbound stored credentials. Registrations and tokens saved without an issuer stay unbound after the upgrade. Clear them once so the client re-registers.
- Rotate if exposed. If a client may have connected to an untrusted MCP server, rotate its client secret or signing key and revoke its tokens at the authorization server.
- Know what the fix doesn't cover. The TypeScript advisory notes that new interactive sign-ins still go to the authorization server the MCP server names, so complete them only with servers you trust.
For server operators, publish metadata that makes the client's job easy: a correct resource and authorization_servers in your protected resource metadata, a scope in the WWW-Authenticate challenge, and an authorization server that includes iss in authorization responses and advertises authorization_response_iss_parameter_supported. The draft says it expects to upgrade iss inclusion from SHOULD to MUST in a future revision.
Checking the server side
You cannot test your client SDK from the outside, but you can check what your server tells clients. The Agent Protocol Inspector runs a live MCP handshake against a URL, detects OAuth-protected servers via RFC 9728, and reports them as protected rather than missing, so you can confirm your endpoint is discoverable and refuses anonymous access. It does not exercise a client's credential handling, so treat it as a check on the server's published metadata.
Where ContextIQ fits
Which MCP servers an agent may talk to, and which authorization servers may receive its credentials, are policy decisions as much as code ones. ContextIQ Agent Data Policy is an Enterprise Preview that treats the approved model and tool endpoints as part of a data-handling decision, alongside isolation, read-only input and persistence. It is not generally available; we are working with design partners. To see whether it fits your workload, request an Enterprise Preview demo.
Follow Trango Compute on LinkedIn
We post updates on new tools, context engineering patterns, and LLM cost research.