@@ -26,8 +26,6 @@ import {
2626 AlertCircle ,
2727 Zap ,
2828 PlugZap ,
29- RotateCcw ,
30- CheckCircle2 ,
3129} from 'lucide-react' ;
3230import { ConnectIDEs } from '@/components/ConnectIDEs' ;
3331import type { GatewayStatus } from '@/lib/api/gateway' ;
@@ -147,6 +145,7 @@ export default function ClientsPage() {
147145 active_sessions : 0 ,
148146 connected_backends : 0 ,
149147 } ) ;
148+
150149
151150 // Feature set grant state
152151 const viewSpace = useViewSpace ( ) ;
@@ -640,116 +639,68 @@ export default function ClientsPage() {
640639 // the IDE, then come back and approve. Each step is a distinct
641640 // visual block so users don't skim past "and then approve here".
642641 < div className = "max-w-4xl mx-auto space-y-6" data-testid = "clients-empty-connect" >
643- < Card >
642+ { /* Generic 3-step welcome. Always visible while there are no
643+ connected clients — no dismiss — because a user hitting
644+ this page with nothing connected needs the instructions
645+ every time, not just the first time. */ }
646+ < Card data-testid = "clients-empty-onboarding" >
644647 < CardContent className = "p-8" >
645- < div className = "flex items-start gap-4 mb-2 " >
648+ < div className = "flex items-start gap-4 mb-1 " >
646649 < div className = "flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 text-white shadow-[0_6px_16px_-4px_rgb(99_102_241/0.45)] flex-shrink-0" >
647650 < PlugZap className = "h-6 w-6" />
648651 </ div >
649652 < div >
650- < h2 className = "text-xl font-semibold" > Connect your first IDE</ h2 >
653+ < h2 className = "text-xl font-semibold" > Let's hook up your first IDE</ h2 >
651654 < p className = "text-sm text-[rgb(var(--muted))] mt-1" >
652- Nothing has connected to mcpmux yet. Pick the path that matches your IDE
653- below — the approval prompt shows up on this page either way.
655+ mcpmux is one connection your AI client uses to reach every MCP server.
656+ Three steps and you're done:
654657 </ p >
655658 </ div >
656659 </ div >
657660
658- { /* Two distinct install paths. Mixing them into a single
659- numbered list made users think "copy config" auto-installs
660- somewhere — it doesn't; it's clipboard content they have
661- to paste themselves. Keep them separated. */ }
662- < div className = "mt-6 grid grid-cols-1 md:grid-cols-2 gap-4" >
663- { /* Track A — one-click install (VS Code, Cursor) */ }
664- < div className = "rounded-xl border border-primary-300/60 dark:border-primary-700/40 bg-gradient-to-br from-primary-50/50 to-transparent dark:from-primary-900/15 p-4" >
665- < div className = "flex items-center gap-2 mb-3" >
666- < div className = "flex h-7 w-7 items-center justify-center rounded-lg bg-primary-500 text-white text-xs font-bold" >
667- A
668- </ div >
669- < h3 className = "font-semibold text-sm" >
670- VS Code · Cursor
671- < span className = "ml-2 text-[10px] uppercase tracking-wide text-primary-700 dark:text-primary-300 bg-primary-500/10 rounded px-1.5 py-0.5" >
672- One-click
673- </ span >
674- </ h3 >
661+ < ol className = "mt-6 space-y-3 text-sm" >
662+ < li className = "flex gap-3 items-start" >
663+ < span className = "flex-shrink-0 flex h-7 w-7 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-900/40 text-primary-700 dark:text-primary-300 font-semibold text-sm" >
664+ 1
665+ </ span >
666+ < div className = "flex-1" >
667+ < p className = "font-medium" > Pick your IDE below and follow its prompt</ p >
668+ < p className = "text-[rgb(var(--muted))] text-xs mt-0.5" >
669+ Each card tells you exactly what the button does — either one-click
670+ install or copy a small config for you to paste.
671+ </ p >
675672 </ div >
676- < ol className = "space-y-2 text-xs" >
677- < li className = "flex gap-2" >
678- < span className = "font-semibold text-[rgb(var(--muted))] w-4 shrink-0" > 1.</ span >
679- < span >
680- Click the IDE icon below → click < span className = "font-medium" > Add to VS Code</ span > /
681- < span className = "font-medium" > Add to Cursor</ span > . The IDE opens and registers mcpmux.
682- </ span >
683- </ li >
684- < li className = "flex gap-2" >
685- < span className = "font-semibold text-[rgb(var(--muted))] w-4 shrink-0" > 2.</ span >
686- < span >
687- Start a new chat / reload the MCP server in that IDE so it actually
688- connects.
689- </ span >
690- </ li >
691- < li className = "flex gap-2" >
692- < span className = "font-semibold text-emerald-600 dark:text-emerald-400 w-4 shrink-0" > 3.</ span >
693- < span >
694- < span className = "font-medium text-emerald-700 dark:text-emerald-300" >
695- Approve here.
696- </ span > { ' ' }
697- mcpmux will pop a dialog on this app — nothing is routed to the IDE
698- until you accept.
699- </ span >
700- </ li >
701- </ ol >
702- </ div >
703-
704- { /* Track B — manual copy-paste (everyone else) */ }
705- < div className = "rounded-xl border border-[rgb(var(--border))] bg-[rgb(var(--surface))] p-4" >
706- < div className = "flex items-center gap-2 mb-3" >
707- < div className = "flex h-7 w-7 items-center justify-center rounded-lg bg-[rgb(var(--muted))] text-white text-xs font-bold" >
708- B
709- </ div >
710- < h3 className = "font-semibold text-sm" >
711- Windsurf · Claude Code · JetBrains · Android Studio
712- < span className = "ml-2 text-[10px] uppercase tracking-wide text-[rgb(var(--muted))] bg-[rgb(var(--surface-elevated))] rounded px-1.5 py-0.5 border border-[rgb(var(--border))]" >
713- Manual
714- </ span >
715- </ h3 >
673+ </ li >
674+ < li className = "flex gap-3 items-start" >
675+ < span className = "flex-shrink-0 flex h-7 w-7 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-900/40 text-primary-700 dark:text-primary-300 font-semibold text-sm" >
676+ 2
677+ </ span >
678+ < div className = "flex-1" >
679+ < p className = "font-medium" > Open or restart the IDE</ p >
680+ < p className = "text-[rgb(var(--muted))] text-xs mt-0.5" >
681+ Your client only connects to mcpmux the next time its MCP servers
682+ start up.
683+ </ p >
716684 </ div >
717- < ol className = "space-y-2 text-xs" >
718- < li className = "flex gap-2" >
719- < span className = "font-semibold text-[rgb(var(--muted))] w-4 shrink-0" > 1.</ span >
720- < span >
721- Click the IDE icon below → < span className = "font-medium" > Copy config</ span > or
722- < span className = "font-medium" > Copy command</ span > (your clipboard now has the
723- snippet).
724- </ span >
725- </ li >
726- < li className = "flex gap-2" >
727- < span className = "font-semibold text-[rgb(var(--muted))] w-4 shrink-0" > 2.</ span >
728- < span >
729- Paste it into that IDE's MCP config file (or run the copied
730- command for Claude Code).
731- </ span >
732- </ li >
733- < li className = "flex gap-2" >
734- < span className = "font-semibold text-[rgb(var(--muted))] w-4 shrink-0" > 3.</ span >
735- < span >
736- Restart the IDE or reload its MCP servers — mcpmux is not picked up
737- until you do.
738- </ span >
739- </ li >
740- < li className = "flex gap-2" >
741- < span className = "font-semibold text-emerald-600 dark:text-emerald-400 w-4 shrink-0" > 4.</ span >
742- < span >
743- < span className = "font-medium text-emerald-700 dark:text-emerald-300" >
744- Approve here.
745- </ span > { ' ' }
746- mcpmux will pop a dialog on this app as soon as the IDE reaches the
747- gateway.
685+ </ li >
686+ < li className = "flex gap-3 items-start" >
687+ < span className = "flex-shrink-0 flex h-7 w-7 items-center justify-center rounded-full bg-emerald-100 dark:bg-emerald-900/40 text-emerald-700 dark:text-emerald-300 font-semibold text-sm" >
688+ 3
689+ </ span >
690+ < div className = "flex-1" >
691+ < p className = "font-medium" >
692+ Approve the connection{ ' ' }
693+ < span className = "ml-1 inline-flex items-center px-1.5 py-0.5 rounded-md bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 text-[10px] font-semibold uppercase tracking-wide" >
694+ right here
748695 </ span >
749- </ li >
750- </ ol >
751- </ div >
752- </ div >
696+ </ p >
697+ < p className = "text-[rgb(var(--muted))] text-xs mt-0.5" >
698+ mcpmux will pop a dialog on this app the moment your IDE reaches
699+ the gateway. Until you accept it, nothing is routed.
700+ </ p >
701+ </ div >
702+ </ li >
703+ </ ol >
753704
754705 { ! gatewayStatus . running && (
755706 < div className = "mt-5 flex items-start gap-2 p-3 rounded-lg border border-amber-300 dark:border-amber-700/60 bg-amber-50 dark:bg-amber-900/20 text-xs" >
@@ -768,8 +719,8 @@ export default function ClientsPage() {
768719 </ CardContent >
769720 </ Card >
770721
771- { /* Reuse the shared ConnectIDEs grid from the Dashboard so users
772- don't have to navigate away . */ }
722+ { /* IDE grid — each card's popover tells the user what pressing
723+ the button actually does + what to do next . */ }
773724 < ConnectIDEs
774725 gatewayUrl = { gatewayStatus . url || 'http://localhost:45818' }
775726 gatewayRunning = { gatewayStatus . running }
0 commit comments