Skip to content

feat: support OAuth 2.1 for streamable-http MCP servers#11874

Open
edelauna wants to merge 6 commits intoRooCodeInc:mainfrom
edelauna:feat/mcp-oauth-streamable-http
Open

feat: support OAuth 2.1 for streamable-http MCP servers#11874
edelauna wants to merge 6 commits intoRooCodeInc:mainfrom
edelauna:feat/mcp-oauth-streamable-http

Conversation

@edelauna
Copy link

@edelauna edelauna commented Mar 6, 2026

Related GitHub Issue

Closes: #8119

Description

Implements OAuth 2.1 support for streamable-http MCP servers per RFC 9728 (Protected Resource Metadata), RFC 8414 (Authorization Server Metadata), RFC 7591 (Dynamic Client Registration), and RFC 8707 (Resource Indicators).

Key design decisions:

  • Non-blocking OAuth: When client.connect() throws UnauthorizedError, the OAuth browser flow runs in the background via _completeOAuthFlow() so the extension (chat window, other servers) isn't blocked waiting for the user's browser session.

  • SDK workarounds: The MCP SDK's discoverOAuthMetadata() constructs the RFC 8414 well-known URL incorrectly for auth servers with path components (e.g., https://example.com/auth/public). It uses new URL("/.well-known/oauth-authorization-server", issuer) which is origin-relative and discards the path. This causes cascading failures: metadata discovery fails → dynamic client registration uses a wrong fallback URL → 404. See upstream issues:

We work around this by:

  1. Performing our own RFC 9728 + RFC 8414 discovery (utils/oauth.ts)
  2. Pre-registering the client using the correct registration_endpoint
  3. Correcting the authorization URL in redirectToAuthorization()
  4. Exchanging auth codes directly via exchangeCodeForTokens() using the correct token_endpoint

Token persistence: Tokens are stored in VS Code's SecretStorage via SecretStorageService, keyed by server host. Cached tokens are reused on reconnect without re-running the full OAuth flow.

  • Reconnect after auth: After token exchange, the stale connection is deleted and connectToServer() is called fresh — the SDK's transport can't be reused after UnauthorizedError because its internal _abortController is already set.

New files:

  • McpOAuthClientProvider.ts — Implements the SDK's OAuthClientProvider interface
  • SecretStorageService.ts — Thin wrapper around VS Code SecretStorage for OAuth tokens
  • utils/oauth.ts — RFC 8414-compliant metadata discovery (replaces SDK's broken implementation)
  • utils/callbackServer.ts — Local HTTP server for OAuth redirect callback with CSRF state validation

Test Procedure

Unit tests (42 tests across 4 files):
npx vitest run services/mcp/tests/McpOAuthClientProvider.spec.ts services/mcp/tests/SecretStorageService.spec.ts services/mcp/utils/tests/oauth.spec.ts services/mcp/utils/tests/callbackServer.spec.ts

  • McpOAuthClientProvider.spec.ts — 25 tests covering create, clientMetadata, token storage/expiry, PKCE, redirect, auth code, close
  • SecretStorageService.spec.ts — 7 tests covering CRUD, key isolation, malformed data
  • oauth.spec.ts — 8 tests covering RFC 8414 URL construction for path/no-path issuers, error handling
  • callbackServer.spec.ts — 2 tests covering callback handling and CSRF state validation

E2e test (apps/vscode-e2e/src/suite/mcp-oauth.test.ts):

  • Full OAuth flow test: mock server returns 401 → resource metadata → auth server metadata → dynamic registration → token exchange → authenticated MCP connection
  • Token reuse test: reconnect uses cached token without re-running OAuth

Manual testing:

		"temporal-kapa": {
			"type": "streamable-http",
			"url": "https://temporal.mcp.kapa.ai"
		},
		"Claude Code (figma)" : { // <------ seems like figma hardcodes specific client name
			"type": "streamable-http",
			"url": "https://mcp.figma.com/mcp"
		}
image

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue.
  • Scope: My changes are focused on the linked issue.
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes.
  • Documentation Impact: I have considered if my changes require documentation updates.
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Documentation Updates

  • No documentation updates are required.

Additional Notes

  • The SDK workarounds can be removed once the upstream issues are resolved (primarily typescript-sdk#545).
  • MCP_OAUTH_TEST_MODE=true env var enables headless e2e testing by making the callback server resolve immediately with a test auth code.
  • The client_name in OAuth client registration uses the MCP server config key (e.g., "figma") so providers see a meaningful name.

Get in Touch

0x7777777_

Interactively review PR in Roo Code Cloud

@edelauna edelauna changed the title Feat/mcp oauth streamable http feat: support OAuth 2.1 for streamable-http MCP servers Mar 6, 2026
@edelauna edelauna marked this pull request as ready for review March 6, 2026 14:45
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. Enhancement New feature or request labels Mar 6, 2026
@edelauna
Copy link
Author

edelauna commented Mar 7, 2026

Adding Screenshares of the sign-in and usage process.

MCP OAuth Flow:

oauth-connect-example-compressed.mp4

MCP Usage:

mcp-usage-example.mp4

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

Labels

Enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENHANCEMENT] Support MCP OAuth 2.1 for HTTP MCP servers (discovery + PKCE)

1 participant