Recognize mcp-go authorization-required sentinels as auth#5225
Open
Recognize mcp-go authorization-required sentinels as auth#5225
Conversation
Add five test sub-cases that exercise the mcp-go authorization-required sentinel chain. These fail on main: wrapBackendError does not recognize the new transport.ErrAuthorizationRequired or ErrOAuthAuthorizationRequired sentinels, so the error falls through to ErrBackendUnavailable and the health monitor categorizes the probe as BackendUnhealthy. Cases added: - TestWrapBackendError: ErrAuthorizationRequired, the wrapped production chain (transport.Error + AuthorizationRequiredError), and ErrOAuthAuthorizationRequired must each map to ErrAuthenticationFailed. - TestHealthChecker_CheckHealth_AuthErrorsCategorizesAsUnauthenticated: the raw mcp-go chain reaching the checker must classify as BackendUnauthenticated when no outgoing auth strategy is configured. - TestHealthChecker_CheckHealth_AuthErrorWithOutgoingAuthIsHealthy: upstream_inject + the raw mcp-go chain (North's exact reproducer) must classify as BackendHealthy with nil error so the monitor records a successful check and the WARN spam stops.
mcp-go v0.49.0 introduced transport.ErrAuthorizationRequired (and the companion transport.ErrOAuthAuthorizationRequired from the OAuth-handler path) as sentinels for 401 responses. wrapBackendError did not check for either, so the error fell through to ErrBackendUnavailable and the vMCP health monitor categorized auth-protected backends (e.g. GitHub Copilot behind upstreamInject) as BackendUnhealthy on every 30s probe, producing WARN log spam and an ever-climbing consecutive_failures counter. Two-part fix: 1. wrapBackendError now matches both new sentinels via errors.Is and wraps them as ErrAuthenticationFailed. This is the principled fix for the production code path through BackendClient. 2. IsAuthenticationError now matches the substring "authorization required" as a fallback for paths where the typed sentinel is not preserved through the error chain (e.g. tests using a mock BackendClient, or future error-chain breakage in mcp-go's Unwrap). With both in place, the existing auth-aware classification logic (#4935) engages: backends with an outgoing auth strategy configured report BackendHealthy on auth challenge (the expected response to a no-credential probe), and misconfigured backends correctly surface BackendUnauthenticated. Closes #5223
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5225 +/- ##
=======================================
Coverage 67.87% 67.88%
=======================================
Files 610 610
Lines 62383 62389 +6
=======================================
+ Hits 42340 42350 +10
+ Misses 16871 16864 -7
- Partials 3172 3175 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
R3 + R4 (test matrix gap on outgoing-auth strategies):
TestHealthChecker_CheckHealth_AuthErrorWithOutgoingAuthIsHealthy
previously covered the new mcp-go authorization-required chain only
against upstream_inject. Add token_exchange and header_injection rows
so the matrix pins all three strategies that route through the same
authErrorStatus branch.
R5 (negative test for substring over-classification):
TestIsAuthenticationError gains a row for a benign validation message
containing the words 'authorization' and 'required' separated by
punctuation ("field 'authorization' required"). This pins that the
current matcher's contiguous "authorization required" substring does
not over-classify, so a future loosening of the matcher (e.g.
whitespace-tolerant) would not silently regress.
R7 (test names should describe assertions, not tickets):
Renamed the issue-numbered cases to describe the chain shape
("transport.Error wrapping AuthorizationRequiredError"). The issue
reference remains in the case comment.
Note on R4 (StrategyTypeUnauthenticated against the new chain):
TestCategorizeError already pins the misconfig branch at the unit
level for sentinel-shaped errors. Extending the integration table
would require restructuring it to vary auth_config per row, which is
out of scope for this PR; the unit-level coverage is sufficient.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #5223
Summary
The vMCP gateway emits a
backend remains unhealthyWARN every 30s forupstreamInjectbackends that are functionally fine. North hit 2780+consecutive "failures" on
github-copilot-entry— pure log noise, sincethe backend is reachable and only fails because the background probe
carries no user credentials.
Root cause: mcp-go v0.49.0 added two sentinels for 401 responses
(
transport.ErrAuthorizationRequiredandtransport.ErrOAuthAuthorizationRequired).wrapBackendErrordid notcheck for either, so the error fell through to
ErrBackendUnavailableand the health monitor categorized the probe as
BackendUnhealthy. Theauth-aware fix from #4935 ("treat 401/403 from auth-configured backends
as healthy") therefore never engaged.
errors.Ischecks for both new mcp-go sentinels inwrapBackendErrorso production probes throughBackendClientarecorrectly mapped to
ErrAuthenticationFailed."authorization required"toIsAuthenticationErroras afallback for paths where the typed sentinel is not preserved through
the error chain (covers the integration-test path where a mock
bypasses
wrapBackendError, and any future Unwrap breakage).With both in place, North’s
upstream_injectbackend reportsBackendHealthy(probe gets anilerror), the consecutive_failurescounter stays at 0, and the WARN spam stops. Misconfigured backends
(no outgoing auth strategy) continue to surface as
BackendUnauthenticatedso operators see the diagnostic signal.Type of change
Test plan
task test)task lint-fix)Five new test sub-cases were added in a separate red commit before the
fix to lock in the regression:
TestWrapBackendError: three sub-cases —ErrAuthorizationRequired,the wrapped production chain
(
transport.Error→*AuthorizationRequiredError), andErrOAuthAuthorizationRequired— each must map toErrAuthenticationFailed.TestHealthChecker_CheckHealth_AuthErrorsCategorizesAsUnauthenticated:the raw mcp-go chain reaching the checker classifies as
BackendUnauthenticatedwhen no outgoing auth strategy is configured.TestHealthChecker_CheckHealth_AuthErrorWithOutgoingAuthIsHealthy:upstream_inject+ the raw mcp-go chain (Norths exact reproducer)classifies as
BackendHealthywithnilerror so the monitorrecords a successful check.
All five sub-cases were red on
main(verified before commit 1) andgreen after the fix.
Does this introduce a user-facing change?
WARN spam (
backend remains unhealthy) forupstreamInjectbackendsthat respond with 401/
authorization requiredto no-credential probeswill stop. Backends previously misclassified as
BackendUnhealthyunder these conditions will now be reported as
BackendHealthy, andthe consecutive_failures counter will reset. No CRD or API changes.
Generated with Claude Code