21 KiB
Gap Card — S08 · MCP Hub
UX-refactor planning artifact. Execution model is the LOCKED in-place incremental refactor of
apps/web+ targeted backend extensions. Mockup is directional; PRD acceptance criteria win. Every claim below cites a real file. PRD =docs/Waggle_OS_UX_Refactor_Master_Handoff_Package/Waggle_OS_UX_Refactor_PRD.md.
1. Screen & purpose
MCP Hub — the power-user capability-extension surface for Model Context Protocol servers (PRD §12.8,
PRD line 566-579). Belongs to the Extend IA layer (PRD §10.4; blueprint line 74, 81: route group
/mcps). Explicit blueprint directive: "Do not bury Connectors/MCPs inside Settings" (blueprint line 79).
Purpose (PRD §12.8): "Enable power-user extension through Model Context Protocol servers." MCPs are auditable capability providers for power users and agents (blueprint line 140). The governing acceptance criterion: "MCPs are powerful but always visible, scoped, auditable, and reversible." (PRD line 579).
Primary user journey — J11 / Journey 9 "Install MCP" (PRD lines 736-743; blueprint line 206-208): power user opens MCP Hub → selects e.g. Postgres MCP → reviews risk and scope → approves installation → MCP becomes available to selected workspace/agent → install audit is recorded.
Mockup (directional only — screen_08_mcp_hub.png): top toolbar with tabs (Installed / Available /
Marketplace / Custom / Remote Registry), search, "Add MCP Server" CTA; left an "Installed MCPs" table
(name, status pill, connected-to, last-used, scope, actions) over an "Available MCPs" category-filtered
grid; right rail with an "MCP Overview" donut (counts), "MCP Health", "Recent Activity" (audit/log feed),
and a "Custom MCP" add affordance.
2. Required states (PRD / Blueprint)
Tabs (PRD §12.8 line 572): Installed · Available · Marketplace · Custom · Remote Registry.
MCP object fields (PRD §12.8 line 573): name, description, version, status, connected to,
last used, locality (local/remote), risk, permissions, logs. Target schema
Extension/MCP (blueprint line 554, PRD §15.2 ExtensionType): id, type, name, source, version, endpoint, capabilities, riskLevel, approvalClass, status, health, installedAt, approvedBy.
Actions (PRD §12.8 line 574): install · start/stop · test · scope · view logs · revoke · add custom MCP. Risky MCPs require approval + audit trail (PRD line 575; blueprint line 161 "elevated connectors/MCPs … require approval").
Extension lifecycle states (PRD §14.7 lines 906-914): Available · Installed · Update available · Installing · Failed install · Risk approval required · Disabled/revoked. Plus per-server runtime status (blueprint line 334-335): installed; available; running; stopped; error; risk-approval-needed.
Connector/MCP UI states (PRD §14.5 / blueprint line 467): Loading, empty, populated, error.
Scope (acceptance-critical): an MCP must be scopeable to workspace and/or agent (PRD line 742; blueprint line 207 "scope to workspace/agent"; line 601 "Connector and MCP installs are visible, permissioned, health-checked and auditable"; line 600 "a workspace agent cannot run with hidden memory/tool/MCP access").
Offline degradation (blueprint line 516): offline mode degrades MCPs gracefully while keeping local workspace/memory available.
3. Current state in repo
There is NO dedicated MCP Hub app today. MCPs surface in three thin, mostly-static places:
-
Static catalog (discovery only) —
apps/web/src/components/os/apps/connectors/McpCatalog.tsx+McpServerCard.tsx+mcp-registry.ts(a re-export shim of@waggle/shared). Rendered as the "MCP Servers" tab insideConnectorsApp.tsx(apps/web/src/components/os/apps/ConnectorsApp.tsx:197-205,348-349). The catalog data isMCP_CATALOG/MCP_CATEGORIES/CATEGORY_EMOJIinpackages/shared/src/mcp-catalog.ts(McpServerinterface at:17;MCP_CATALOGat:53; 14 categories at:30). Per the backend-map (05f§12, lines 391-401), this catalog is static and not connected at runtime — a discovery/install-command directory.- The only "install" UX is copy-a-shell-command-then-restart (
McpServerCard.tsx:30-37,98-138copiesserver.installCmd; tooltip text:111-116: "Copy the command … Run it in your terminal … Restart Waggle"). There is no in-app install, start/stop, test, scope, revoke, logs, risk, or status — none of the PRD §12.8 actions/fields exist in the UI.
- The only "install" UX is copy-a-shell-command-then-restart (
-
Live runtime status (read-only) —
GET /api/capabilities/status(packages/server/src/local/routes/capabilities.ts:35-53,103-110) returnsmcpServers:[{name,state,healthy,tools}]from the real runtime. But the runtime is empty by default (see #3 below), so this list is always[]on a stock install. -
The MCP runtime engine (rich, but unwired) —
packages/agent/src/mcp/mcp-runtime.ts.McpRuntimehas the full lifecycle the PRD needs:addServer(config)(:308),removeServer(name)(:327),startAll/stopAll, per-instancestart()/stop()/callTool()(McpServerInstance:102,154,178),getServerStates()(:362),getHealthy()(:370),isServerHealthy(name)(:399), and per-workspace scoping viaMcpServerConfig.workspaceId(:13) +getToolsForWorkspace(workspaceId)(:385). CRITICAL GAP: the runtime is instantiated empty —const mcpRuntime = new McpRuntime()with comment "empty by default" (packages/server/src/local/index.ts:911), and nothing ever callsaddServer()(grepmcpRuntime.addServer/.mcp.json/loadMcpConfig→ 0 matches inpackages/server/src). So there is no persistence layer that loads installed MCP configs at boot, and no HTTP route to install/ start/stop/test/remove a server.
Install-audit substrate is present (AuditCapabilityType includes 'mcp' —
packages/core/src/install-audit.ts:22; InstallAuditStore.record/getRecent/getByCapability exist) but
has no read HTTP route (substrate-types inventory §d: writes only; GET /api/audit/installs exists in
skills.ts but is a generic recent-installs feed). MCP installs are not currently audited because there is
no MCP install path to audit.
Adapter: no getMcps/installMcp/testMcp methods in apps/web/src/lib/adapter.ts (grep → 0 matches).
Disposition: create-new (with substrate reuse). A net-new MCPHub app (MCPHubApp.tsx) promoted
out of ConnectorsApp's MCP tab, **plus net-new /api/mcps/* backend routes wiring the existing McpRuntime
- a new persisted config store + install-audit**. The static catalog component (
McpCatalog/McpServerCard) is reused as the "Available"/"Marketplace" tab content. This is the single largest backend gap on the Extend layer — the engine exists but is entirely unwired to UI or persistence.
4. Frontend work
New IA placement: add an mcp-hub (or reuse the dead marketplace-adjacent slot) AppId in
apps/web/src/lib/dock-tiers.ts, register chrome in Desktop.tsx appConfig + a renderAppContent
switch case (per frontend inventory §b lines 123-129), and add a dock entry under the Extend zone-parent
(dock-tiers.ts zone model). MCP Hub is power-user/PRO-tier-gated (PRD personas: power user/developer needs
MCPs — PRD line 151).
Components to CREATE (apps/web/src/components/os/apps/):
MCPHubApp.tsx— shell with the 5 PRD tabs (Installed / Available / Marketplace / Custom / Remote Registry). MirrorConnectorsApp.tsx's sidebar-tab + filter layout (:183-216) so it's visually consistent with the Connector Hub it splits from.mcp/InstalledMcpTable.tsx— table of running/installed servers: name, status pill (running/stopped/ error/installing — mapsMcpServerStatemcp-runtime.ts:16), connected-to, last-used, locality, scope chip, row actions (start/stop, test, scope, view logs, revoke). Drives PRD §12.8 line 573-574.mcp/AddCustomMcpForm.tsx— Custom tab:{ name, command, args[], env{}, workspaceId? }→POST /api/mcps(matchesMcpServerConfigmcp-runtime.ts:8-14). Reuseinjection-scannerpatterns server-side.mcp/McpDetailPanel.tsx(right rail) — MCP Overview counts, MCP Health, permissions/scope editor, logs viewer, and the risk/approval banner ("Risk approval required" state). Reuse the approvals surface pattern fromApprovalsApp.tsx/ inline-chat approval (same backend/api/approval/*).mcp/McpScopeDialog.tsx— scope-to-workspace/agent picker (writesworkspaceIdonto the server config; later,mcpIds[]onto workspace/agent per §6).
Components to REUSE (keep-promote):
McpCatalog.tsx+McpServerCard.tsx+mcp-registry.ts→ become the Available + Marketplace + Remote Registry tab bodies. TodayMcpServerCardonly shows a copy-command strip (McpServerCard.tsx:98-138) — rework it to add a real Install button (calls the newinstallMcpadapter method) while keeping copy-command as the offline fallback.recommendConnectorsfrom@waggle/shared(already usedMcpCatalog.tsx:15,69) for "Recommended" tile.LockedFeature.tsxfor tier gating;ContextMenu.tsxfor row actions; shadcntable,badge,dialog,tabs,tooltipprimitives (components/ui/*, frontend inventory §e).
Adapter methods to ADD (apps/web/src/lib/adapter.ts — the single sidecar gateway, §c lines 162-265):
getMcps(), installMcp(payload), addCustomMcp(config), startMcp(id), stopMcp(id), testMcp(id),
scopeMcp(id, scope), revokeMcp(id), getMcpLogs(id) — one per PRD §16.9 / §12.8 action.
New hook: hooks/useMcps.ts (mirrors the useConnectors-style pattern) returning
{ installed, available, custom, install, start, stop, test, scope, revoke, refresh }. Wire SSE/poll over
/api/capabilities/status or a new /api/mcps for live status (cache-invalidate on
"connector sync" / install completion per blueprint line 514).
5. Backend work (PRD §16.9 endpoints — all MCP rows)
All net-new routes live in a new
packages/server/src/local/routes/mcps.ts, registered inlocal/index.ts, wiring the already-builtMcpRuntime(server.agentState.mcpRuntime, decoratedlocal/index.ts:1361). No new SQLite table is strictly required — but a persisted config source IS (see migration flag).
| PRD §16.9 endpoint | Status | Extend vs net-new + substrate it touches |
|---|---|---|
GET /api/mcps |
PARTIAL → build net-new route over existing data | No /api/mcps exists (grep-confirmed in backend-routes inventory, line 422 & §16.9 line 522). The data is derivable today: live runtime status from McpRuntime.getServerStates()/getHealthy()/getAllTools() (already surfaced in capabilities.ts:35-53), enriched with catalog metadata from MCP_CATALOG (@waggle/shared). Net-new thin route in mcps.ts that joins runtime state + persisted config + catalog into the Extension/MCP shape (§2). Touches: McpRuntime (agent), MCP_CATALOG (shared), new config store. |
POST /api/mcps/install |
PARTIAL → net-new, route through existing install path | No /api/mcps/install (inventory §16.9 line 523). Closest install paths are marketplace (POST /api/marketplace/install — SecurityGate + audit, marketplace.ts:224-319) and plugin install (POST /api/plugins/install). Net-new MCP install that: (a) persists an McpServerConfig, (b) calls mcpRuntime.addServer(config) + start() (mcp-runtime.ts:308,102), (c) runs the SecurityGate + writes an install_audit row with capability_type:'mcp' (install-audit.ts:22) — satisfying the "install audit is recorded" acceptance step (PRD line 743). Risky servers → return risk approval required state instead of starting. Tier: PRO (match marketplace). |
POST /api/mcps/:id/test |
MISSING | No MCP test/health route (inventory §16.9 line 524). Closest analog is GET /api/connectors/:id/health. Net-new: resolve the server, start() if needed, assert isHealthy() (mcp-runtime.ts:94,399) and/or do a tools/list round-trip, return health + discovered tools. Touches McpRuntime. |
POST /api/mcps/:id/revoke |
MISSING | No MCP revoke/uninstall by id (inventory §16.9 line 525). Closest: DELETE /api/plugins/:name. Net-new: mcpRuntime.removeServer(name) (mcp-runtime.ts:327) + delete persisted config + write install_audit action:'rejected'/'revoked'. Touches McpRuntime + config store + install-audit. |
GET /api/marketplace |
PARTIAL | Listing is GET /api/marketplace/search (marketplace.ts); the Marketplace tab reuses it. Alias of /search (inventory §16.9 line 526). Not MCP-specific work. |
POST /api/marketplace/install |
EXISTS | marketplace.ts (Tier PRO, SecurityGate) — reused by the Marketplace tab. |
Additional routes implied by PRD §12.8 actions not in §16.9 list (net-new):
POST /api/mcps(add custom MCP) — blueprint API line 530 listsGET/POST /mcps. Persists config +addServer+ audit. Net-new.POST /api/mcps/:id/startandPOST /api/mcps/:id/stop— PRD §12.8 "start/stop" action. Map toMcpServerInstance.start()/stop()(mcp-runtime.ts:102,154). Net-new.PATCH /api/mcps/:id/permissions(scope/permissions) — blueprint API line 530 (PATCH /mcps/:id/permissions). WritesworkspaceId/scope onto the persisted config (runtime honors it viagetToolsForWorkspace,mcp-runtime.ts:385). Net-new.GET /api/mcps/:id/logs— PRD §12.8 "view logs". No log capture exists inMcpServerInstancetoday (stderr is piped:113but not retained). Net-new + small runtime change to buffer stderr/state-change events for retrieval.GET /api/extend/audit(or extend/api/audit/installs) — surface the install-audit trail for the MCP Hub right-rail "Recent Activity".InstallAuditStore.getRecent()exists but has no MCP-scoped HTTP read (substrate-types §d gap #1). Net-new (implied addition to §16.9, flagged in substrate inventory).
Migration / persistence flag (IMPORTANT):
- No
.mindSQLite migration is required for the MCP entity itself — installed MCP configs can persist as a JSON file (mirrorsworkspace.json/.mcp.jsonconvention; note repo root already has an untracked.mcp.jsonper git status) read at boot to populatemcpRuntime.addServer(). This persistence layer is entirely net-new (todaymcpRuntimeis empty and never populated —local/index.ts:911). This is the load-bearing backend gap: without it, "Installed" MCPs do not survive restart. - Install-audit DDL drift (latent, flag for the plan):
AuditRiskLevelTS includes'critical'(install-audit.ts:16) but both CHECK constraints allow onlylow|medium|high(install-audit.ts:65,schema.ts:130). Any MCP install recordingriskLevel:'critical'would throw a CHECK violation — map CRITICAL →'high'+approvalClass:'blocked'as marketplace.ts already does (marketplace.ts:224-319), or fix the DDL.
6. Shared types needed (PRD §15 vs lib/types.ts)
ExtensionType(PRD §15.2 line 953:'skill'|'connector'|'mcp'|'model'|'template'|'external_tool') — MISSING inapps/web/src/lib/types.ts(substrate-types §e: none of the §15.2 unions exist in FE). Add it.Extension/Mcpinterface (blueprint line 554:id, type, name, source, version, endpoint, capabilities, riskLevel, approvalClass, status, health, installedAt, approvedBy) — MISSING. NoExtension/Mcptype anywhere in FE. Add tolib/types.ts(and ideallypackages/shared/src/types.tsso the sidecar route and FE share it). The existingMcpServer(@waggle/sharedmcp-catalog.ts:17:id,name,description,author,category,url,installCmd,capabilities,official?,logo?) is the catalog/discovery shape — distinct from the installed-instanceMcpshape; keep both, the Hub joins them byid.- Runtime types already exist server-side and should be the source of truth for the route contract:
McpServerConfig,McpServerState,McpToolInfo(packages/agent/src/mcp/mcp-runtime.ts:8-22). MirrorMcpServerState('starting'|'ready'|'error'|'stopped') into the FE status union (map to PRD §14.7 states). - Reuse from install-audit:
AuditCapabilityType,AuditRiskLevel,AuditApprovalClass(packages/core/src/install-audit.ts:15-22) for the riskLevel/approvalClass/audit fields — do not invent parallel enums. mcpIds[]on Workspace/Agent (PRD §15.3WorkspaceConfigV2.mcpIds, §15.5 AgentmcpIds) — MISSING from bothWorkspaceConfig(workspace-manager.ts, substrate-types §a) andAgentDef(packages/shared/src/types.ts:36-47, §e). Needed for the "scope to workspace/agent" action. Additive optional field (no DB migration —workspace.jsonis JSON).
7. Dependencies (screens / phases first)
- Connector Hub (S07) — MCP Hub is split OUT of
ConnectorsApp.tsx's MCP tab; do the Connector Hub promotion first (or jointly) so the shared sidebar-tab/filter/health/state patterns are settled and the MCP tab can be cleanly extracted. SameExtend-layer dock placement work. - Extend layer / dock-zone IA — requires the new
AppId+ dock-zone wiring (frontend inventory §b/§d: consolidate onAppId, add Extend zone entries). Blocks any new Extend app from being reachable. - Install-audit read route (§d gap) — shared with Skills (S?) and Connectors governance; build once, reuse across the Extend layer.
- Approvals surface — risk-approval flow reuses existing
/api/approval/*+ApprovalsApppatterns; no new approval substrate, but the wiring depends on that surface staying stable. - Marketplace — the Marketplace tab reuses
GET /api/marketplace/search+POST /api/marketplace/install(already exist); ensure marketplace MCP packages are tagged so they route to the MCP install path. - Phase hint: later phase. Backend wiring (persisted config store + runtime population +
/api/mcps/*) is the prerequisite for any non-static UI; the static catalog already ships, so this is a depth upgrade, not a day-1 blocker. Sequence after Home/Workspace/Memory core screens.
8. Effort: XL
Largest Extend-layer gap: the runtime engine exists but is completely unwired (empty McpRuntime, no
persistence, no HTTP surface, copy-command-only UI). Requires a net-new persisted MCP-config store + boot-time
runtime population + ~8 net-new sidecar routes (install/start/stop/test/scope/revoke/logs + custom add) +
SecurityGate/audit integration + a full new app shell with 5 tabs, installed-table, detail/logs panel, scope
dialog, risk-approval flow, and adapter/hook layer — while preserving the static catalog as the
Available/Marketplace tabs. (Frontend-only would be M; the backend wiring + persistence + governance is what
pushes it to XL.)
9. Open questions
- Persistence location/format for installed MCP configs — JSON file (
.mcp.jsonat dataDir, mirroringworkspace.json; root already has an untracked.mcp.json) vs a newinstall_audit-adjacent table? Recommendation: JSON file (no migration), but confirm the dataDir path + multi-workspace scoping model. - Scope model — is an MCP scoped by writing
workspaceIdonto its single config (1 server : 1 workspace, per currentMcpServerConfig.workspaceId), or by anmcpIds[]membership array on each workspace/agent (N:N)? PRD §15.3/§15.5 implymcpIds[](N:N); the runtime today only supports the single-workspaceIdfield (mcp-runtime.ts:13,385). N:N needs a runtime change. - Logs capture —
McpServerInstancepipes stderr but does not retain it (mcp-runtime.ts:113). Add a ring-buffer of stderr + stateChange events forGET /api/mcps/:id/logs, or defer logs to a later phase? - "Remote Registry" tab semantics — is this the Composio gateway (already referenced in
McpCatalog.tsx:106,294-301), the officialmodelcontextprotocol/serversregistry, or a remote (HTTP/SSE-transport) MCP class distinct from local stdio? The runtime today is stdio-only (mcp-runtime.ts:108-115); remote-transport MCPs would need a new transport inMcpServerInstance. - Install execution surface — does in-app "Install" run
npx …(spawn a child process to install the package) in the sidecar, or only register config + rely on a globally-installed binary? Security review needed (spawning installers vs the current copy-to-terminal model). Tauri IPC/CSP implications. - Tier gate — confirm MCP Hub is PRO+ (matches marketplace install gate) vs power-user-density-tier
(
UserTier) only. Three tier vocabularies to reconcile (frontend inventory §f).