Skip to content

feat(046): adaptive onboarding wizard with state-driven steps#433

Merged
Dumbris merged 8 commits into
mainfrom
046-local-first-onboarding
Apr 30, 2026
Merged

feat(046): adaptive onboarding wizard with state-driven steps#433
Dumbris merged 8 commits into
mainfrom
046-local-first-onboarding

Conversation

@Dumbris
Copy link
Copy Markdown
Member

@Dumbris Dumbris commented Apr 28, 2026

Summary

Adds a first-run onboarding wizard that adapts to the user's state. Two predicates drive which steps render — any AI client connected to mcpproxy? and any upstream MCP server configured?. With both false the wizard shows two steps; with one false it shows just that one; with both true the dashboard loads directly. Skip on any step lands on the dashboard; once engaged, the wizard never auto-shows again. A "Run setup wizard" link in the dashboard re-runs it on demand.

Reuses Spec 039's connect endpoint and the existing add-server flow — never duplicates either. Quarantine remains the trust boundary; the server step explains it plainly but does not change quarantine behaviour.

Spec: specs/046-local-first-onboarding/spec.md. This PR delivers US1 (adaptive wizard) end-to-end. US2 (tier-2 client adapters) and US3 (heartbeat funnel telemetry + D1 schema migration) are tracked as follow-up work; signals required by US3 are already exposed by this PR.

Why now

Production telemetry over 1,491 unique installs (2026-03-23 → 2026-04-28) shows the dominant onboarding gap is the retention cliff (11.8% day-2 retention), not server addition (97.9% of installs already have servers via import). The wizard's high-leverage primary path is therefore connecting an AI client, with adding a server treated as a secondary step that explains quarantine rather than gating addition.

Backend

  • internal/storage/models.go + bbolt.go + manager.go — new OnboardingBucket and OnboardingState record (engaged, first-shown timestamp, per-step status); thread-safe Get/Save accessors.
  • internal/runtime/runtime.go + internal/server/server.go — pass-through wrappers to storage.
  • internal/connect/connect.goGetConnectedCount() / GetConnectedIDs() reusing Spec 039's adapter table.
  • internal/httpapi/onboarding.go (new):
    • GET /api/v1/onboarding/state → live predicates (has_connected_client, has_configured_server, connected_client_count, connected_client_ids, configured_server_count) + persisted record + derived should_show_wizard.
    • POST /api/v1/onboarding/mark → updates per-step status, first-shown timestamp, engagement.
  • internal/httpapi/server.goServerController interface gains GetOnboardingState / SaveOnboardingState; routes registered alongside /api/v1/connect.
  • Test mocks (security_test.go, contracts_test.go) updated.

Frontend

  • frontend/src/types/api.tsOnboardingState, OnboardingStateResponse, OnboardingMarkRequest.
  • frontend/src/services/api.tsgetOnboardingState() / markOnboardingState().
  • frontend/src/stores/onboarding.ts (new) — Pinia store with derived visibleSteps and per-step mark helpers.
  • frontend/src/components/OnboardingWizard.vue (new) — adaptive 1-2 step modal with progress indicator, per-step skip, quarantine explainer in the server step. Reuses the existing AddServerModal for server addition.
  • frontend/src/views/Dashboard.vue — auto-show on mount when should_show_wizard, "Run setup wizard" link for manual re-run.

OpenAPI

  • oas/docs.go and oas/swagger.yaml regenerated with the new /onboarding endpoints.

Test plan

  • Storage / connect / httpapi unit tests pass
  • State combo: no clients + no servers — 2-step wizard with progress indicator, both steps visible
  • State combo: has clients + no servers — single server step with quarantine explainer
  • State combo: has clients + has servers — wizard suppressed, dashboard loads directly
  • Skip on each step — engaged + step status persisted, no partial commits
  • Reload after engaged — wizard does not auto-show again
  • "Run setup wizard" link re-opens the wizard after engagement
  • Funnel data (first-shown, engaged-at, per-step status) persisted in BBolt
  • Reviewer to test: register a real AI client through the wizard and verify the resulting client config is byte-clean and a backup was created

Out of scope (follow-ups)

  • US2 — tier-2 client adapters (Antigravity, Zed, OpenCode, Amazon Q, Kiro, LM Studio, Cline, Roo Code, Junie, Copilot CLI, Copilot JetBrains, Pi, Amp). One-PR-per-adapter against the existing per-client table.
  • US3 — heartbeat fields (connected_client_count, connected_client_ids, wizard_funnel_state) + Cloudflare D1 schema migration + website /telemetry page docs. Backend signals are already exposed; next PR wires them into HeartbeatPayload.

Related #46

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 28, 2026

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d4f403
Status: ✅  Deploy successful!
Preview URL: https://44f35901.mcpproxy-docs.pages.dev
Branch Preview URL: https://046-local-first-onboarding.mcpproxy-docs.pages.dev

View logs

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 28, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 6.45161% with 145 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/httpapi/onboarding.go 0.00% 71 Missing ⚠️
internal/runtime/runtime.go 0.00% 20 Missing ⚠️
internal/storage/bbolt.go 4.76% 20 Missing ⚠️
internal/connect/connect.go 0.00% 13 Missing ⚠️
internal/server/server.go 0.00% 8 Missing ⚠️
internal/storage/manager.go 0.00% 8 Missing ⚠️
internal/httpapi/import.go 58.33% 4 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

📦 Build Artifacts

Workflow Run: View Run
Branch: 046-local-first-onboarding

