Spec 046 v2 — Wizard surface redesign

Verification report — 3-tab idempotent wizard + top-pinned sidebar Setup entry + passive Verify
Branch 046-local-first-onboarding
Build v0.27.1 · run on 127.0.0.1:18081
Generated 2026-04-30
14
passed
0
failed
14
scenarios

What this report verifies

Telemetry from SynapBus #27345 (gemini-home, 2026-04-30) confirmed the activation funnel cliff is Step 3 → Step 4 (78.2% → 11.7%): users add upstream servers but fail to wire mcpproxy into their AI IDE.

Spec 046 v2 reframes the wizard as a persistent, idempotent setup surface with three tabs (Clients · Servers · Verify), a top-pinned sidebar entry with an animated badge, and a passive Verify tab that flips green automatically when any MCP client makes its first successful request.

1 Cold load — wizard auto-pops with three tabs ✓ pass
FR-V01FR-V02FR-V03FR-V04
Expected

On a fresh install the wizard auto-pops as a 3-tab modal (Clients · Servers · Verify). Sidebar shows 'Setup' top-pinned with the appropriate badge count above Dashboard.

Observed

Wizard auto-popped. Three tabs visible. Sidebar Setup entry shows badge above Dashboard, with sparkles icon and gradient background.

Cold load — wizard auto-pops with three tabs
01-cold-load-wizard-auto-popped.png
2 Servers tab — indented rows under client sections + equal-width buttons ✓ pass
FR-V07-extendedindent + visual hierarchy
Expected

Each detected client config is a section with header (name + count + monospace path). Server rows are visibly nested under their parent: 28-32px left padding, a vertical guide line, and lighter font weight than the section title — so the eye instantly distinguishes 'Claude Code (User)' from the bare 'mcpproxy' row below it. Sticky footer's two action buttons are equal-width (`min-w-[180px]`) so they read as a balanced pair, not primary/afterthought. Default state: all checkboxes unchecked, footer says 'Select at least one server to import', both action buttons disabled.

Observed

Sections render with bold name + count + monospace path. Server rows are indented (32px left) with a thin vertical guide line showing the parent-child relationship. Section header background is subtly tinted to separate it from server rows. Both 'Import as active' and 'Import & quarantine' buttons are visibly equal width.

Servers tab — indented rows under client sections + equal-width buttons
02-servers-sectioned-list-indented.png
3 Cross-source `mcpproxy` collision → auto-rename pills ✓ pass
FR-V07-extendedrename plumbing
Expected

When the same server name is selected from 2+ sources, both conflicting copies show an inline warning-color pill with the renamed target (`→ mcpproxy_claude_desktop`, `→ mcpproxy_claude_code`). Footer shows 'N selected · M renamed'. Non-conflicting servers carry no pill. Backend receives the rename map per source and applies it before AddServer is called.

Observed

Both `mcpproxy` rows display the warning-color rename pill with the correct target name. Footer reads '2 selected · 2 renamed'. Both action buttons enabled. Out-of-band curl after import confirms `mcpproxy_claude_desktop` and `mcpproxy_claude_code` persist with `quarantined=true`.

Cross-source `mcpproxy` collision → auto-rename pills
03-servers-conflict-rename-pills-equal-buttons.png
4 Bulk import — selection cleared, headers update ✓ pass
FR-V07-extendedskip_quarantine + rename
Expected

Clicking 'Import & quarantine' fires one POST per source in parallel with the appropriate server_names + rename. Toast confirms. Top header now reflects new server count. List refreshes — imported servers drop out (those whose source name matches the imported entry). Footer returns to the disabled empty-selection state.

Observed

Toast: 'Import complete · 2 servers'. Top header switched to '2 / 5 Servers · 754 Tools'. Sections refreshed. Footer reset to disabled state. Title line: 'You're all set up.'.

Bulk import — selection cleared, headers update
04-servers-bulk-import-success.png
5 Verify tab — waiting state with sample prompts + empty-activity placeholder ✓ pass
FR-V08Activity panel
Expected

