@spaced-out/genesis-mcp 1.0.0

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/index.js ADDED
@@ -0,0 +1,1947 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Genesis UI Design System MCP Server
5
+ *
6
+ * Provides AI assistants with access to the Genesis UI Design System through MCP.
7
+ * This version reads from pre-bundled JSON data.
8
+ *
9
+ * Usage with any MCP client:
10
+ * {
11
+ * "mcpServers": {
12
+ * "genesis-design-system": {
13
+ * "command": "npx",
14
+ * "args": ["-y", "@spaced-out/genesis-mcp@latest"]
15
+ * }
16
+ * }
17
+ * }
18
+ */
19
+
20
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
21
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
22
+ import {
23
+ CallToolRequestSchema,
24
+ ListResourcesRequestSchema,
25
+ ListToolsRequestSchema,
26
+ ReadResourceRequestSchema,
27
+ } from '@modelcontextprotocol/sdk/types.js';
28
+ import { readFileSync } from 'fs';
29
+ import { join, dirname } from 'path';
30
+ import { fileURLToPath } from 'url';
31
+
32
+ const __filename = fileURLToPath(import.meta.url);
33
+ const __dirname = dirname(__filename);
34
+
35
+ // Load pre-bundled design system data
36
+ let designSystemData;
37
+ try {
38
+ const dataPath = join(__dirname, 'data', 'design-system.json');
39
+ designSystemData = JSON.parse(readFileSync(dataPath, 'utf-8'));
40
+ } catch (error) {
41
+ console.error('❌ ERROR: Could not load design system data');
42
+ console.error(' Make sure to run "npm run build" before using this server');
43
+ console.error(` Error: ${error.message}`);
44
+ process.exit(1);
45
+ }
46
+
47
+ // Log successful load
48
+ // note(vish): Using console.error() because stdout is reserved for MCP protocol messages.
49
+ // All logging/debugging output must go to stderr to avoid corrupting the protocol.
50
+ console.error('✅ Loaded Genesis design system data');
51
+ console.error(` Version: ${designSystemData.metadata.version}`);
52
+ console.error(` Components: ${Object.keys(designSystemData.components).length}`);
53
+ console.error(` Hooks: ${Object.keys(designSystemData.hooks).length}`);
54
+ console.error(` Utils: ${Object.keys(designSystemData.utils || {}).length}`);
55
+ console.error(` Types: ${Object.keys(designSystemData.types || {}).length}`);
56
+ console.error(` Built: ${designSystemData.metadata.buildDate}`);
57
+
58
+ /**
59
+ * Get all components
60
+ */
61
+ function getAllComponents() {
62
+ return Object.keys(designSystemData.components).sort();
63
+ }
64
+
65
+ /**
66
+ * Get all hooks
67
+ */
68
+ function getAllHooks() {
69
+ return Object.keys(designSystemData.hooks).sort();
70
+ }
71
+
72
+ /**
73
+ * Get all utils
74
+ */
75
+ function getAllUtils() {
76
+ return Object.keys(designSystemData.utils || {}).sort();
77
+ }
78
+
79
+ /**
80
+ * Get all types
81
+ */
82
+ function getAllTypes() {
83
+ return Object.keys(designSystemData.types || {}).sort();
84
+ }
85
+
86
+ /**
87
+ * Get component details
88
+ */
89
+ function getComponentDetails(componentName) {
90
+ return designSystemData.components[componentName] || null;
91
+ }
92
+
93
+ /**
94
+ * Get hook details
95
+ */
96
+ function getHookDetails(hookName) {
97
+ return designSystemData.hooks[hookName] || null;
98
+ }
99
+
100
+ /**
101
+ * Get util details
102
+ */
103
+ function getUtilDetails(utilName) {
104
+ return designSystemData.utils?.[utilName] || null;
105
+ }
106
+
107
+ /**
108
+ * Get type details
109
+ */
110
+ function getTypeDetails(typeName) {
111
+ return designSystemData.types?.[typeName] || null;
112
+ }
113
+
114
+ /**
115
+ * Get all design tokens
116
+ */
117
+ function getAllDesignTokens() {
118
+ return designSystemData.tokens;
119
+ }
120
+
121
+ /**
122
+ * Search components by name or content
123
+ */
124
+ function searchComponents(query) {
125
+ const components = getAllComponents();
126
+ const results = [];
127
+ const lowerQuery = query.toLowerCase();
128
+
129
+ for (const componentName of components) {
130
+ if (componentName.toLowerCase().includes(lowerQuery)) {
131
+ const details = getComponentDetails(componentName);
132
+
133
+ let description = '';
134
+ if (details.files.story?.content) {
135
+ const storyContent = details.files.story.content;
136
+ const descMatch = storyContent.match(/description:\s*{[\s\S]*?component:\s*`([\s\S]*?)`/);
137
+ description = descMatch ? descMatch[1].trim().split('\n')[0] : '';
138
+ }
139
+
140
+ results.push({
141
+ name: componentName,
142
+ description,
143
+ hasStory: !!details.files.story,
144
+ hasCSS: !!details.files.css,
145
+ path: details.path,
146
+ });
147
+ }
148
+ }
149
+
150
+ return results;
151
+ }
152
+
153
+ /**
154
+ * Get CSS Module styling guidelines
155
+ */
156
+ function getCSSModuleGuidelines() {
157
+ return {
158
+ description: 'Comprehensive CSS Module guidelines and best practices for the Genesis Design System',
159
+ contextNote: {
160
+ critical: '⚠️ IMPORTANT: Patterns differ based on context',
161
+ insideDesignSystem: 'Use .module.css files with PascalCase naming and relative token imports',
162
+ usingDesignSystem: 'Use .css files (no .module) with kebab-case naming and @spaced-out/ui-design-system imports',
163
+ recommendation: 'Call get_css_module_import_patterns tool for detailed context-specific examples',
164
+ },
165
+ typescriptRequirement: '⚠️ CRITICAL: Use pure TypeScript only. NO Flow types (Flow.AbstractComponent). Use React.forwardRef<HTMLElement, Props> pattern.',
166
+ cssModuleImportPattern: {
167
+ critical: '🚨 MANDATORY: ALWAYS import CSS Modules as a default import with "css" as the variable name',
168
+ insideDesignSystem: 'import css from \'./ComponentName.module.css\';',
169
+ usingDesignSystem: 'import css from \'./component-name.css\';',
170
+ incorrect: [
171
+ 'import \'./ComponentName.module.css\'; // ❌ WRONG - No side-effect imports',
172
+ 'import * as css from \'./ComponentName.module.css\'; // ❌ WRONG - No namespace imports',
173
+ 'import styles from \'./ComponentName.module.css\'; // ❌ WRONG - Must use "css" as variable name',
174
+ ],
175
+ classNameUsage: {
176
+ correct: 'className={css.tableHeaderContent} // Use camelCase property access',
177
+ incorrect: [
178
+ 'className="table-header-content" // ❌ WRONG - No string literals',
179
+ 'className="tableHeaderContent" // ❌ WRONG - No string literals',
180
+ 'className={\'table-header-content\'} // ❌ WRONG - No string literals in braces',
181
+ ],
182
+ },
183
+ explanation: 'CSS Modules require a default import to access class names as properties. The variable MUST be named "css" for consistency across the codebase. Class names are accessed as camelCase properties (css.myClass) not as string literals.',
184
+ },
185
+ criticalRules: [
186
+ '🚨 MANDATORY: Import CSS Modules as: import css from \'./Component.module.css\'',
187
+ '🚨 MANDATORY: Use className={css.className} NOT className="class-name"',
188
+ '⚠️ ALWAYS use design tokens instead of hardcoded values',
189
+ '⚠️ Use BEM modifier pattern: .baseClass.modifier NOT .baseClass--modifier',
190
+ '⚠️ Use descendant selectors for child element styling: .container .childClass NOT :global() selectors',
191
+ '⚠️ Prefer using size tokens (size58, size34) over hardcoded pixel values',
192
+ '⚠️ Import tokens from ../../styles/variables/_<category>.css',
193
+ ],
194
+ classNamingConventions: {
195
+ description: 'Follow BEM-inspired patterns with CSS Modules',
196
+ correct: [
197
+ '.wrapper { } // Base class',
198
+ '.header { } // Element',
199
+ '.header.isOpen { } // State modifier using additional class',
200
+ '.panel.active { } // State modifier',
201
+ '.container.warning { } // Theme/variant modifier',
202
+ '.container .warning { } // Descendant element styling',
203
+ ],
204
+ incorrect: [
205
+ '.header--isOpen { } // ❌ Don\'t use double dash for modifiers',
206
+ '.panel--active { } // ❌ Use single class modifier instead',
207
+ '.container--warning { } // ❌ Use .container.warning',
208
+ '.container--warning :global(.child) { } // ❌ Avoid :global, use .container .warning',
209
+ ],
210
+ reasoning: 'CSS Modules provide scoping, so we can use simpler class names. The double-dash BEM syntax is less necessary and adds verbosity. Use single dot for modifiers and descendant selectors for child elements.',
211
+ },
212
+ tokenUsage: {
213
+ description: 'Always prefer design tokens over hardcoded values',
214
+ sizeTokens: {
215
+ available: ['size12', 'size16', 'size18', 'size20', 'size24', 'size28', 'size32', 'size34', 'size40', 'size48', 'size58', 'size60', 'sizeFluid'],
216
+ usage: 'Use for widths, heights, min-widths, min-heights, icon sizes, etc.',
217
+ correct: 'width: size58; height: size34;',
218
+ incorrect: 'width: 58px; height: 34px;',
219
+ },
220
+ spaceTokens: {
221
+ available: ['spaceNone', 'spaceXSmall', 'spaceSmall', 'spaceMedium', 'spaceLarge', 'spaceXLarge'],
222
+ usage: 'Use for padding, margin, gaps',
223
+ correct: 'padding: spaceSmall spaceMedium;',
224
+ incorrect: 'padding: 12px 20px;',
225
+ },
226
+ borderTokens: {
227
+ available: ['borderRadiusNone', 'borderRadiusSmall', 'borderRadiusMedium', 'borderRadiusLarge', 'borderWidthNone', 'borderWidthPrimary', 'borderWidthSecondary', 'borderWidthTertiary'],
228
+ usage: 'Use for border-radius, border-width',
229
+ correct: 'border: borderWidthPrimary solid colorBorderPrimary;',
230
+ incorrect: 'border: 1px solid #E1E1E1; border-radius: 116px;',
231
+ },
232
+ colorTokens: {
233
+ description: 'Use semantic color tokens, never hex values',
234
+ correct: 'background-color: colorInformationLightest; color: colorInformation;',
235
+ incorrect: 'background: #e6f0fe; color: #0769f0;',
236
+ },
237
+ },
238
+ stateStylingPattern: {
239
+ description: 'How to apply state-based styling to containers and their children',
240
+ pattern: `// Base container
241
+ .container {
242
+ display: flex;
243
+ padding: spaceMedium;
244
+ border-radius: borderRadiusSmall;
245
+ align-items: center;
246
+ justify-content: center;
247
+ }
248
+
249
+ // Container background/border for each state
250
+ .container.information {
251
+ background-color: colorInformationLightest;
252
+ border-color: colorInformation;
253
+ }
254
+
255
+ .container.warning {
256
+ background-color: colorWarningLightest;
257
+ border-color: colorWarning;
258
+ }
259
+
260
+ // Child element styling using descendant selectors
261
+ .container .information {
262
+ color: colorInformation;
263
+ }
264
+
265
+ .container .warning {
266
+ color: colorWarning;
267
+ }`,
268
+ explanation: 'This pattern separates container state styling from child element styling. Apply state classes to both the container and child elements, then use descendant selectors to apply appropriate colors/styles.',
269
+ useCases: [
270
+ 'Icon colors within state containers (alerts, badges, cards)',
271
+ 'Text colors within status panels',
272
+ 'Button or link colors within themed sections',
273
+ 'Any child element that should inherit state-based styling',
274
+ ],
275
+ doNot: 'Avoid using :global() selector unless absolutely necessary for targeting third-party components you don\'t control',
276
+ },
277
+ compositionPattern: {
278
+ description: 'Leverage CSS Modules composition for reusability',
279
+ example: `.header {
280
+ composes: subTitleSmall from '../../styles/typography.module.css';
281
+ composes: borderPrimary from '../../styles/border.module.css';
282
+ // Additional styles...
283
+ }`,
284
+ availableCompositions: [
285
+ 'Typography: bodySmall, bodyMedium, bodyLarge, subTitleSmall, subTitleMedium, etc. from typography.module.css',
286
+ 'Borders: borderPrimary, borderSecondary, borderTopPrimary, borderBottomPrimary from border.module.css',
287
+ ],
288
+ },
289
+ realWorldExample: {
290
+ description: 'Complete example with state-based styling for multiple child elements',
291
+ importPattern: `// 🚨 MANDATORY: Import CSS Module as default with "css" variable name
292
+ import css from './ComponentName.module.css';
293
+
294
+ // Usage in JSX:
295
+ <div className={css.wrapper}>
296
+ <div className={css.header}>
297
+ <span className={css.title}>Title</span>
298
+ </div>
299
+ </div>
300
+
301
+ // ❌ NEVER DO THIS:
302
+ // import './ComponentName.module.css';
303
+ // <div className="wrapper">
304
+ // <div className={'wrapper'}>`,
305
+ code: `@value (
306
+ colorBorderPrimary,
307
+ colorBackgroundTertiary,
308
+ colorNeutralLightest,
309
+ colorNeutral,
310
+ colorInformationLightest,
311
+ colorInformation,
312
+ colorTextPrimary
313
+ ) from '../../styles/variables/_color.css';
314
+ @value (sizeFluid, size58, size34) from '../../styles/variables/_size.css';
315
+ @value (spaceSmall, spaceMedium, spaceXSmall) from '../../styles/variables/_space.css';
316
+ @value (borderRadiusSmall, borderWidthPrimary) from '../../styles/variables/_border.css';
317
+
318
+ .wrapper {
319
+ display: flex;
320
+ flex-flow: column;
321
+ width: sizeFluid;
322
+ }
323
+
324
+ .header {
325
+ composes: subTitleSmall from '../../styles/typography.module.css';
326
+ min-height: size58;
327
+ border-radius: borderRadiusSmall;
328
+ border: borderWidthPrimary solid colorBorderPrimary;
329
+ display: flex;
330
+ align-items: center;
331
+ padding: spaceSmall spaceMedium;
332
+ background-color: colorBackgroundTertiary;
333
+ }
334
+
335
+ /* State container - applies background */
336
+ .statusPanel {
337
+ display: flex;
338
+ gap: spaceSmall;
339
+ padding: spaceMedium;
340
+ border-radius: borderRadiusSmall;
341
+ border: borderWidthPrimary solid;
342
+ }
343
+
344
+ .statusPanel.neutral {
345
+ background-color: colorNeutralLightest;
346
+ border-color: colorNeutral;
347
+ }
348
+
349
+ .statusPanel.information {
350
+ background-color: colorInformationLightest;
351
+ border-color: colorInformation;
352
+ }
353
+
354
+ /* Child elements - use descendant selectors for colors */
355
+ .statusPanel .neutral {
356
+ color: colorNeutral;
357
+ }
358
+
359
+ .statusPanel .information {
360
+ color: colorInformation;
361
+ }
362
+
363
+ /* Works for text, icons, or any child element */
364
+ .statusText {
365
+ composes: bodySmall from '../../styles/typography.module.css';
366
+ }`,
367
+ },
368
+ commonMistakes: [
369
+ {
370
+ mistake: '🚨 CRITICAL: Incorrect CSS Module import or usage',
371
+ wrong: `import './Component.module.css'; // Side-effect import
372
+ import styles from './Component.module.css'; // Wrong variable name
373
+ <div className="wrapper"> // String literal
374
+ <div className={'wrapper'}> // String literal in braces`,
375
+ right: `import css from './Component.module.css'; // Default import with "css" variable
376
+ <div className={css.wrapper}> // Property access with camelCase`,
377
+ explanation: 'CSS Modules MUST be imported as default with variable name "css" and used via property access, never as string literals.',
378
+ },
379
+ {
380
+ mistake: 'Using hardcoded pixel values',
381
+ wrong: 'width: 58px; height: 78px; padding: 12px 16px;',
382
+ right: 'width: size58; height: size58; padding: spaceSmall spaceMedium;',
383
+ },
384
+ {
385
+ mistake: 'Using hex colors directly',
386
+ wrong: 'background: #fcf8e7; color: #0769f0; border-color: #e1e1e1;',
387
+ right: 'background: colorWarningLightest; color: colorInformation; border-color: colorBorderPrimary;',
388
+ },
389
+ {
390
+ mistake: 'Using BEM double-dash with CSS Modules',
391
+ wrong: '.container--active { } .panel--warning { } .button--disabled { }',
392
+ right: '.container.active { } .panel.warning { } .button.disabled { }',
393
+ },
394
+ {
395
+ mistake: 'Using :global() unnecessarily for child elements',
396
+ wrong: '.container--warning :global(svg) { color: orange; } .alert--error :global(.text) { color: red; }',
397
+ right: '.container .warning { color: colorWarning; } .alert .error { color: colorDanger; }',
398
+ },
399
+ {
400
+ mistake: 'Not grouping token imports by category',
401
+ wrong: '@value (colorPrimary, spaceMedium, colorSecondary, borderRadius) from \'../../styles/variables/...\'',
402
+ right: 'Import each category from its own file: colors from _color.css, spacing from _space.css, etc.',
403
+ },
404
+ ],
405
+ };
406
+ }
407
+
408
+ /**
409
+ * Get CSS Module import and usage patterns
410
+ */
411
+ function getCSSModuleImportPatterns() {
412
+ return {
413
+ title: '🚨 MANDATORY CSS Module Import and Usage Pattern',
414
+ critical: 'This pattern is REQUIRED but differs based on context: developing INSIDE the design system vs USING the design system in another app.',
415
+
416
+ contextWarning: {
417
+ title: '⚠️ CRITICAL: Two Different Contexts',
418
+ description: 'The patterns differ significantly based on where you are developing',
419
+ contexts: {
420
+ insideDesignSystem: 'Building components IN the ui-design-system repository',
421
+ usingDesignSystem: 'Building features in an app that USES @spaced-out/ui-design-system',
422
+ },
423
+ howToKnow: 'Check package.json: If it has "name": "@spaced-out/ui-design-system", you are INSIDE. If it has "@spaced-out/ui-design-system" as a dependency, you are USING it.',
424
+ },
425
+
426
+ insideDesignSystem: {
427
+ title: '📦 Context: Developing INSIDE the Design System Repository',
428
+ fileNaming: {
429
+ tsx: 'ComponentName.tsx (PascalCase)',
430
+ css: 'ComponentName.module.css (PascalCase + .module.css)',
431
+ examples: ['DataTable.tsx + DataTable.module.css', 'UserProfile.tsx + UserProfile.module.css'],
432
+ },
433
+ cssImport: {
434
+ pattern: "import css from './ComponentName.module.css';",
435
+ examples: [
436
+ "import css from './DataTable.module.css';",
437
+ "import css from './UserProfile.module.css';",
438
+ ],
439
+ },
440
+ tokenImports: {
441
+ pattern: "@value (tokenName) from '../../styles/variables/_category.css';",
442
+ explanation: 'Use RELATIVE paths to the styles directory',
443
+ examples: [
444
+ "@value (size480) from '../../styles/variables/_size.css';",
445
+ "@value (colorPrimary) from '../../styles/variables/_color.css';",
446
+ "@value (spaceMedium) from '../../styles/variables/_space.css';",
447
+ ],
448
+ },
449
+ componentImports: {
450
+ pattern: "from 'src/components/ComponentName';",
451
+ examples: [
452
+ "import {Button} from 'src/components/Button';",
453
+ "import {Table} from 'src/components/Table';",
454
+ ],
455
+ },
456
+ utilImports: {
457
+ pattern: "from 'src/utils/utilName';",
458
+ examples: [
459
+ "import classify from 'src/utils/classify';",
460
+ "import {generateTestId} from 'src/utils/qa';",
461
+ ],
462
+ },
463
+ },
464
+
465
+ usingDesignSystem: {
466
+ title: '🏗️ Context: Using Design System in Another Application',
467
+ fileNaming: {
468
+ tsx: 'component-name.tsx (kebab-case)',
469
+ css: 'component-name.css (kebab-case, NO .module.css suffix)',
470
+ examples: ['data-table.tsx + data-table.css', 'user-profile.tsx + user-profile.css'],
471
+ critical: '🚨 File is .css NOT .module.css',
472
+ },
473
+ cssImport: {
474
+ pattern: "import css from './component-name.css';",
475
+ examples: [
476
+ "import css from './data-table.css';",
477
+ "import css from './user-profile.css';",
478
+ ],
479
+ note: 'Notice: NO .module.css suffix in the filename',
480
+ },
481
+ tokenImports: {
482
+ pattern: "@value (tokenName) from '@spaced-out/ui-design-system/lib/styles/variables/_category.css';",
483
+ explanation: 'Use NPM PACKAGE path, not relative paths',
484
+ examples: [
485
+ "@value (size480) from '@spaced-out/ui-design-system/lib/styles/variables/_size.css';",
486
+ "@value (colorPrimary) from '@spaced-out/ui-design-system/lib/styles/variables/_color.css';",
487
+ "@value (spaceMedium) from '@spaced-out/ui-design-system/lib/styles/variables/_space.css';",
488
+ ],
489
+ critical: '🚨 Must use @spaced-out/ui-design-system/lib/styles/... NOT relative paths',
490
+ },
491
+ componentImports: {
492
+ pattern: "from '@spaced-out/ui-design-system/lib/components/ComponentName';",
493
+ examples: [
494
+ "import {Button} from '@spaced-out/ui-design-system/lib/components/Button';",
495
+ "import {Table} from '@spaced-out/ui-design-system/lib/components/Table';",
496
+ ],
497
+ },
498
+ utilImports: {
499
+ pattern: "from '@spaced-out/ui-design-system/lib/utils/utilName';",
500
+ examples: [
501
+ "import classify from '@spaced-out/ui-design-system/lib/utils/classify';",
502
+ "import {generateTestId} from '@spaced-out/ui-design-system/lib/utils/qa';",
503
+ ],
504
+ },
505
+ },
506
+
507
+ comparisonTable: {
508
+ title: 'Side-by-Side Comparison',
509
+ aspect: {
510
+ tsxFileName: {
511
+ insideDS: 'ComponentName.tsx (PascalCase)',
512
+ usingDS: 'component-name.tsx (kebab-case)',
513
+ },
514
+ cssFileName: {
515
+ insideDS: 'ComponentName.module.css',
516
+ usingDS: 'component-name.css (NO .module)',
517
+ },
518
+ cssImport: {
519
+ insideDS: "import css from './ComponentName.module.css';",
520
+ usingDS: "import css from './component-name.css';",
521
+ },
522
+ tokenImport: {
523
+ insideDS: "@value (size480) from '../../styles/variables/_size.css';",
524
+ usingDS: "@value (size480) from '@spaced-out/ui-design-system/lib/styles/variables/_size.css';",
525
+ },
526
+ componentImport: {
527
+ insideDS: "import {Button} from 'src/components/Button';",
528
+ usingDS: "import {Button} from '@spaced-out/ui-design-system/lib/components/Button';",
529
+ },
530
+ },
531
+ },
532
+
533
+ correctImport: {
534
+ pattern: 'import css from \'./ComponentName.module.css\';',
535
+ explanation: 'CSS Modules MUST be imported as a default import with the variable name "css"',
536
+ examples: [
537
+ 'import css from \'./DataTable.module.css\';',
538
+ 'import css from \'./UserProfile.module.css\';',
539
+ 'import css from \'./NavigationBar.module.css\';',
540
+ ],
541
+ },
542
+
543
+ incorrectImports: {
544
+ sideEffect: {
545
+ code: 'import \'./ComponentName.module.css\';',
546
+ error: '❌ WRONG: Side-effect imports do not provide access to class names',
547
+ why: 'This imports the CSS but you cannot access the class names in your component',
548
+ },
549
+ namespace: {
550
+ code: 'import * as css from \'./ComponentName.module.css\';',
551
+ error: '❌ WRONG: Namespace imports are not supported for CSS Modules',
552
+ why: 'CSS Modules use default exports, not named exports',
553
+ },
554
+ wrongVariableName: {
555
+ code: 'import styles from \'./ComponentName.module.css\';',
556
+ error: '❌ WRONG: Variable name must be "css" not "styles" or any other name',
557
+ why: 'Consistency across the codebase requires using "css" as the variable name',
558
+ },
559
+ },
560
+
561
+ correctUsage: {
562
+ pattern: 'className={css.className}',
563
+ explanation: 'Class names are accessed as camelCase properties of the css object',
564
+ examples: [
565
+ '<div className={css.wrapper}>',
566
+ '<div className={css.tableHeader}>',
567
+ '<button className={css.submitButton}>',
568
+ '<span className={css.errorMessage}>',
569
+ ],
570
+ withClassify: [
571
+ 'className={classify(css.wrapper, css.active)}',
572
+ 'className={classify(css.button, { [css.disabled]: isDisabled })}',
573
+ 'className={classify(css.container, classNames?.wrapper)}',
574
+ ],
575
+ },
576
+
577
+ incorrectUsage: {
578
+ stringLiteral: {
579
+ code: '<div className="wrapper">',
580
+ error: '❌ WRONG: Never use string literals for class names',
581
+ why: 'CSS Modules provide scoped class names that must be accessed via the css object',
582
+ },
583
+ stringInBraces: {
584
+ code: '<div className={\'wrapper\'}>',
585
+ error: '❌ WRONG: String literals in braces are still wrong',
586
+ why: 'You must use the css object property access: className={css.wrapper}',
587
+ },
588
+ kebabCase: {
589
+ code: '<div className={css[\'table-header\']}>',
590
+ error: '❌ WRONG: Use camelCase properties, not kebab-case strings',
591
+ why: 'CSS class names are automatically converted to camelCase properties',
592
+ },
593
+ },
594
+
595
+ cssFileNaming: {
596
+ inDesignSystem: {
597
+ pattern: 'ComponentName.module.css',
598
+ examples: [
599
+ 'DataTable.module.css',
600
+ 'UserProfile.module.css',
601
+ 'NavigationBar.module.css',
602
+ ],
603
+ },
604
+ inMainApp: {
605
+ pattern: 'component-name.module.css (kebab-case)',
606
+ examples: [
607
+ 'data-table.module.css',
608
+ 'user-profile.module.css',
609
+ 'navigation-bar.module.css',
610
+ ],
611
+ note: 'The main application uses kebab-case for file names',
612
+ },
613
+ },
614
+
615
+ cssSyntax: {
616
+ classDefinition: 'Use camelCase for class names in CSS files',
617
+ correct: [
618
+ '.wrapper { }',
619
+ '.tableHeader { }',
620
+ '.submitButton { }',
621
+ '.errorMessage { }',
622
+ ],
623
+ incorrect: [
624
+ '.table-header { } // ❌ Use .tableHeader instead',
625
+ '.submit_button { } // ❌ Use .submitButton instead',
626
+ ],
627
+ },
628
+
629
+ completeExamples: {
630
+ insideDesignSystem: {
631
+ title: 'Complete Example: Building INSIDE Design System',
632
+ tsx: `// DataTable.tsx (in ui-design-system repo)
633
+ import * as React from 'react';
634
+ import classify from 'src/utils/classify';
635
+ import {generateTestId} from 'src/utils/qa';
636
+ import {Button} from 'src/components/Button';
637
+ import {SearchInput} from 'src/components/SearchInput';
638
+
639
+ // 📦 INSIDE DESIGN SYSTEM: Import with .module.css and relative path
640
+ import css from './DataTable.module.css';
641
+
642
+ export const DataTable = () => (
643
+ <div className={css.wrapper}>
644
+ <div className={css.header}>
645
+ <SearchInput className={css.search} />
646
+ <Button>Filter</Button>
647
+ </div>
648
+ <table className={css.table}>
649
+ <thead className={css.thead}>
650
+ <tr>
651
+ <th className={css.th}>Name</th>
652
+ <th className={css.th}>Status</th>
653
+ </tr>
654
+ </thead>
655
+ </table>
656
+ </div>
657
+ );`,
658
+ css: `/* DataTable.module.css (in ui-design-system repo) */
659
+ /* 📦 INSIDE DESIGN SYSTEM: Import tokens with RELATIVE paths */
660
+ @value (
661
+ colorBorderPrimary,
662
+ colorBackgroundTertiary
663
+ ) from '../../styles/variables/_color.css';
664
+
665
+ @value (
666
+ spaceSmall,
667
+ spaceMedium
668
+ ) from '../../styles/variables/_space.css';
669
+
670
+ @value (size480) from '../../styles/variables/_size.css';
671
+
672
+ .wrapper {
673
+ display: flex;
674
+ flex-direction: column;
675
+ width: size480;
676
+ }
677
+
678
+ .header {
679
+ padding: spaceMedium;
680
+ background: colorBackgroundTertiary;
681
+ }
682
+
683
+ .table {
684
+ border: 1px solid colorBorderPrimary;
685
+ }`,
686
+ },
687
+ usingDesignSystem: {
688
+ title: 'Complete Example: USING Design System in Another App',
689
+ tsx: `// data-table.tsx (in your app repo, NOT in ui-design-system)
690
+ import * as React from 'react';
691
+ import classify from '@spaced-out/ui-design-system/lib/utils/classify';
692
+ import {generateTestId} from '@spaced-out/ui-design-system/lib/utils/qa';
693
+ import {Button} from '@spaced-out/ui-design-system/lib/components/Button';
694
+ import {SearchInput} from '@spaced-out/ui-design-system/lib/components/SearchInput';
695
+
696
+ // 🏗️ USING DESIGN SYSTEM: Import WITHOUT .module.css, kebab-case filename
697
+ import css from './data-table.css';
698
+
699
+ export const DataTable = () => (
700
+ <div className={css.wrapper}>
701
+ <div className={css.header}>
702
+ <SearchInput className={css.search} />
703
+ <Button>Filter</Button>
704
+ </div>
705
+ <table className={css.table}>
706
+ <thead className={css.thead}>
707
+ <tr>
708
+ <th className={css.th}>Name</th>
709
+ <th className={css.th}>Status</th>
710
+ </tr>
711
+ </thead>
712
+ </table>
713
+ </div>
714
+ );`,
715
+ css: `/* data-table.css (in your app repo, NOT in ui-design-system) */
716
+ /* 🏗️ USING DESIGN SYSTEM: Import tokens from NPM PACKAGE path */
717
+ @value (
718
+ colorBorderPrimary,
719
+ colorBackgroundTertiary
720
+ ) from '@spaced-out/ui-design-system/lib/styles/variables/_color.css';
721
+
722
+ @value (
723
+ spaceSmall,
724
+ spaceMedium
725
+ ) from '@spaced-out/ui-design-system/lib/styles/variables/_space.css';
726
+
727
+ @value (size480) from '@spaced-out/ui-design-system/lib/styles/variables/_size.css';
728
+
729
+ .wrapper {
730
+ display: flex;
731
+ flex-direction: column;
732
+ width: size480;
733
+ }
734
+
735
+ .header {
736
+ padding: spaceMedium;
737
+ background: colorBackgroundTertiary;
738
+ }
739
+
740
+ .table {
741
+ border: 1px solid colorBorderPrimary;
742
+ }`,
743
+ },
744
+ },
745
+ };
746
+ }
747
+
748
+ /**
749
+ * Get design token import guidelines
750
+ */
751
+ function getDesignTokenImportGuidelines() {
752
+ return {
753
+ description: 'Guidelines for importing design tokens in CSS Module files',
754
+ importPattern: 'All design tokens must be imported from their respective CSS variable files in src/styles/variables/',
755
+ availableTokenFiles: [
756
+ {
757
+ file: '../../styles/variables/_color.css',
758
+ description: 'Color tokens for text, backgrounds, borders, icons, etc.',
759
+ exampleTokens: ['colorTextPrimary', 'colorBackgroundPrimary', 'colorBorderPrimary', 'colorIconPrimary'],
760
+ },
761
+ {
762
+ file: '../../styles/variables/_space.css',
763
+ description: 'Spacing tokens for padding, margin, gaps, etc.',
764
+ exampleTokens: ['spaceXSmall', 'spaceSmall', 'spaceMedium', 'spaceLarge', 'spaceXLarge'],
765
+ },
766
+ {
767
+ file: '../../styles/variables/_border.css',
768
+ description: 'Border tokens for border-radius and border-width',
769
+ exampleTokens: ['borderRadiusSmall', 'borderRadiusMedium', 'borderRadiusLarge', 'borderWidthPrimary'],
770
+ },
771
+ {
772
+ file: '../../styles/variables/_size.css',
773
+ description: 'Size tokens for dimensions',
774
+ exampleTokens: ['sizeSmall', 'sizeMedium', 'sizeLarge'],
775
+ },
776
+ {
777
+ file: '../../styles/variables/_font.css',
778
+ description: 'Font tokens for typography',
779
+ exampleTokens: ['fontFamilyPrimary', 'fontSizeSmall', 'fontSizeMedium', 'fontWeightRegular'],
780
+ },
781
+ {
782
+ file: '../../styles/variables/_shadow.css',
783
+ description: 'Shadow tokens for box-shadow',
784
+ exampleTokens: ['shadowSmall', 'shadowMedium', 'shadowLarge'],
785
+ },
786
+ {
787
+ file: '../../styles/variables/_elevation.css',
788
+ description: 'Elevation tokens for layering',
789
+ exampleTokens: ['elevationLow', 'elevationMedium', 'elevationHigh'],
790
+ },
791
+ {
792
+ file: '../../styles/variables/_motion.css',
793
+ description: 'Motion tokens for animations and transitions',
794
+ exampleTokens: ['durationFast', 'durationNormal', 'durationSlow', 'easingStandard'],
795
+ },
796
+ {
797
+ file: '../../styles/variables/_opacity.css',
798
+ description: 'Opacity tokens',
799
+ exampleTokens: ['opacityDisabled', 'opacityMedium'],
800
+ },
801
+ ],
802
+ correctExample: `@value (
803
+ colorBackgroundTertiary,
804
+ colorBorderPrimary,
805
+ colorTextPrimary
806
+ ) from '../../styles/variables/_color.css';
807
+
808
+ @value (
809
+ borderRadiusLarge,
810
+ borderWidthPrimary
811
+ ) from '../../styles/variables/_border.css';
812
+
813
+ @value (
814
+ spaceSmall,
815
+ spaceMedium
816
+ ) from '../../styles/variables/_space.css';
817
+
818
+ .wrapper {
819
+ background: colorBackgroundTertiary;
820
+ border: borderWidthPrimary solid colorBorderPrimary;
821
+ border-radius: borderRadiusLarge;
822
+ padding: spaceMedium;
823
+ }`,
824
+ incorrectExample: `/* ❌ WRONG - Do not import from 'ui-design-system' */
825
+ @value (
826
+ colorBorderPrimary,
827
+ spaceSmall
828
+ ) from 'ui-design-system';`,
829
+ notes: [
830
+ 'Always use the relative path from the component directory: ../../styles/variables/_<category>.css',
831
+ 'Group imports by token category (color, space, border, etc.)',
832
+ 'Only import the tokens you actually use in your component',
833
+ 'Use CSS Modules @value syntax for importing tokens',
834
+ ],
835
+ };
836
+ }
837
+
838
+ /**
839
+ * Extract exported types from component code
840
+ */
841
+ function extractExportedTypes(componentCode) {
842
+ if (!componentCode) return [];
843
+
844
+ const exports = [];
845
+
846
+ // Match: export type TypeName = ...
847
+ const typeExportRegex = /export\s+type\s+(\w+)\s*=\s*([^;]+);/g;
848
+ let match;
849
+ while ((match = typeExportRegex.exec(componentCode)) !== null) {
850
+ exports.push({
851
+ kind: 'type',
852
+ name: match[1],
853
+ definition: match[2].trim(),
854
+ raw: match[0],
855
+ });
856
+ }
857
+
858
+ // Match: export interface InterfaceName { ... }
859
+ const interfaceExportRegex = /export\s+interface\s+(\w+)(?:<[^>]+>)?\s*{/g;
860
+ while ((match = interfaceExportRegex.exec(componentCode)) !== null) {
861
+ exports.push({
862
+ kind: 'interface',
863
+ name: match[1],
864
+ definition: 'interface',
865
+ raw: match[0],
866
+ });
867
+ }
868
+
869
+ // Match: export const CONSTANT_NAME = Object.freeze({ ... })
870
+ const constExportRegex = /export\s+const\s+([A-Z_]+)\s*=\s*Object\.freeze\s*\(\s*{([^}]+)}\s*\)/g;
871
+ while ((match = constExportRegex.exec(componentCode)) !== null) {
872
+ exports.push({
873
+ kind: 'const',
874
+ name: match[1],
875
+ definition: `Object.freeze({${match[2]}})`,
876
+ raw: match[0],
877
+ });
878
+ }
879
+
880
+ // Match: export enum EnumName { ... }
881
+ const enumExportRegex = /export\s+enum\s+(\w+)\s*{/g;
882
+ while ((match = enumExportRegex.exec(componentCode)) !== null) {
883
+ exports.push({
884
+ kind: 'enum',
885
+ name: match[1],
886
+ definition: 'enum',
887
+ raw: match[0],
888
+ });
889
+ }
890
+
891
+ return exports;
892
+ }
893
+
894
+ /**
895
+ * Get component template for onboarding
896
+ *
897
+ * IMPORTANT: This template uses pure TypeScript patterns.
898
+ * DO NOT use Flow types (Flow.AbstractComponent) in new components.
899
+ */
900
+ function getComponentTemplate() {
901
+ return {
902
+ 'ComponentName.tsx': `import * as React from 'react';
903
+
904
+ import classify from 'src/utils/classify';
905
+ import {generateTestId} from 'src/utils/qa';
906
+
907
+ // 🚨 CRITICAL: CSS Modules MUST be imported as default import with variable name "css"
908
+ // ✅ Correct: import css from './ComponentName.module.css';
909
+ // ❌ WRONG: import './ComponentName.module.css';
910
+ // ❌ WRONG: import * as css from './ComponentName.module.css';
911
+ // ❌ WRONG: import styles from './ComponentName.module.css';
912
+ import css from 'src/components/ComponentName/ComponentName.module.css';
913
+
914
+ // IMPORTANT: Use pure TypeScript - NO Flow types
915
+ // ✅ Correct: React.forwardRef<HTMLDivElement, Props>
916
+ // ❌ Wrong: Flow.AbstractComponent<Props, HTMLDivElement>
917
+
918
+ // IMPORTANT: Use className with CSS Module property access
919
+ // ✅ Correct: className={css.wrapper}
920
+ // ❌ WRONG: className="wrapper"
921
+ // ❌ WRONG: className={'wrapper'}
922
+
923
+ type ClassNames = Readonly<{
924
+ wrapper?: string;
925
+ }>;
926
+
927
+ export interface ComponentNameProps {
928
+ classNames?: ClassNames;
929
+ children?: React.ReactNode;
930
+ testId?: string;
931
+ }
932
+
933
+ export const ComponentName = React.forwardRef<HTMLDivElement, ComponentNameProps>(
934
+ (
935
+ {
936
+ classNames,
937
+ children,
938
+ testId,
939
+ ...props
940
+ },
941
+ ref,
942
+ ) => (
943
+ <div
944
+ {...props}
945
+ ref={ref}
946
+ className={classify(css.wrapper, classNames?.wrapper)}
947
+ data-testid={generateTestId({base: testId, slot: 'root'})}
948
+ >
949
+ {children}
950
+ </div>
951
+ ),
952
+ );
953
+
954
+ ComponentName.displayName = 'ComponentName';
955
+ `,
956
+
957
+ 'ComponentName.module.css': `@value (
958
+ colorBorderPrimary,
959
+ colorBackgroundPrimary
960
+ ) from '../../styles/variables/_color.css';
961
+
962
+ @value (
963
+ spaceSmall,
964
+ spaceMedium
965
+ ) from '../../styles/variables/_space.css';
966
+
967
+ @value (
968
+ borderRadiusSmall
969
+ ) from '../../styles/variables/_border.css';
970
+
971
+ .wrapper {
972
+ padding: spaceMedium;
973
+ background: colorBackgroundPrimary;
974
+ border: 1px solid colorBorderPrimary;
975
+ border-radius: borderRadiusSmall;
976
+ }
977
+ `,
978
+
979
+ 'ComponentName.stories.tsx': `import * as React from 'react';
980
+
981
+ import type {ComponentNameProps} from 'src/components/ComponentName/ComponentName';
982
+ import {ComponentName} from 'src/components/ComponentName/ComponentName';
983
+
984
+ export default {
985
+ tags: ['autodocs'],
986
+ title: 'Components/ComponentName',
987
+ component: ComponentName,
988
+ argTypes: {
989
+ children: {
990
+ description: 'The content of the component',
991
+ table: {
992
+ type: {summary: 'React.ReactNode'},
993
+ },
994
+ },
995
+ classNames: {
996
+ description: 'External classNames to be applied',
997
+ control: {
998
+ type: 'object',
999
+ },
1000
+ table: {
1001
+ type: {summary: '{wrapper?: string}'},
1002
+ },
1003
+ },
1004
+ testId: {
1005
+ description: 'Test ID for the component',
1006
+ control: {
1007
+ type: 'text',
1008
+ },
1009
+ table: {
1010
+ type: {summary: 'string'},
1011
+ },
1012
+ },
1013
+ },
1014
+ parameters: {
1015
+ docs: {
1016
+ subtitle: 'Generates a ComponentName component',
1017
+ description: {
1018
+ component: \`
1019
+ \\\`\\\`\\\`js
1020
+ import { ComponentName } from "@spaced-out/ui-design-system/lib/components/ComponentName";
1021
+ \\\`\\\`\\\`
1022
+ Brief description of what this component does and when to use it.
1023
+ \`,
1024
+ },
1025
+ },
1026
+ storySource: {
1027
+ componentPath: '/src/components/ComponentName/ComponentName',
1028
+ },
1029
+ },
1030
+ };
1031
+
1032
+ export const DefaultStory = (args: ComponentNameProps) => (
1033
+ <ComponentName {...args} />
1034
+ );
1035
+
1036
+ DefaultStory.args = {
1037
+ children: 'ComponentName content',
1038
+ testId: 'component-name-default',
1039
+ };
1040
+
1041
+ DefaultStory.storyName = 'Default';
1042
+ `,
1043
+
1044
+ 'index.ts': `export {ComponentName} from 'src/components/ComponentName/ComponentName';
1045
+ export type {ComponentNameProps} from 'src/components/ComponentName/ComponentName';
1046
+ `,
1047
+ };
1048
+ }
1049
+
1050
+ /**
1051
+ * Analyze component dependencies
1052
+ */
1053
+ function analyzeComponentDependencies(componentName) {
1054
+ const details = getComponentDetails(componentName);
1055
+ if (!details || !details.files.main) {
1056
+ return null;
1057
+ }
1058
+
1059
+ // Extract imports from the component
1060
+ const content = details.files.main.content;
1061
+ const importRegex = /import\s+.*?from\s+['"](.+?)['"]/g;
1062
+ const imports = [];
1063
+ let match;
1064
+
1065
+ while ((match = importRegex.exec(content)) !== null) {
1066
+ imports.push(match[1]);
1067
+ }
1068
+
1069
+ // Categorize imports
1070
+ const dependencies = {
1071
+ components: imports.filter(imp => imp.includes('src/components/')),
1072
+ hooks: imports.filter(imp => imp.includes('src/hooks/')),
1073
+ utils: imports.filter(imp => imp.includes('src/utils/')),
1074
+ styles: imports.filter(imp => imp.includes('src/styles/')),
1075
+ external: imports.filter(imp => !imp.startsWith('src/')),
1076
+ };
1077
+
1078
+ return dependencies;
1079
+ }
1080
+
1081
+ /**
1082
+ * Initialize MCP Server
1083
+ */
1084
+ const server = new Server(
1085
+ {
1086
+ name: 'genesis-design-system',
1087
+ version: designSystemData.metadata.version,
1088
+ },
1089
+ {
1090
+ capabilities: {
1091
+ resources: {},
1092
+ tools: {},
1093
+ },
1094
+ }
1095
+ );
1096
+
1097
+ /**
1098
+ * List available resources
1099
+ */
1100
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
1101
+ const components = getAllComponents();
1102
+ const hooks = getAllHooks();
1103
+ const utils = getAllUtils();
1104
+ const types = getAllTypes();
1105
+
1106
+ const resources = [
1107
+ {
1108
+ uri: 'genesis://overview',
1109
+ name: 'Design System Overview',
1110
+ mimeType: 'text/markdown',
1111
+ description: 'Overview of the Genesis UI Design System',
1112
+ },
1113
+ {
1114
+ uri: 'genesis://components',
1115
+ name: 'All Components',
1116
+ mimeType: 'application/json',
1117
+ description: `List of all ${components.length} available components`,
1118
+ },
1119
+ {
1120
+ uri: 'genesis://hooks',
1121
+ name: 'All Hooks',
1122
+ mimeType: 'application/json',
1123
+ description: `List of all ${hooks.length} available hooks`,
1124
+ },
1125
+ {
1126
+ uri: 'genesis://utils',
1127
+ name: 'All Utils',
1128
+ mimeType: 'application/json',
1129
+ description: `List of all ${utils.length} available utility modules`,
1130
+ },
1131
+ {
1132
+ uri: 'genesis://types',
1133
+ name: 'All Types',
1134
+ mimeType: 'application/json',
1135
+ description: `List of all ${types.length} available type definitions`,
1136
+ },
1137
+ {
1138
+ uri: 'genesis://tokens',
1139
+ name: 'Design Tokens',
1140
+ mimeType: 'application/json',
1141
+ description: 'All design tokens (colors, spacing, typography, etc.)',
1142
+ },
1143
+ ];
1144
+
1145
+ // Add individual component resources
1146
+ components.forEach(component => {
1147
+ resources.push({
1148
+ uri: `genesis://component/${component}`,
1149
+ name: `Component: ${component}`,
1150
+ mimeType: 'text/markdown',
1151
+ description: `Documentation and code for ${component} component`,
1152
+ });
1153
+ });
1154
+
1155
+ // Add individual hook resources
1156
+ hooks.forEach(hook => {
1157
+ resources.push({
1158
+ uri: `genesis://hook/${hook}`,
1159
+ name: `Hook: ${hook}`,
1160
+ mimeType: 'text/markdown',
1161
+ description: `Documentation and code for ${hook} hook`,
1162
+ });
1163
+ });
1164
+
1165
+ // Add individual util resources
1166
+ utils.forEach(util => {
1167
+ resources.push({
1168
+ uri: `genesis://util/${util}`,
1169
+ name: `Util: ${util}`,
1170
+ mimeType: 'text/markdown',
1171
+ description: `Utility functions for ${util}`,
1172
+ });
1173
+ });
1174
+
1175
+ // Add individual type resources
1176
+ types.forEach(type => {
1177
+ resources.push({
1178
+ uri: `genesis://type/${type}`,
1179
+ name: `Type: ${type}`,
1180
+ mimeType: 'text/markdown',
1181
+ description: `Type definitions for ${type}`,
1182
+ });
1183
+ });
1184
+
1185
+ return { resources };
1186
+ });
1187
+
1188
+ /**
1189
+ * Read resource content
1190
+ */
1191
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1192
+ const uri = request.params.uri;
1193
+
1194
+ if (uri === 'genesis://overview') {
1195
+ const overview = `# Genesis UI Design System
1196
+
1197
+ **Version:** ${designSystemData.metadata.version}
1198
+ **Last Built:** ${new Date(designSystemData.metadata.buildDate).toLocaleString()}
1199
+
1200
+ ## Statistics
1201
+
1202
+ - **Components:** ${Object.keys(designSystemData.components).length}
1203
+ - **Hooks:** ${Object.keys(designSystemData.hooks).length}
1204
+ - **Utils:** ${Object.keys(designSystemData.utils || {}).length}
1205
+ - **Types:** ${Object.keys(designSystemData.types || {}).length}
1206
+ - **Design Token Categories:** ${Object.keys(designSystemData.tokens).length}
1207
+
1208
+ ## Available Components
1209
+
1210
+ ${getAllComponents().map(c => `- ${c}`).join('\n')}
1211
+
1212
+ ## Available Hooks
1213
+
1214
+ ${getAllHooks().map(h => `- ${h}`).join('\n')}
1215
+
1216
+ ## Available Utils
1217
+
1218
+ ${getAllUtils().map(u => `- ${u}`).join('\n')}
1219
+
1220
+ ## Available Types
1221
+
1222
+ ${getAllTypes().map(t => `- ${t}`).join('\n')}
1223
+
1224
+ ## Design Token Categories
1225
+
1226
+ ${Object.keys(designSystemData.tokens).map(cat => `- **${cat}**: ${Object.keys(designSystemData.tokens[cat]).length} file(s)`).join('\n')}
1227
+ `;
1228
+
1229
+ return {
1230
+ contents: [
1231
+ {
1232
+ uri,
1233
+ mimeType: 'text/markdown',
1234
+ text: overview,
1235
+ },
1236
+ ],
1237
+ };
1238
+ }
1239
+
1240
+ if (uri === 'genesis://components') {
1241
+ const components = getAllComponents();
1242
+ return {
1243
+ contents: [
1244
+ {
1245
+ uri,
1246
+ mimeType: 'application/json',
1247
+ text: JSON.stringify(components, null, 2),
1248
+ },
1249
+ ],
1250
+ };
1251
+ }
1252
+
1253
+ if (uri === 'genesis://hooks') {
1254
+ const hooks = getAllHooks();
1255
+ return {
1256
+ contents: [
1257
+ {
1258
+ uri,
1259
+ mimeType: 'application/json',
1260
+ text: JSON.stringify(hooks, null, 2),
1261
+ },
1262
+ ],
1263
+ };
1264
+ }
1265
+
1266
+ if (uri === 'genesis://utils') {
1267
+ const utils = getAllUtils();
1268
+ return {
1269
+ contents: [
1270
+ {
1271
+ uri,
1272
+ mimeType: 'application/json',
1273
+ text: JSON.stringify(utils, null, 2),
1274
+ },
1275
+ ],
1276
+ };
1277
+ }
1278
+
1279
+ if (uri === 'genesis://types') {
1280
+ const types = getAllTypes();
1281
+ return {
1282
+ contents: [
1283
+ {
1284
+ uri,
1285
+ mimeType: 'application/json',
1286
+ text: JSON.stringify(types, null, 2),
1287
+ },
1288
+ ],
1289
+ };
1290
+ }
1291
+
1292
+ if (uri === 'genesis://tokens') {
1293
+ const tokens = getAllDesignTokens();
1294
+ return {
1295
+ contents: [
1296
+ {
1297
+ uri,
1298
+ mimeType: 'application/json',
1299
+ text: JSON.stringify(tokens, null, 2),
1300
+ },
1301
+ ],
1302
+ };
1303
+ }
1304
+
1305
+ if (uri.startsWith('genesis://component/')) {
1306
+ const componentName = uri.replace('genesis://component/', '');
1307
+ const details = getComponentDetails(componentName);
1308
+
1309
+ if (!details) {
1310
+ throw new Error(`Component ${componentName} not found`);
1311
+ }
1312
+
1313
+ let markdown = `# ${componentName}\n\n`;
1314
+ markdown += `**Path:** \`${details.path}\`\n\n`;
1315
+ markdown += `**Files:** ${details.allFiles.join(', ')}\n\n`;
1316
+
1317
+ // Extract and display exported types prominently
1318
+ const exportedTypes = extractExportedTypes(details.files.main?.content);
1319
+ if (exportedTypes.length > 0) {
1320
+ markdown += `## 🎯 Exported Types (REUSE THESE!)\n\n`;
1321
+ markdown += `This component exports ${exportedTypes.length} reusable type(s)/constant(s):\n\n`;
1322
+
1323
+ for (const type of exportedTypes) {
1324
+ markdown += `### ${type.name}\n`;
1325
+ markdown += `- **Kind:** ${type.kind}\n`;
1326
+ markdown += `- **Definition:** \`${type.definition}\`\n`;
1327
+ markdown += `- **Import:** \`import type { ${type.name} } from 'src/components/${componentName}';\`\n\n`;
1328
+ }
1329
+
1330
+ markdown += `⚠️ **IMPORTANT:** Always import and reuse these types instead of redefining them!\n\n`;
1331
+ }
1332
+
1333
+ if (details.files.story) {
1334
+ markdown += `## Story Documentation\n\n`;
1335
+ markdown += '```typescript\n';
1336
+ markdown += details.files.story.content;
1337
+ markdown += '\n```\n\n';
1338
+ }
1339
+
1340
+ if (details.files.main) {
1341
+ markdown += `## Component Implementation\n\n`;
1342
+ markdown += '```typescript\n';
1343
+ markdown += details.files.main.content;
1344
+ markdown += '\n```\n\n';
1345
+ }
1346
+
1347
+ if (details.files.css) {
1348
+ markdown += `## Styles\n\n`;
1349
+ markdown += '```css\n';
1350
+ markdown += details.files.css.content;
1351
+ markdown += '\n```\n\n';
1352
+ }
1353
+
1354
+ // Display additional files (sub-components)
1355
+ if (details.files.additional && Object.keys(details.files.additional).length > 0) {
1356
+ markdown += `## Additional Files (Sub-components)\n\n`;
1357
+
1358
+ for (const [fileName, fileData] of Object.entries(details.files.additional)) {
1359
+ markdown += `### ${fileName}\n\n`;
1360
+ markdown += '```typescript\n';
1361
+ markdown += fileData.content;
1362
+ markdown += '\n```\n\n';
1363
+ }
1364
+ }
1365
+
1366
+ return {
1367
+ contents: [
1368
+ {
1369
+ uri,
1370
+ mimeType: 'text/markdown',
1371
+ text: markdown,
1372
+ },
1373
+ ],
1374
+ };
1375
+ }
1376
+
1377
+ if (uri.startsWith('genesis://hook/')) {
1378
+ const hookName = uri.replace('genesis://hook/', '');
1379
+ const details = getHookDetails(hookName);
1380
+
1381
+ if (!details) {
1382
+ throw new Error(`Hook ${hookName} not found`);
1383
+ }
1384
+
1385
+ let markdown = `# ${hookName}\n\n`;
1386
+ markdown += `**Path:** \`${details.path}\`\n\n`;
1387
+ markdown += `**Files:** ${details.allFiles.join(', ')}\n\n`;
1388
+
1389
+ if (details.files.story) {
1390
+ markdown += `## Story Documentation\n\n`;
1391
+ markdown += '```typescript\n';
1392
+ markdown += details.files.story.content;
1393
+ markdown += '\n```\n\n';
1394
+ }
1395
+
1396
+ if (details.files.main) {
1397
+ markdown += `## Hook Implementation\n\n`;
1398
+ markdown += '```typescript\n';
1399
+ markdown += details.files.main.content;
1400
+ markdown += '\n```\n\n';
1401
+ }
1402
+
1403
+ return {
1404
+ contents: [
1405
+ {
1406
+ uri,
1407
+ mimeType: 'text/markdown',
1408
+ text: markdown,
1409
+ },
1410
+ ],
1411
+ };
1412
+ }
1413
+
1414
+ if (uri.startsWith('genesis://util/')) {
1415
+ const utilName = uri.replace('genesis://util/', '');
1416
+ const details = getUtilDetails(utilName);
1417
+
1418
+ if (!details) {
1419
+ throw new Error(`Util ${utilName} not found`);
1420
+ }
1421
+
1422
+ let markdown = `# ${utilName}\n\n`;
1423
+ markdown += `**Path:** \`${details.path}\`\n\n`;
1424
+ markdown += `**Files:** ${details.allFiles.join(', ')}\n\n`;
1425
+
1426
+ // Display all files in the util module
1427
+ if (details.files && Object.keys(details.files).length > 0) {
1428
+ markdown += `## Utility Files\n\n`;
1429
+
1430
+ for (const [fileName, fileData] of Object.entries(details.files)) {
1431
+ markdown += `### ${fileName}\n\n`;
1432
+ markdown += '```typescript\n';
1433
+ markdown += fileData.content;
1434
+ markdown += '\n```\n\n';
1435
+ }
1436
+ }
1437
+
1438
+ return {
1439
+ contents: [
1440
+ {
1441
+ uri,
1442
+ mimeType: 'text/markdown',
1443
+ text: markdown,
1444
+ },
1445
+ ],
1446
+ };
1447
+ }
1448
+
1449
+ if (uri.startsWith('genesis://type/')) {
1450
+ const typeName = uri.replace('genesis://type/', '');
1451
+ const details = getTypeDetails(typeName);
1452
+
1453
+ if (!details) {
1454
+ throw new Error(`Type ${typeName} not found`);
1455
+ }
1456
+
1457
+ let markdown = `# ${typeName}\n\n`;
1458
+ markdown += `**Path:** \`${details.path}\`\n\n`;
1459
+ markdown += `## Type Definitions\n\n`;
1460
+ markdown += '```typescript\n';
1461
+ markdown += details.content;
1462
+ markdown += '\n```\n\n';
1463
+
1464
+ return {
1465
+ contents: [
1466
+ {
1467
+ uri,
1468
+ mimeType: 'text/markdown',
1469
+ text: markdown,
1470
+ },
1471
+ ],
1472
+ };
1473
+ }
1474
+
1475
+ throw new Error(`Unknown resource: ${uri}`);
1476
+ });
1477
+
1478
+ /**
1479
+ * List available tools
1480
+ */
1481
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
1482
+ return {
1483
+ tools: [
1484
+ {
1485
+ name: 'list_components',
1486
+ description: 'List all available components in the design system',
1487
+ inputSchema: {
1488
+ type: 'object',
1489
+ properties: {},
1490
+ },
1491
+ },
1492
+ {
1493
+ name: 'get_component',
1494
+ description: 'Get detailed information about a specific component including its TypeScript definition, story, styles, and EXPORTED TYPES. The exported types section shows reusable types/interfaces/constants that you should import and use instead of redefining them (e.g., IconType, IconSize from Icon component).',
1495
+ inputSchema: {
1496
+ type: 'object',
1497
+ properties: {
1498
+ name: {
1499
+ type: 'string',
1500
+ description: 'Name of the component (e.g., "Button", "Dropdown", "Icon")',
1501
+ },
1502
+ },
1503
+ required: ['name'],
1504
+ },
1505
+ },
1506
+ {
1507
+ name: 'search_components',
1508
+ description: 'Search for components by name',
1509
+ inputSchema: {
1510
+ type: 'object',
1511
+ properties: {
1512
+ query: {
1513
+ type: 'string',
1514
+ description: 'Search query',
1515
+ },
1516
+ },
1517
+ required: ['query'],
1518
+ },
1519
+ },
1520
+ {
1521
+ name: 'list_hooks',
1522
+ description: 'List all available hooks in the design system',
1523
+ inputSchema: {
1524
+ type: 'object',
1525
+ properties: {},
1526
+ },
1527
+ },
1528
+ {
1529
+ name: 'get_hook',
1530
+ description: 'Get detailed information about a specific hook',
1531
+ inputSchema: {
1532
+ type: 'object',
1533
+ properties: {
1534
+ name: {
1535
+ type: 'string',
1536
+ description: 'Name of the hook (e.g., "useDebounce", "useModal")',
1537
+ },
1538
+ },
1539
+ required: ['name'],
1540
+ },
1541
+ },
1542
+ {
1543
+ name: 'list_utils',
1544
+ description: 'List all available utility modules in the design system',
1545
+ inputSchema: {
1546
+ type: 'object',
1547
+ properties: {},
1548
+ },
1549
+ },
1550
+ {
1551
+ name: 'get_util',
1552
+ description: 'Get detailed information about a specific utility module including all its functions',
1553
+ inputSchema: {
1554
+ type: 'object',
1555
+ properties: {
1556
+ name: {
1557
+ type: 'string',
1558
+ description: 'Name of the utility module (e.g., "classify", "qa", "dom")',
1559
+ },
1560
+ },
1561
+ required: ['name'],
1562
+ },
1563
+ },
1564
+ {
1565
+ name: 'list_types',
1566
+ description: 'List all available type definitions in the design system',
1567
+ inputSchema: {
1568
+ type: 'object',
1569
+ properties: {},
1570
+ },
1571
+ },
1572
+ {
1573
+ name: 'get_type',
1574
+ description: 'Get detailed information about a specific type definition file',
1575
+ inputSchema: {
1576
+ type: 'object',
1577
+ properties: {
1578
+ name: {
1579
+ type: 'string',
1580
+ description: 'Name of the type file (e.g., "common", "typography", "charts")',
1581
+ },
1582
+ },
1583
+ required: ['name'],
1584
+ },
1585
+ },
1586
+ {
1587
+ name: 'get_design_tokens',
1588
+ description: 'Get all design tokens including colors, spacing, typography, shadows, etc.',
1589
+ inputSchema: {
1590
+ type: 'object',
1591
+ properties: {
1592
+ category: {
1593
+ type: 'string',
1594
+ description: 'Optional: Filter by token category (e.g., "color", "space", "size")',
1595
+ },
1596
+ },
1597
+ },
1598
+ },
1599
+ {
1600
+ name: 'get_component_template',
1601
+ description: 'Get template files for creating a new component (useful for onboarding new components)',
1602
+ inputSchema: {
1603
+ type: 'object',
1604
+ properties: {},
1605
+ },
1606
+ },
1607
+ {
1608
+ name: 'get_css_module_import_patterns',
1609
+ description: '🚨 MANDATORY: Get the required CSS Module import and usage pattern based on context. This tool shows TWO different patterns: (1) Building INSIDE ui-design-system repo: .module.css files, PascalCase, relative imports. (2) USING design system in another app: .css files, kebab-case, @spaced-out/ui-design-system imports. MUST be called FIRST before generating any component to understand context and use correct patterns. Failure to follow context-specific patterns will cause import errors.',
1610
+ inputSchema: {
1611
+ type: 'object',
1612
+ properties: {},
1613
+ },
1614
+ },
1615
+ {
1616
+ name: 'get_design_token_import_guidelines',
1617
+ description: 'Get guidelines and best practices for importing design tokens in CSS Module files. This includes the correct import paths, available token files, and examples of correct vs incorrect imports. ALWAYS call this function before creating CSS Module files to ensure correct token imports.',
1618
+ inputSchema: {
1619
+ type: 'object',
1620
+ properties: {},
1621
+ },
1622
+ },
1623
+ {
1624
+ name: 'get_css_module_guidelines',
1625
+ description: 'Get comprehensive CSS Module styling guidelines including MANDATORY import pattern (import css from "./file.module.css"), class naming conventions, token usage, icon color patterns, and common mistakes to avoid. 🚨 CRITICAL: Call this BEFORE creating or modifying any component files to ensure correct CSS Module usage and design system patterns. This includes the mandatory pattern: import css from "./Component.module.css" and className={css.className}.',
1626
+ inputSchema: {
1627
+ type: 'object',
1628
+ properties: {},
1629
+ },
1630
+ },
1631
+ {
1632
+ name: 'analyze_component_dependencies',
1633
+ description: 'Analyze dependencies of a component to understand what other components and utilities it uses',
1634
+ inputSchema: {
1635
+ type: 'object',
1636
+ properties: {
1637
+ name: {
1638
+ type: 'string',
1639
+ description: 'Name of the component',
1640
+ },
1641
+ },
1642
+ required: ['name'],
1643
+ },
1644
+ },
1645
+ ],
1646
+ };
1647
+ });
1648
+
1649
+ /**
1650
+ * Handle tool calls
1651
+ */
1652
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1653
+ const { name, arguments: args } = request.params;
1654
+
1655
+ try {
1656
+ switch (name) {
1657
+ case 'list_components': {
1658
+ const components = getAllComponents();
1659
+ return {
1660
+ content: [
1661
+ {
1662
+ type: 'text',
1663
+ text: JSON.stringify(components, null, 2),
1664
+ },
1665
+ ],
1666
+ };
1667
+ }
1668
+
1669
+ case 'get_component': {
1670
+ const details = getComponentDetails(args.name);
1671
+ if (!details) {
1672
+ return {
1673
+ content: [
1674
+ {
1675
+ type: 'text',
1676
+ text: `Component "${args.name}" not found`,
1677
+ },
1678
+ ],
1679
+ isError: true,
1680
+ };
1681
+ }
1682
+
1683
+ // Extract exported types from the component code
1684
+ const exportedTypes = extractExportedTypes(details.files.main?.content);
1685
+
1686
+ // Enhance details with exported types information
1687
+ const enhancedDetails = {
1688
+ ...details,
1689
+ exportedTypes: exportedTypes.length > 0 ? {
1690
+ summary: `This component exports ${exportedTypes.length} type(s)/constant(s) that you should reuse:`,
1691
+ types: exportedTypes,
1692
+ usage: exportedTypes.length > 0
1693
+ ? `Import these types: import type { ${exportedTypes.map(t => t.name).join(', ')} } from 'src/components/${args.name}';`
1694
+ : '',
1695
+ } : null,
1696
+ };
1697
+
1698
+ return {
1699
+ content: [
1700
+ {
1701
+ type: 'text',
1702
+ text: JSON.stringify(enhancedDetails, null, 2),
1703
+ },
1704
+ ],
1705
+ };
1706
+ }
1707
+
1708
+ case 'search_components': {
1709
+ const results = searchComponents(args.query);
1710
+ return {
1711
+ content: [
1712
+ {
1713
+ type: 'text',
1714
+ text: JSON.stringify(results, null, 2),
1715
+ },
1716
+ ],
1717
+ };
1718
+ }
1719
+
1720
+ case 'list_hooks': {
1721
+ const hooks = getAllHooks();
1722
+ return {
1723
+ content: [
1724
+ {
1725
+ type: 'text',
1726
+ text: JSON.stringify(hooks, null, 2),
1727
+ },
1728
+ ],
1729
+ };
1730
+ }
1731
+
1732
+ case 'get_hook': {
1733
+ const details = getHookDetails(args.name);
1734
+ if (!details) {
1735
+ return {
1736
+ content: [
1737
+ {
1738
+ type: 'text',
1739
+ text: `Hook "${args.name}" not found`,
1740
+ },
1741
+ ],
1742
+ isError: true,
1743
+ };
1744
+ }
1745
+ return {
1746
+ content: [
1747
+ {
1748
+ type: 'text',
1749
+ text: JSON.stringify(details, null, 2),
1750
+ },
1751
+ ],
1752
+ };
1753
+ }
1754
+
1755
+ case 'list_utils': {
1756
+ const utils = getAllUtils();
1757
+ return {
1758
+ content: [
1759
+ {
1760
+ type: 'text',
1761
+ text: JSON.stringify(utils, null, 2),
1762
+ },
1763
+ ],
1764
+ };
1765
+ }
1766
+
1767
+ case 'get_util': {
1768
+ const details = getUtilDetails(args.name);
1769
+ if (!details) {
1770
+ return {
1771
+ content: [
1772
+ {
1773
+ type: 'text',
1774
+ text: `Util "${args.name}" not found`,
1775
+ },
1776
+ ],
1777
+ isError: true,
1778
+ };
1779
+ }
1780
+ return {
1781
+ content: [
1782
+ {
1783
+ type: 'text',
1784
+ text: JSON.stringify(details, null, 2),
1785
+ },
1786
+ ],
1787
+ };
1788
+ }
1789
+
1790
+ case 'list_types': {
1791
+ const types = getAllTypes();
1792
+ return {
1793
+ content: [
1794
+ {
1795
+ type: 'text',
1796
+ text: JSON.stringify(types, null, 2),
1797
+ },
1798
+ ],
1799
+ };
1800
+ }
1801
+
1802
+ case 'get_type': {
1803
+ const details = getTypeDetails(args.name);
1804
+ if (!details) {
1805
+ return {
1806
+ content: [
1807
+ {
1808
+ type: 'text',
1809
+ text: `Type "${args.name}" not found`,
1810
+ },
1811
+ ],
1812
+ isError: true,
1813
+ };
1814
+ }
1815
+ return {
1816
+ content: [
1817
+ {
1818
+ type: 'text',
1819
+ text: JSON.stringify(details, null, 2),
1820
+ },
1821
+ ],
1822
+ };
1823
+ }
1824
+
1825
+ case 'get_design_tokens': {
1826
+ const allTokens = getAllDesignTokens();
1827
+ const tokens = args.category ? { [args.category]: allTokens[args.category] } : allTokens;
1828
+ return {
1829
+ content: [
1830
+ {
1831
+ type: 'text',
1832
+ text: JSON.stringify(tokens, null, 2),
1833
+ },
1834
+ ],
1835
+ };
1836
+ }
1837
+
1838
+ case 'get_component_template': {
1839
+ const template = getComponentTemplate();
1840
+ return {
1841
+ content: [
1842
+ {
1843
+ type: 'text',
1844
+ text: JSON.stringify(template, null, 2),
1845
+ },
1846
+ ],
1847
+ };
1848
+ }
1849
+
1850
+ case 'get_css_module_import_patterns': {
1851
+ const patterns = getCSSModuleImportPatterns();
1852
+ return {
1853
+ content: [
1854
+ {
1855
+ type: 'text',
1856
+ text: JSON.stringify(patterns, null, 2),
1857
+ },
1858
+ ],
1859
+ };
1860
+ }
1861
+
1862
+ case 'get_design_token_import_guidelines': {
1863
+ const guidelines = getDesignTokenImportGuidelines();
1864
+ return {
1865
+ content: [
1866
+ {
1867
+ type: 'text',
1868
+ text: JSON.stringify(guidelines, null, 2),
1869
+ },
1870
+ ],
1871
+ };
1872
+ }
1873
+
1874
+ case 'get_css_module_guidelines': {
1875
+ const guidelines = getCSSModuleGuidelines();
1876
+ return {
1877
+ content: [
1878
+ {
1879
+ type: 'text',
1880
+ text: JSON.stringify(guidelines, null, 2),
1881
+ },
1882
+ ],
1883
+ };
1884
+ }
1885
+
1886
+ case 'analyze_component_dependencies': {
1887
+ const dependencies = analyzeComponentDependencies(args.name);
1888
+ if (!dependencies) {
1889
+ return {
1890
+ content: [
1891
+ {
1892
+ type: 'text',
1893
+ text: `Component "${args.name}" not found or has no main file`,
1894
+ },
1895
+ ],
1896
+ isError: true,
1897
+ };
1898
+ }
1899
+ return {
1900
+ content: [
1901
+ {
1902
+ type: 'text',
1903
+ text: JSON.stringify(dependencies, null, 2),
1904
+ },
1905
+ ],
1906
+ };
1907
+ }
1908
+
1909
+ default:
1910
+ return {
1911
+ content: [
1912
+ {
1913
+ type: 'text',
1914
+ text: `Unknown tool: ${name}`,
1915
+ },
1916
+ ],
1917
+ isError: true,
1918
+ };
1919
+ }
1920
+ } catch (error) {
1921
+ return {
1922
+ content: [
1923
+ {
1924
+ type: 'text',
1925
+ text: `Error executing tool ${name}: ${error.message}`,
1926
+ },
1927
+ ],
1928
+ isError: true,
1929
+ };
1930
+ }
1931
+ });
1932
+
1933
+ /**
1934
+ * Start the server
1935
+ */
1936
+ async function main() {
1937
+ const transport = new StdioServerTransport();
1938
+ await server.connect(transport);
1939
+ console.error('Genesis Design System MCP Server running on stdio');
1940
+ console.error(`Version: ${designSystemData.metadata.version}`);
1941
+ }
1942
+
1943
+ main().catch((error) => {
1944
+ console.error('Fatal error:', error);
1945
+ process.exit(1);
1946
+ });
1947
+