Skip to content

feat: add OAuth support for external MCP servers in the Playground#1323

Merged
walker-tx merged 104 commits intomainfrom
feat/oauth-support-in-elements
Feb 5, 2026
Merged

feat: add OAuth support for external MCP servers in the Playground#1323
walker-tx merged 104 commits intomainfrom
feat/oauth-support-in-elements

Conversation

@simplesagar
Copy link
Member

@simplesagar simplesagar commented Jan 21, 2026

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.

CleanShot 2026-02-04 at 10 43 39@2x

Clicking "Connect" spawns a popup that allows the user to connect their 3rd party account.

CleanShot 2026-02-04 at 10 45 10

Known Limitations

  1. Not all External MCP Servers will work with this. If the data from the registry is bad, if DCR is not properly configured on the provider, or if the provider requires client registration, then their servers won't work in the Playground.
  2. This does not implement OAuth for Elements. Just playground.

Related

Resolves AGE-1150


Open with Devin

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]>
@vercel
Copy link

vercel bot commented Jan 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gram Error Error Feb 5, 2026 2:20pm
gram-docs-redirect Ready Ready Preview, Comment Feb 5, 2026 2:20pm

Request Review

@linear
Copy link

linear bot commented Jan 21, 2026

@changeset-bot
Copy link

changeset-bot bot commented Jan 21, 2026

🦋 Changeset detected

Latest commit: fdb57cd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
dashboard Patch
@gram/client Patch
server Patch

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]>
@simplesagar simplesagar changed the title feat(elements): add OAuth 2.1 support for external MCP servers feat: add OAuth support for external MCP servers in the Playground Jan 21, 2026
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]>
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View issues and 6 additional flags in Devin Review.

Open in Devin Review

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 11 additional findings in Devin Review.

Open in Devin Review

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 13 additional findings in Devin Review.

Open in Devin Review

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 16 additional findings in Devin Review.

Open in Devin Review

walker-tx and others added 2 commits February 5, 2026 08:20
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>
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 16 additional findings in Devin Review.

Open in Devin Review

Comment on lines +253 to +256
if (window.location.origin !== apiUrl) {
toast.error("OAuth configuration error: redirect origin mismatch");
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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:

  1. The frontend is served from a CDN or different domain than the API
  2. Development setups where GRAM_SITE_URL differs from GRAM_SERVER_URL
  3. 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.

Suggested change
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
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@walker-tx walker-tx merged commit a7422f8 into main Feb 5, 2026
28 checks passed
@walker-tx walker-tx deleted the feat/oauth-support-in-elements branch February 5, 2026 14:48
@github-actions github-actions bot locked and limited conversation to collaborators Feb 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

preview Spawn a preview environment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants