Skip to content

fix(mcp): use stored OAuth credentials for auto-detect HTTP servers#3417

Open
amitksingh1490 wants to merge 1 commit into
mainfrom
fix/mcp-oauth-autodetect
Open

fix(mcp): use stored OAuth credentials for auto-detect HTTP servers#3417
amitksingh1490 wants to merge 1 commit into
mainfrom
fix/mcp-oauth-autodetect

Conversation

@amitksingh1490
Copy link
Copy Markdown
Contributor

Summary

Fix auto-detect HTTP MCP servers failing to connect even after a successful mcp login, by using stored OAuth credentials directly and no longer masking authentication errors behind the SSE fallback.

Context

After running forge mcp login <name> against an OAuth-protected HTTP MCP server (e.g. Vanta at https://mcp.vanta.com/mcp), the server still failed to connect and landed in the failed-servers map — despite valid credentials being stored in ~/.forge/.mcp-credentials.json.

Root cause, traced through the connection flow in crates/forge_infra/src/mcp_client.rs:

  1. An auto-detect HTTP server (no explicit oauth setting) was probed unauthenticated first via create_standard_http_connection.
  2. The StreamableHTTP transport correctly received 401 Unauthorized (with a WWW-Authenticate header).
  3. The code then fell back to the SSE transport, which issues a GET. Many MCP servers (including Vanta) reject that with 405 Method Not Allowed.
  4. The 405 error masked the original 401, so the subsequent error-string check matched none of the auth keywords.
  5. As a result the stored-credentials / OAuth path was never reached, and the valid token was never used.

Changes

  • Use stored credentials first: In the auto-detect HTTP branch, if credentials already exist for the server URL (i.e. the user has run mcp login), authenticate directly with them instead of doing an unauthenticated probe. Added has_stored_credentials helper.
  • Stop masking auth errors in the SSE fallback: create_standard_http_connection no longer falls back to SSE when the failure is an authentication error — it surfaces the 401/auth error so auto-detection can trigger the OAuth path.
  • Robust auth-error detection: Replaced the brittle inline string match with is_auth_error, which walks the full error source chain looking for rmcp's Auth required / WWW-Authenticate markers as well as 401 / unauthorized / oauth.
  • Added unit tests for is_auth_error.

Key Implementation Details

The change is generic across all HTTP MCP servers — there is nothing server-specific in it. It only affects the McpServerConfig::Http match arm; oauth: false (disabled), explicitly-configured oauth: {...}, and Stdio servers retain their existing behavior. The stored-credentials-first path is safe because credentials are only ever persisted via an explicit mcp login.

rmcp's worker-based transport can surface a 401 as a generic transport/connection error rather than a clean AuthRequired, so is_auth_error inspects the entire error source chain instead of relying on the top-level message.

Use Cases

  • Connect to OAuth-protected HTTP MCP servers (Vanta, and any server using the same 405-on-SSE pattern) after a single mcp login.
  • More reliable OAuth auto-detection for HTTP servers whose unauthenticated SSE probe returns a non-auth status.

Testing

# Unit tests for the new auth-error detection
cargo test -p forge_infra mcp_client

# Lint
cargo clippy -p forge_infra

Manual verification:

forge mcp import '{ "mcpServers": { "vanta": { "url": "https://mcp.vanta.com/mcp" } } }'
forge mcp login vanta
forge mcp reload
# vanta tools should now be available instead of appearing as a failed server

Links

  • File: crates/forge_infra/src/mcp_client.rs

Co-Authored-By: ForgeCode <noreply@forgecode.dev>
@github-actions github-actions Bot added the type: fix Iterations on existing features or infrastructure. label May 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: fix Iterations on existing features or infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant