Three MCP auth bugs in 30 days, one root cause: Trusting what the other side said
The MCP Python SDK, the Rust SDK (rmcp), and LiteLLM each accepted authentication input that nobody verified. Here is what broke, who is affected, and the checklist that closes the gap.
Three authentication flaws in the MCP ecosystem reached public attention in a 30-day window that started on September 2, 2026. They hit different projects, and one of them sits on the other side of the connection from the other two. But all three come down to the same mistake: code accepted authentication input that nobody verified.
The short version:
- MCP Python SDK (
mcp1.9.1 to 1.29.1 and 2.0.0 to 2.1.1): a malicious MCP server could make the OAuth client send its client secret, authorization code, and PKCE verifier to an attacker's token endpoint. Fixed in 1.30.0 and 2.2.0. Machine-to-machine providers also need an explicitissuer=argument. GHSA-qx49-fqc8-xw99, CVSS 7.5, no CVE assigned when the advisory was published on September 28. - MCP Rust SDK, rmcp (before 2.0.0): the client never checked that the
resourcefield in a server's protected resource metadata matched the server it connected to, so a malicious server could point the OAuth flow at a real authorization server and walk away with a token. Fixed in 2.0.0. CVE-2026-63127, published September 16, CVSS 8.2. - LiteLLM (before 1.84.0): the opposite direction. The gateway's MCP endpoint trusted a client's made-up
Authorizationheader and let the request through unauthenticated. Fixed in 1.84.0. CVE-2026-59822, CVSS 8.8. The bug was disclosed in the summer, and CISA added it to its Known Exploited Vulnerabilities catalog on September 2, which is what puts it in this window.
If you build an MCP client, run one of these SDKs, or operate a LiteLLM gateway, upgrade first and read the checklists below second.
The three bugs side by side
Two of these fixes shipped well before the write-ups. The rmcp 2.0.0 release is dated June 29. The Python SDK advisory went public with no CVE, and the issuer checks appeared in the 1.30.0 and 2.2.0 release notes under behavior changes. That matters because vulnerability scanners and patch-triage queues key off CVEs.
Two bugs where the client trusted the server
An MCP client connects to servers it does not control, and those servers supply the metadata the client then acts on. That makes the client the party that has to do the checking. Two SDKs did not.
Bug 1: The Python SDK had two separate gaps
An MCP client starts by asking the MCP server where to log in. The server answers with the address of an authorization server. The client fetches that server's metadata and, if it is doing its job, confirms that the issuer in the metadata matches the address it was told to use (RFC 8414).
The Python SDK failed in two distinct ways.
Gap 1: The issuer was not validated consistently
The two version lines failed differently:
- In 1.9.1 to 1.29.1, there was no issuer check on any path.
- In 2.0.0 to 2.1.1, the check existed but was skipped in two cases: when the server publishes no protected resource metadata (the legacy fallback, which a 404 triggers), and when the server returns
403 insufficient_scope.
Gap 2: Credentials were not tied to the server that issued them
The SDK did not record which authorization server a client registration or secret belonged to, so it could present them to whichever server it was pointed at.
A malicious server only has to return a 404 or a 403 at the right moment. The SDK then sends the client secret, authorization code, and PKCE verifier to a token endpoint the attacker controls. The two unattended providers, ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider, are the worse case because no person is present to notice anything odd. On every version, those two providers had no way to name their authorization server at all.
Upgrading is necessary but not sufficient:
- Upgrade to 1.30.0 or 2.2.0 or later.
- For the two machine-to-machine providers, pass
issuer=explicitly (for exampleissuer="https://auth.example.com"). Since 1.30.0, leaving it out triggers aDeprecationWarning, and the advisory says it becomes mandatory in 3.0. - Clear any registrations stored before 1.30.0 so clients re-register against the expected issuer.
- Rotate secrets if a vulnerable client ever connected to a server you do not control.
- Move off the deprecated 1.x
RFC7523OAuthClientProvider, which has noissuer=option.
Bug 2: rmcp believed what the server said about itself
MCP servers advertise their authorization servers through protected resource metadata (RFC 9728). That document includes a resource field naming the resource it describes, and the client is supposed to check that it matches the MCP server URL it actually connected to.
rmcp did not. A malicious MCP server could publish metadata that pointed at a real, legitimate authorization server. The user completes a normal-looking OAuth flow, and the attacker ends up holding a token that works against the legitimate resource within its scope. The fix requires the metadata to include a matching resource and rejects it before the client trusts any advertised authorization server or scopes. The weakness class is CWE-345, insufficient verification of data authenticity.
This is the same family as the token confusion attacks that resource indicators (RFC 8707) exist to prevent. See our post on MCP resource indicators for the audience-binding half of the story.
One bug where the server trusted the client
Bug 3: LiteLLM let a made-up header through
LiteLLM is a gateway, and the bug is in its MCP endpoint. The Streamable HTTP route supported OAuth2 passthrough. A fabricated Authorization header pushed a request into that passthrough fallback, which replaced the failed key check with an empty UserAPIKeyAuth object. The request then went through as if it were authenticated.
No client trusted a server here. The gateway trusted a request it never checked, which is the same mistake from the other direction.
The vulnerability was disclosed in the summer. What happened in September is that CISA added it to the Known Exploited Vulnerabilities catalog on September 2 with a September 16 federal deadline. That listing is the strongest signal of the three: attackers are using it. Reporting on the in-the-wild activity describes a chain with a separate Starlette flaw (CVE-2026-48710) to reach model configurations and upstream provider keys. The fix is LiteLLM 1.84.0. Until you can upgrade, block the /mcp/ routes at your reverse proxy or API gateway.
The common root cause: Input nobody verified
All three bugs trusted data that came from the other side of the connection:

In each case a verification step was missing, optional, or skipped on one code path, and the code accepted the input instead of rejecting it. A check that lives only on the happy path will eventually be missing from some other path.
Our posts on OAuth mix-up attacks and RFC 9207 and how MCP authorization breaks in production cover other places the same pattern appears.
Checklist for MCP client authors
- Derive the expected issuer and resource first. Compute them from the URL the user configured, before fetching any metadata. Never take the expected value from the response you are about to validate.
- Validate the issuer on every discovery path. That includes fallbacks, retries, redirects, and
403 insufficient_scopestep-up. Theissuerin authorization server metadata must match the issuer used to build the well-known URL (RFC 8414). - Validate the
resourcein protected resource metadata. It must match the MCP server URL you connected to. Reject metadata where it is absent (RFC 9728). - Send the
resourceparameter and bind tokens to it (RFC 8707). - Check
isson the authorization response. Compare it to the issuer you recorded before sending the code anywhere (RFC 9207). - Bind stored credentials to an issuer. Key client registrations and secrets by issuer, never reuse them with a different authorization server, and re-register when the server changes.
- Treat absence as failure. A missing field is a rejection, not a cue to substitute a default.
- Test the unhappy paths. Write tests for a 404 on discovery, a 403 step-up to a different provider, a mismatched issuer, and a missing
resource.
Checklist for MCP server and gateway operators
- Deny by default. If token validation fails, the request fails. Never substitute a default identity or an empty auth object.
- Test with fabricated tokens. Send arbitrary non-empty
Authorizationvalues at every MCP route, including passthrough and fallback modes. - Publish correct metadata. Make sure your authorization server
issuerand your protected resourceresourcevalues match exactly what clients will use to build the discovery URLs, so strict clients accept them. - Inventory what is exposed. Know which gateways, MCP routes, and SDK versions you run, and who can reach them.
How to check whether you are affected
Frequently asked questions
Is the MCP Python SDK vulnerability assigned a CVE?Not when the advisory was published on September 28. It is tracked as GitHub advisory GHSA-qx49-fqc8-xw99 with a CVSS score of 7.5. Without a CVE, scanners that rely on CVE feeds may not flag it.
Which MCP Python SDK versions are affected?mcp 1.9.1 through 1.29.1 and 2.0.0 through 2.1.1. The fixed versions are 1.30.0 and 2.2.0.
Is upgrading the MCP Python SDK enough?Not for ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider. You must also pass an explicit issuer= argument, otherwise those providers still follow whichever authorization server the MCP server names. Omitting it triggers a DeprecationWarning today, and it becomes mandatory in 3.0. Clear stored registrations from before 1.30.0 as well.
What is CVE-2026-63127?A flaw in the MCP Rust SDK (rmcp) before version 2.0.0. The client did not validate the resource field in RFC 9728 protected resource metadata, so a malicious MCP server could capture an access token meant for another resource. It was published September 16, is rated CVSS 8.2, and is fixed in 2.0.0.
What is CVE-2026-59822?An authentication bypass in LiteLLM's MCP Streamable HTTP endpoint before version 1.84.0. A fabricated Authorization header pushed requests into an OAuth2 passthrough fallback that replaced the failed key check with an empty auth object, so the request went through unauthenticated. CISA added it to its Known Exploited Vulnerabilities catalog on September 2, 2026.
What do these MCP vulnerabilities have in common?Each trusted authentication input from the other side of the connection without verifying it. In the two SDK bugs a client trusted a server. In LiteLLM a server trusted a client. The fix is to verify on every code path and treat missing values as failures.
Do these bugs affect MCP servers built on AuthKit?[Verify with the AuthKit team. The two SDK bugs live in MCP clients, not in authorization servers. Suggested answer: the client-side flaws affect the applications that connect to your MCP server, not the server itself, but correct and consistent metadata helps strict clients connect cleanly.]
Sources
- GHSA-qx49-fqc8-xw99: MCP Python SDK
- Cycode: account takeover in Anthropic's MCP Python SDK
- CVE-2026-63127: rmcp, GitLab Advisory Database
- rmcp pull request 937, prevent OAuth resource spoofing
- rmcp v2.0.0 release
- CVE-2026-59822: LiteLLM authentication bypass, Sherlock Forensics
- GHSA-7488-6r32-c95q: LiteLLM
- CISA adds seven Known Exploited Vulnerabilities to catalog, Sept 2, 2026