Skip to content

fix(mcp): show "needs auth" instead of "failed" for OAuth servers#15986

Closed
mattzcarey wants to merge 1 commit intoanomalyco:devfrom
mattzcarey:fix/mcp-oauth-state-needs-auth
Closed

fix(mcp): show "needs auth" instead of "failed" for OAuth servers#15986
mattzcarey wants to merge 1 commit intoanomalyco:devfrom
mattzcarey:fix/mcp-oauth-state-needs-auth

Conversation

@mattzcarey
Copy link

Summary

  • When connecting to an OAuth-protected MCP server for the first time, McpOAuthProvider.state() throws a generic Error("No OAuth state saved...") because no state has been saved yet
  • This error is not an UnauthorizedError, so the connection handler classifies the server as "failed" instead of "needs_auth"
  • Users see a cryptic "failed" status instead of the helpful "Needs authentication (run: opencode mcp auth <server>)" toast notification
  • Fix state() to generate and persist a new 32-byte random hex state (matching the existing pattern in startAuth()) instead of throwing

Root cause trace

  1. StreamableHTTPClientTransport.send() gets 401 → calls auth(provider, ...)
  2. MCP SDK's authInternal() calls provider.state() to check for existing state
  3. state() throws generic Error → propagates back to connection handler
  4. error instanceof UnauthorizedError is false → falls through to status: "failed"

With this fix

  1. state() generates a new random state → auth flow proceeds normally
  2. SDK returns 'REDIRECT' → transport throws UnauthorizedError
  3. Connection handler catches UnauthorizedError → sets status: "needs_auth"
  4. User sees toast: "Server requires authentication. Run: opencode mcp auth <server>"

Test plan

  • state() generates and persists a 64-char hex string when no state exists
  • state() returns existing saved state when one exists
  • First unauthenticated connect results in needs_auth status (not failed)
  • All existing MCP tests pass (9/9 across 3 files)

When connecting to an OAuth-protected MCP server for the first time,
McpOAuthProvider.state() threw a generic Error because no OAuth state
had been saved yet. This error bypassed the UnauthorizedError check in
the connection handler, causing the UI to show "failed" instead of
"Needs authentication (run: opencode mcp auth <server>)".

Fix state() to generate and persist a new 32-byte random hex state
(matching the existing pattern in startAuth) instead of throwing. This
allows the MCP SDK's auth flow to complete normally, surface
UnauthorizedError, and trigger the correct needs_auth status with a
helpful toast notification.
@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

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 the needs:compliance This means the issue will auto-close after 2 hours. label Mar 4, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

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

Based on my search, I found one potentially related PR:

Related PR:

The other results (#11477, #11925) are older feature PRs for adding OAuth authentication UI and are less likely to be direct duplicates of this specific bug fix.

@mattzcarey
Copy link
Author

Closing as duplicate of #15547 which addresses the same root cause (McpOAuthProvider.state() throwing instead of generating a state on first connect).

@mattzcarey mattzcarey closed this Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs:compliance This means the issue will auto-close after 2 hours. needs:issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant