Repository navigation
feat: in-chat MCP OAuth connect — api (CLEAN-75) - #71
Merged
Merged
Conversation
Data-model groundwork for the in-chat "Connect" OAuth flow: - add 'oauth' to McpServerAuthTypes + create/update DTO validation - McpServer.oauthClientId (shared client_id from one-time dynamic client registration; per-agent refresh tokens live in the agent secret store) - McpOauthState table: short-lived PKCE handshake state (state, agentId, mcpServerId, codeVerifier), consumed by the OAuth callback - thread oauthClientId through types/mapper/gateway + fix the mcp() test mock - migration 20260910120000_mcp_oauth_connect (additive, safe on prod) Verified: tsc --noEmit clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The agent drives the connect: a runtime tool (next chunk) calls this, the agent hands the user a link, the user logs in, and the callback stores a per-agent token and wakes the agent. - oauth submodule: McpOauthClient (RFC 8414 discovery, RFC 7591 DCR as a public PKCE client, code exchange), McpOauthService (PKCE start, callback exchange, per-agent secret store, bridle wake), McpOauthController (POST start [agent-JWT self-scoped], GET callback [@public, browser], GET status) - IBridleGateway.notifyMcpConnected -> pushes `mcp_connected` over the same agent control channel as debug_set/sync - getMcps / AgentMcpDto expose the server id so the runtime can key the `mcpOauth:<id>` token secret - PUBLIC_API_URL wired into the ranch-api deployment (redirect_uri base) Verified: tsc --noEmit clean; terraform fmt clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The runtime authenticates to ranch with x-bridle-api-key (not an agent JWT), so the connect-flow endpoints it calls must accept it. Switch start/status to BridleApiKeyGuard; callback stays @public. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MCP_SERVERS_B64 (baked into the agent pod) is built by toRuntimeConfig, which dropped the server id. The runtime keys an oauth server's per-agent token secret by id (mcpOauth:<id>), so without it oauth MCPs can't resolve their token. Add id to the runtime config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CLEAN-75 — https://dreamvention.atlassian.net/browse/CLEAN-75
Lets an agent connect an OAuth-protected MCP server (first case: Silpo AI Factory) with no admin button and no manual token pasting. The agent decides when to connect: it calls a runtime tool, hands the user a login link in chat, the user logs in, and a callback stores a per-agent token and wakes the agent. Verified against Silpo's real OAuth metadata (authorization_code + refresh_token, PKCE S256, dynamic client registration, public client — no client_credentials, so the one-time browser login is required; headless refresh after).
This PR (api + deploy)
authType: 'oauth';McpServer.oauthClientId(shared client_id from one-time DCR);McpOauthStatePKCE table; migration20260910120000_mcp_oauth_connect(additive).McpOauthClient(RFC 8414 discovery, RFC 7591 DCR, code exchange),McpOauthService(PKCE start, callback exchange, per-agent secret store, bridle wake),McpOauthController—POST start+GET status(x-bridle-api-key, the runtime auth),GET callback(@public, the user's browser).notifyMcpConnectedpushesmcp_connectedover the agent control channel (same as debug_set/sync).mcpOauth:<id>token secret.PUBLIC_API_URLwired into the ranch-api deployment (OAuth callback redirect_uri base).Runtime side
Separate PR on CleanSlice/runtime (
feat/CLEAN-75-mcp-oauth-connect): headlessOAuthClientProvider+ a${name}__connecttool.Notes / follow-ups
Verified:
tsc --noEmitclean;terraform fmtclean.🤖 Generated with Claude Code