Skip to content

fix(mcp): classify auth-validation failures before logging so expected rejections stop masking outages - #1669

Draft
Dhravya wants to merge 1 commit into
mainfrom
d/polylane-autofix-fix-mcp-classify-auth-validation-failures-before-logging-so-expected-rejections-eb01
Draft

Dhravya wants to merge 1 commit into
mainfrom
d/polylane-autofix-fix-mcp-classify-auth-validation-failures-before-logging-so-expected-rejections-eb01

Conversation

@Dhravya

@Dhravya Dhravya commented Sep 14, 2026

Copy link
Copy Markdown
Member

The MCP authentication layer treated two very different situations as if they were the same. Whenever a request arrived with a token it could not accept, the service raised a loud, top-severity alarm — regardless of whether the token was simply invalid (the normal, expected outcome of internet scanners and misconfigured clients probing a public endpoint) or whether the authentication backend itself was actually struggling. Because the public MCP endpoint receives a constant stream of bad tokens, this generated roughly 20,000 top-severity alarms every six hours as steady background noise.

The consequence: anyone watching for real problems could not find them. A genuine authentication-backend outage would produce the exact same kind of alarm as the endless stream of expected rejections, so it would be buried in the noise and indistinguishable from normal traffic. That noise also dominated the account-wide error rollup, drowning out signal from other services. Merging this change makes routine token rejections quiet and keeps only genuine backend trouble loud, so a real outage stands out immediately.

Before / After

Before: Operators watching the monitoring surfaces saw a relentless flood of top-severity errors coming from the MCP service — thousands every hour — driven entirely by ordinary invalid-token traffic hitting the public endpoint. Because every rejected token, whether just a bad credential or a symptom of a failing auth backend, was reported the same loud way, the two cases were impossible to tell apart. The account-wide error rollup was dominated by this baseline, masking signal from other services. If the authentication backend actually went down, that event would look identical to the everyday noise, so nobody could distinguish a real incident from routine scanner traffic.

After: Routine, expected token rejections no longer raise top-severity alarms; they are recorded quietly at debug level, so they drop out of the error views and stop dominating the account rollup. Genuine authentication-backend trouble — a session endpoint returning 5xx, an unreachable backend, or a JWKS fetch failure — is still reported loudly, now under its own clearly named category. Operators see a near-empty error stream during normal operation, and a real backend outage stands out immediately as a distinct, visible signal instead of hiding among thousands of expected rejections. The rejection and retry behavior clients experience is unchanged.

What changed

Both validateApiKey and validateOAuthToken in apps/mcp/src/server/auth/index.ts previously logged every caught failure with console.error before classifying it. The catch blocks now classify first using the existing transientAuthErrorFor(error) helper:

  • Transient backend failures (non-401/403 session status, unreachable backend, JWKS/timeout errors) log at ERROR under a new distinct template "Auth backend transient failure:", then throw TransientAuthError as before.
  • Expected permanent rejections (invalid/revoked key, wrong audience, expired token, missing claims, session 401/403) log at console.debug and return null as before.

No return values, status codes, or imports changed. The 401 invalid_token and 503 Retry-After: 5 contract in apps/mcp/src/server/index.ts is untouched.

Tests

apps/mcp/src/server/auth/index.test.ts was extended: the two transient cases (session 500, timeout) now assert an ERROR-level call under the "Auth backend transient failure:" template, and the rejection cases (wrong audience, expired token, session 401) now assert no ERROR-level output. Existing console.error mocks in rejection tests were switched to console.debug to suppress noise.

Checks run

  • cd apps/mcp && bun run test:unit (vitest) — passed, 23 tests across 4 files.
  • cd apps/mcp && bun run check-types (tsc for both tsconfigs) — passed.
  • bunx biome check apps/mcp/src/server/auth/index.ts apps/mcp/src/server/auth/index.test.ts — passed, no issues on the touched files.
  • bun run format-lint (Biome, repo root) — reports pre-existing errors/warnings in unrelated files (e.g. a test-runner script with console.log/forEach diagnostics); none originate from the files changed here, confirmed by the scoped Biome run above.

Assumptions

  • No repository configuration values or defaults were changed, so there were no existing assertions on a current value to reconcile.
  • The distinct ERROR template text "Auth backend transient failure:" was chosen to keep real backend outages queryable and separable from the rejection baseline, matching the plan.
Open in Web Open in Cursor 

Fixes: supermemory-mcp auth rejections: ~20k ERROR rows/6h invisible to metrics and Sentry

What caused this

Affected: acc_ecd28c165001ayzji6subtuo · severity low

A check caught it, an investigation traced it, an autofix wrote the change. Each step links to its record.

Step Where to look
1. An issue from an exploration was opened Open the investigation
2. A fix run traced the cause Fix run thread
3. An autofix wrote the change, and it succeeded Autofix fix_0a1122…
4. This pull request opened it for review this PR

View thread

This pull request originated from a Polylane autofix. Polylane investigated the issue and delegated the fix to Cursor, which authored this pull request.

Split the validateApiKey and validateOAuthToken catch blocks so
transient auth-backend failures log at ERROR under a distinct
'Auth backend transient failure:' template while expected rejections
log at debug. Return values and 401/503 status contract are unchanged.

Co-authored-by: Dhravya Shah <dhravya@supermemory.com>
@polylane polylane Bot added the polylane label Sep 14, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
supermemory-mcp 59e3fb4 Sep 14 2026, 06:03 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
supermemory-app 59e3fb4 Commit Preview URL

Branch Preview URL
Sep 14 2026, 06:04 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants