@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/chunk-L4SOXQCS.mjs +2359 -0
- package/dist/index.js +619 -14
- package/dist/index.mjs +460 -2198
- package/dist/registry.d.mts +119 -0
- package/dist/registry.d.ts +119 -0
- package/dist/registry.js +2389 -0
- package/dist/registry.mjs +18 -0
- package/package.json +8 -3
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:
|
|
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:
|
|
29
|
+
count: 12
|
|
30
30
|
},
|
|
31
31
|
feedback: {
|
|
32
32
|
label: "Feedback",
|
|
33
33
|
description: "Communicating system state and user action results",
|
|
34
|
-
count:
|
|
34
|
+
count: 9
|
|
35
35
|
},
|
|
36
36
|
"data-display": {
|
|
37
37
|
label: "Data Display",
|
|
38
38
|
description: "Presenting information in structured formats",
|
|
39
|
-
count:
|
|
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.
|
|
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
|
|
2557
|
-
if (!
|
|
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(
|
|
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 "${
|
|
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
|
|
2595
|
-
if (!
|
|
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(
|
|
3066
|
+
const component = getComponent(componentName);
|
|
2607
3067
|
if (!component) {
|
|
2608
3068
|
return {
|
|
2609
3069
|
content: [
|
|
2610
3070
|
{
|
|
2611
3071
|
type: "text",
|
|
2612
|
-
text: `Component "${
|
|
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);
|