Build1 publisher2 min readPublished
MCP's Python SDK fix needs a hard-coded OAuth issuer to protect machine-to-machine clients
MCP Python SDK maintainers rated a flaw that let a connected server choose where OAuth secrets were sent High, at 7.5. For its two machine-to-machine providers, upgrading changes nothing until the client names the issuer it expects.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened
- Cycode, which reported the flaw, showed a hostile server answering discovery with a 404 so the SDK falls back to asking that server, leaving the issuer check nothing to compare.
- Through that path a hostile server could collect the client_secret, the authorization code and the PKCE code_verifier.
- Fixed releases are mcp 1.30.0 and 2.2.0, covering both the 1.x and 2.x lines of the package.
- WorkOS grouped the flaw with a Rust rmcp bug that skipped the resource-field check and a LiteLLM bug that trusted a forged Authorization header, all within 30 days.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Each machine-to-machine client now needs an owner who knows its expected issuer and writes it into configuration, a code change a dependency bump cannot make on its own.
- exposure A person watching the login flow has nothing to catch, so detection has to sit in the client before any token request is built.
- constraint Pinning only works for providers known in advance; agents reaching servers nobody chose need an issuer allowlist or a client that withholds the secret.
Two documents decide where a client's secret goes. Protected resource metadata, from RFC 9728, is the MCP server describing itself and listing the authorization servers that handle its logins [10]. Authorization server metadata, from RFC 8414, is the login provider naming its issuer and its token endpoint [10]. The token endpoint is where the secret is sent [11]. "Whoever writes that URL decides who gets your credentials," the author of a dev.to post on the advisory wrote [12].
In the fallback Cycode described, one server ends up writing both answers [4]. The opening move is a 404, a response every web server already knows how to send [4]. Meanwhile the user sees a real login page at the real URL [5]. Watching the browser catches nothing. The check has to happen inside the client.
A pinned issuer gives the client something to compare against when discovery fails, and the server cannot edit it. The advisory, GHSA-qx49-fqc8-xw99 [1], is direct about the machine-to-machine providers, as quoted in the post: "upgrading changes nothing until you also pass issuer=" [7]. Upgrading gets you the fixed code [6]. On those providers, the pin is what puts the fix to use [7]. I think hard-coding the issuer is the right default for any client whose login provider is known at deploy time.
That default stops working where the post says agents now operate: against servers nobody on the team picked by hand [16]. A team cannot pin an issuer it has never seen. For those connections, I'd expect the workable options to be an allowlist of issuers or a client that refuses to send the secret.
The post includes a TypeScript model. It has two clients, one honest server and three hostile ones: one returns 404 on discovery, one names its own server, and one claims the real resource [13]. The author calls it "my small model of the bug class, not the SDK's code" [14]. So it is a result on the author's own workload, a mock network the author wrote. To carry over, a real SDK has to check the pin on every discovery path before a token request leaves. That includes the fallback path the Python SDK got wrong [4].
The two bugs WorkOS set beside this one involve different checks [8]. Pinning the issuer covers neither the resource field rmcp skipped nor the Authorization header LiteLLM trusted [8]. WorkOS summarised the three as "code accepted authentication input that nobody verified" [9].
What to watch
- Whether the Python SDK makes issuer= mandatory for its machine-to-machine providers, so that an upgrade alone closes the hole.
- Whether other MCP SDKs, including TypeScript, fall back to asking the MCP server when authorization metadata discovery returns a 404.
- A published fix and affected-version range for rmcp's resource-field bug, CVE-2026-63127.