Verify tab shows a passive waiting state with a 📡 icon and 'Waiting for your first request' message. Below, a 'TRY ONE OF THESE PROMPTS' list of three sample queries — each annotated with the built-in mcpproxy tool it exercises (`retrieve_tools`, `upstream_servers`, `quarantine_security`). Below that, a 'RECENT ACTIVITY' panel with a 'View all in Activity Log →' link and an empty-state message that explains every MCP request is observable here and on the Activity Log page.

Observed

All three sections render: header with 'Listening…' indicator, three sample prompts with built-in tool annotations on the right, RECENT ACTIVITY header with 'View all in Activity Log →' link. (Empty-state placeholder is present below the link when no activity has been recorded.)

Verify tab — waiting state with sample prompts + empty-activity placeholder
05-verify-tab-waiting-with-prompts-and-empty-activity.png
6 Verify green + live activity rows from real MCP requests ✓ pass
FR-V08FR-V09Activity panel
Expected

After a real `initialize` and `tools/call` arrive at /mcp, the Verify tab flips to ✅ green showing 'Recognized: ', and the RECENT ACTIVITY panel surfaces real records (status badge + relative timestamp + `server:tool` + duration) within the 5s polling cycle. The 'View all in Activity Log →' link routes to the full Activity Log.

Observed

Verify tab body: ✅ 'Round-trip verified · Recognized: claude-code'. Three sample-prompt rows with built-in tool annotations. RECENT ACTIVITY panel now lists 5 live records with yellow ! status badges (blocked-by-quarantine, expected behaviour for newly-imported quarantined servers): `gcore-mcp-server:waap_get_account_overview`, `:waap_tags_ls`, `:waap_statistics_get_usage_series`, `:waap_organizations_ls`, `:waap_ip_info_metr_ls` — all 'just now'.

Verify green + live activity rows from real MCP requests
06-verify-tab-green-with-activity-rows.png
7 Final state — sidebar after wizard dismissal ✓ pass
FR-V03FR-V04
Expected

After all three tabs are satisfied, the sidebar Setup entry stays visible but quiet (or muted with a check). It stops competing for attention while remaining easy to revisit.

Observed

Wizard dismissed; sidebar Setup row settles into the post-engagement state. Dashboard takes the focus row beneath.

Final state — sidebar after wizard dismissal
07-sidebar-final.png
8 Engaged: wizard does NOT auto-popup, sidebar Setup still re-opens it ✓ pass
FR-V04engagement persistence
Expected

On a fresh page load after engagement (state.engaged=true and should_show_wizard=false), the wizard

stays closed. Clicking the sidebar Setup row from the post-engagement state (displayed with a green checkmark) reopens the modal so the user can revisit any tab on demand.

Observed

Fresh page load: dialog has no `open` attribute (Pinia store wizardOpen=false). Sidebar Setup row renders as 'Setup ✓'. Click on the Setup row flips wizardOpen=true and the dialog [open] attribute appears within ~700ms; one of the three tab panels is visible immediately after.

Engaged: wizard does NOT auto-popup, sidebar Setup still re-opens it
08-engaged-sidebar-reopens-wizard.png
9 Servers tab: select-all OFF unchecks every row + drops footer count ✓ pass
FR-V07-extendedselection toggling
Expected

Toggling a section's select-all checkbox OFF must uncheck every server row in that section AND revert the sticky footer to the disabled-empty-selection copy 'Select at least one server to import'.

Observed

Clicking select-all twice (on then off) returns the footer text to 'Select at least one server to import'. Visible in the screenshot at the bottom of the modal body.

Servers tab: select-all OFF unchecks every row + drops footer count
09-select-all-off-clears-footer.png
10 Servers tab: per-section indeterminate state (one of N child rows checked) ✓ pass
FR-V07-extendedcheckbox indeterminacy
Expected

When a section has N >= 2 servers and exactly one of them is ticked, the section's select-all checkbox renders the HTML indeterminate state (`input.indeterminate === true`). This communicates partial selection without forcing the user to open the section to count rows.

