openclaw - ✅(Solved) Fix [Feature]: OAuth authentication for remote MCP servers [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#61611Fetched 2026-04-08 02:56:52
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
1
Author
Participants
Timeline (top)
referenced ×2cross-referenced ×1labeled ×1

Wire up the MCP SDK's existing authProvider support on StreamableHTTPClientTransport and SSEClientTransport so openclaw can authenticate to OAuth-protected remote MCP servers.

Root Cause

  {"url": "https://mcp.example.com", "headers": {"Authorization": "Bearer <token>"}}

This breaks down for OAuth-protected servers because:

  • Access tokens are short-lived (minutes, not hours)
  • There's no way to trigger an OAuth login flow
  • There's no token refresh — when the token expires, the server silently fails to connect
  • Users must manually obtain and paste new tokens repeatedly

Fix Action

Fix / Workaround

  • Affected users: Anyone connecting to OAuth-protected remote MCP servers (the standard for hosted MCP services per the MCP Authorization spec)

  • Severity: Blocks workflow — these servers are effectively unusable today

  • Frequency: Every connection to an OAuth-protected server

  • Consequence: Users either can't use these servers at all, or resort to fragile workarounds (manually pasting short-lived tokens, building custom extensions)

  • Community demand: #29053 (open, 14+ comments), #8188 (closed, pointed to MCPorter workaround with ~2.4s
    cold-start per invocation), #4240, #4834

  • MCP Authorization spec: https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization

  • MCP SDK built-in support: StreamableHTTPClientTransport and SSEClientTransport both accept authProvider:
    OAuthClientProvider — OpenClaw just isn't passing it

  • Other MCP clients (Claude Code, Cursor) already support OAuth for remote MCP servers

PR fix notes

PR #62134: feat: OAuth authentication for remote MCP servers

Description (problem / solution / changelog)

Summary

  • Problem: OAuth-protected remote MCP servers are unusable — the only auth mechanism is static headers, which break when tokens are short-lived (minutes)
  • Why it matters: The MCP Authorization spec defines OAuth as the standard auth flow. Claude Code, Cursor, and other MCP clients support it. OpenClaw does not, blocking users from connecting to a growing
    number of OAuth-protected MCP services.
  • What changed: Pass the SDK's existing authProvider option to StreamableHTTPClientTransport and SSEClientTransport when auth: "oauth" is configured. New OAuthClientProvider adapter handles browser redirect,
    local callback server, and token persistence. UnauthorizedError caught during connection triggers the flow automatically.
  • What did NOT change (scope boundary): No changes to existing server configurations. No new dependencies. No changes to stdio transport. No CLI commands added (follow-up). No device flow / headless auth (would require MCP spec extension).

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Related #61611
  • Related #29053
  • Related #8188
  • Related #4240
  • Related #4834
  • This PR fixes a bug or regression

Root Cause (if applicable)

N/A — new feature.

Regression Test Plan (if applicable)

N/A — new feature. Happy to add tests based on maintainer feedback on the approach. The testable surface:

  • createMcpOAuthProvider returns a valid OAuthClientProvider that persists/loads state
  • waitForOAuthCallback resolves with auth code on successful callback, rejects on timeout/error
  • resolveMcpTransport passes authProvider only when auth: "oauth" is set
  • UnauthorizedError triggers the OAuth flow; other errors propagate normally

User-visible / Behavior Changes

  • New config option: auth: "oauth" on remote MCP server configs
  • When set, browser opens automatically on first connection for OAuth login
  • Tokens persist in ~/.openclaw/mcp-auth/<server>.json and auto-refresh
  • When no browser is available, authorization URL is logged for manual use
  • Servers without auth: "oauth" are completely unaffected

Diagram (if applicable)

Before:
[connect to OAuth MCP server] -> [401] -> [server skipped with warning]

After: [connect to OAuth MCP server] -> [401 / UnauthorizedError]
-> [SDK calls redirectToAuthorization] -> [browser opens] -> [user logs in] -> [redirect to 127.0.0.1:8093/mcp/callback]
-> [finishAuth(code)] -> [SDK exchanges code for tokens]
-> [retry connection] -> [success, tokens auto-refresh]

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? Yes
  • New/changed network calls? No (the SDK already handles OAuth HTTP calls; we just enable it)
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • Explanation: OAuth tokens (access + refresh) are persisted as plain JSON in ~/.openclaw/mcp-auth/. This is consistent with how OpenClaw stores provider OAuth tokens in auth-profiles.json. A local HTTP server listens on 127.0.0.1:8093 during the auth flow only, accepting the OAuth callback redirect.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22+
  • Model/provider: N/A (MCP transport layer, provider-independent)
  • Integration/channel: Remote MCP servers with OAuth
  • Relevant config: openclaw mcp set myserver '{"url": "https://mcp.example.com", "auth": "oauth"}'

