@thesage/mcp 0.7.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.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  getComponentCount,
8
8
  getComponentsByCategory,
9
9
  searchComponents
10
- } from "./chunk-K5RGZGQI.mjs";
10
+ } from "./chunk-L4SOXQCS.mjs";
11
11
 
12
12
  // src/index.ts
13
13
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -19,7 +19,7 @@ import {
19
19
  var server = new Server(
20
20
  {
21
21
  name: "sds-mcp-server",
22
- version: "0.1.0"
22
+ version: "0.8.0"
23
23
  },
24
24
  {
25
25
  capabilities: {
@@ -95,6 +95,65 @@ var TOOLS = [
95
95
  },
96
96
  required: ["name"]
97
97
  }
98
+ },
99
+ {
100
+ name: "get_app_shell",
101
+ 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.",
102
+ inputSchema: {
103
+ type: "object",
104
+ properties: {
105
+ framework: {
106
+ type: "string",
107
+ enum: ["nextjs", "vite"],
108
+ description: 'Target framework. Defaults to "vite".'
109
+ },
110
+ theme: {
111
+ type: "string",
112
+ enum: ["studio", "terra", "volt"],
113
+ description: 'Default theme. Defaults to "studio".'
114
+ }
115
+ }
116
+ }
117
+ },
118
+ {
119
+ name: "get_examples",
120
+ description: "Get usage examples for a specific component, including common patterns, compound component usage, and integration with other SDE components.",
121
+ inputSchema: {
122
+ type: "object",
123
+ properties: {
124
+ name: {
125
+ type: "string",
126
+ description: 'Component name (e.g., "Button", "Dialog", "Card")'
127
+ }
128
+ },
129
+ required: ["name"]
130
+ }
131
+ },
132
+ {
133
+ name: "get_audit_checklist",
134
+ 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.",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {}
138
+ }
139
+ },
140
+ {
141
+ name: "eject_component",
142
+ 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.",
143
+ inputSchema: {
144
+ type: "object",
145
+ properties: {
146
+ name: {
147
+ type: "string",
148
+ description: 'Component name (e.g., "Button", "Card", "Dialog")'
149
+ },
150
+ targetDir: {
151
+ type: "string",
152
+ description: 'Target directory relative to project root. Defaults to "src/components/ui"'
153
+ }
154
+ },
155
+ required: ["name"]
156
+ }
98
157
  }
99
158
  ];
100
159
  function formatComponentList(components) {
@@ -284,6 +343,243 @@ function formatInstallationInstructions(component) {
284
343
  `;
285
344
  return output;
286
345
  }
346
+ function generateAppShell(framework, theme) {
347
+ if (framework === "nextjs") {
348
+ return `# Next.js App Router Setup with Sage Design Engine
349
+
350
+ ## 1. Install dependencies
351
+
352
+ \`\`\`bash
353
+ pnpm add @thesage/ui
354
+ pnpm add -D tailwindcss@^3.4 postcss autoprefixer tailwindcss-animate
355
+ \`\`\`
356
+
357
+ ## 2. app/layout.tsx
358
+
359
+ \`\`\`tsx
360
+ import { ThemeProvider, TooltipProvider, Toaster } from '@thesage/ui'
361
+ import '@thesage/ui/globals.css'
362
+
363
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
364
+ return (
365
+ <html lang="en" suppressHydrationWarning>
366
+ <body>
367
+ <ThemeProvider defaultTheme="${theme}" defaultMode="system">
368
+ <TooltipProvider delayDuration={300}>
369
+ {children}
370
+ <Toaster position="bottom-right" />
371
+ </TooltipProvider>
372
+ </ThemeProvider>
373
+ </body>
374
+ </html>
375
+ )
376
+ }
377
+ \`\`\`
378
+
379
+ ## 3. tailwind.config.js
380
+
381
+ \`\`\`js
382
+ /** @type {import('tailwindcss').Config} */
383
+ module.exports = {
384
+ content: [
385
+ './app/**/*.{ts,tsx}',
386
+ './components/**/*.{ts,tsx}',
387
+ './node_modules/@thesage/ui/dist/**/*.{js,mjs}',
388
+ ],
389
+ darkMode: 'class',
390
+ theme: { extend: {} },
391
+ plugins: [require('tailwindcss-animate')],
392
+ }
393
+ \`\`\`
394
+
395
+ ## 4. postcss.config.js
396
+
397
+ \`\`\`js
398
+ module.exports = {
399
+ plugins: { tailwindcss: {}, autoprefixer: {} },
400
+ }
401
+ \`\`\`
402
+
403
+ ## 5. app/page.tsx (starter)
404
+
405
+ \`\`\`tsx
406
+ import { Button, Card, Heading, Text } from '@thesage/ui'
407
+
408
+ export default function Home() {
409
+ return (
410
+ <main className="min-h-screen bg-background p-8">
411
+ <Card className="mx-auto max-w-md p-6">
412
+ <Heading as="h1" size="lg">Welcome</Heading>
413
+ <Text className="mt-2">Your app is ready.</Text>
414
+ <Button className="mt-4">Get Started</Button>
415
+ </Card>
416
+ </main>
417
+ )
418
+ }
419
+ \`\`\``;
420
+ }
421
+ return `# Vite + React Setup with Sage Design Engine
422
+
423
+ ## 1. Install dependencies
424
+
425
+ \`\`\`bash
426
+ pnpm add @thesage/ui
427
+ pnpm add -D tailwindcss@^3.4 postcss autoprefixer tailwindcss-animate
428
+ \`\`\`
429
+
430
+ ## 2. src/main.tsx
431
+
432
+ \`\`\`tsx
433
+ import React from 'react'
434
+ import ReactDOM from 'react-dom/client'
435
+ import { ThemeProvider, TooltipProvider, Toaster } from '@thesage/ui'
436
+ import '@thesage/ui/globals.css'
437
+ import App from './App'
438
+
439
+ ReactDOM.createRoot(document.getElementById('root')!).render(
440
+ <React.StrictMode>
441
+ <ThemeProvider defaultTheme="${theme}" defaultMode="system">
442
+ <TooltipProvider delayDuration={300}>
443
+ <App />
444
+ <Toaster position="bottom-right" />
445
+ </TooltipProvider>
446
+ </ThemeProvider>
447
+ </React.StrictMode>
448
+ )
449
+ \`\`\`
450
+
451
+ ## 3. tailwind.config.js
452
+
453
+ \`\`\`js
454
+ /** @type {import('tailwindcss').Config} */
455
+ export default {
456
+ content: [
457
+ './index.html',
458
+ './src/**/*.{ts,tsx}',
459
+ './node_modules/@thesage/ui/dist/**/*.{js,mjs}',
460
+ ],
461
+ darkMode: 'class',
462
+ theme: { extend: {} },
463
+ plugins: [require('tailwindcss-animate')],
464
+ }
465
+ \`\`\`
466
+
467
+ ## 4. postcss.config.js
468
+
469
+ \`\`\`js
470
+ export default {
471
+ plugins: { tailwindcss: {}, autoprefixer: {} },
472
+ }
473
+ \`\`\`
474
+
475
+ ## 5. src/App.tsx (starter)
476
+
477
+ \`\`\`tsx
478
+ import { Button, Card, Heading, Text, ThemeToggle, ThemeSwitcher } from '@thesage/ui'
479
+
480
+ export default function App() {
481
+ return (
482
+ <div className="min-h-screen bg-background p-8">
483
+ <div className="mx-auto max-w-2xl space-y-8">
484
+ <div className="flex items-center justify-between">
485
+ <Heading as="h1" size="xl">My App</Heading>
486
+ <div className="flex gap-2">
487
+ <ThemeSwitcher />
488
+ <ThemeToggle />
489
+ </div>
490
+ </div>
491
+ <Card className="p-6">
492
+ <Text>Welcome to your new app. Start building!</Text>
493
+ <Button className="mt-4">Get Started</Button>
494
+ </Card>
495
+ </div>
496
+ </div>
497
+ )
498
+ }
499
+ \`\`\``;
500
+ }
501
+ function generateAuditChecklist() {
502
+ return `## SDE Usage Audit Checklist
503
+
504
+ ### Provider Wrapping
505
+ - [ ] ThemeProvider wraps the entire app
506
+ - [ ] TooltipProvider wraps any area using Tooltip components
507
+ - [ ] <Toaster /> is rendered at app root (required for toast notifications)
508
+ - [ ] '@thesage/ui/globals.css' is imported at the top level
509
+
510
+ ### Styling
511
+ - [ ] No hardcoded colors (no bg-white, text-black, bg-blue-500, text-gray-900)
512
+ - [ ] All colors use CSS variables (bg-background, text-foreground, bg-primary, border-border, etc.)
513
+ - [ ] className merging uses cn() utility, not string concatenation
514
+ - [ ] Dark mode works correctly via ThemeProvider (no manual dark: class management needed)
515
+ - [ ] tailwind.config.js content array includes: './node_modules/@thesage/ui/dist/**/*.{js,mjs}'
516
+
517
+ ### Accessibility
518
+ - [ ] All interactive elements are keyboard-navigable
519
+ - [ ] Dialogs trap focus and return focus on close
520
+ - [ ] Form inputs have associated Label components
521
+ - [ ] Animated components use useMotionPreference hook
522
+ - [ ] AlertDialog used (not Dialog) for destructive confirmations
523
+ - [ ] Images have alt text
524
+
525
+ ### Imports
526
+ - [ ] Components imported from '@thesage/ui' (not relative paths to node_modules)
527
+ - [ ] Heavy features use subpath imports:
528
+ - @thesage/ui/forms (react-hook-form + zod)
529
+ - @thesage/ui/dates (date-fns + react-day-picker)
530
+ - @thesage/ui/tables (@tanstack/react-table)
531
+ - @thesage/ui/dnd (@dnd-kit)
532
+ - [ ] No duplicate imports (same component from both @thesage/ui and a local file)
533
+ - [ ] Peer dependencies installed for subpath imports that need them
534
+
535
+ ### Component Usage
536
+ - [ ] Compound components use correct structure (e.g., Dialog needs DialogTrigger + DialogContent)
537
+ - [ ] Sheet used for desktop side panels, Drawer for mobile bottom sheets
538
+ - [ ] Combobox used (not Select) when searchable dropdown is needed
539
+ - [ ] Switch for instant toggles, Checkbox for form submission
540
+ - [ ] Toast/Sonner for transient notifications, Alert for persistent messages`;
541
+ }
542
+ function generateEjectInstructions(component, targetDir) {
543
+ const srcPath = `node_modules/@thesage/ui/src/components/${component.category}/${component.name}.tsx`;
544
+ const destPath = `${targetDir}/${component.name}.tsx`;
545
+ return `## Eject: ${component.name}
546
+
547
+ **Step 1:** Copy the source file:
548
+ \`\`\`bash
549
+ mkdir -p ${targetDir}
550
+ cp ${srcPath} ${destPath}
551
+ \`\`\`
552
+
553
+ **Step 2:** Rewrite imports in the copied file:
554
+ - Change \`from '../../lib/utils'\` \u2192 \`from '@/lib/utils'\`
555
+ - Change \`from '../actions/Button'\` \u2192 \`from '@thesage/ui'\` (keep using package for non-ejected deps)
556
+ - Change \`from '../../hooks/useMotionPreference'\` \u2192 \`from '@thesage/ui/hooks'\`
557
+
558
+ **Step 3:** Update your app imports:
559
+ \`\`\`tsx
560
+ // Before:
561
+ import { ${component.name} } from '@thesage/ui'
562
+ // After:
563
+ import { ${component.name} } from '@/${targetDir}/${component.name}'
564
+ \`\`\`
565
+
566
+ **Step 4:** Ensure \`cn()\` utility exists locally:
567
+ \`\`\`tsx
568
+ // src/lib/utils.ts
569
+ import { clsx, type ClassValue } from 'clsx'
570
+ import { twMerge } from 'tailwind-merge'
571
+ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) }
572
+ \`\`\`
573
+
574
+ **Step 5:** Install clsx + tailwind-merge if not already present:
575
+ \`\`\`bash
576
+ pnpm add clsx tailwind-merge
577
+ \`\`\`
578
+
579
+ You now own this component. Modify it freely.
580
+
581
+ **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.`;
582
+ }
287
583
  server.setRequestHandler(ListToolsRequestSchema, async () => {
288
584
  return { tools: TOOLS };
289
585
  });