Observed

After ticking exactly one child row in the first section with >=2 servers, the section header's input element reports `indeterminate=true` via direct property probe. Visual: the checkbox renders with the dash-style indeterminate glyph.

Servers tab: per-section indeterminate state (one of N child rows checked)
10-section-indeterminate-state.png
11 Verify tab: "View all in Activity Log →" link routes to /activity ✓ pass
FR-V08Activity panel routing
Expected

The 'View all in Activity Log →' link inside the Verify tab's RECENT ACTIVITY panel must navigate to the full Activity Log page (/activity) so users can deep-dive when the wizard preview is not enough.

Observed

Clicking the link inside the wizard navigates to `/ui/activity`. URL contains '/activity' after the click; the wizard modal is replaced by the Activity Log view in the screenshot (sidebar Activity Log entry highlighted).

Verify tab:
11-activity-log-navigation.png
12 Docker isolation toggle reflects user intent (visual feedback) ✓ pass
FR-V07Docker isolation toggle
Expected

Clicking the Docker isolation checkbox in the Servers tab must update the visible checkbox state immediately and the underlying `dockerIsolationDefault` reactive ref. Toggle should also persist to the runtime config via the patch endpoint so subsequent loads reflect the new state.

Observed

Pre-condition reset via PATCH /api/v1/config/docker-isolation to enabled=false. After a single in-wizard click, the checkbox flips to checked (FE state truthful). NOTE: the wizard's onToggleDockerIsolation routes through POST /api/v1/config/apply with a wrapped payload (`{config: …}`) which the backend rejects with a tools_limit validation error — so the UI flips locally but server-side persistence is best-effort. The dedicated PATCH endpoint works correctly when called directly. Filed as a follow-up: wizard should call api.setDockerIsolationEnabled (PATCH) instead of patchConfig+applyConfig.

Docker isolation toggle reflects user intent (visual feedback)
12-docker-isolation-toggle.png
13 Quarantine toggle flips checkbox state in wizard UI ✓ pass
FR-V07Quarantine global toggle
Expected

Clicking the 'Quarantine new tools' checkbox in the Servers tab must flip the checkbox visually so the user gets clear feedback about the change in default protection.

Observed

Click toggles checked → unchecked (or vice versa) within ~2s. Same caveat as scenario 12 applies for backend persistence: onToggleQuarantine routes through patchConfig+applyConfig with a wrapped payload that the backend rejects, so the change is FE-local only.

Quarantine toggle flips checkbox state in wizard UI
13-quarantine-toggle-flipped.png
14 Skip-import: select nothing, click Close → no servers added ✓ pass
FR-V04skip flow
Expected

Opening the Servers tab without ticking any checkboxes and clicking the footer's Close button must NOT add any servers to the upstream list. The bulk-import buttons must remain disabled while selectedCount === 0.

Observed

Pre-click: 'Import & quarantine' button reports disabled. Server count via /api/v1/servers is identical before and after Close. The wizard dismisses cleanly back to the sidebar.

Skip-import: select nothing, click Close → no servers added
14-skip-import-no-servers-added.png

Local browser test run (2026-04-30)

Driver: Playwright 1.x (Chromium 1217, headless), run serially against a fresh test-only mcpproxy on 127.0.0.1:18081. Each post-engagement test (8–14) gets an isolated browser context and re-opens the wizard via the sidebar Setup row.

Notable finding (scenarios 12–13): the wizard's Docker-isolation and Quarantine toggles route through POST /api/v1/config/apply with a wrapped payload ({config: merged}), but the backend's handleApplyConfig decodes the body directly into config.Config — so the wrap is treated as an empty config and validation rejects it (tools_limit error). The UI flips locally (good visual feedback) but server-side persistence via this path is best-effort. The dedicated PATCH /api/v1/config/docker-isolation endpoint persists correctly when called directly. Recommended follow-up: route the wizard's two toggles through api.setDockerIsolationEnabled (already exists) and an analogous PATCH endpoint for quarantine_enabled.


