@thesage/mcp 0.6.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ var COMPONENT_CATEGORIES = {
16
16
  forms: {
17
17
  label: "Forms",
18
18
  description: "Input controls for data collection",
19
- count: 18
19
+ count: 19
20
20
  },
21
21
  navigation: {
22
22
  label: "Navigation",
@@ -26,17 +26,17 @@ var COMPONENT_CATEGORIES = {
26
26
  overlays: {
27
27
  label: "Overlays",
28
28
  description: "Contextual content that appears above the main UI",
29
- count: 11
29
+ count: 12
30
30
  },
31
31
  feedback: {
32
32
  label: "Feedback",
33
33
  description: "Communicating system state and user action results",
34
- count: 7
34
+ count: 9
35
35
  },
36
36
  "data-display": {
37
37
  label: "Data Display",
38
38
  description: "Presenting information in structured formats",
39
- count: 16
39
+ count: 19
40
40
  },
41
41
  layout: {
42
42
  label: "Layout",
@@ -2168,6 +2168,170 @@ const toRef = useRef(null)
2168
2168
  export default function OGImage() {
2169
2169
  return <OpenGraphCard title="My Page" description="A great description" variant="primary" />
2170
2170
  }`
2171
+ },
2172
+ // ============================================================================
2173
+ // PHASE 16 - MISSING COMPONENTS
2174
+ // ============================================================================
2175
+ "stat-card": {
2176
+ name: "StatCard",
2177
+ category: "data-display",
2178
+ description: "Displays key metrics and statistics with label, value, trend indicator, and optional icon. Ideal for dashboards, analytics views, and KPI displays.",
2179
+ keywords: ["stat", "metric", "kpi", "dashboard", "analytics", "number", "trend", "card"],
2180
+ useCases: [
2181
+ "Dashboard metric displays",
2182
+ "KPI tracking panels",
2183
+ "Analytics summary cards",
2184
+ "Revenue/user count displays"
2185
+ ],
2186
+ dependencies: ["class-variance-authority"],
2187
+ props: {
2188
+ label: { type: "string", description: 'The metric label (e.g. "Revenue")', required: true },
2189
+ value: { type: "string | number", description: 'The metric value (e.g. "$45,231")', required: true },
2190
+ change: { type: "number", description: "Percentage change (e.g. 5.2 or -3.1)" },
2191
+ trend: { type: "'up' | 'down' | 'flat'", description: "Direction of the trend" },
2192
+ icon: { type: "ReactNode", description: "Optional icon displayed in the top-right" },
2193
+ description: { type: "string", description: "Additional description text below the value" },
2194
+ variant: { type: "'default' | 'outline' | 'glass'", default: "'default'", description: "Visual style variant" },
2195
+ size: { type: "'sm' | 'default' | 'lg'", default: "'default'", description: "Size variant" }
2196
+ },
2197
+ subComponents: ["StatCardGroup"],
2198
+ example: `<StatCard label="Total Revenue" value="$45,231" change={12.5} trend="up" description="from last month" />`
2199
+ },
2200
+ "empty-state": {
2201
+ name: "EmptyState",
2202
+ category: "feedback",
2203
+ description: "Placeholder for empty content areas with icon, title, description, and call-to-action. Use when no data is available or a search returns no results.",
2204
+ keywords: ["empty", "placeholder", "no-data", "no-results", "blank", "zero-state"],
2205
+ useCases: [
2206
+ "Empty search results",
2207
+ "Empty inbox/messages",
2208
+ "No data available state",
2209
+ "First-time user onboarding prompt"
2210
+ ],
2211
+ dependencies: ["class-variance-authority"],
2212
+ props: {
2213
+ icon: { type: "ReactNode", description: "Icon displayed above the title" },
2214
+ title: { type: "string", description: "Primary message", required: true },
2215
+ description: { type: "string", description: "Secondary explanation text" },
2216
+ action: { type: "ReactNode", description: "Call-to-action element (e.g. Button)" },
2217
+ size: { type: "'sm' | 'default' | 'lg'", default: "'default'", description: "Size variant" }
2218
+ },
2219
+ example: `<EmptyState
2220
+ icon={<Inbox />}
2221
+ title="No messages yet"
2222
+ description="When you receive messages, they will appear here."
2223
+ action={<Button>Send a message</Button>}
2224
+ />`
2225
+ },
2226
+ "timeline": {
2227
+ name: "Timeline",
2228
+ category: "data-display",
2229
+ description: "Chronological event display with connecting lines, icons, and status indicators.",
2230
+ keywords: ["timeline", "events", "history", "chronological", "activity", "log"],
2231
+ useCases: ["Activity feeds", "Order tracking", "Project milestones", "Event history"],
2232
+ dependencies: ["class-variance-authority"],
2233
+ props: {
2234
+ orientation: { type: "'vertical' | 'horizontal'", default: "'vertical'", description: "Layout orientation" }
2235
+ },
2236
+ subComponents: ["TimelineItem"],
2237
+ example: `<Timeline>
2238
+ <TimelineItem title="Order placed" timestamp="Jan 1" status="completed" />
2239
+ <TimelineItem title="Shipped" status="active" />
2240
+ <TimelineItem title="Delivered" status="pending" isLast />
2241
+ </Timeline>`
2242
+ },
2243
+ "stepper": {
2244
+ name: "Stepper",
2245
+ category: "feedback",
2246
+ description: "Multi-step progress indicator for wizards and multi-step forms.",
2247
+ keywords: ["stepper", "wizard", "steps", "progress", "multi-step", "workflow"],
2248
+ useCases: ["Multi-step forms", "Checkout flows", "Onboarding wizards", "Setup processes"],
2249
+ dependencies: ["class-variance-authority"],
2250
+ props: {
2251
+ currentStep: { type: "number", description: "Zero-based index of the current step", required: true },
2252
+ orientation: { type: "'horizontal' | 'vertical'", default: "'horizontal'", description: "Layout orientation" },
2253
+ size: { type: "'sm' | 'default' | 'lg'", default: "'default'", description: "Size variant" },
2254
+ clickable: { type: "boolean", default: "false", description: "Allow clicking steps to navigate" }
2255
+ },
2256
+ subComponents: ["StepperStep"],
2257
+ example: `<Stepper currentStep={1}>
2258
+ <StepperStep label="Account" />
2259
+ <StepperStep label="Profile" />
2260
+ <StepperStep label="Complete" />
2261
+ </Stepper>`
2262
+ },
2263
+ "file-upload": {
2264
+ name: "FileUpload",
2265
+ category: "forms",
2266
+ description: "Drag-and-drop file upload zone with validation, file list, and remove functionality",
2267
+ keywords: ["file", "upload", "drag", "drop", "dropzone", "attachment", "browse"],
2268
+ useCases: ["Document uploads", "Image uploads", "Form attachments", "Bulk file import"],
2269
+ dependencies: ["react-dropzone", "class-variance-authority"],
2270
+ props: {
2271
+ accept: { type: "Record<string, string[]>", description: "Accepted file types (MIME types)" },
2272
+ maxSize: { type: "number", description: "Max file size in bytes" },
2273
+ maxFiles: { type: "number", description: "Max number of files" },
2274
+ multiple: { type: "boolean", default: "false", description: "Allow multiple file selection" },
2275
+ disabled: { type: "boolean", default: "false", description: "Disabled state" },
2276
+ onFilesSelected: { type: "(files: File[]) => void", description: "Callback when valid files are selected" },
2277
+ onFilesRejected: { type: "(rejections: FileRejection[]) => void", description: "Callback when files are rejected" },
2278
+ label: { type: "string", description: "Label text" },
2279
+ description: { type: "string", description: "Description text shown in the drop zone" },
2280
+ size: { type: "'sm' | 'default' | 'lg'", default: "'default'", description: "Size variant" }
2281
+ },
2282
+ example: `<FileUpload
2283
+ label="Upload documents"
2284
+ accept={{ 'image/*': ['.png', '.jpg'] }}
2285
+ maxSize={5 * 1024 * 1024}
2286
+ onFilesSelected={(files) => handleUpload(files)}
2287
+ />`
2288
+ },
2289
+ "tree-view": {
2290
+ name: "TreeView",
2291
+ category: "data-display",
2292
+ description: "Hierarchical data display with expand/collapse, keyboard navigation, and selection",
2293
+ keywords: ["tree", "hierarchy", "file browser", "nested", "expand", "collapse", "folder"],
2294
+ useCases: ["File browsers", "Category hierarchies", "Organizational charts", "Navigation trees"],
2295
+ dependencies: ["class-variance-authority"],
2296
+ props: {
2297
+ nodes: { type: "TreeNode[]", description: "Array of tree nodes", required: true },
2298
+ expanded: { type: "string[]", description: "Controlled expanded node IDs" },
2299
+ defaultExpanded: { type: "string[]", description: "Initially expanded node IDs" },
2300
+ onExpandChange: { type: "(expanded: string[]) => void", description: "Callback on expand state change" },
2301
+ selected: { type: "string", description: "Currently selected node ID" },
2302
+ onSelectChange: { type: "(nodeId: string) => void", description: "Callback on selection change" }
2303
+ },
2304
+ example: `<TreeView
2305
+ nodes={[
2306
+ { id: 'src', label: 'src', children: [
2307
+ { id: 'index', label: 'index.ts' },
2308
+ ]},
2309
+ ]}
2310
+ onSelectChange={(id) => console.log(id)}
2311
+ />`
2312
+ },
2313
+ "notification-center": {
2314
+ name: "NotificationCenter",
2315
+ category: "overlays",
2316
+ description: "Dropdown notification panel with grouped notifications, read/unread state, and actions",
2317
+ keywords: ["notification", "bell", "alert", "inbox", "unread", "badge", "messages"],
2318
+ useCases: ["App notifications", "Activity feeds", "System alerts", "Message center"],
2319
+ dependencies: [],
2320
+ props: {
2321
+ notifications: { type: "NotificationItem[]", description: "Array of notification items", required: true },
2322
+ onMarkRead: { type: "(id: string) => void", description: "Callback when a notification is marked as read" },
2323
+ onMarkAllRead: { type: "() => void", description: "Callback to mark all notifications as read" },
2324
+ onDismiss: { type: "(id: string) => void", description: "Callback when a notification is dismissed" },
2325
+ trigger: { type: "ReactNode", description: "Custom trigger element" },
2326
+ maxHeight: { type: "number", default: "400", description: "Maximum height of the notification list" },
2327
+ emptyMessage: { type: "string", default: "'No notifications'", description: "Message shown when empty" }
2328
+ },
2329
+ example: `<NotificationCenter
2330
+ notifications={[
2331
+ { id: '1', title: 'New message', timestamp: new Date(), read: false },
2332
+ ]}
2333
+ onMarkRead={(id) => markAsRead(id)}
2334
+ />`
2171
2335
  }
2172
2336
  };
2173
2337
  function getComponentsByCategory(category) {
@@ -2196,7 +2360,7 @@ function getComponentCount() {
2196
2360
  var server = new import_server.Server(
2197
2361
  {
2198
2362
  name: "sds-mcp-server",
2199
- version: "0.1.0"
2363
+ version: "0.8.0"
2200
2364
  },
2201
2365
  {
2202
2366
  capabilities: {
@@ -2272,6 +2436,65 @@ var TOOLS = [
2272
2436
  },
2273
2437
  required: ["name"]
2274
2438
  }
2439
+ },
2440
+ {
2441
+ name: "get_app_shell",
2442
+ description: "Returns a complete, ready-to-use app shell with ThemeProvider, TooltipProvider, Toaster, tailwind.config, postcss.config, and globals.css import. Use this when scaffolding a new project with SDE.",
2443
+ inputSchema: {
2444
+ type: "object",
2445
+ properties: {
2446
+ framework: {
2447
+ type: "string",
2448
+ enum: ["nextjs", "vite"],
2449
+ description: 'Target framework. Defaults to "vite".'
2450
+ },
2451
+ theme: {
2452
+ type: "string",
2453
+ enum: ["studio", "terra", "volt"],
2454
+ description: 'Default theme. Defaults to "studio".'
2455
+ }
2456
+ }
2457
+ }
2458
+ },
2459
+ {
2460
+ name: "get_examples",
2461
+ description: "Get usage examples for a specific component, including common patterns, compound component usage, and integration with other SDE components.",
2462
+ inputSchema: {
2463
+ type: "object",
2464
+ properties: {
2465
+ name: {
2466
+ type: "string",
2467
+ description: 'Component name (e.g., "Button", "Dialog", "Card")'
2468
+ }
2469
+ },
2470
+ required: ["name"]
2471
+ }
2472
+ },
2473
+ {
2474
+ name: "get_audit_checklist",
2475
+ description: "Returns a post-generation checklist to verify SDE component usage is correct. Checks: provider wrapping, CSS variable usage (no hardcoded colors), accessibility attributes, motion preference respect, and import correctness.",
2476
+ inputSchema: {
2477
+ type: "object",
2478
+ properties: {}
2479
+ }
2480
+ },
2481
+ {
2482
+ name: "eject_component",
2483
+ description: "Copy a component's source code into your local project for full customization. Returns step-by-step instructions to eject the component with rewritten imports.",
2484
+ inputSchema: {
2485
+ type: "object",
2486
+ properties: {
2487
+ name: {
2488
+ type: "string",
2489
+ description: 'Component name (e.g., "Button", "Card", "Dialog")'
2490
+ },
2491
+ targetDir: {
2492
+ type: "string",
2493
+ description: 'Target directory relative to project root. Defaults to "src/components/ui"'
2494
+ }
2495
+ },
2496
+ required: ["name"]
2497
+ }
2275
2498
  }
2276
2499
  ];
2277
2500
  function formatComponentList(components) {
@@ -2461,6 +2684,243 @@ function formatInstallationInstructions(component) {
2461
2684
  `;
2462
2685
  return output;
2463
2686
  }
2687
+ function generateAppShell(framework, theme) {
2688
+ if (framework === "nextjs") {
2689
+ return `# Next.js App Router Setup with Sage Design Engine
2690
+
2691
+ ## 1. Install dependencies
2692
+
2693
+ \`\`\`bash
2694
+ pnpm add @thesage/ui
2695
+ pnpm add -D tailwindcss@^3.4 postcss autoprefixer tailwindcss-animate
2696
+ \`\`\`
2697
+
2698
+ ## 2. app/layout.tsx
2699
+
2700
+ \`\`\`tsx
2701
+ import { ThemeProvider, TooltipProvider, Toaster } from '@thesage/ui'
2702
+ import '@thesage/ui/globals.css'
2703
+
2704
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
2705
+ return (
2706
+ <html lang="en" suppressHydrationWarning>
2707
+ <body>
2708
+ <ThemeProvider defaultTheme="${theme}" defaultMode="system">
2709
+ <TooltipProvider delayDuration={300}>
2710
+ {children}
2711
+ <Toaster position="bottom-right" />
2712
+ </TooltipProvider>
2713
+ </ThemeProvider>
2714
+ </body>
2715
+ </html>
2716
+ )
2717
+ }
2718
+ \`\`\`
2719
+
2720
+ ## 3. tailwind.config.js
2721
+
2722
+ \`\`\`js
2723
+ /** @type {import('tailwindcss').Config} */
2724
+ module.exports = {
2725
+ content: [
2726
+ './app/**/*.{ts,tsx}',
2727
+ './components/**/*.{ts,tsx}',
2728
+ './node_modules/@thesage/ui/dist/**/*.{js,mjs}',
2729
+ ],
2730
+ darkMode: 'class',
2731
+ theme: { extend: {} },
2732
+ plugins: [require('tailwindcss-animate')],
2733
+ }
2734
+ \`\`\`
2735
+
2736
+ ## 4. postcss.config.js
2737
+
2738
+ \`\`\`js
2739
+ module.exports = {
2740
+ plugins: { tailwindcss: {}, autoprefixer: {} },
2741
+ }
2742
+ \`\`\`
2743
+
2744
+ ## 5. app/page.tsx (starter)
2745
+
2746
+ \`\`\`tsx
2747
+ import { Button, Card, Heading, Text } from '@thesage/ui'
2748
+
2749
+ export default function Home() {
2750
+ return (
2751
+ <main className="min-h-screen bg-background p-8">
2752
+ <Card className="mx-auto max-w-md p-6">
2753
+ <Heading as="h1" size="lg">Welcome</Heading>
2754
+ <Text className="mt-2">Your app is ready.</Text>
2755
+ <Button className="mt-4">Get Started</Button>
2756
+ </Card>
2757
+ </main>
2758
+ )
2759
+ }
2760
+ \`\`\``;
2761
+ }
2762
+ return `# Vite + React Setup with Sage Design Engine
2763
+
2764
+ ## 1. Install dependencies
2765
+
2766
+ \`\`\`bash
2767
+ pnpm add @thesage/ui
2768
+ pnpm add -D tailwindcss@^3.4 postcss autoprefixer tailwindcss-animate
2769
+ \`\`\`
2770
+
2771
+ ## 2. src/main.tsx
2772
+
2773
+ \`\`\`tsx
2774
+ import React from 'react'
2775
+ import ReactDOM from 'react-dom/client'
2776
+ import { ThemeProvider, TooltipProvider, Toaster } from '@thesage/ui'
2777
+ import '@thesage/ui/globals.css'
2778
+ import App from './App'
2779
+
2780
+ ReactDOM.createRoot(document.getElementById('root')!).render(
2781
+ <React.StrictMode>
2782
+ <ThemeProvider defaultTheme="${theme}" defaultMode="system">
2783
+ <TooltipProvider delayDuration={300}>
2784
+ <App />
2785
+ <Toaster position="bottom-right" />
2786
+ </TooltipProvider>
2787
+ </ThemeProvider>
2788
+ </React.StrictMode>
2789
+ )
2790
+ \`\`\`
2791
+
2792
+ ## 3. tailwind.config.js
2793
+
2794
+ \`\`\`js
2795
+ /** @type {import('tailwindcss').Config} */
2796
+ export default {
2797
+ content: [
2798
+ './index.html',
2799
+ './src/**/*.{ts,tsx}',
2800
+ './node_modules/@thesage/ui/dist/**/*.{js,mjs}',
2801
+ ],
2802
+ darkMode: 'class',
2803
+ theme: { extend: {} },
2804
+ plugins: [require('tailwindcss-animate')],
2805
+ }
2806
+ \`\`\`
2807
+
2808
+ ## 4. postcss.config.js
2809
+
2810
+ \`\`\`js
2811
+ export default {
2812
+ plugins: { tailwindcss: {}, autoprefixer: {} },
2813
+ }
2814
+ \`\`\`
2815
+
2816
+ ## 5. src/App.tsx (starter)
2817
+
2818
+ \`\`\`tsx
2819
+ import { Button, Card, Heading, Text, ThemeToggle, ThemeSwitcher } from '@thesage/ui'
2820
+
2821
+ export default function App() {
2822
+ return (
2823
+ <div className="min-h-screen bg-background p-8">
2824
+ <div className="mx-auto max-w-2xl space-y-8">
2825
+ <div className="flex items-center justify-between">
2826
+ <Heading as="h1" size="xl">My App</Heading>
2827
+ <div className="flex gap-2">
2828
+ <ThemeSwitcher />
2829
+ <ThemeToggle />
2830
+ </div>
2831
+ </div>
2832
+ <Card className="p-6">
2833
+ <Text>Welcome to your new app. Start building!</Text>
2834
+ <Button className="mt-4">Get Started</Button>
2835
+ </Card>
2836
+ </div>
2837
+ </div>
2838
+ )
2839
+ }
2840
+ \`\`\``;
2841
+ }
2842
+ function generateAuditChecklist() {
2843
+ return `## SDE Usage Audit Checklist
2844
+
2845
+ ### Provider Wrapping
2846
+ - [ ] ThemeProvider wraps the entire app
2847
+ - [ ] TooltipProvider wraps any area using Tooltip components
2848
+ - [ ] <Toaster /> is rendered at app root (required for toast notifications)
2849
+ - [ ] '@thesage/ui/globals.css' is imported at the top level
2850
+
2851
+ ### Styling
2852
+ - [ ] No hardcoded colors (no bg-white, text-black, bg-blue-500, text-gray-900)
2853
+ - [ ] All colors use CSS variables (bg-background, text-foreground, bg-primary, border-border, etc.)
2854
+ - [ ] className merging uses cn() utility, not string concatenation
2855
+ - [ ] Dark mode works correctly via ThemeProvider (no manual dark: class management needed)
2856
+ - [ ] tailwind.config.js content array includes: './node_modules/@thesage/ui/dist/**/*.{js,mjs}'
2857
+
2858
+ ### Accessibility
2859
+ - [ ] All interactive elements are keyboard-navigable
2860
+ - [ ] Dialogs trap focus and return focus on close
2861
+ - [ ] Form inputs have associated Label components
2862
+ - [ ] Animated components use useMotionPreference hook
2863
+ - [ ] AlertDialog used (not Dialog) for destructive confirmations
2864
+ - [ ] Images have alt text
2865
+
2866
+ ### Imports
2867
+ - [ ] Components imported from '@thesage/ui' (not relative paths to node_modules)
2868
+ - [ ] Heavy features use subpath imports:
2869
+ - @thesage/ui/forms (react-hook-form + zod)
2870
+ - @thesage/ui/dates (date-fns + react-day-picker)
2871
+ - @thesage/ui/tables (@tanstack/react-table)
2872
+ - @thesage/ui/dnd (@dnd-kit)
2873
+ - [ ] No duplicate imports (same component from both @thesage/ui and a local file)
2874
+ - [ ] Peer dependencies installed for subpath imports that need them
2875
+
2876
+ ### Component Usage
2877
+ - [ ] Compound components use correct structure (e.g., Dialog needs DialogTrigger + DialogContent)
2878
+ - [ ] Sheet used for desktop side panels, Drawer for mobile bottom sheets
2879
+ - [ ] Combobox used (not Select) when searchable dropdown is needed
2880
+ - [ ] Switch for instant toggles, Checkbox for form submission
2881
+ - [ ] Toast/Sonner for transient notifications, Alert for persistent messages`;
2882
+ }
2883
+ function generateEjectInstructions(component, targetDir) {
2884
+ const srcPath = `node_modules/@thesage/ui/src/components/${component.category}/${component.name}.tsx`;
2885
+ const destPath = `${targetDir}/${component.name}.tsx`;
2886
+ return `## Eject: ${component.name}
2887
+
2888
+ **Step 1:** Copy the source file:
2889
+ \`\`\`bash
2890
+ mkdir -p ${targetDir}
2891
+ cp ${srcPath} ${destPath}
2892
+ \`\`\`
2893
+
2894
+ **Step 2:** Rewrite imports in the copied file:
2895
+ - Change \`from '../../lib/utils'\` \u2192 \`from '@/lib/utils'\`
2896
+ - Change \`from '../actions/Button'\` \u2192 \`from '@thesage/ui'\` (keep using package for non-ejected deps)
2897
+ - Change \`from '../../hooks/useMotionPreference'\` \u2192 \`from '@thesage/ui/hooks'\`
2898
+
2899
+ **Step 3:** Update your app imports:
2900
+ \`\`\`tsx
2901
+ // Before:
2902
+ import { ${component.name} } from '@thesage/ui'
2903
+ // After:
2904
+ import { ${component.name} } from '@/${targetDir}/${component.name}'
2905
+ \`\`\`
2906
+
2907
+ **Step 4:** Ensure \`cn()\` utility exists locally:
2908
+ \`\`\`tsx
2909
+ // src/lib/utils.ts
2910
+ import { clsx, type ClassValue } from 'clsx'
2911
+ import { twMerge } from 'tailwind-merge'
2912
+ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) }
2913
+ \`\`\`
2914
+
2915
+ **Step 5:** Install clsx + tailwind-merge if not already present:
2916
+ \`\`\`bash
2917
+ pnpm add clsx tailwind-merge
2918
+ \`\`\`
2919
+
2920
+ You now own this component. Modify it freely.
2921
+
2922
+ **Note:** The ejected component still works with SDE themes and CSS variables. You get full control over the markup and styling while staying in the SDE ecosystem.`;
2923
+ }
2464
2924
  server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
2465
2925
  return { tools: TOOLS };
2466
2926
  });
@@ -2553,8 +3013,8 @@ Try searching for:
2553
3013
  };