@@ -376,8 +672,8 @@ Try searching for:
376
672
  };
377
673
  }
378
674
  case "get_component": {
379
- const name2 = args?.name;
380
- if (!name2) {
675
+ const componentName = args?.name;
676
+ if (!componentName) {
381
677
  return {
382
678
  content: [
383
679
  {
@@ -388,14 +684,14 @@ Try searching for:
388
684
  isError: true
389
685
  };
390
686
  }
391
- const component = getComponent(name2);
687
+ const component = getComponent(componentName);
392
688
  if (!component) {
393
689
  const allNames = getAllComponentNames();
394
690
  return {
395
691
  content: [
396
692
  {
397
693
  type: "text",
398
- text: `Component "${name2}" not found.
694
+ text: `Component "${componentName}" not found.
399
695
 
400
696
  Available components:
401
697
  ${allNames.join(", ")}`
@@ -414,8 +710,8 @@ ${allNames.join(", ")}`
414
710
  };
415
711
  }
416
712
  case "install_component": {
417
- const name2 = args?.name;
418
- if (!name2) {
713
+ const componentName = args?.name;
714
+ if (!componentName) {
419
715
  return {
420
716
  content: [
421
717
  {
@@ -426,13 +722,13 @@ ${allNames.join(", ")}`
426
722
  isError: true
427
723
  };
428
724
  }
429
- const component = getComponent(name2);
725
+ const component = getComponent(componentName);
430
726
  if (!component) {
431
727
  return {
432
728
  content: [
433
729
  {
434
730
  type: "text",
435
- text: `Component "${name2}" not found. Use search_components to find available components.`
731
+ text: `Component "${componentName}" not found. Use search_components to find available components.`
436
732
  }
437
733
  ],
438
734
  isError: true
@@ -447,6 +743,150 @@ ${allNames.join(", ")}`
447
743
  ]
448
744
  };
449
745
  }
746
+ case "get_app_shell": {
747
+ const framework = args?.framework || "vite";
748
+ const theme = args?.theme || "studio";
749
+ return {
750
+ content: [
751
+ {
752
+ type: "text",
753
+ text: generateAppShell(framework, theme)
754
+ }
755
+ ]
756
+ };
757
+ }
758
+ case "get_examples": {
759
+ const componentName = args?.name;
760
+ if (!componentName) {
761
+ return {
762
+ content: [
763
+ {
764
+ type: "text",
765
+ text: "Error: name parameter is required"
766
+ }
767
+ ],
768
+ isError: true
769
+ };
770
+ }
771
+ const component = getComponent(componentName);
772
+ if (!component) {
773
+ return {
774
+ content: [
775
+ {
776
+ type: "text",
777
+ text: `Component "${componentName}" not found. Use search_components to find available components.`
778
+ }
779
+ ],
780
+ isError: true
781
+ };
782
+ }
783
+ let output = `## ${component.name} Examples
784
+
785
+ `;
786
+ if (component.example) {
787
+ output += `### Basic Usage
788
+
789
+ `;
790
+ output += `\`\`\`tsx
791
+ ${component.example}
792
+ \`\`\`
793
+
794
+ `;
795
+ }
796
+ const importParts = [component.name];
797
+ if (component.subComponents) {
798
+ importParts.push(...component.subComponents);
799
+ }
800
+ output += `### Import
801
+
802
+ `;
803
+ output += `\`\`\`tsx
804
+ import { ${importParts.join(", ")} } from '@thesage/ui'
805
+ \`\`\`
806
+
807
+ `;
808
+ if (component.useCases.length > 0) {
809
+ output += `### Common Use Cases
810
+
811
+ `;
812
+ component.useCases.forEach((useCase) => {
813
+ output += `- ${useCase}
814
+ `;
815
+ });
816
+ output += "\n";
817
+ }
818
+ if (component.props && Object.keys(component.props).length > 0) {
819
+ output += `### Key Props
820
+
821
+ `;
822
+ Object.entries(component.props).forEach(([propName, prop]) => {
823
+ const defaultStr = prop.default ? ` (default: ${prop.default})` : "";
824
+ output += `- **${propName}**: \`${prop.type}\`${defaultStr} \u2014 ${prop.description}
825
+ `;
826
+ });
827
+ output += "\n";
828
+ }
829
+ output += `
830
+ Full API reference: https://thesage.dev/llms-full.txt
831
+ `;
832
+ return {
833
+ content: [
834
+ {
835
+ type: "text",
836
+ text: output
837
+ }
838
+ ]
839
+ };
840
+ }
841
+ case "get_audit_checklist": {
842
+ return {
843
+ content: [
844
+ {
845
+ type: "text",
846
+ text: generateAuditChecklist()
847
+ }
848
+ ]
849
+ };
850
+ }
851
+ case "eject_component": {
852
+ const componentName = args?.name;
853
+ const targetDir = args?.targetDir || "src/components/ui";
854
+ if (!componentName) {
855
+ return {
856
+ content: [
857
+ {
858
+ type: "text",
859
+ text: "Error: name parameter is required"
860
+ }
861
+ ],
862
+ isError: true
863
+ };
864
+ }
865
+ const component = getComponent(componentName);
866
+ if (!component) {
867
+ const allNames = getAllComponentNames();
868
+ return {
869
+ content: [
870
+ {
871
+ type: "text",
872
+ text: `Component "${componentName}" not found.
873
+
874
+ Available components:
875
+ ${allNames.join(", ")}`
876
+ }
877
+ ],
878
+ isError: true
879
+ };
880
+ }
881
+ return {
882
+ content: [
883
+ {
884
+ type: "text",
885
+ text: generateEjectInstructions(component, targetDir)
886
+ }
887
+ ]
888
+ };
889
+ }
450
890
  default:
451
891
  return {
452
892
  content: [
@@ -473,8 +913,9 @@ ${allNames.join(", ")}`
473
913
  async function main() {
474
914
  const transport = new StdioServerTransport();
475
915
  await server.connect(transport);
476
- console.error("Sage UI MCP Server running");
916
+ console.error("Sage UI MCP Server v0.8.0 running");
477
917
  console.error(`Components available: ${getComponentCount()}`);
918
+ console.error(`Tools available: ${TOOLS.length}`);
478
919
  }
479
920
  main().catch((error) => {
480
921
  console.error("Fatal error:", error);
@@ -46,7 +46,7 @@ declare const COMPONENT_CATEGORIES: {
46
46
  readonly forms: {
47
47
  readonly label: "Forms";
48
48
  readonly description: "Input controls for data collection";
49
- readonly count: 18;
49
+ readonly count: 19;
50
50
  };
51
51
  readonly navigation: {
52
52
  readonly label: "Navigation";
@@ -56,17 +56,17 @@ declare const COMPONENT_CATEGORIES: {
56
56
  readonly overlays: {
57
57
  readonly label: "Overlays";
58
58
  readonly description: "Contextual content that appears above the main UI";
59
- readonly count: 11;
59
+ readonly count: 12;
60
60
  };
61
61
  readonly feedback: {
62
62
  readonly label: "Feedback";
63
63
  readonly description: "Communicating system state and user action results";
64
- readonly count: 7;
64
+ readonly count: 9;
65
65
  };
66
66
  readonly 'data-display': {
67
67
  readonly label: "Data Display";
68
68
  readonly description: "Presenting information in structured formats";
69
- readonly count: 16;
69
+ readonly count: 19;
70
70
  };
71
71
  readonly layout: {
72
72
  readonly label: "Layout";
@@ -46,7 +46,7 @@ declare const COMPONENT_CATEGORIES: {
46
46
  readonly forms: {
47
47
  readonly label: "Forms";
48
48
  readonly description: "Input controls for data collection";
49
- readonly count: 18;
49
+ readonly count: 19;
50
50
  };
51
51
  readonly navigation: {
52
52
  readonly label: "Navigation";
@@ -56,17 +56,17 @@ declare const COMPONENT_CATEGORIES: {
56
56
  readonly overlays: {
57
57
  readonly label: "Overlays";
58
58
  readonly description: "Contextual content that appears above the main UI";
59
- readonly count: 11;
59
+ readonly count: 12;
60
60
  };
61
61
  readonly feedback: {
62
62
  readonly label: "Feedback";
63
63
  readonly description: "Communicating system state and user action results";
64
- readonly count: 7;
64
+ readonly count: 9;
65
65
  };
66
66
  readonly 'data-display': {
67
67
  readonly label: "Data Display";
68
68
  readonly description: "Presenting information in structured formats";
69
- readonly count: 16;
69
+ readonly count: 19;
70
70
  };
71
71
  readonly layout: {
72
72
  readonly label: "Layout";