Skip to content

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 Gram Elements.

Key changes:

  • Add external OAuth endpoints at /oauth-external/* (avoids route conflicts with /oauth/{mcpSlug}/*)
  • Implement DCR to dynamically register as an OAuth client with MCP servers
  • Add session header authentication fallback for cross-origin requests
  • Store encrypted tokens and DCR credentials in database

Changes

Server

  • external_oauth.go: Main OAuth service with DCR, PKCE (S256), and encrypted token storage
  • start.go: Route registration for /oauth-external/* endpoints
  • schema.sql: Tables for user_oauth_tokens and external_oauth_client_registrations
  • queries.sql: SQLC queries for token and DCR credential management
  • conventions.go: New slog helpers for OAuth status logging

Client/Elements

  • PlaygroundAuth.tsx: OAuth connect/disconnect UI with Gram-Session header for cross-origin auth
  • useOAuthStatus.ts / useOAuthToken.ts: Hooks for checking OAuth state and retrieving tokens

Recent Fixes

  • OAuth popup UX: After authorization completes, popup now shows a clean success page with checkmark and auto-closes after 1.5s (instead of loading the full dashboard)
  • Auto-refresh status: Status now automatically refreshes when popup closes (using refetchStatus() with 300ms delay)
  • Debug logging: Added comprehensive logging for OAuth status checks (user_id, org_id, issuer, status, connected, expired)

Known Limitations

⚠️ Agent/Chat path does not yet support external MCP tool execution. The LoadToolsetTools function explicitly returns an error for ToolKindExternalMCP. OAuth authentication works, but the Playground chat cannot execute external MCP tools like Linear.

Follow-up work needed:

  1. Unfold external MCP tools in the agent path (like unfoldExternalMCPTools does for MCP JSON-RPC)
  2. Fetch OAuth tokens from DB during tool execution
  3. Pass tokens to external MCP servers via Authorization header

Test plan

  • OAuth authorization flow completes successfully
  • OAuth callback stores tokens correctly
  • Status endpoint returns authentication state
  • Disconnect removes stored tokens
  • DCR credentials are cached and reused per issuer
  • OAuth popup shows success page and auto-closes
  • Status auto-refreshes after popup closes
  • External MCP tools work in Playground chat (blocked - needs agent path support)

Related

Resolves AGE-1150

🤖 Generated with Claude Code

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 Ready Ready Preview, Comment Jan 29, 2026 9:39pm
gram-docs-redirect Ready Ready Preview, Comment Jan 29, 2026 9:39pm

Request Review

@linear
Copy link

linear bot commented Jan 21, 2026

@changeset-bot
Copy link

changeset-bot bot commented Jan 21, 2026

⚠️ No Changeset found

Latest commit: 68da726

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a 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]>
@github-actions
Copy link
Contributor

github-actions bot commented Jan 29, 2026

atlas migrate lint on server/migrations

Status Step Result
1 new migration file detected atlas.sum
ERD and visual diff generated View Visualization
Migration Integrity Check
File atlas.sum is invalid
checksum mismatch
Read the full linting report on Atlas Cloud

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants