The token never should have left the building. A legitimate MCP client, holding a fresh OAuth access token scoped to a user’s mailbox, connects to a new server to pick up a capability — and the token ends up in the hands of a server that had no business seeing it. The handshake looked ordinary. The consent screen, if there even was one, looked ordinary. The credential walked out the door anyway.
That is the shape of the vulnerability The Hacker News reported on 1 October 2026 in Anthropic’s official Model Context Protocol (MCP) Python SDK. Before you reach for the pitchforks: MCP the protocol is not broken, and OAuth is not suddenly unsafe for agents. This is an implementation bug in the SDK’s OAuth client — the layer that is supposed to keep one server’s token from reaching another. It failed at that job. One caveat up front, because it shapes everything below: the reporting confirms the effect — a malicious server extracting OAuth credentials during the connection handshake — but not every detail of the root cause. Where I’m walking through protocol facts, I’ll say so. Where I’m reconstructing the mechanism, I’ll flag it plainly. Don’t mistake the second for the first.
First — MCP ships, and nobody is thinking about tokens yet
Anthropic released MCP as an open standard for connecting AI agents to external systems. The mental model is clean: an MCP server exposes tools and resources — a mailbox, a file store, a CRM, a database. An MCP client (your desktop agent, your IDE plugin, a hosted assistant) connects to that server and lets the model call those tools on your behalf.
The trust boundary is the whole point. The client holds your identity. The server holds the data. The client decides what the model is allowed to reach, and the server decides what it will hand back. When that boundary is honoured, a sketchy server can only ever return sketchy data — annoying, maybe a prompt-injection risk, but not an identity compromise. When the boundary leaks in the other direction, the server gets your credentials. That is a categorically worse day.
Then — OAuth gets bolted on, with a rule that matters later
Early MCP had no real authorization story. Later spec revisions fixed that by making the MCP server an OAuth 2.0/2.1 resource server and the client an OAuth client, with a separate authorization server doing the issuing. Discovery works the way the RFCs say it should: the client reads Protected Resource Metadata (RFC 9728) from the server at /.well-known/oauth-protected-resource, that points it at an authorization server, it fetches that server’s metadata (RFC 8414), runs an authorization-code flow with PKCE, and gets a token back. That part is documented protocol, not my guesswork.
The load-bearing rule in all of this is RFC 8707 — Resource Indicators. The client is supposed to send a resource parameter naming the specific server the token is for, and the authorization server is supposed to stamp that audience into the token. A token minted for Server A should be rejected by Server B, and a well-behaved client should never send Server A’s token to Server B in the first place. Audience binding is the seatbelt. Remember that it exists.
The discovery — a researcher connects to a server that lies
Here is where the reporting thins out and inference begins, so take the next two paragraphs as my reconstruction, not as confirmed root cause. The attack abuses the connection handshake. A malicious server advertises itself during setup and the SDK’s OAuth client, trusting the server-supplied metadata and its own cached token state, presents a bearer token the server was never entitled to. In OAuth terms this is the confused-deputy / token-passthrough failure the community has been warning about for a year. The client is the deputy. The malicious server points, and the deputy hands over a credential bound to someone else’s resource.
Two mechanics could plausibly produce that result. I don’t know from the reporting which one — or which combination — is actually at fault, and Anthropic’s advisory is the authority here:
- Weak audience enforcement. If the SDK reuses a cached access token across connections without re-checking that the token’s audience matches the server it’s about to talk to, a new server simply receives a token minted for a different one. The RFC 8707 seatbelt would be unbuckled on the client side.
- Attacker-controlled discovery. Because the server supplies its own metadata, a hostile server can steer the flow — the authorization endpoint it names, the redirect handling, the
resourcevalue — in ways the SDK might not sufficiently pin. Mishandle the loopback redirect or thestatecheck and you can leak an authorization code or token straight back to the attacker during what looks like a clean grant.
What the reporting is clear on: no prompt injection required. The model doesn’t need to be tricked into anything. This happens below the model, in the transport-and-auth plumbing, which is why it’s worth taking seriously — your usual “the LLM got fooled” instincts don’t help you here.
1 October 2026 — the advisory, and the patch
Anthropic published the fix and The Hacker News wrote it up the same day. This is the one date I can stand behind. Two things the reporting I’m working from does not nail down, and I won’t pretend otherwise: the exact PyPI package string, and the vulnerable-versus-fixed version numbers. The affected component is Anthropic’s official MCP Python SDK; the commonly published name for it is mcp, but confirm that against the advisory before you paste it into a lockfile. And the specific version range is exactly the number that matters for triage — go read it at Anthropic’s GitHub Security Advisory rather than trusting a figure I’d be inventing. Treat anything you installed before 1 October as suspect until you’ve checked it there.
Check what you’re actually running and move:
Upgrading stops the next leak. It does nothing about tokens that already left. Which brings us to the part most teams will underweight.
Right now — the three things that actually broke
One: any token a vulnerable client could have presented is potentially burned. Access tokens are the obvious casualty, but the ugly one is refresh tokens. An access token expires in an hour; a refresh token can mint new ones for days or months. If your MCP client held refresh tokens — common for long-running agents so the user isn’t re-consenting hourly — assume the worst and rotate them, not just the access tokens.
Two: the trust list stopped being decorative. Most people’s MCP server list grew by accretion — a Slack server here, a GitHub server a teammate suggested there, a “handy” community server from a gist. The bug turns every entry you didn’t vet into a potential credential sink. The server you connected to once to try it out is exactly the shape of the attacker’s foothold.
Three: the “it’s just one SDK” comfort is thinner than it sounds. This specific bug is in the Python SDK. But audience binding and discovery-trust are design responsibilities every MCP implementation shares — the TypeScript SDK and every third-party client have to get the same thing right. One SDK got caught. That’s not evidence the others are clean; it’s a prompt to go check them against the same RFC 8707 question: does this client ever send a token to a server it wasn’t minted for?
This week — rotate like you mean it
Rotation is provider-specific, and here’s the honest framing: the reporting confirms the SDK could leak OAuth credentials, not which providers any given deployment had tokens for. So this is conditional on your setup. If your vulnerable MCP client held tokens from the providers below, here’s how to rotate them. Revocation per RFC 7009 is a reasonable first move for any OAuth provider that supports it:
If you held Google tokens: revoke at https://oauth2.googleapis.com/revoke, or pull the app’s access under myaccount.google.com → Security → Third-party access. For workspace-wide exposure, review and reset the OAuth client in the Google Cloud console.
If you held Microsoft Entra ID tokens: refresh tokens don’t honour a simple revoke, so invalidate sessions and rotate the secret:
If you held GitHub tokens: for an OAuth/GitHub App, rotate the client secret in the app settings and revoke existing authorizations; for PATs used as a shortcut, delete and reissue. If you held Okta tokens: revoke via POST /oauth2/v1/revoke or clear the user’s sessions, and rotate the app’s client secret. In every case: if a client secret was stored where the vulnerable SDK ran, treat the secret as exposed and rotate it too — not just the tokens it signed.
Then prune the server list to the ones you can name a reason for, and pin your SDK so a future pip install can’t quietly drag you back onto a vulnerable build.
The next dated beat is already on the calendar, and it isn’t Anthropic’s. It’s your refresh-token lifetime. Every hour you wait, the access tokens age out on their own — but the long-lived refresh tokens a stolen grant could still be exercising don’t. That clock is the one worth watching.
