typescript-sdk/.changeset/probe-auth-status-not-era-evidence.md

1.7 KiB

@modelcontextprotocol/client
patch

The version-negotiation probe no longer misclassifies auth-protected or failing servers as legacy. Auth status is never era evidence: a 401 or 403 rejection of the server/discover probe now surfaces as a typed authorization failure — an SdkHttpError with code ClientHttpAuthentication (401) or ClientHttpForbidden (403), carrying the HTTP status, reason phrase, and response text — instead of triggering the legacy initialize fallback (which put a doomed initialize on the wire) or, under pin mode, the false "server did not offer pinned protocol version" diagnostic. The codes are deliberately not EraNegotiationFailed, so era-recovery flows keyed on that code cannot persist a verdict for an unauthorized exchange. A 5xx rejecting the probe is a server failure and now also rejects typed (SdkHttpError(EraNegotiationFailed)) instead of demoting a mid-deploy modern server to legacy — the legacy fallback now fires only on the 4xx shapes the spec licenses.

With an authProvider, a 401 (and a 403 insufficient_scope challenge) runs the transport's auth flow first — a plain 403 rejects the same as without a provider — and whatever escapes it propagates unchanged, identity intact: the HTTP transports stamp errors at their auth seams (the token() read, onUnauthorized including custom callbacks, the 403 step-up flow, and their own auth-failure constructions), so UnauthorizedError for finishAuth(), the flow's typed failures (OAuthError, InsufficientScopeError, the 401-after-re-authentication diagnostic), and even an untyped TypeError thrown inside the flow all reach the caller as thrown — never rewrapped, never consumed by the probe's browser CORS heuristic as legacy-era evidence.