Skip to content

feat: in-chat MCP OAuth connect — api (CLEAN-75) - #71

Merged
dmitriyzhuk merged 4 commits into
mainfrom
feat/CLEAN-75-mcp-oauth-connect
Sep 10, 2026
Merged

dmitriyzhuk merged 4 commits into
mainfrom
feat/CLEAN-75-mcp-oauth-connect

Conversation

@dmitriyzhuk

Copy link
Copy Markdown
Contributor

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)

  • mcpServer: authType: 'oauth'; McpServer.oauthClientId (shared client_id from one-time DCR); McpOauthState PKCE table; migration 20260910120000_mcp_oauth_connect (additive).
  • mcpServer/oauth submodule: 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).
  • bridle: notifyMcpConnected pushes mcp_connected over the agent control channel (same as debug_set/sync).
  • workflow / getMcps / AgentMcpDto: expose the server id so the runtime can key the mcpOauth:<id> token secret.
  • deploy: PUBLIC_API_URL wired into the ranch-api deployment (OAuth callback redirect_uri base).

Runtime side

Separate PR on CleanSlice/runtime (feat/CLEAN-75-mcp-oauth-connect): headless OAuthClientProvider + a ${name}__connect tool.

Notes / follow-ups

  • Same-session live-reload of the freshly-connected tools (mcp_connected handler) is a follow-up — AgentRuntime takes its tool list as a fixed array at boot; tools currently appear on the next boot. The token + refresh work immediately.
  • Swagger regen skipped (no admin/app UI consumes these yet — chat-driven).

Verified: tsc --noEmit clean; terraform fmt clean.

🤖 Generated with Claude Code

dmitriyzhuk and others added 4 commits September 10, 2026 17:14
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>
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.

1 participant