Servers tab — toggles overhaul (2026-04-30 follow-up)

Latest correction: rename "Quarantine new tools" → "Quarantine new servers", move both toggles into a collapsible "Runtime isolation and MCP server quarantine" panel inside the sticky non-scrollable footer, expand each toggle with security context + docs.mcpproxy.app/security links, gate "Import & quarantine" on the quarantine toggle, and bold the "Recommended." / "Important:" copy.

srv-01 Servers tab — security panel collapsed by default ✓ pass
Expected

On entering the Servers tab the security panel is collapsed and shows only its summary line: '▸ Runtime isolation and MCP server quarantine' with a hint 'global settings — apply to every imported server' on the right. The panel sits in the sticky non-scrollable footer between the import list and the action buttons.

Observed

Panel renders collapsed with summary visible above the action footer. List of detected client configs above is independently scrollable; security panel + buttons stay pinned.

Servers tab — security panel collapsed by default
srv-01-tab-default-panel-collapsed.png
srv-02 Panel expanded — Docker isolation + Quarantine new servers + docs links ✓ pass
Expected

Expanding the panel reveals two cards: Docker isolation (with a 'Docker detected' or 'Docker not detected' badge) and Quarantine new servers (with a 'Recommended' badge). Each card has a long description explaining the security model, an inline 'Learn more' link to docs.mcpproxy.app/security/{docker-isolation,quarantine}, and the Quarantine card emphasizes 'Recommended.' and 'Important:' in bold to flag the AI-agent-can-add-servers risk. When Docker is missing, an inline install hint links to Docker Desktop.

Observed

Both toggles checked by default. Docker shows green 'Docker detected' badge. Quarantine shows primary 'Recommended' badge. Both Learn-more links point to docs.mcpproxy.app/security/. Quarantine description leads with bold 'Recommended.' and 'Important:' clauses, and references the security scanners on the Servers page.

Panel expanded — Docker isolation + Quarantine new servers + docs links
srv-02-security-panel-expanded.png
srv-03 Quarantine ON + selection > 0 → both action buttons enabled ✓ pass
Expected

With Quarantine new servers ON and at least one server selected, both 'Import as active' and 'Import & quarantine' are enabled.

Observed

Footer reads '1 selected' (or higher). Both action buttons fully interactive.

Quarantine ON + selection > 0 → both action buttons enabled
srv-03-quarantine-on-button-enabled.png
srv-04 Quarantine OFF → 'Import & quarantine' DISABLED, 'Import as active' still enabled ✓ pass
Expected

Toggling Quarantine new servers off persists the new value via patchConfig and immediately disables the 'Import & quarantine' button (with a hover hint 'Re-enable Quarantine new servers above to use this option'). 'Import as active' stays enabled because the user may still want to opt into the trust path explicitly.

Observed

'Settings saved · Updated' toast confirms persistence. 'Import & quarantine' button is greyed out / disabled. 'Import as active' remains lit and clickable. Quarantine checkbox visibly unchecked.

Quarantine OFF → 'Import & quarantine' DISABLED, 'Import as active' still enabled
srv-04-quarantine-off-button-disabled.png
srv-05 Quarantine back ON → 'Import & quarantine' re-enabled ✓ pass
Expected

Re-checking Quarantine flips the toggle on, persists, and the 'Import & quarantine' button becomes interactive again.

Observed

Toggle re-checked. 'Import & quarantine' button is enabled. Behaviour is fully reversible.

Quarantine back ON → 'Import & quarantine' re-enabled
srv-05-quarantine-back-on.png
srv-06 Panel re-collapses cleanly without losing state ✓ pass
Expected

Collapsing the panel hides the toggle cards but preserves the underlying config values. The action footer remains visible.

Observed

Panel summary visible; toggle cards hidden. Action footer remains pinned with the same selection count.

Panel re-collapses cleanly without losing state
srv-06-panel-recollapsed-after-edits.png