Steps

  1. Configure a remote MCP server with auth: "oauth"
  2. Start an OpenClaw session that uses the configured server
  3. Browser should open for authorization
  4. Complete login, browser redirects to localhost callback
  5. Connection completes, tools are available

Expected

  • Browser opens, user authorizes, connection succeeds, tokens persist for future sessions

Actual

  • Not yet tested against a live OAuth MCP server (prototype stage)

Evidence

  • pnpm build passes for our files (pre-existing type errors in provider-error-patterns.ts and attempt.ts are unrelated to this change)
  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: Build compiles, import boundaries respected, config threading verified across all layers
  • Edge cases checked: Non-OAuth servers unaffected, auth provider only created when auth: "oauth" set, callback server cleans up on timeout/error, headless fallback logs URL
  • What you did not verify: Live end-to-end OAuth flow against a real server, concurrent auth flows for multiple servers, port conflict on 8093

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? Yes — new optional auth field on McpServerConfig, ignored if absent
  • Migration needed? No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: Port 8093 could conflict with another service during OAuth callback
    • Mitigation: Only listens during the auth flow (seconds), bound to 127.0.0.1 only. Follow-up could add dynamic port selection.
  • Risk: Token files in ~/.openclaw/mcp-auth/ are unencrypted
    • Mitigation: Consistent with existing auth-profiles.json approach. File permissions follow OS defaults.

🤖 AI-assisted contribution

Changed files

  • src/agents/mcp-http.ts (modified, +4/-0)
  • src/agents/mcp-oauth-provider.test.ts (added, +240/-0)
  • src/agents/mcp-oauth-provider.ts (added, +389/-0)
  • src/agents/mcp-transport-config.test.ts (modified, +15/-0)
  • src/agents/mcp-transport-config.ts (modified, +2/-0)
  • src/agents/mcp-transport.test.ts (added, +178/-0)
  • src/agents/mcp-transport.ts (modified, +31/-10)
  • src/agents/pi-bundle-mcp-runtime.oauth.test.ts (added, +227/-0)
  • src/agents/pi-bundle-mcp-runtime.ts (modified, +45/-3)
  • src/config/types.mcp.ts (modified, +2/-0)

Code Example

{"url": "https://mcp.example.com", "headers": {"Authorization": "Bearer <token>"}}

---

# One-time setup                                          
  openclaw mcp set myserver '{"url": "https://mcp.example.com", "auth": "oauth"}'
                                                                                                               
  # First connection: browser opens, user logs in, tokens stored automatically                                 
  # Subsequent connections: tokens refresh transparently
RAW_BUFFERClick to expand / collapse

Summary

Wire up the MCP SDK's existing authProvider support on StreamableHTTPClientTransport and SSEClientTransport so openclaw can authenticate to OAuth-protected remote MCP servers.

Problem to solve

Remote MCP servers that require OAuth authentication (per the https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) are unusable with openclaw today. The only auth mechanism for remote servers is static headers:

  {"url": "https://mcp.example.com", "headers": {"Authorization": "Bearer <token>"}}

This breaks down for OAuth-protected servers because:

  • Access tokens are short-lived (minutes, not hours)
  • There's no way to trigger an OAuth login flow
  • There's no token refresh — when the token expires, the server silently fails to connect
  • Users must manually obtain and paste new tokens repeatedly

The MCP Authorization spec defines a standard OAuth flow (Authorization Code + PKCE with protected resource metadata discovery) that MCP clients are expected to support.

Proposed solution

The @modelcontextprotocol/sdk (v1.29.0, already an openclaw dependency) has built-in OAuth support that openclaw is not using. Both StreamableHTTPClientTransport and SSEClientTransport accept an authProvider: OAuthClientProvider option that handles the entire MCP auth spec — discovery, PKCE, token exchange, refresh, 401 challenges, and scope step-up.

