feat: add OAuth support for external MCP servers in the Playground#1323
feat: add OAuth support for external MCP servers in the Playground#1323
Conversation
This implements OAuth 2.1 Dynamic Client Registration (DCR) flow support for external MCP servers in Gram Elements, following the RFC for MCP-Compliant Playground. Backend changes: - Add user_oauth_tokens table for storing encrypted OAuth tokens - Create external_oauth.go with authorize, callback, status, disconnect, and token endpoints - Implement PKCE (S256) for secure authorization code flow - Add GetToolsetByID query for toolset lookup Elements changes: - Add ExternalOAuthConfig and OAuthApiConfig types - Create useOAuthStatus hook for checking auth status - Create useOAuthToken hook for fetching access tokens - Integrate OAuth status into ElementsProvider context - Handle OAuth callback URL parameters Dashboard changes: - Update PlaygroundElements to pass OAuth config when toolset has external OAuth server configured Resolves AGE-1150 Co-Authored-By: Claude Opus 4.5 <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: fdb57cd The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Add visual OAuth connection status and connect/disconnect buttons in the Playground Authentication section, matching the RFC mockups. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Replace Spinner from moonshine with Loader2 from lucide-react. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Update PlaygroundAuth to detect OAuth requirements from external MCP tools discovered via the MCP protocol, in addition to the existing external OAuth server configuration. - Add getExternalMcpOAuthConfig() to extract OAuth config from rawTools - Add ExternalMcpOAuthConnection component for MCP OAuth 2.1/2.0 flows - Support both legacy externalOauthServer and MCP-discovered OAuth - Display "MCP OAuth 2.1" or "OAuth 2.0" label based on discovered version This aligns with the RFC architecture where the Playground acts as an MCP client that self-discovers OAuth requirements from imported catalog tools. Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add external OAuth endpoints at /oauth-external/* to avoid route conflicts - Implement Dynamic Client Registration (DCR) per RFC 7591 for MCP OAuth 2.1 - Add session header authentication fallback for cross-origin requests - Fix OAuth state cache key consistency using StateID field - Update frontend to pass Gram-Session header for OAuth status/disconnect - Add external_oauth_client_registrations table for storing DCR credentials The OAuth flow now works for external MCP tools like Linear. Note: Agent/chat path does not yet support external MCP tool execution (returns error for ToolKindExternalMCP). This PR focuses on the authentication flow. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Use refetch() instead of invalidateQueries() for more reliable status update after OAuth flow completes. Added small delay to ensure server has processed the callback. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add comprehensive logging to help debug OAuth status check issues: - Log user_id, organization_id, and issuer when checking status - Log whether token was found or not found - Log final status (authenticated/needs_auth/disconnected), connected, and expired flags Also add new slog helpers to attr package: - SlogOAuthStatus for OAuth status string - SlogOAuthConnected for connection boolean - SlogOAuthExpired for expiration boolean Co-Authored-By: Claude Opus 4.5 <[email protected]>
After OAuth authorization completes, instead of redirecting to the full
dashboard URL (which loaded the entire app in the popup), now show a
minimal success page with:
- Checkmark icon
- "Connected to {provider}" message
- Auto-close after 1.5 seconds
This provides better UX as users see immediate feedback that the
connection succeeded, and the popup closes automatically.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…support-in-elements
…-api/gram into feat/oauth-support-in-elements
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
| if (window.location.origin !== apiUrl) { | ||
| toast.error("OAuth configuration error: redirect origin mismatch"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
🔴 Overly restrictive origin check prevents OAuth flow in cross-origin deployments
The handleConnect function blocks OAuth flows when window.location.origin !== apiUrl, but this check is too restrictive for legitimate cross-origin deployments.
Root Cause
In PlaygroundAuth.tsx:253-256, the code checks:
if (window.location.origin !== apiUrl) {
toast.error("OAuth configuration error: redirect origin mismatch");
return;
}However, the OAuth flow opens a popup to ${apiUrl}/oauth-external/authorize (line 265-266), which redirects to the external OAuth provider and then back to the server's callback endpoint. The popup polling mechanism at lines 286-297 only checks popup.closed, which works regardless of the popup's origin.
This check would fail in legitimate deployment scenarios where:
- The frontend is served from a CDN or different domain than the API
- Development setups where
GRAM_SITE_URLdiffers fromGRAM_SERVER_URL - Multi-region deployments with different frontend/backend domains
Impact: Users in cross-origin deployments cannot connect external MCP OAuth providers - they see an error toast and the OAuth flow never starts, even though the underlying mechanism would work correctly.
| if (window.location.origin !== apiUrl) { | |
| toast.error("OAuth configuration error: redirect origin mismatch"); | |
| return; | |
| } | |
| // Note: Cross-origin OAuth flows are supported since the popup navigates | |
| // to the server's authorize endpoint and returns via server-side callback | |
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Implements MCP OAuth 2.1 with Dynamic Client Registration (DCR) per RFC 7591 for external MCP servers in the Gram Playground.
The MCP service will detect requests to external MCP servers (from the catalog) that originate from the playground. If a token for that MCP server has been stored, then Gram will inject it into the request headers before forwarding it to the underlying MCP server.
UI/UX Updates
When viewing a toolset that does not have an OAuth token available yet, the user won't be able to interact with the playground chat.
Clicking "Connect" spawns a popup that allows the user to connect their 3rd party account.
Known Limitations
Related
Resolves AGE-1150