Skip to content

fix(mcp): trigger OAuth dance inside startAuth to get redirect URL#28740

Open
achetronic wants to merge 1 commit into
anomalyco:devfrom
achetronic:fix/mcp-oauth-startauth-race
Open

fix(mcp): trigger OAuth dance inside startAuth to get redirect URL#28740
achetronic wants to merge 1 commit into
anomalyco:devfrom
achetronic:fix/mcp-oauth-startauth-race

Conversation

@achetronic
Copy link
Copy Markdown

@achetronic achetronic commented May 22, 2026

Issue for this PR

Closes #28741

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

opencode mcp auth <server> against any OAuth-protected MCP server fails right away with Failed to get tools. The browser never opens, the callback server stays idle. Reproduced on v1.15.6 with a Keycloak backend using dynamic client registration.

The MCP SDK's connect() resolves before it actually starts the OAuth flow — that only kicks in on the first real request. So startAuth() thinks everything is fine and returns authorizationUrl="", and authenticate() ends up calling defs()listTools() on a client that isn't authenticated yet. By the time the SDK gets around to firing onRedirect and filling capturedUrl, it's too late: defs() has already swallowed the UnauthorizedError and reported failure.

This PR makes startAuth() itself call listTools() right after connect(). That forces the SDK to do the OAuth dance there and then, so capturedUrl is ready by the time we return. authenticate() sees a real authorization URL, opens the browser, and waits for the callback like it's supposed to.

How did you verify your code works?

Ran the patched build end-to-end against my own Keycloak-backed MCP server:

  • rm -f ~/.local/share/opencode/mcp-auth.json to start clean
  • bun run --conditions=browser ./src/index.ts --log-level DEBUG --print-logs mcp auth myserver
  • Browser opens, I log in, Keycloak redirects to http://127.0.0.1:19876/mcp/oauth/callback?code=..., opencode picks up the code, exchanges it for tokens and stores them.

Also confirmed bun run typecheck passes clean.

Screenshots / recordings

N/A (CLI flow, no UI changes).

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

The MCP SDK's connect() resolves before initiating OAuth, which made
startAuth() return with authorizationUrl="" and authenticate() then
called defs() on an unauthenticated client, failing with "Failed to get
tools" before the browser was ever opened.

Force a listTools() right after connect() so the SDK runs the auth
dance and onRedirect populates capturedUrl in time.
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions Bot added needs:issue needs:compliance This means the issue will auto-close after 2 hours. labels May 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on the search results, I found a potentially related PR:

Related PR:

  • fix: force OAuth flow when server accepts unauthenticated connections #26236 - fix: force OAuth flow when server accepts unauthenticated connections
    • This PR appears to address a similar issue where OAuth flows need to be triggered even when servers accept unauthenticated connections. Your PR is specifically about triggering the OAuth dance inside startAuth() to get the redirect URL before authentication fails.

Additionally, there are other OAuth/MCP-related PRs in the codebase:

However, #26236 is the most similar in addressing the core issue of forcing OAuth flow at the right time. If that PR was merged and this issue persists, it may be a more specific edge case (with Keycloak dynamic client registration).

@github-actions github-actions Bot removed needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels May 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP OAuth flow fails before opening the browser

1 participant