feat(mcp): add cf-portal OAuth catalog entry + Connect UI - #83
Merged
Conversation
Adds end-to-end OAuth-MCP onboarding for cf-portal (and improves the generic OAuth-MCP UX for github + browser-rendering as a side effect). The Agents SDK's MCPClientManager already handles PKCE + DCR + token refresh + the in-DO mutex that prevents Kenny Johnson's documented re-auth loop — all wired up via `this.mcp` on the per-user CodingAgent hub DO. What was missing was the UI to trigger `addMcpServer()` from the integrations panel. Backend: - src/mcp-catalog.ts — register `cf-portal` with `knownHosts: [portal.mcp.cfdata.org, cf-mcp.cloudflareaccess.com]` so the OAuth dance hits authorization/token/registration endpoints without admin-allowlisting them. - src/index.ts — new `GET /api/mcp/oauth-servers` returns the user's Agents-SDK-managed MCP servers (id/name/url/state/toolCount) so the UI can render "Connected — N tools" vs "Not connected" for OAuth catalog entries. Frontend: - public/js/dodo-settings.js — `loadIntegrations()` now fans out to `/api/mcp/oauth-servers` in parallel; `renderIntegrations()` routes OAuth catalog entries through a new `renderOAuthCard()` that shows "Connect with OAuth" or "Disconnect / Refresh" based on state. `connectOAuthCatalog()` opens the OAuth provider in a popup, polls the popup's closed state, and refreshes the list. Tests: - test/mcp-catalog-unit.test.ts — catalog entries carry the right `auth_type` and `knownHosts`. - test/dodo.test.ts — two new tests for `/api/mcp/oauth-servers` (shape + auth gate). 817/817 pass. No changes to coding-agent.ts or user-control.ts — the existing per-user OAuth hub pattern is reused as-is. Plan: memory/workload/plans/2026-05-26-dodo-cf-portal-oauth.md
jonnyparris
added a commit
that referenced
this pull request
May 26, 2026
…rst UI layout (#84) Two follow-ups to PR #83: 1. **Catalog seed was first-run-only.** The cf-portal entry I added to `mcp-catalog.ts` never appeared in `/api/mcp-catalog` because the endpoint reads from UserControl's `approved_mcps` table, which is seeded once when the table is empty. Existing DOs (i.e. mine, and everyone else's) had been seeded back when the catalog was just browser-rendering + github, so the new entry never propagated. Fix: use `INSERT OR IGNORE` on every onStart, keyed by `mcp_url` (PRIMARY KEY). New catalog entries flow through; admin edits and soft-deletes on existing rows are preserved. 2. **Integrations buried below Skills + Tools.** Settings panel order was Skills (~50 entries) → Tools (~30 entries) → Integrations. Moved Integrations above both. Skills and Tools are now wrapped in collapsed `<details>` so they don't dominate the scroll. No tests added — the seed behaviour is exercised implicitly by every test that hits `/api/mcp-catalog` (which seeds via onStart). The UI move is markup-only. beep-boop-🤖
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.
What
Adds first-class OAuth-MCP support for cf-portal — and incidentally fixes the UX for github + browser-rendering OAuth too. "Connect with OAuth" button now appears in the integrations panel for any OAuth catalog entry.
Why the 7-phase plan collapsed to one PR
While reading the existing code, found that the Agents SDK's
MCPClientManageralready handles every "hard" thing the original plan called for:/agents/oauth/callback) ✓loadOAuthToolsFromHub) ✓The backend HTTP routes (
/api/mcp/start-auth,/api/mcp/delete-auth,/api/mcp/refresh-state,/agents/*callback) already existed. The host allowlist already pre-allowed catalogknownHosts. Even the github catalog entry was wired withauth_type: "oauth"and a knownHost.What was actually missing:
/api/mcp/start-authand open the popupThat's the whole PR.
Changes
Backend (44 lines):
src/mcp-catalog.ts— registercf-portalwithknownHosts: [portal.mcp.cfdata.org, cf-mcp.cloudflareaccess.com]so the OAuth dance (auth/token/registration endpoints) and the MCP server itself are both implicitly allowed without admin allowlisting.src/index.ts— newGET /api/mcp/oauth-serversproxiesstub.getMcpServers()from the user's hub DO, with tool-count rollup.Frontend (106 lines):
public/js/dodo-settings.js—loadIntegrations()now also fetches/api/mcp/oauth-serversin parallel; newrenderOAuthCard()shows state-aware actions ("Connect with OAuth" / "Refresh" / "Disconnect");connectOAuthCatalog()opens a 560×720 popup and polls for closure before refreshing the list.Tests (45 lines):
test/mcp-catalog-unit.test.ts— catalog shape assertions.test/dodo.test.ts—/api/mcp/oauth-servershappy path + auth gate.Verification
npm run typecheck: cleannpx vitest run: 817/817 pass (no regressions; +6 new tests)coding-agent.ts,user-control.ts, or any DO migrationsManual test plan (post-deploy)
backstage_*,jira-cli_*, etc.) should appear in tool listingPlan reference
memory/workload/plans/2026-05-26-dodo-cf-portal-oauth.md— original 7-phase plan. Most of phases 1–5 turned out to already exist incoding-agent.ts. Phase 6 (catalog) and Phase 4 (frontend popup) are what this PR delivers.beep-boop-🤖