The Same Bug, Two SDKs, One CVE: MCP’s TypeScript OAuth Client Gets CVE-2026-104850 and the Python Twin Gets Nothing
On 6 October 2026, GitHub assigned CVE-2026-104850 to the Model Context Protocol TypeScript SDK. The defect is stated plainly in the record: in affected versions “the SDK’s OAuth client support let the MCP server a client connected to decide which authorization server received the client’s OAuth credentials.” Credentials were never bound to the authorization server they belonged to. A malicious or compromised MCP server names its own authorization server in its protected resource metadata, and — with no user interaction — the client hands over the refresh_token and client_secret it stored from an earlier sign-in, or the configured secret or signed assertion of a bundled machine-to-machine provider.
CVSS 3.1 7.5, vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N, CWE-345 and CWE-522. Affected: @modelcontextprotocol/sdk 1.12.0 through 1.30.1 and @modelcontextprotocol/client 2.0.0 through 2.1.0.
If that description feels familiar, it should. It is, nearly word for word, the flaw Cycode disclosed in the Python SDK on 28 September. The two advisories were published two days apart and share a CVSS score, a vector, a CWE pair, and a summary line. One of them now exists in NVD and OSV. The other does not exist anywhere outside its own repository.
Sixteen months of npm releases
The affected range starts at @modelcontextprotocol/sdk 1.12.0, which the npm registry records as published on 22 May 2025. The fix, 1.31.0, was published 28 September 2026 at 18:59:36Z. Every 1.x release in between — the versions that carried MCP from a protocol proposal into the default integration layer for agent tooling — shipped an OAuth client that let the far end choose where credentials went.
The fix landed as pull request #2887, “fix(client): deprecate omitting expectedIssuer and apply the SEP-2352 issuer check consistently,” merged 28 September 2026 at 18:27:47Z — 21 files, 499 insertions, 77 deletions, thirty-two minutes before the release went out. The repository advisory GHSA-6qxp-vccf-f47h followed on 30 September; the CVE on 6 October; NVD ingested it the same day and still lists it Awaiting Analysis.
Upgrading does not fix it
This is the part that will quietly defeat most remediation tickets. Both SDKs ship the patch in a state where the patch does not, by itself, protect you. The TypeScript advisory is explicit about three cases where “upgrading to the patched version is not enough”:
- Bundled providers need a new argument.
ClientCredentialsProviderand the private-key and cross-app providers require anexpectedIssuervalue. “Without it they still use whichever authorization server the MCP server names.” Omitting it is deprecated and merely logs a warning; it becomes mandatory in 3.0. - Credentials already on disk stay unbound. Tokens and client information persisted by any 1.x version before 1.31.0 carry no
issuerfield, so “they still go to whichever authorization server is named at first use.” You must add the issuer to them or clear them and make users sign in again. - Custom providers must save the new field. An
OAuthClientProvideryou wrote yourself has to persist exactly whatsaveTokens()andsaveClientInformation()hand it,issuerincluded.
The Python advisory carries the same shape of caveat for issuer=, and notes that on 1.30.0 the omission warning is a DeprecationWarning — which Python hides by default. A warning nobody sees, guarding a control that is off until you turn it on.
And some of the exposure survives the fix entirely. The TypeScript advisory lists what is “not covered”: a fresh interactive sign-in still goes to whatever authorization server the MCP server names, direct calls to refreshAuthorization() and exchangeAuthorization() are unprotected, and 2.x with skipIssuerMetadataValidation: true opts straight back out. That last flag belongs on the same review checklist as the redirectPolicy: 'follow' escape hatch we flagged in last week’s redirect advisories — load-bearing security settings with innocuous names.
The database asymmetry, measured
We checked where each of these advisories is actually visible to tooling, by direct API lookup:
- GHSA-6qxp-vccf-f47h (TypeScript): resolves in the global GitHub Advisory Database (HTTP 200, carrying
cve_id: CVE-2026-104850), resolves in OSV under both its GHSA and CVE identifiers, and is in NVD. - GHSA-qx49-fqc8-xw99 (Python, published 28 September 2026 at 20:06:03Z, severity high, CVSS 7.5, CWE-345/CWE-522): 404 from the global GitHub Advisory Database. 404 from OSV. No CVE.
- GHSA-w4fh-qvv9-3v23 (Python, published 5 October 2026, medium, CVSS 3.1 6.8, CWE-863): 404 from both. No CVE.
Two of the three are readable only through the per-repository /security-advisories endpoint. A team that watches the CVE feed and runs a package auditor learns about the TypeScript client and never learns about the Python one — even though the Python one describes full account takeover, was the subject of published third-party research, and affects mcp from 1.9.1 through 1.29.1 and 2.0.0 through 2.1.1.
This is the second time in a week we have had to report the same distribution failure for this project, and the third mechanism by which MCP advisories fail to reach scanners. The engineering is not the weak point here. The write-ups are specific, honest about residual risk, and give per-case remediation. The pipeline that carries them to the tools your organisation actually queries is what keeps breaking.
The third advisory: an audience check that ships switched off
GHSA-w4fh-qvv9-3v23 deserves separate attention because it is the server-side mirror image, and because it is the clearest example of a fix that is not a fix until you configure it. In mcp 1.10.0 through 1.29.1 and 2.0.0 through 2.1.1, the SDK’s bearer authentication had no way to check that a token was issued for your server. Where one authorization server issues tokens for several services, a valid token for a different service was accepted by your MCP server.
The advisory’s own words on the remedy: “Upgrading alone changes nothing.” The patched releases add a validate_token_resource setting that is off unless you set it, and it only works if you also return the token’s audience from verify_token() as AccessToken.resource. Make one change without the other and every request gets a 401. The default flips to on in 3.0.
Audience confusion in a shared identity provider is precisely the failure mode that makes MCP servers attractive lateral-movement targets: the agent platform, the ticketing integration and the internal data gateway all sit behind one issuer, and until you set this flag, a token minted for the least sensitive of them opens the most sensitive.
What to do
- Upgrade clients and then configure them.
@modelcontextprotocol/sdkto 1.31.0 or later (1.32.1 is current, published 5 October 2026),@modelcontextprotocol/clientto 2.2.0 or later (2.3.1 current),mcpto 1.30.0 or 2.2.0 or later (2.3.0 current). Then passexpectedIssuer/issuer=to every machine-to-machine provider. An upgrade ticket that stops at the version bump has not closed this. - Clear or relabel stored OAuth state. Anything written by an affected version has no issuer binding. Clearing it costs users one sign-in; leaving it preserves the vulnerability on patched code.
- Turn the server audience check on deliberately. Set
validate_token_resource=Truewithresource_server_urland return the audience fromverify_token()in the same change. If your verifier already checksaud, set the flag toFalseexplicitly to record that decision. - Rotate if an untrusted server was ever in reach. The advisory is direct: rotate the client secret or signing key and revoke the tokens of any client that may have connected to an MCP server you do not control. Credential rotation is cheap next to reconstructing a year of connection history.
- Treat clients as inventoried software. All three advisories affect the client side. MCP clients live inside IDE extensions, internal agent harnesses and CI glue — the components least likely to appear in a dependency review. Grep your estate for
@modelcontextprotocol/sdkandmcpoutsidepackage.jsonfiles you already track. - Poll the repositories directly. On current evidence, the two
/security-advisoriesendpoints for the TypeScript and Python SDKs deliver more coverage of MCP SDK exposure than any CVE feed or SCA tool. That is two scheduled API calls.
Verification note: every figure above was read from primary sources. We retrieved GHSA-6qxp-vccf-f47h, GHSA-qx49-fqc8-xw99, GHSA-w4fh-qvv9-3v23 and GHSA-6prh-2h8m-c8cw through the GitHub REST per-repository advisory endpoints for modelcontextprotocol/typescript-sdk and modelcontextprotocol/python-sdk, and quote their published severities, CVSS vectors, CWE identifiers, affected ranges, publication timestamps and remediation text as returned. We retrieved the CVE-2026-104850 record from the CVE Program API (assigner GitHub_M, published 2026-10-06T16:08:21.514Z) and from NVD (published 2026-10-06T17:17:15.827, vulnStatus Awaiting Analysis). We confirmed the global-database and OSV status of each GHSA by direct lookup: GHSA-6qxp-vccf-f47h returns 200 from both, while GHSA-qx49-fqc8-xw99 and GHSA-w4fh-qvv9-3v23 return 404 from both. Release dates come from the npm registry (@modelcontextprotocol/sdk 1.12.0 on 2025-05-22, 1.31.0 on 2026-09-28, 1.32.1 on 2026-10-05) and PyPI (mcp 1.30.0 and 2.2.0 on 2026-09-07, 2.3.0 on 2026-10-02). Pull request #2887 metadata (merged 2026-09-28T18:27:47Z, 21 files, +499/−77) comes from the GitHub pulls API. We did not exploit, or attempt to exploit, any of these issues.
Sources:
- GHSA-6qxp-vccf-f47h / CVE-2026-104850 — OAuth client could send credentials to an authorization server chosen by the MCP server (published 30 September 2026, high, CVSS 3.1 7.5, CWE-345/CWE-522)
- NVD — CVE-2026-104850 (published 6 October 2026;
@modelcontextprotocol/sdk≥1.12.0 <1.31.0,@modelcontextprotocol/client≥2.0.0 <2.2.0) - GHSA-qx49-fqc8-xw99 — the Python twin (published 28 September 2026, high, CVSS 3.1 7.5;
mcp1.9.1–1.29.1 and 2.0.0a1–2.1.1; no CVE) - GHSA-w4fh-qvv9-3v23 — server bearer authentication accepts tokens issued for a different resource server (published 5 October 2026, medium, CVSS 3.1 6.8, CWE-863;
validate_token_resourceoff by default) - modelcontextprotocol/typescript-sdk #2887 — deprecate omitting
expectedIssuerand apply the SEP-2352 issuer check consistently (merged 28 September 2026) - modelcontextprotocol/typescript-sdk v2.2.0 release notes (28 September 2026) —
expectedIssuerupgrade guidance