feat(mcp): OAuth 2.1 + PKCE for outbound MCP servers#4441
feat(mcp): OAuth 2.1 + PKCE for outbound MCP servers#4441waleedlatif1 wants to merge 28 commits intostagingfrom
Conversation
Adds spec-compliant OAuth support for MCP servers that require it (Linear, Slack, Notion, Atlassian, etc.) using the SDK's OAuthClientProvider. Tokens are persisted per-user-per-server and refreshed automatically. Also supports pre-registered OAuth clients for servers that don't expose Dynamic Client Registration.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview Introduces a new Updates server create/update logic to probe and persist auth mode ( Updates the settings UI to configure optional OAuth client credentials under Advanced settings, automatically start OAuth after creating an OAuth server, and provide a “Connect with OAuth” action with query invalidation on successful authorization; also refactors MCP query keys/mutations and bumps Reviewed by Cursor Bugbot for commit 3f840d6. Configure here. |
Greptile SummaryThis PR adds spec-compliant OAuth 2.1 + PKCE support for outbound MCP servers, including auto-detection via an unauthenticated probe, encrypted per-user token storage in a new
Confidence Score: 5/5Safe to merge; the OAuth flow is well-guarded and the previous review cycle resolved all meaningful correctness and security issues. The core OAuth flow (state CSRF, PKCE verifier encryption, token encryption at rest, scheme-check before window.open, stale-token cleanup on credential change) is correctly implemented. The remaining comments are minor quality suggestions that do not affect correctness. apps/sim/app/api/mcp/oauth/callback/route.ts — error parameter logged without sanitisation; apps/sim/app/workspace/.../mcp/mcp.tsx — onMessage else-branch and de-memoized selectedServer. Important Files Changed
Sequence DiagramsequenceDiagram
participant UI as Browser (MCP Settings)
participant Start as /api/mcp/oauth/start
participant Popup as OAuth Popup
participant AS as Authorization Server
participant CB as /api/mcp/oauth/callback
participant DB as mcp_server_oauth (DB)
participant SDK as MCP SDK (mcpAuth)
UI->>Start: GET ?serverId=&workspaceId=
Start->>DB: getOrCreateOauthRow()
Start->>SDK: mcpAuth(provider, {serverUrl})
SDK-->>Start: throws McpOauthRedirectRequired(authorizationUrl)
Start-->>UI: {status:redirect, authorizationUrl}
UI->>Popup: window.open(authorizationUrl)
Popup->>AS: User consents
AS->>CB: GET /callback?code=&state=
CB->>DB: loadOauthRowByState(state)
CB->>DB: clearState() - burn before exchange
CB->>SDK: mcpAuth(provider, {authorizationCode})
SDK->>AS: POST /token (code + PKCE verifier)
AS-->>SDK: access_token + refresh_token
SDK->>DB: saveTokens() encrypted
CB->>DB: clearVerifier()
CB-->>Popup: postMessage({type:mcp-oauth, ok:true, serverId})
Popup-->>UI: message event
UI->>UI: invalidate queries, toast.success
Reviews (20): Last reviewed commit: "fix(mcp): forward serverId on callback f..." | Re-trigger Greptile |
- provider: clear `state` from DB in `invalidateCredentials` to prevent stale state values from matching during CSRF check - storage: encrypt PKCE `codeVerifier` at rest to match `tokens` / `clientInformation` security posture - queries: `useForceRefreshMcpTools` now writes the fetched payload directly into the query cache instead of invalidating, eliminating the duplicate network round-trip - mcp settings: track per-server OAuth pending state so a "Connecting…" spinner only disables the card whose flow is in progress - mcp settings: surface existing `oauthClientId` in edit modal so the Advanced section auto-expands and displays the saved value
|
Greptile summary findings addressed in f587e82:
The point about clearing a pre-registered Client ID by emptying the field is a follow-up — |
|
@greptile |
|
@cursor review |
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
|
@greptile |
|
@cursor review |
…in edit modal Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 3ea52c9. Configure here.
…Id on tool reauth errors Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
- use mcp-oauth-${serverId} window target so concurrent OAuth flows on
different servers don't reuse and clobber the same popup
- drop redundant setQueryData before invalidate in useForceRefreshMcpTools
- replace hardcoded text-red-500 with text-[var(--text-error)] token
- normalize Plus icon to default h-[14px] w-[14px]
- drop useMemo on cheap toolsByServer/selectedServer derivations
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
- hoist serverId from try-block const into outer scope so the catch's htmlClose carries it through to postMessage. Without it, parent's onMessage couldn't clear connectingOauthServers and the UI button stayed stuck on "Connecting…" until popup close. - relax https-only authorization URL check to permit http://localhost, http://127.0.0.1, and http://[::1] per OAuth 2.1 loopback exemption, unblocking local OAuth-protected MCP server development. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 3f840d6. Configure here.
Summary
OAuthClientProviderWWW-Authenticate/oauth-protected-resource)mcp_server_oauthtable; SDK refreshes automatically before expiry/api/mcp/oauth/start→/api/mcp/oauth/callback) withstateCSRF protectionreauth_requiredfrom tool execution when refresh token is invalid so the UI can prompt to reconnectType of Change
Testing
Tested manually against OAuth-protected MCP servers (Linear). Existing header-auth servers regression-checked.
Checklist