The Official MCP Python SDK Leaked OAuth Credentials to Malicious Servers. If You Connect to Third-Party MCP Servers, Patch and Rotate

A flaw in the official MCP (Model Context Protocol) Python SDK let a malicious MCP server steal the OAuth credentials of any client that connected to it. Not a low-value session token, but client secrets, authorization codes, and PKCE proof keys, enough to mint valid access tokens with the full permissions of the connecting application. The root cause is simple and old: the SDK sent credentials without first verifying who it was sending them to. Cycode reported it, along with seven other credited researchers, and the fix landed in SDK versions 1.30.0 and 2.2.0.

MCP is how AI agents plug into external tools and services, and it is spreading fast into exactly the kind of automation that runs on servers and in build pipelines. If any of your systems act as an MCP client and connect to servers you do not fully control, this is a credential-theft problem you need to close now, and patching alone does not finish the job.

What the flaw actually was

When an MCP client authenticates with OAuth, it is supposed to send its credentials to a known, trusted authorization server. The vulnerable SDK skipped the step of validating that authorization server’s identity before handing over the credentials. That single omission is the whole vulnerability.

Because of it, a malicious MCP server could redirect a connecting client to an attacker-controlled endpoint and intercept:

  • Client secrets. These are long-lived credentials. Unlike a session token that expires, a stolen client secret keeps working until someone rotates it.
  • Authorization codes.
  • PKCE proof keys. PKCE exists specifically to stop an intercepted authorization code from being reused. Capturing the proof key defeats that single-use protection, so PKCE, the control you would expect to save you here, does not.

With those in hand, an attacker obtains valid access tokens carrying the full application permissions of the victim client. This is not a crash or a data-read bug. It is credential theft that hands over the identity your client uses to reach everything it is authorized to reach.

Who is affected, and who is not

The flaw is in the client-side OAuth providers of the SDK. Affected providers include OAuthClientProvider, ClientCredentialsOAuthProvider, PrivateKeyJWTOAuthProvider, and the deprecated 1.x RFC7523OAuthClientProvider.

Notably not affected: MCP servers themselves, stdio clients, and applications that attach tokens with their own custom logic. So the risk sits specifically with applications acting as OAuth MCP clients that connect out to MCP servers. If that describes any automation you run, you are in scope.

Severity is rated CVSS 7.5 (high) for non-interactive providers and 6.5 for the interactive provider. The higher score for non-interactive providers makes sense: unattended, server-to-server automation is exactly where a stolen long-lived client secret does the most damage, because there is no human in the loop to notice anything odd.

Affected and fixed versions

Version line Affected Fixed in
1.x 1.9.1 through 1.29.1 1.30.0
2.x 2.0.0 through 2.1.1 2.2.0

One detail worth flagging: the fix first shipped on September 7 listed as a behavior change, not a security fix, with the formal advisory only following on September 28. If you update dependencies by scanning changelogs for the word “security,” you would have missed this for three weeks. That is a recurring trap with SDK-level fixes, and a reason to treat behavior-change notes in a security-sensitive dependency as worth a second look.

What to do, and why patching is only step one

Upgrading closes the hole going forward. It does not undo credentials that may already have been exposed if your client ever connected to a server you did not fully trust. Work through all of these:

  • Upgrade the SDK to 1.30.0 or 2.2.0, whichever matches your line.
  • Two providers need an extra step after upgrading. ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider require you to pass the issuer= parameter post-upgrade. Without it, you have installed the fix but not fully engaged it.
  • Clear stored OAuth client registrations. Old registrations can carry the unsafe state forward.
  • If your client ever connected to an untrusted or third-party MCP server, assume the credentials are burned. Rotate client secrets and revoke tokens at the login service. Because the stolen client secret is long-lived, rotation is the only thing that actually stops a quiet attacker who already grabbed it. Revocation without rotation is not enough. If you are not sure whether an affected server was ever compromised in the process, that assessment is exactly what our emergency incident response team handles.

Also note a Python-specific gotcha: version 1.30.0’s deprecation warnings are hidden by default in Python, so you may not see prompts telling you that a provider now needs the issuer= parameter. Do not rely on the warnings to remind you. Make the config change deliberately.

The takeaway

There are no known attacks using this yet, which makes now the good time to act rather than the reason to wait. The lesson underneath is one that keeps repeating as agents and MCP wire more systems together: connecting to an MCP server is a trust decision, and a client that hands over credentials without verifying who is on the other end turns every untrusted server into a credential-theft opportunity. The bug is a classic missing-validation flaw wearing new MCP clothes, and the credentials at stake are long-lived client secrets with full application permissions. Upgrade to 1.30.0 or 2.2.0, set the issuer= parameter where required, clear old registrations, and rotate anything that ever touched a server you did not control. If you want an outside check on where your automation’s OAuth credentials and long-lived secrets are actually exposed, that is what our penetration testing and managed security hardening services are for.