The Server Names Its Own Audience: MCP OAuth Resource Confusion in mcp-go and ChatCLI

Two GitHub security advisories, published a month apart and reported by the same researcher, describe the same failure in two different MCP OAuth clients: the client let the server it was connecting to dictate which resource the authorization server should mint a token for. A malicious or compromised MCP server could therefore name a different resource on the same authorization server, receive a token audience-bound to that resource, and replay it there. The advisories are GHSA-xf6j-693p-wqxx for mark3labs/mcp-go (published 9 October 2026, fixed in v1.2.1) and GHSA-95vg-3r9c-2v5q for diillson/ChatCLI (published 11 October 2026, fixed in v1.215.2). NVD published the corresponding CVE records — CVE-2026-123272 and CVE-2026-123271 — on 5 November 2026, each scored CVSS 8.0 (High). Both credit Theodoros Lampropoulos (@GimmyDatBeeR), who reported the issues privately.

The missing check, in both clients

The underlying protocol rule is RFC 9728 section 3.3: when a client fetches Protected Resource Metadata from a server’s well-known path, it must verify that the declared resource actually identifies the resource it addressed. In mcp-go, that check was applied only to metadata advertised through a WWW-Authenticate resource_metadata parameter — not to metadata fetched from the well-known path. The unvalidated declared resource was then used as the RFC 8707 resource parameter at every step: dynamic client registration, the authorization request, the token exchange and token refresh. In ChatCLI, the /mcp login flow and the @mcp-login tool took the resource member of the server-supplied metadata and used it, again without the RFC 9728 identity check, as the resource indicator of the authorization request, the code exchange, the stored OAuth metadata and every refresh. After the user completed consent, ChatCLI sent the resulting token — audience-bound to the other resource — to the malicious server as its bearer credential, and the server could replay it against that other resource.

Both advisories frame exploitation the same honest way: this is not a drive-by. It requires a malicious or compromised MCP server, an authorization server that supports dynamic client registration and honours the requested resource indicator, a second resource on that authorization server that accepts the token, and — in ChatCLI’s case — the user completing consent. The CVSS vectors reflect that: network attack vector, high complexity, user interaction required, changed scope, high confidentiality and integrity impact (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N). The weakness in both records is CWE-346, origin validation error. That combination — difficult setup, severe payoff — is exactly the profile defenders underrate in internal tooling, where “only log in to servers you trust” is treated as a control rather than a hope.

ChatCLI’s advisory discloses more than the headline

The ChatCLI advisory is the more instructive of the two because the fix release bundled related hardening that reads like a catalogue of adjacent MCP transport sins. Beyond the resource-indicator fix — the canonical URL of the configured server is now the only accepted resource indicator, and metadata naming another resource is refused — the same release confines the SSE transport’s messages endpoint to the server’s origin, makes MCP transports refuse cross-origin and downgrading redirects, requires authorization-server metadata to name its issuer (RFC 8414 section 3.3) and use HTTPS endpoints, follows resource_metadata only on the server’s own origin, and caps discovery documents. The advisory also notes two additional weaknesses in affected versions worth knowing: the resource_metadata URL from a WWW-Authenticate challenge was followed on any origin, and discovery documents were read without a size limit. That last pair connects directly to themes we have covered before: the MoLing SSE session leak showed what unauthenticated, cross-origin-reachable MCP endpoints cost, and the mcp-go unbounded-body denial of service showed what uncapped reads cost. This advisory fixes all three shapes at once.

Why one reporter finding both matters

The same researcher privately reporting the identical flaw class in the most-used Go MCP SDK and in a separate CLI client, within days of each other, suggests the root cause is not a coding slip but a specification-reading habit: implementers wire up RFC 9728 discovery and RFC 8707 indicators, and the “verify the metadata describes the server you actually addressed” step is the one that gets skipped, because in testing the metadata always does. It is the same structural pattern as the AWS API MCP audience-validation bypass from earlier this month — the audience/token-recipient binding is where MCP authorization keeps failing. Any team running its own MCP OAuth client implementation should treat these two advisories as a checklist item: fetch your client’s well-known metadata handling and confirm the RFC 9728 section 3.3 binding exists on every discovery path, not just the challenge-response one.

What to actually do

  1. Upgrade mcp-go to v1.2.1 or later (affected: v0.48.0 through v1.2.0) and ChatCLI to v1.215.2 or later (affected: v1.159.0 through v1.215.1). The fix commits are public (mcp-go commit 90d247e, PR #1053 / issue #1052).
  2. After upgrading ChatCLI, if you ever logged in to an untrusted MCP server, run /mcp logout <server> and revoke the ChatCLI client at the authorization server — credentials stored by affected versions for another resource are never sent again, but revocation closes the window on tokens already issued.
  3. Audit any in-house MCP OAuth client for the same missing check: the declared resource from well-known metadata must be validated against the addressed server before it is used as an RFC 8707 resource indicator anywhere (registration, authorization, exchange, refresh).
  4. Treat “only connect to trusted MCP servers” as a workaround with an expiry date, per the advisory’s own framing — then enforce it with an allowlist, since consent dialogs do not protect users when the consent itself names the wrong audience.

Our verification was read-only. We read both GitHub security advisories first-hand in full (GHSA-xf6j-693p-wqxx for mcp-go, published 9 October 2026: affected ≥0.48.0 ≤1.2.0, patched 1.2.1, fix in commit 90d247e via PR #1053 / issue #1052; GHSA-95vg-3r9c-2v5q for ChatCLI, published 11 October 2026: affected ≥1.159.0 ≤1.215.1, patched 1.215.2, SSE/redirect/issuer hardening in the same release), the full NVD JSON records for CVE-2026-123272 (Deferred) and CVE-2026-123271 (Received, both published 5 November 2026, CVSS 8.0, CWE-346, VulnCheck-sourced descriptions), and both release tags. We checked the site search index for 123271/123272, mcp-go OAuth, and ChatCLI coverage and found no overlap — the prior mcp-go post covers a different flaw (unbounded-body DoS, CVE-2026-108859). We ran no client, registered no OAuth application, replayed no token, and contacted no vendor. Brave Search was used for discovery only.

Sources: