Published
- 5 min read
By Allan D - Editor, AI Security Wire
MCP Python SDK OAuth Flaw: Rogue Servers Can Steal Client Credentials
The official MCP Python SDK could hand OAuth client secrets, authorization codes and signed client assertions to an authorization server chosen by the MCP server it was talking to. The flaw is tracked as GHSA-qx49-fqc8-xw99, published September 28, 2026 by the SDK maintainers with a High rating of CVSS 7.5. No CVE has been assigned. The advisory page does not state whether anyone has exploited it.
In our view the trust model is the problem. An MCP client connecting to a remote server is expected to trust that server enough to call its tools. It is not supposed to trust it to decide where the client’s long-lived credentials go.
What the advisory says
The affected package is mcp on PyPI. Per the advisory, vulnerable ranges are 1.9.1 up to but not including 1.30.0, and 2.0.0a1 up to but not including 2.2.0. Patched releases are 1.30.0 and 2.2.0. The advisory lists CWE-345 (Insufficient Verification of Data Authenticity) and CWE-522 (Insufficiently Protected Credentials).
The CVSS 3.1 vector for the headline score is AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N. For the interactive OAuthClientProvider, the advisory scores 6.5 because user interaction is required. Both are maintainer-assigned scores in the GitHub advisory; NVD has no record, since no CVE exists.
Four providers are affected: OAuthClientProvider, ClientCredentialsOAuthProvider, PrivateKeyJWTOAuthProvider and the deprecated 1.x RFC7523OAuthClientProvider.
How the redirect works
Two gaps combine. First, the client did not validate the issuer field in the authorization server metadata on every discovery path. Second, stored or pre-provisioned client credentials were not bound to the authorization server they belong to.
A malicious or compromised MCP server can exploit this in two ways, according to the advisory:
- Name its own authorization server in the protected resource metadata it serves.
- Publish no protected resource metadata at all, and present the user’s real authorization server as the
issuerwhile the token endpoint points at infrastructure the attacker controls.
Either way the client sends its credentials to the attacker’s token endpoint. Depending on the provider, the attacker receives the client secret, the authorization code, the PKCE code verifier, or a signed client assertion from PrivateKeyJWTOAuthProvider. In our view, a client secret is typically long-lived, so a single connection to a hostile server could leave a reusable credential in someone else’s hands.
The exposure differs by release line. On 1.9.1 through 1.29.1 there is no issuer check and no credential binding on any path. On 2.0.0 through 2.1.1 both gaps apply when the server publishes no protected resource metadata (the legacy fallback) or returns 403 insufficient_scope. On both lines, ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider had no way to specify which authorization server to use at all.
Who is exposed
Any Python application that uses the SDK as an OAuth-enabled MCP client and connects to servers outside its own control. In our view that includes agent frameworks, IDE assistants and internal tooling that let users add remote MCP servers by URL. Our assessment: the realistic risk sits with clients where adding a server is a low-friction user action, because the attacker only needs to get one malicious or compromised server into the client’s list.
Per the advisory, servers built with the SDK, stdio clients, and clients that attach their own tokens or headers are not affected.
Remediation
The advisory’s guidance is more involved than a version bump:
- Upgrade to 1.30.0, 2.2.0 or later.
- Pass
issuer=(an explicit issuer URL string) toClientCredentialsOAuthProviderandPrivateKeyJWTOAuthProvider. Without it they still follow the server-advertised authorization server. Omitting it currently produces a deprecation warning and becomes required in 3.0. On 1.30.0 that warning is aDeprecationWarning, which Python hides by default, so do not rely on seeing it. - Migrate off the deprecated
RFC7523OAuthClientProvider. It has noissueroption. - Clear stored OAuth client information once so the client re-registers. Stored registrations without an issuer remain unbound, including all 1.x registrations stored before 1.30.0. If you placed a pre-registered client in storage yourself, set
issueron that record. - If a client may have connected to an untrusted MCP server, rotate its client secret and revoke its tokens at the authorization server.
The advisory states there is no workaround for earlier versions other than connecting OAuth-enabled clients only to MCP servers you trust.
Detection and hunting
The advisory publishes no indicators of compromise, so we cannot offer any. Practical checks, which are our suggestions rather than vendor guidance:
- Inventory every service with
mcppinned between the vulnerable ranges (pip show mcp, lockfile search, SBOM query). - List the remote MCP server URLs each OAuth client has connected to, and flag any you do not recognise.
- Review authorization server logs for token requests using your client IDs from unexpected source addresses, and for use of a client secret after a client connected to a third-party server.
- Run staging with
python -W default(or-W error::DeprecationWarning) so the missing-issuerwarning on the client-credentials providers surfaces, since Python hides it by default.
Why this matters beyond one SDK
MCP’s authorization flow leans on metadata the server itself supplies. In our view, a client believing a server’s description of where to authenticate is a pattern other MCP client implementations should be checked for. We did not search other SDKs, so we cannot say whether they are affected.
For this advisory, the lack of a CVE as of October 23, 2026 is a practical problem. Teams that gate patching on CVE feeds may not see this one, and a CVE could still be assigned later. The advisory credits eight accounts.
References
Frequently Asked Questions
- Is there a CVE for this MCP Python SDK vulnerability?
- No. As of October 23, 2026 the GitHub advisory GHSA-qx49-fqc8-xw99 lists no CVE ID, so scanners keyed to CVE feeds may not flag it. Track it by the GHSA identifier and by the installed mcp package version.
- Which versions of the mcp package are affected and which are fixed?
- The advisory lists 1.9.1 up to but not including 1.30.0, and 2.0.0a1 up to but not including 2.2.0. The patched releases are 1.30.0 and 2.2.0. Servers built with the SDK and stdio clients are listed as not affected.
- Is upgrading enough to be safe?
- Not always. ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider still follow the server-advertised authorization server unless you pass an explicit issuer. Previously stored client registrations also remain unbound until you clear them so the client re-registers.