fix(mcp/oauth): failed fast when callback port is busy instead of advertising a random one

When the MCP OAuth callback server's preferred port (default 3000) was unavailable, `OAuthCallbackFlow.#startCallbackServer` silently bound a random port and forwarded the mismatched `redirect_uri` to the authorization server. Providers that validate redirect URIs against a registered callback (e.g. Atlassian) returned an opaque HTTP 500, leaving the local flow waiting for a callback that never arrived until the 5-minute timeout fired.

Added `OAuthCallbackFlowOptions.allowPortFallback` (default `true`, preserving every existing AI-provider flow) and threaded `allowPortFallback: false` through `MCPOAuthFlow`'s `resolveCallbackOptions`. With fallback disabled, login now throws a `ConfigurationError` that names the busy port and the remediation (free the port, or set `oauth.callbackPort`/`oauth.redirectUri` in `mcp.json`) before opening the browser. The existing `oauth.redirectUri`-strict path is reworded along the same lines so callers see one consistent message family.

Fixes #3887
This commit is contained in:
roboomp
2026-06-30 10:05:24 +00:00
parent 63ff563eca
commit d2c767507b
6 changed files with 205 additions and 5 deletions
@@ -161,6 +161,13 @@ function resolveCallbackOptions(config: MCPOAuthConfig): OAuthCallbackFlowOption
callbackPath: resolveCallbackPath(config.callbackPath, redirectUri),
callbackHostname: resolveCallbackHostname(redirectUri),
redirectUri,
// MCP providers (Atlassian, GitHub MCP, etc.) generally validate the
// redirect URI against a registered callback. Silently advertising a
// random port when the preferred one is busy means the authorization
// server returns an opaque 500 and the browser callback never fires,
// leaving the user staring at the 5-minute timeout. Fail fast so the
// caller can show an actionable error before opening the browser.
allowPortFallback: false,
};
}