The change:

  1. Add auth?: "oauth" to McpServerConfig as an opt-in config field
  2. Implement OAuthClientProvider bridging to a simple per-server JSON file store (~/.openclaw/mcp-auth/<server>.json)
  3. Pass authProvider to the SDK transport constructors when auth: "oauth" is configured
  4. Catch UnauthorizedError during connection, start a local callback server, open the browser for authorization, call transport.finishAuth(code), and retry
  5. Include headless fallback (log the authorization URL when no browser is available)

User experience:

  # One-time setup                                          
  openclaw mcp set myserver '{"url": "https://mcp.example.com", "auth": "oauth"}'
                                                                                                               
  # First connection: browser opens, user logs in, tokens stored automatically                                 
  # Subsequent connections: tokens refresh transparently

No breaking changes. Servers without auth: "oauth" work exactly as before.

Alternatives considered

  • Plugin-based approach: Build an OpenClaw plugin that handles OAuth and registers tools via registerTool(). This works but reimplements tool definitions outside MCP, adds plugin-specific maintenance burden, and doesn't solve the general problem for the ecosystem.
  • Local MCP proxy plugin: A plugin that handles OAuth and runs a local stdio proxy forwarding to the remote server. Preserves MCP architecture but adds an extra runtime to debug.
  • Manual token management: The current status quo. Unusable for servers with short-lived tokens.

Wiring up the SDK's existing authProvider is the simplest approach — ~60 lines of changes across existing files plus ~260 lines for the new OAuthClientProvider adapter and callback server.

Impact

  • Affected users: Anyone connecting to OAuth-protected remote MCP servers (the standard for hosted MCP services per the MCP Authorization spec)
  • Severity: Blocks workflow — these servers are effectively unusable today
  • Frequency: Every connection to an OAuth-protected server
  • Consequence: Users either can't use these servers at all, or resort to fragile workarounds (manually pasting short-lived tokens, building custom extensions)

Evidence/examples

  • Community demand: #29053 (open, 14+ comments), #8188 (closed, pointed to MCPorter workaround with ~2.4s
    cold-start per invocation), #4240, #4834
  • MCP Authorization spec: https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization
  • MCP SDK built-in support: StreamableHTTPClientTransport and SSEClientTransport both accept authProvider:
    OAuthClientProvider — OpenClaw just isn't passing it
  • Other MCP clients (Claude Code, Cursor) already support OAuth for remote MCP servers

Additional information

  • This covers the Authorization Code + PKCE flow, which is the MCP spec's primary interactive auth path. It works for environments with browser access (desktop, WSL, SSH with X11). Headless/VPS environments can use the client credentials flow (a separate MCP auth extension) or SSH tunneling.
  • Device Authorization Grant (RFC 8628) would be the proper solution for headless environments without browser access, but it's not currently part of the MCP Authorization spec or the https://github.com/modelcontextprotocol/ext-auth. Supporting it would likely require an MCP community proposal first, so we consider it a future consideration rather than in-scope here.

extent analysis

TL;DR

Implement the OAuthClientProvider to enable OAuth authentication for remote MCP servers by passing the authProvider to the SDK transport constructors.

Guidance

  • Update the McpServerConfig to include an optional auth field with the value "oauth" to opt-in for OAuth authentication.
  • Implement the OAuthClientProvider to handle the OAuth flow, including discovery, PKCE, token exchange, refresh, and scope step-up.
  • Catch UnauthorizedError during connection and start a local callback server to handle authorization.
  • Pass the authProvider to the StreamableHTTPClientTransport and SSEClientTransport constructors when auth: "oauth" is configured.

Example

// Example of updated McpServerConfig
const serverConfig = {
  url: "https://mcp.example.com",
  auth: "oauth"
};

// Example of implementing OAuthClientProvider
const authProvider = new OAuthClientProvider({
  // Implement the necessary methods for OAuth flow
});

Notes

This solution assumes that the @modelcontextprotocol/sdk version 1.29.0 is being used, which has built-in OAuth support. The implementation of the OAuthClientProvider will require additional code to handle the OAuth flow.

Recommendation

Apply the workaround by implementing the OAuthClientProvider and updating the McpServerConfig to support OAuth authentication. This will enable users to connect to OAuth-protected remote MCP servers without manually obtaining and pasting short-lived tokens.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Feature]: OAuth authentication for remote MCP servers [1 pull requests, 1 participants]