feat(assistants): MCP OAuth flow on AuthRequired#2935
Conversation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a configured MCP server returns AuthRequired the assistant runtime mints a per-flow OAuth state, drives RFC 8414 discovery + RFC 7591 dynamic client registration + PKCE authorize URL construction, surfaces the URL to the model so it can relay to the user, and consumes the authorization code on callback. The OAuth state is a signed JWT carrying the AES-GCM encrypted PKCE verifier so a leaked redirect URL cannot defeat PKCE. Auth completion is delivered to the model as a follow-up turn event (<message-context> envelope with EventType, MCPServerID, MCPSlug, Status, Error/ErrorDescription) so the agent can mcp_force_reconnect and continue. The system addendum names the exact event types and field names instead of relying on the model to infer them. Closes AGE-2437. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 9f6e7fe The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f121365d84
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
🚀 Preview Environment (PR #2935)Preview URL: https://pr-2935.dev.getgram.ai
Gram Preview Bot |
- DCR registers with client_secret_basic; store encrypted secret in flow
claims and send via HTTP Basic on token exchange.
- Capture full MCP endpoint URL in flow claims so customer-domain MCPs
are reachable from the callback. Slug derived at use sites.
- Move thread correlation lookup to assistantrepo.ResolveThreadCorrelation.
- Body-close defers wrapped with o11y.NoLogDefer.
- Token-exchange failure now logs at error level.
- Route paths camelCased: /rpc/assistantMcpAuth.{create,/{id}/oauth/callback}.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae748bc7cc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Auth-required notices sat before persisted chat history, making them the oldest items in the transcript and the first candidates for context-trim — exactly the prompt the assistant needs to act on. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
AuthRequired, the assistant runtime drives RFC 8414 discovery + RFC 7591 dynamic client registration + PKCE authorize URL construction, then surfaces the auth URL to the model so it can relay to the user.assistant_mcp_auththread event; the agent reacts by callingmcp_force_reconnectand continuing.stateis a signed JWT carrying the AES-GCM encrypted PKCE verifier so a leaked redirect URL can't defeat PKCE (signing alone is not confidential).assistant_mcp_auth_required/assistant_mcp_auth,AuthURL,MCPServerID,MCPSlug,Status,ErrorDescription) so it doesn't have to infer the protocol.Why
consumeMCPAuthGrantdiscards the token responseThe flow only fires for Gram-hosted MCP URLs (
/mcp/{slug}), sometadata.TokenEndpointresolves to Gram's own OAuth token endpoint, not the upstream MCP provider's. The POST exists to prime the Gram session issuer: by the time the token endpoint returns 2xx, the grant has been recorded and the owner'sremote_sessionfor that issuer has been persisted server-side. Capturing the returned bearer in this handler would be redundant — assistants never present a bearer to/mcp/{slug}; the public MCP path resolves the assistant token via the assistant-token special case inserver/internal/mcp/serve_platform.goand looks up the owner's stored remote_session for the upstream call (seeassistant_resolver_integration_test.go::TestServePublic_AssistantTokenResolvesOwnerRemoteSessionToUpstream).Closes AGE-2437
✻ Clauded...