2554
3014
  }
2555
3015
  case "get_component": {
2556
- const name2 = args?.name;
2557
- if (!name2) {
3016
+ const componentName = args?.name;
3017
+ if (!componentName) {
2558
3018
  return {
2559
3019
  content: [
2560
3020
  {
@@ -2565,14 +3025,14 @@ Try searching for:
2565
3025
  isError: true
2566
3026
  };
2567
3027
  }
2568
- const component = getComponent(name2);
3028
+ const component = getComponent(componentName);
2569
3029
  if (!component) {
2570
3030
  const allNames = getAllComponentNames();
2571
3031
  return {
2572
3032
  content: [
2573
3033
  {
2574
3034
  type: "text",
2575
- text: `Component "${name2}" not found.
3035
+ text: `Component "${componentName}" not found.
2576
3036
 
2577
3037
  Available components:
2578
3038
  ${allNames.join(", ")}`
@@ -2591,8 +3051,8 @@ ${allNames.join(", ")}`
2591
3051
  };
2592
3052
  }
2593
3053
  case "install_component": {
2594
- const name2 = args?.name;
2595
- if (!name2) {
3054
+ const componentName = args?.name;
3055
+ if (!componentName) {
2596
3056
  return {
2597
3057
  content: [
2598
3058
  {
@@ -2603,13 +3063,13 @@ ${allNames.join(", ")}`
2603
3063
  isError: true
2604
3064
  };
2605
3065
  }
2606
- const component = getComponent(name2);
3066
+ const component = getComponent(componentName);
2607
3067
  if (!component) {
2608
3068
  return {
2609
3069
  content: [
2610
3070
  {
2611
3071
  type: "text",
2612
- text: `Component "${name2}" not found. Use search_components to find available components.`
3072
+ text: `Component "${componentName}" not found. Use search_components to find available components.`
2613
3073
  }
2614
3074
  ],
2615
3075
  isError: true
@@ -2624,6 +3084,150 @@ ${allNames.join(", ")}`
2624
3084
  ]
2625
3085
  };
2626
3086
  }
3087
+ case "get_app_shell": {
3088
+ const framework = args?.framework || "vite";
3089
+ const theme = args?.theme || "studio";
3090
+ return {
3091
+ content: [
3092
+ {
3093
+ type: "text",
3094
+ text: generateAppShell(framework, theme)
3095
+ }
3096
+ ]
3097
+ };
3098
+ }
3099
+ case "get_examples": {
3100
+ const componentName = args?.name;
3101
+ if (!componentName) {
3102
+ return {
3103
+ content: [
3104
+ {
3105
+ type: "text",
3106
+ text: "Error: name parameter is required"
3107
+ }
3108
+ ],
3109
+ isError: true
3110
+ };
3111
+ }
3112
+ const component = getComponent(componentName);
3113
+ if (!component) {
3114
+ return {
3115
+ content: [
3116
+ {
3117
+ type: "text",
3118
+ text: `Component "${componentName}" not found. Use search_components to find available components.`
3119
+ }
3120
+ ],
3121
+ isError: true
3122
+ };
3123
+ }
3124
+ let output = `## ${component.name} Examples
3125
+
3126
+ `;
3127
+ if (component.example) {
3128
+ output += `### Basic Usage
3129
+
3130
+ `;
3131
+ output += `\`\`\`tsx
3132
+ ${component.example}
3133
+ \`\`\`
3134
+
3135
+ `;
3136
+ }
3137
+ const importParts = [component.name];
3138
+ if (component.subComponents) {
3139
+ importParts.push(...component.subComponents);
3140
+ }
3141
+ output += `### Import
3142
+
3143
+ `;
3144
+ output += `\`\`\`tsx
3145
+ import { ${importParts.join(", ")} } from '@thesage/ui'
3146
+ \`\`\`
3147
+
3148
+ `;
3149
+ if (component.useCases.length > 0) {
3150
+ output += `### Common Use Cases
3151
+
3152
+ `;
3153
+ component.useCases.forEach((useCase) => {
3154
+ output += `- ${useCase}
3155
+ `;
3156
+ });
3157
+ output += "\n";
3158
+ }
3159
+ if (component.props && Object.keys(component.props).length > 0) {
3160
+ output += `### Key Props
3161
+
3162
+ `;
3163
+ Object.entries(component.props).forEach(([propName, prop]) => {
3164
+ const defaultStr = prop.default ? ` (default: ${prop.default})` : "";
3165
+ output += `- **${propName}**: \`${prop.type}\`${defaultStr} \u2014 ${prop.description}
3166
+ `;
3167
+ });
3168
+ output += "\n";
3169
+ }
3170
+ output += `
3171
+ Full API reference: https://thesage.dev/llms-full.txt
3172
+ `;
3173
+ return {
3174
+ content: [
3175
+ {
3176
+ type: "text",
3177
+ text: output
3178
+ }
3179
+ ]
3180
+ };
3181
+ }
3182
+ case "get_audit_checklist": {
3183
+ return {
3184
+ content: [
3185
+ {
3186
+ type: "text",
3187
+ text: generateAuditChecklist()
3188
+ }
3189
+ ]
3190
+ };
3191
+ }
3192
+ case "eject_component": {
3193
+ const componentName = args?.name;
3194
+ const targetDir = args?.targetDir || "src/components/ui";
3195
+ if (!componentName) {
3196
+ return {
3197
+ content: [
3198
+ {
3199
+ type: "text",
3200
+ text: "Error: name parameter is required"
3201
+ }
3202
+ ],
3203
+ isError: true
3204
+ };
3205
+ }
3206
+ const component = getComponent(componentName);
3207
+ if (!component) {
3208
+ const allNames = getAllComponentNames();
3209
+ return {
3210
+ content: [
3211
+ {
3212
+ type: "text",
3213
+ text: `Component "${componentName}" not found.
3214
+
3215
+ Available components:
3216
+ ${allNames.join(", ")}`
3217
+ }
3218
+ ],
3219
+ isError: true
3220
+ };
3221
+ }
3222
+ return {
3223
+ content: [
3224
+ {
3225
+ type: "text",
3226
+ text: generateEjectInstructions(component, targetDir)
3227
+ }
3228
+ ]
3229
+ };
3230
+ }
2627
3231
  default:
2628
3232
  return {
2629
3233
  content: [
@@ -2650,8 +3254,9 @@ ${allNames.join(", ")}`
2650
3254
  async function main() {
2651
3255
  const transport = new import_stdio.StdioServerTransport();
2652
3256
  await server.connect(transport);
2653
- console.error("Sage UI MCP Server running");
3257
+ console.error("Sage UI MCP Server v0.8.0 running");
2654
3258
  console.error(`Components available: ${getComponentCount()}`);
3259
+ console.error(`Tools available: ${TOOLS.length}`);
2655
3260
  }
2656
3261
  main().catch((error) => {
2657
3262
  console.error("Fatal error:", error);