Skip to content

Commit 6a92037

Browse files
committed
feat(builtin-servers): add Built-in Servers tab; move + rebrand Tool Optimization
New "Built-in Servers" nav tab (parallels "My Servers") — home for McpMux's own bundled MCP servers. The self-management (mcpmux_*) controls move out of Settings into it, rebranded "Tool Optimization": enable switch, the tools it exposes (read/write annotated), grants panel, and audit log. Memory / Skills / Plugins are shown as "coming soon" so the framework shape is visible. Settings no longer carries the meta-tools card. Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent 84d1ce3 commit 6a92037

5 files changed

Lines changed: 284 additions & 66 deletions

File tree

apps/desktop/src/App.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Moon,
1212
FolderOpen,
1313
Download,
14+
Boxes,
1415
X,
1516
} from 'lucide-react';
1617
import {
@@ -39,6 +40,7 @@ import { ServersPage } from '@/features/servers';
3940
import { SpacesPage } from '@/features/spaces';
4041
import { WorkspacesPage } from '@/features/workspaces';
4142
import { SettingsPage } from '@/features/settings';
43+
import { BuiltinServersPage } from '@/features/builtinServers';
4244
import { AutoStartConflictResolver } from '@/features/gateway/AutoStartConflictResolver';
4345
import { WorkspaceBindingSheet } from '@/features/workspaces';
4446
import { MetaToolApprovalDialog } from '@/features/metaTools';
@@ -211,6 +213,13 @@ function AppContent() {
211213
onClick={() => navigateTo('servers')}
212214
data-testid="nav-my-servers"
213215
/>
216+
<SidebarItem
217+
icon={<Boxes className="h-4 w-4" />}
218+
label="Built-in Servers"
219+
active={activeNav === 'builtin-servers'}
220+
onClick={() => navigateTo('builtin-servers')}
221+
data-testid="nav-builtin-servers"
222+
/>
214223
<SidebarItem
215224
icon={<Server className="h-4 w-4" />}
216225
label="Discover"
@@ -364,6 +373,7 @@ function AppContent() {
364373
{activeNav === 'featuresets' && <FeatureSetsPage />}
365374
{activeNav === 'workspaces' && <WorkspacesPage />}
366375
{activeNav === 'clients' && <ClientsPage />}
376+
{activeNav === 'builtin-servers' && <BuiltinServersPage />}
367377
{activeNav === 'settings' && <SettingsPage />}
368378
</div>
369379
</AppShell>
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
/**
2+
* Built-in Servers page.
3+
*
4+
* Home for the MCP servers McpMux ships itself — distinct from the servers the
5+
* user installs (those live under "My Servers"). Each built-in server can be
6+
* enabled/disabled; when on, its tools appear to connected MCP clients
7+
* alongside the user's own tools.
8+
*
9+
* Today there's one concrete built-in server — "Tool Optimization" (the
10+
* `mcpmux_*` self-management toolset). Memory / Skills / Plugins are scaffolded
11+
* as "coming soon" so the shape of the framework is visible. Per-workspace
12+
* enable/disable + per-tool toggles land on top of this in a later pass.
13+
*/
14+
15+
import { useEffect, useState } from 'react';
16+
import {
17+
Card,
18+
CardHeader,
19+
CardTitle,
20+
CardDescription,
21+
CardContent,
22+
Switch,
23+
useToast,
24+
ToastContainer,
25+
} from '@mcpmux/ui';
26+
import {
27+
Sparkles,
28+
Brain,
29+
BookOpen,
30+
Puzzle,
31+
Wrench,
32+
Eye,
33+
Pencil,
34+
Boxes,
35+
} from 'lucide-react';
36+
import { getMetaToolsEnabled, setMetaToolsEnabled } from '@/lib/api/metaTools';
37+
import { MetaToolAuditLog, MetaToolGrantsPanel } from '@/features/metaTools';
38+
39+
/** A tool the Tool Optimization server exposes — display metadata only. */
40+
interface BuiltinTool {
41+
name: string;
42+
description: string;
43+
write: boolean;
44+
}
45+
46+
/**
47+
* The `mcpmux_*` tools, mirrored from the gateway's meta-tool registry. Kept
48+
* here for display; the gateway is the source of truth for what's actually
49+
* advertised. Writes require a native approval before they run.
50+
*/
51+
const TOOL_OPTIMIZATION_TOOLS: BuiltinTool[] = [
52+
{
53+
name: 'mcpmux_list_all_tools',
54+
description: 'Browse every tool available in the resolved Space, unfiltered.',
55+
write: false,
56+
},
57+
{
58+
name: 'mcpmux_list_feature_sets',
59+
description: 'See the feature sets defined in the Space.',
60+
write: false,
61+
},
62+
{
63+
name: 'mcpmux_create_feature_set',
64+
description: 'Build a focused feature set from chosen tools.',
65+
write: true,
66+
},
67+
{
68+
name: 'mcpmux_bind_current_workspace',
69+
description: 'Map the current folder to a feature set so it persists.',
70+
write: true,
71+
},
72+
];
73+
74+
interface ComingSoonServer {
75+
id: string;
76+
name: string;
77+
description: string;
78+
icon: React.ReactNode;
79+
}
80+
81+
const COMING_SOON: ComingSoonServer[] = [
82+
{
83+
id: 'memory',
84+
name: 'Memory',
85+
description: 'Persistent notes and recall the AI can read and write across sessions.',
86+
icon: <Brain className="h-5 w-5" />,
87+
},
88+
{
89+
id: 'skills',
90+
name: 'Skills',
91+
description: 'Search a catalog of skills and pull the ones you want into a workspace.',
92+
icon: <BookOpen className="h-5 w-5" />,
93+
},
94+
{
95+
id: 'plugins',
96+
name: 'Plugins',
97+
description: 'Extend McpMux with community plugins exposed as first-class tools.',
98+
icon: <Puzzle className="h-5 w-5" />,
99+
},
100+
];
101+
102+
export function BuiltinServersPage() {
103+
const { toasts, success, error, dismiss } = useToast();
104+
105+
const [enabled, setEnabled] = useState(true);
106+
const [loading, setLoading] = useState(true);
107+
108+
useEffect(() => {
109+
getMetaToolsEnabled()
110+
.then(setEnabled)
111+
.catch((e) => console.error('Failed to load meta_tools_enabled', e))
112+
.finally(() => setLoading(false));
113+
}, []);
114+
115+
// Live-sync if the switch is flipped elsewhere (the gateway forwards
116+
// `meta-tools-changed` after a toggle); keep this page honest without a
117+
// manual refresh.
118+
useEffect(() => {
119+
let unlisten: (() => void) | undefined;
120+
void import('@tauri-apps/api/event').then(({ listen }) => {
121+
void listen<{ enabled: boolean }>('meta-tools-changed', (e) => {
122+
setEnabled(e.payload.enabled);
123+
}).then((fn) => {
124+
unlisten = fn;
125+
});
126+
});
127+
return () => unlisten?.();
128+
}, []);
129+
130+
const handleToggle = async (next: boolean) => {
131+
const previous = enabled;
132+
setEnabled(next);
133+
try {
134+
await setMetaToolsEnabled(next);
135+
success(
136+
next ? 'Tool Optimization enabled' : 'Tool Optimization disabled',
137+
next
138+
? 'Connected clients now see the mcpmux_* tools.'
139+
: 'The mcpmux_* tools are hidden from connected clients.'
140+
);
141+
} catch (e) {
142+
setEnabled(previous);
143+
error('Failed to save', e instanceof Error ? e.message : String(e));
144+
}
145+
};
146+
147+
return (
148+
<div className="flex h-full flex-col" data-testid="builtin-servers-page">
149+
<header className="flex-shrink-0 border-b border-[rgb(var(--border-subtle))] p-8">
150+
<div className="mx-auto max-w-[2000px]">
151+
<h1 className="text-3xl font-bold">Built-in Servers</h1>
152+
<p className="mt-2 max-w-2xl text-base text-[rgb(var(--muted))]">
153+
MCP servers that ship with McpMux — separate from the ones you install under{' '}
154+
<span className="font-medium text-[rgb(var(--foreground))]">My Servers</span>. Turn a
155+
server on and its tools appear to every connected client alongside your own.
156+
</p>
157+
</div>
158+
</header>
159+
160+
<div className="flex-1 overflow-auto p-8">
161+
<div className="mx-auto max-w-3xl space-y-6">
162+
{/* Tool Optimization — the mcpmux_* self-management server */}
163+
<Card data-testid="builtin-server-tool-optimization">
164+
<CardHeader>
165+
<div className="flex items-start justify-between gap-4">
166+
<div className="min-w-0">
167+
<CardTitle className="flex items-center gap-2">
168+
<span className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary-500/10 text-primary-500">
169+
<Sparkles className="h-5 w-5" />
170+
</span>
171+
Tool Optimization
172+
<span className="rounded-md border border-[rgb(var(--border-subtle))] bg-[rgb(var(--surface))] px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-[rgb(var(--muted))]">
173+
Built-in
174+
</span>
175+
</CardTitle>
176+
<CardDescription className="mt-2">
177+
Lets the AI keep its own toolset lean: it can browse every available tool,
178+
assemble a focused feature set, and pin it to the current folder — each with
179+
your approval. Reads are silent; writes pop a native approval dialog.
180+
</CardDescription>
181+
</div>
182+
<Switch
183+
checked={enabled}
184+
onCheckedChange={handleToggle}
185+
disabled={loading}
186+
data-testid="meta-tools-enabled-switch"
187+
/>
188+
</div>
189+
</CardHeader>
190+
<CardContent className="space-y-6">
191+
{/* Tools this server exposes */}
192+
<div>
193+
<div className="mb-2 flex items-center gap-2 text-xs font-semibold uppercase tracking-wide text-[rgb(var(--muted))]">
194+
<Wrench className="h-3.5 w-3.5" />
195+
Tools ({TOOL_OPTIMIZATION_TOOLS.length})
196+
</div>
197+
<div
198+
className={`overflow-hidden rounded-xl border border-[rgb(var(--border))] transition-opacity ${
199+
enabled ? '' : 'opacity-50'
200+
}`}
201+
>
202+
<div className="divide-y divide-[rgb(var(--border-subtle))]">
203+
{TOOL_OPTIMIZATION_TOOLS.map((t) => (
204+
<div key={t.name} className="flex items-start gap-3 px-4 py-2.5">
205+
{t.write ? (
206+
<Pencil className="mt-0.5 h-4 w-4 flex-shrink-0 text-amber-500" />
207+
) : (
208+
<Eye className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-500" />
209+
)}
210+
<div className="min-w-0 flex-1">
211+
<div className="flex items-center gap-2">
212+
<span className="truncate font-mono text-sm">{t.name}</span>
213+
<span
214+
className={`rounded px-1.5 py-0.5 text-[10px] font-medium ${
215+
t.write
216+
? 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300'
217+
: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300'
218+
}`}
219+
>
220+
{t.write ? 'write · approval' : 'read'}
221+
</span>
222+
</div>
223+
<p className="mt-0.5 text-xs text-[rgb(var(--muted))]">{t.description}</p>
224+
</div>
225+
</div>
226+
))}
227+
</div>
228+
</div>
229+
</div>
230+
231+
<MetaToolGrantsPanel />
232+
<MetaToolAuditLog />
233+
</CardContent>
234+
</Card>
235+
236+
{/* Framework preview — servers that slot into this same shell later. */}
237+
<div>
238+
<div className="mb-3 flex items-center gap-2">
239+
<Boxes className="h-4 w-4 text-[rgb(var(--muted))]" />
240+
<h2 className="text-sm font-semibold text-[rgb(var(--foreground))]">
241+
More built-in servers
242+
</h2>
243+
<span className="text-xs text-[rgb(var(--muted))]">coming soon</span>
244+
</div>
245+
<div className="grid gap-4 sm:grid-cols-3">
246+
{COMING_SOON.map((s) => (
247+
<div
248+
key={s.id}
249+
className="rounded-xl border border-dashed border-[rgb(var(--border))] bg-[rgb(var(--surface))] p-4 opacity-75"
250+
data-testid={`builtin-server-coming-soon-${s.id}`}
251+
>
252+
<div className="mb-2 flex items-center justify-between">
253+
<span className="flex h-9 w-9 items-center justify-center rounded-lg bg-[rgb(var(--background))] text-[rgb(var(--muted))]">
254+
{s.icon}
255+
</span>
256+
<span className="rounded-md bg-[rgb(var(--background))] px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-[rgb(var(--muted))]">
257+
Soon
258+
</span>
259+
</div>
260+
<div className="text-sm font-semibold">{s.name}</div>
261+
<p className="mt-1 text-xs text-[rgb(var(--muted))]">{s.description}</p>
262+
</div>
263+
))}
264+
</div>
265+
</div>
266+
</div>
267+
</div>
268+
269+
<ToastContainer toasts={toasts} onClose={dismiss} />
270+
</div>
271+
);
272+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { BuiltinServersPage } from './BuiltinServersPage';

0 commit comments

Comments
 (0)