Skip to content

feat(mcp-gateway): add management dashboard#3722

Open
pandemicsyn wants to merge 14 commits into
mainfrom
feat/mcp-gateway-dashboard
Open

feat(mcp-gateway): add management dashboard#3722
pandemicsyn wants to merge 14 commits into
mainfrom
feat/mcp-gateway-dashboard

Conversation

@pandemicsyn
Copy link
Copy Markdown
Contributor

@pandemicsyn pandemicsyn commented Jun 4, 2026

Summary

  • Add an admin-gated MCP Gateway management dashboard for personal and organization scopes, including connection discovery, setup, connect URL management, assignments, credentials, and provider sign-in controls.
  • Introduce an app-owned dashboard tRPC/control-plane surface while preserving the two-plane architecture: apps/web manages configuration and OAuth workflows, while services/mcp-gateway remains responsible for runtime token verification and credential-injecting proxy behavior.
  • Harden dashboard lifecycle boundaries: scope-bind management mutations, persist initial static provider credentials atomically, normalize DB timestamps at the tRPC boundary, reject OAuth client authentication-method changes without a secret lifecycle, require confirmations for destructive actions, and keep the dashboard rollout gated to admins for personal connections.
  • Move resource-specific OAuth registration under /api/mcp-gateway/oauth/register/resource/... to avoid Next.js dynamic route conflicts while retaining the required registration capability.
  • Separate Kilo gateway scopes from upstream provider scopes. This avoids incorrectly sending Kilo's profile scope to remote providers, preserves remote protected-resource resource values, stores upstream scope provenance on the config, and lets both dashboard sign-in and Codex-triggered sign-in use the same resolved provider OAuth configuration.

Verification

  • Manual dashboard demo and OAuth flow verification were shared in Slack.
  • Additional manual verification details:

Visual Changes

Before After
MCP Gateway dashboard unavailable (screenshot pending) Personal connection list and setup flow (screenshot pending)
Organization MCP Gateway management unavailable (screenshot pending) Organization detail, assignment, and credential management surface (screenshot pending)

Reviewer Notes

  • This PR now targets main; the underlying gateway implementation and production hostname work have already landed separately.
  • The upstream OAuth scope change adds provider_scopes, provider_scope_source, and provider_resource to the config model because remote provider scopes are not the same thing as Kilo gateway scopes.
  • Focus areas: OAuth/provider sign-in setup behavior, tenant-scoped management mutations, destructive action lifecycle semantics, and provider scope/resource resolution.

@pandemicsyn pandemicsyn force-pushed the feat/mcp-gateway-dashboard branch 7 times, most recently from a1ee590 to d7bc8e4 Compare June 5, 2026 00:58
@pandemicsyn pandemicsyn force-pushed the feat/mcp-gateway-implementation branch from 281a2e3 to e7235c0 Compare June 5, 2026 01:24
@pandemicsyn pandemicsyn force-pushed the feat/mcp-gateway-dashboard branch 2 times, most recently from 80f36bf to 6d97b09 Compare June 5, 2026 02:44
@pandemicsyn pandemicsyn force-pushed the feat/mcp-gateway-implementation branch from 73e1118 to b14e7d9 Compare June 5, 2026 14:44
@pandemicsyn pandemicsyn force-pushed the feat/mcp-gateway-dashboard branch from 6d97b09 to 392d27b Compare June 5, 2026 16:14
Base automatically changed from feat/mcp-gateway-implementation to main June 5, 2026 17:10
@pandemicsyn pandemicsyn force-pushed the feat/mcp-gateway-dashboard branch from ba76bea to 64acfdc Compare June 5, 2026 17:19
@pandemicsyn pandemicsyn marked this pull request as ready for review June 5, 2026 17:23
Comment thread apps/web/src/app/(app)/cloud/mcp-gateway/page.tsx Outdated
Comment thread apps/web/src/routers/mcp-gateway-router.ts Outdated
Comment thread apps/web/src/lib/mcp-gateway/provider-oauth-service.ts Outdated
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented Jun 5, 2026

Code Review Summary

Status: 3 Issues Remaining | Recommendation: Address before merge

Executive Summary

Two new issues found in the d3f15a8e polish commit: an empty-array providerScopes sent to the create mutations is truthy and incorrectly tagged as an explicit override in config-service.ts, and the auth-mode-hint paragraph ID is now duplicated when dynamic registration is unavailable but oauth_dynamic is the selected mode.

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/app/(app)/cloud/mcp-gateway/McpGatewaySetupContent.tsx 258 When user edits then clears the provider scopes field, providerScopes = [] is sent. [] is truthy in JS, so config-service.ts classifies it as providerScopeSource: 'override' instead of falling back to discovered scopes. Fix: return undefined instead of [].
apps/web/src/app/(app)/cloud/mcp-gateway/McpGatewaySetupContent.tsx 495 When discovery && hasProvider && !dynamicAvailable && selectedAuthMode === 'oauth_dynamic' (the default initial state), lines 495 and 501 both render a <p id="auth-mode-hint"> simultaneously. Duplicate IDs break aria-describedby and are an HTML validity violation.
apps/web/src/routers/mcp-gateway-router.ts 364 discover mutation still uses baseProcedure (plain authenticated procedure). It makes outbound HTTP requests to caller-supplied URLs and can be used as an SSRF probe by any authenticated user. Author noted intent to punt on this.

Resolved Since Last Review

File Previous Issue Status
apps/web/src/lib/mcp-gateway/provider-oauth-service.ts startDashboardProviderSignIn hardcoded scopes: ['profile'] ✅ Fixed
apps/web/src/app/(app)/cloud/mcp-gateway/page.tsx adminOnly: false gate missing ✅ Fixed in 5e0479f95
apps/web/src/lib/mcp-gateway/provider-oauth-service.ts requireBearerTokenType redundant null check after the call ✅ Fixed — function now returns string and the post-call null guard is removed
apps/web/src/lib/mcp-gateway/config-service.ts providerScopeSource used as ProviderScopeSource cast ✅ Fixed — now uses flow-sensitive typing
Files Reviewed (8 files — incremental since b7f3b7f)
  • apps/web/src/app/(app)/cloud/mcp-gateway/ConnectionStatusBadge.tsx — cosmetic Tailwind opacity/colour tweak, no issues
  • apps/web/src/app/(app)/cloud/mcp-gateway/McpGatewayDetailContent.tsxrouter.push replaces window.location.assign; useMemo for excludedUserIds is correct
  • apps/web/src/app/(app)/cloud/mcp-gateway/McpGatewaySetupContent.tsx — 2 issues (see above)
  • apps/web/src/components/ui/checkbox.tsxaccent-secondary + text-foreground neutral tweak, no issues
  • apps/web/src/lib/mcp-gateway/config-service.tsas cast removed cleanly; empty-array edge case carried over from router input
  • apps/web/src/lib/mcp-gateway/discovery-service.ts — double validatePublicHttpsDestination call on endpoint is redundant but not a bug
  • apps/web/src/lib/mcp-gateway/provider-oauth-service.tsrequireBearerTokenType now returns string, eliminating the post-call null guard; clean refactor
  • apps/web/src/routers/mcp-gateway-router.tsmcpGatewayProcedure/mcpGatewayAdminProcedure aliases removed in favour of direct baseProcedure/adminProcedure; discover SSRF concern carried forward

Fix these issues in Kilo Cloud


Reviewed by claude-4.6-sonnet-20260217 · 2,190,940 tokens

Review guidance: REVIEW.md from base branch main

const providerScopes = draft.providerScopesEdited
? draft.providerScopes.trim()
? draft.providerScopes.trim().split(/\s+/)
: []
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Empty array sent as providerScopes is truthy and will be treated as an explicit override in config-service.ts.

When a user types into the scope field then clears it, draft.providerScopesEdited is true but draft.providerScopes.trim() is empty, producing providerScopes = []. In config-service.ts lines 64 and 484, input.providerScopes ? 'override' : ... evaluates [] as truthy, so providerScopeSource is set to 'override' and provider_scopes = [] is persisted — instead of falling through to the discovered scopes. The fix is to replace [] with undefined here:

Suggested change
: []
: undefined;

hasProvider &&
!dynamicAvailable &&
selectedAuthMode !== 'oauth_static' && (
<p id="auth-mode-hint" className="text-muted-foreground text-xs">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Duplicate id="auth-mode-hint" — two elements with this ID can render simultaneously.

When discovery && hasProvider && !dynamicAvailable is true and selectedAuthMode === 'oauth_dynamic' (the default), both this <p> (line 495) and the <p> at line 501 render at the same time, each with id="auth-mode-hint". Duplicate IDs invalidate the aria-describedby association and are an HTML validity violation.

The prior conditional aria-describedby was more correct. One approach is to compute a single derived hint string and render exactly one <p id="auth-mode-hint"> conditionally, or to revert the SelectTrigger aria-describedby back to a conditional reference.

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