Available Artifacts

  • archive-darwin-amd64 (26 MB)
  • archive-darwin-arm64 (23 MB)
  • archive-linux-amd64 (15 MB)
  • archive-linux-arm64 (13 MB)
  • archive-windows-amd64 (26 MB)
  • archive-windows-arm64 (23 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (19 MB)
  • installer-dmg-darwin-arm64 (18 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 25172546396 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

claude added 2 commits April 28, 2026 21:09
Adds a first-run onboarding wizard that adapts to the user's state. Two
predicates drive which steps render — "any AI client connected to
mcpproxy?" and "any upstream MCP server configured?". With both false the
wizard shows two steps; with one false it shows just that one; with both
true the dashboard loads directly. Skip on any step lands on the
dashboard; once engaged, the wizard never auto-shows again. A "Run setup
wizard" link in the dashboard re-runs it on demand.

Backend reuses Spec 039's connect endpoint and the existing add-server
flow; the wizard never duplicates either. Quarantine remains the trust
boundary — the server step explains it plainly but does not change
quarantine behaviour.

## Changes

Backend (Go):
- internal/storage/models.go + bbolt.go + manager.go: new
  OnboardingBucket and OnboardingState record (engaged, first-shown,
  per-step status); thread-safe Get/Save accessors.
- internal/runtime/runtime.go + internal/server/server.go: pass-through
  wrappers to storage.
- internal/connect/connect.go: GetConnectedCount() and
  GetConnectedIDs() helpers reusing the existing per-client adapter
  table.
- internal/httpapi/onboarding.go (new): GET /api/v1/onboarding/state
  returns live predicates plus the persisted record plus a derived
  should_show_wizard flag; POST /api/v1/onboarding/mark updates per-step
  status, first-shown timestamp, and engagement.
- internal/httpapi/server.go: ServerController interface gains
  GetOnboardingState / SaveOnboardingState; routes registered
  alongside /api/v1/connect.
- Test mocks (security_test.go, contracts_test.go) updated.

Frontend (Vue 3 + Pinia):
- frontend/src/types/api.ts: OnboardingState, OnboardingStateResponse,
  OnboardingMarkRequest.
- frontend/src/services/api.ts: getOnboardingState() and
  markOnboardingState().
- frontend/src/stores/onboarding.ts (new): Pinia store with derived
  visibleSteps driving adaptive rendering plus per-step mark helpers.
- frontend/src/components/OnboardingWizard.vue (new): adaptive 1-2 step
  modal with progress indicator, per-step skip, and a quarantine
  explainer in the server step. Reuses the existing AddServerModal for
  server addition.
- frontend/src/views/Dashboard.vue: auto-show on mount when
  should_show_wizard, plus a "Run setup wizard" link below
  "Connect Clients" for manual re-run.

Spec:
- specs/046-local-first-onboarding/spec.md plus checklist.

OpenAPI:
- oas/docs.go and oas/swagger.yaml regenerated with the new
  /onboarding endpoints.

## Testing

- Storage / connect / httpapi unit tests pass.
- Manual verification through Chrome browser automation across all
  four state combinations (none / clients-only / servers-only / both)
  with the dev backend on :18080. Auto-show, step advancement, skip,
  re-run, and engaged-suppression all confirmed. Funnel data
  (first-shown, engaged-at, per-step status) persisted in BBolt.

## What's deferred

- US2 tier-2 client adapters (Antigravity, Zed, OpenCode, etc.) —
  purely additive to the existing adapter table; one-PR-per-adapter.
- US3 heartbeat fields and Cloudflare D1 schema migration — backend
  signals are already exposed; just need to wire into HeartbeatPayload
  and migrate the D1 schema.

Related #46
@Dumbris Dumbris force-pushed the 046-local-first-onboarding branch from cd981ea to 4729cc1 Compare April 28, 2026 18:12
claude added 6 commits April 30, 2026 16:20
…ame, activity panel

Related #46

Reframes the first-run wizard as a persistent, idempotent setup surface
driven by the activation funnel telemetry (SynapBus #27345, 78.2% → 11.7%
cliff at IDE connect). Replaces the linear v1 flow with three idempotent
tabs (Clients · Servers · Verify), a top-pinned sidebar entry with an
animated badge, and a passive Verify tab that flips green automatically
on the first successful MCP `initialize` round-trip — closing the loop
on the cliff in-product.

## Changes

Spec
- specs/046-local-first-onboarding/spec.md: appended a "v2 — Wizard
  Surface Redesign" section. v1 text preserved verbatim.

Backend (Go)
- internal/httpapi/onboarding.go: extended /api/v1/onboarding/state with
  first_mcp_client_ever, mcp_clients_seen_ever, incomplete_tab_count.
  ShouldShowWizard semantics widened to cover Verify.
- internal/httpapi/server.go + internal/server/server.go +
  internal/runtime/runtime.go: new GetActivationFirstMCPClient() reads
  Spec 044's existing FirstMCPClientEver / MCPClientsSeenEver from the
  ActivationStore. No new BBolt buckets, no new MCP hook.
- internal/httpapi/import.go: ImportFromPathRequest gains skip_quarantine
  query param ("Import as active") and a rename map applied after
  parsing — so the wizard can disambiguate cross-source name collisions
  like "mcpproxy" appearing in both Claude Code and Claude Desktop.

Frontend (Vue 3 + Pinia)
- frontend/src/components/OnboardingWizard.vue: full rewrite as a 3-tab
  modal at min(960px, 90vw) × min(640px, 85vh). Tabs are bidirectionally
  clickable; per-tab idempotent state.
  - Clients tab: detected sort -> always-pinned trio (Claude Code /
    Codex / Gemini) -> "Show N more" collapse. Inline security
    expander.
  - Servers tab: sectioned checkbox list with indented server rows
    under each section header; per-section "select all" with proper
    indeterminate state; cross-source name collisions render an inline
    rename pill ("→ <renamed>"); sticky non-scrollable footer with
    selection count, conflict count, and two equal-width action buttons
    (Import & quarantine, Import as active). Compact toggles for
    Docker isolation and quarantine_enabled with inline Docker-
    availability warning. Educational note about post-hoc scanning.
  - Verify tab: passive, flips green on FirstMCPClientEver. Three
    sample prompts annotated with the built-in tool each one exercises.
    Recent Activity panel below shows the last 5 records with status
    badges and "View all in Activity Log" link.
- frontend/src/components/SidebarNav.vue: top-pinned "Setup" entry
  above Dashboard for personal edition.
- frontend/src/views/Dashboard.vue: removed the v1 "Run setup wizard"
  button.
- frontend/src/stores/onboarding.ts + types/api.ts + services/api.ts:
  store gains firstMCPClientEver, mcpClientsSeenEver, incompleteTabCount
  computeds. importServersFromPath supports skip_quarantine + rename.

OpenAPI
- oas/swagger.yaml + oas/docs.go: ImportFromPathRequest gains rename.

Verification
- specs/046-local-first-onboarding/verification/: 7 Playwright-captured
  PNGs and a self-contained report.html (1.4 MB, base64-embedded
  screenshots).

## Testing

- internal/httpapi unit + contract tests pass; mocks updated.
- frontend vue-tsc clean.
- Playwright sweep on a fresh data dir: all scenarios pass; out-of-band
  curl confirms rename produces mcpproxy_claude_code /
  mcpproxy_claude_desktop with quarantined=true.
Adds local browser-driven Playwright coverage for:
- engagement persistence (no auto-popup, sidebar reopens)
- select-all toggle off (footer state)
- per-section indeterminate state on partial selection
- "View all in Activity Log →" link navigation
- Docker isolation + quarantine toggle visual feedback
- skip-import flow (no servers added on Close)

Renders updated report.html with 14 scenarios + a "Local browser test
run" section noting the Docker/quarantine toggle persistence finding:
the wizard wraps the apply payload in `{config: …}` but the backend
expects the bare Config — UI flips locally but server-side persistence
falls back to best-effort. Dedicated PATCH endpoint works.

Related #46
Related #46

The wizard's `patchConfig` was wrapping the body in `{config: merged}`,
but `/api/v1/config/apply` decodes the body directly into `config.Config`.
Result: every toggle (require_mcp_auth, docker_isolation, quarantine_enabled)
flipped the UI state but the server-side apply rejected the wrapped payload
with a stale field-validation error ("tools_limit: must be between 1 and
1000"). Surfaced by the v2 verification subagent (scenarios 12, 13).

Settings.vue calls `api.applyConfig(config)` with a bare config object —
the wizard now matches that contract. Also surfaces the apply response's
`success` flag so a server-side rejection produces a visible "Save failed"
toast instead of silently swallowing the error.

Verified end-to-end: setting quarantine_enabled=false + require_mcp_auth=true
via the wizard now persists to disk and a fresh `GET /api/v1/config` reflects
the new values.
…tion docs

Related #46

Reworks the Servers tab security toggles per the latest UX feedback:

- Renamed "Quarantine new tools" → "Quarantine new servers".
- Pulled both toggles out of the scrollable list into a sticky non-
  scrollable footer panel framed as global settings, not per-import
  options.
- Wrapped them in a collapsed-by-default <details> labeled "Runtime
  isolation and MCP server quarantine" with a "global settings — apply
  to every imported server" summary hint.
- Each toggle is now a card with long-form security explanation, links
  to docs.mcpproxy.app/security/{docker-isolation,quarantine}, and
  (for Docker) a green "Docker detected" or warning "Docker not
  detected" badge with an inline Docker Desktop install hint.
- Quarantine description leads with bold "Recommended." and "Important:"
  clauses calling out that the AI agent itself can register upstream
  servers via mcpproxy's built-in MCP tools.
- Docker toggle disables when Docker isn't detected.
- "Import & quarantine" disables when Quarantine new servers is off
  (with a hover hint). "Import as active" stays enabled — opting out
  is an explicit user choice.

Companion docs:
- specs/046-local-first-onboarding/verification/srv-*.png + appended
  "Servers tab — toggles overhaul" section in report.html (6 scenarios).
- CLAUDE.md gains a "Verifying Web UI changes" section describing the
  Playwright + base64-embedded HTML report workflow plus gotchas.
Related #46

Per UX feedback: the "global settings — apply to every imported server"
hint on the Servers-tab security panel summary was muted opacity-50 text
and the wording suggested the toggles only affected the current import.
Both toggles are actually backed by the persistent mcpproxy config —
flipping them writes through to mcp_config.json and stays in effect
forever.

- Promote "Global settings" to a bold primary-color badge (badge-primary
  badge-sm font-semibold) so it reads at a glance.
- Replace the right-side caption with "saved to your mcpproxy config"
  so the persistence story is explicit, not implied.
- Caption hides on narrow viewports (badge alone is enough).
Related #46

The "Docker detected" / "Docker not detected" pills next to the Docker
isolation title were redundant — the install hint already appears below
the description when the daemon is missing, and the toggle's disabled
state is the real signal. Same for the "Recommended" badge next to
"Quarantine new servers" — the description already opens with bold
"Recommended." which carries the weight.

Removing both leaves the panel uncluttered: just the title row,
description with bold-emphasised security context, and the docs link.
The prominent "Global settings — saved to your mcpproxy config" header
still sits top-right of the panel summary so users know they're
editing persistent state.

Verification spec updated to no longer assert the badge count.
@Dumbris Dumbris merged commit 0064d88 into main Apr 30, 2026
38 checks passed
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.

3 participants