@spaced-out/ui-design-system 0.5.18 → 0.5.20

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/mcp/README.md ADDED
@@ -0,0 +1,306 @@
1
+ # Genesis UI Design System MCP Server
2
+
3
+ A Model Context Protocol (MCP) server that provides AI assistants (Claude, Cursor, etc.) with access to the Genesis UI Design System.
4
+
5
+ ## ✨ Features
6
+
7
+ - šŸ“¦ **Component Discovery**: List and search all available components
8
+ - šŸŽØ **Design Tokens**: Access colors, spacing, typography, shadows, and more
9
+ - šŸ“š **Documentation**: Browse component stories and TypeScript definitions
10
+ - šŸ” **Dependency Analysis**: Understand component dependencies
11
+ - šŸš€ **Onboarding Templates**: Get starter templates for new components
12
+ - šŸŖ **Hooks Information**: Query available React hooks
13
+
14
+ ## šŸŽÆ No Local Dependencies Required!
15
+
16
+ Unlike traditional MCP servers that require a local copy of the design system, this server **bundles all data at build time**. Users only need to run `npx` - no cloning, no environment variables!
17
+
18
+ ## šŸ“¦ Installation for Users
19
+
20
+ ### With Cursor
21
+
22
+ Add to your Cursor MCP settings:
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "genesis-design-system": {
28
+ "command": "npx",
29
+ "args": ["-y", "@spaced-out/genesis-mcp-server@latest"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ### With Claude Desktop
36
+
37
+ Add to your Claude config file:
38
+
39
+ **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
40
+ **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
41
+ **Linux**: `~/.config/Claude/claude_desktop_config.json`
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "genesis-design-system": {
47
+ "command": "npx",
48
+ "args": ["-y", "@spaced-out/genesis-mcp-server@latest"]
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ Then restart Cursor or Claude Desktop.
55
+
56
+ ## šŸ’¬ Usage Examples
57
+
58
+ Once configured, you can ask:
59
+
60
+ **Component Discovery:**
61
+
62
+ ```
63
+ "List all components in Genesis"
64
+ "Search for dropdown components"
65
+ "Show me the Button component implementation"
66
+ "What types does the Icon component export?"
67
+ "Show me the Icon component with its exported types"
68
+ ```
69
+
70
+ **Design Tokens:**
71
+
72
+ ```
73
+ "Show me all color tokens"
74
+ "What spacing tokens are available?"
75
+ "List all shadow/elevation tokens"
76
+ ```
77
+
78
+ **Component Onboarding:**
79
+
80
+ ```
81
+ "I want to create a new Toast component, show me the template"
82
+ "Help me onboard an Alert component"
83
+ "What's the standard structure for a Genesis component?"
84
+ ```
85
+
86
+ **Design Token Imports:**
87
+
88
+ ```
89
+ "How do I import design tokens in CSS Module files?"
90
+ "Show me the correct way to import color and spacing tokens"
91
+ "What token files are available and how do I import them?"
92
+ ```
93
+
94
+ **CSS Module Guidelines:**
95
+
96
+ ```
97
+ "Show me CSS Module styling guidelines for Genesis"
98
+ "What are the class naming conventions for CSS Modules?"
99
+ "How should I style child elements in CSS Modules?"
100
+ "What's the correct pattern for state-based styling in CSS?"
101
+ "Show me common CSS mistakes to avoid"
102
+ "How do I apply colors to child elements like text, icons, or buttons?"
103
+ ```
104
+
105
+ **Dependencies:**
106
+
107
+ ```
108
+ "What components does Modal depend on?"
109
+ "Show me the dependencies of Dropdown"
110
+ "What hooks does the Table component use?"
111
+ ```
112
+
113
+ ## šŸ”§ Development
114
+
115
+ ### Building the Data
116
+
117
+ This server extracts design system metadata at build time. To build:
118
+
119
+ ```bash
120
+ # Install dependencies
121
+ yarn install
122
+
123
+ # Build the data bundle
124
+ yarn build
125
+ ```
126
+
127
+ This creates `data/design-system.json` with all component, hook, and token information.
128
+
129
+ ### Testing Locally
130
+
131
+ ```bash
132
+ # After building, test the server
133
+ node index.js
134
+ ```
135
+
136
+ Or test with MCP Inspector:
137
+
138
+ ```bash
139
+ npx @modelcontextprotocol/inspector node index.js
140
+ ```
141
+
142
+ ### Publishing
143
+
144
+ ```bash
145
+ # The prepublishOnly script runs yarn build automatically
146
+ yarn publish
147
+ ```
148
+
149
+ ## šŸ“Š How It Works
150
+
151
+ ### Build Time (CI/CD)
152
+
153
+ 1. `build-mcp-data.js` scans the design system:
154
+
155
+ - Reads all components from `src/components/`
156
+ - Reads all hooks from `src/hooks/`
157
+ - Reads all design tokens from `design-tokens/`
158
+
159
+ 2. Extracts file contents and metadata
160
+
161
+ 3. Bundles everything into `data/design-system.json`
162
+
163
+ 4. Package is published to npm with bundled data
164
+
165
+ ### Runtime (User's Machine)
166
+
167
+ 1. User runs `npx @spaced-out/genesis-mcp-server`
168
+
169
+ 2. Server loads pre-bundled `data/design-system.json`
170
+
171
+ 3. Serves data via MCP protocol (stdio)
172
+
173
+ 4. AI assistant queries the server for design system info
174
+
175
+ **Result:** No local design system needed! ✨
176
+
177
+ ## šŸ—ļø Architecture
178
+
179
+ ```
180
+ ui-design-system/
181
+ ā”œā”€ā”€ src/
182
+ │ ā”œā”€ā”€ components/ # Source components
183
+ │ └── hooks/ # Source hooks
184
+ ā”œā”€ā”€ design-tokens/ # Source tokens
185
+ └── mcp/ # šŸ‘ˆ MCP Server
186
+ ā”œā”€ā”€ index.js # MCP server (reads from data/)
187
+ ā”œā”€ā”€ build-mcp-data.js # Build script (extracts from ../)
188
+ ā”œā”€ā”€ data/
189
+ │ └── design-system.json # Bundled data (git-ignored)
190
+ ā”œā”€ā”€ package.json
191
+ └── README.md
192
+ ```
193
+
194
+ ## šŸ“ Available Tools
195
+
196
+ The MCP server provides these tools to AI assistants:
197
+
198
+ - `list_components` - List all available components
199
+ - `get_component` - Get detailed component information **including exported types** (e.g., IconType, IconSize from Icon component)
200
+ - `search_components` - Search components by name
201
+ - `list_hooks` - List all available hooks
202
+ - `get_hook` - Get detailed hook information
203
+ - `get_design_tokens` - Get all or filtered design tokens
204
+ - `get_component_template` - Get template for new components
205
+ - `get_design_token_import_guidelines` - Get guidelines for importing design tokens in CSS Module files with correct paths and examples
206
+ - `get_css_module_guidelines` - **NEW!** Get comprehensive CSS Module styling guidelines including class naming conventions (BEM patterns), token usage, icon color patterns, and common mistakes to avoid
207
+ - `analyze_component_dependencies` - Analyze component dependencies
208
+
209
+ ## šŸ”„ CI/CD Integration
210
+
211
+ ### Automatic Publishing
212
+
213
+ The MCP server is automatically published to npm via GitHub Actions (`.github/workflows/publish-mcp.yml`).
214
+
215
+ **Triggers:**
216
+
217
+ - Push to `master` branch when files change in:
218
+ - `src/**` (component/hook changes)
219
+ - `design-tokens/**` (token changes)
220
+ - `mcp/**` (MCP server changes)
221
+ - Manual workflow dispatch
222
+
223
+ **Process:**
224
+
225
+ 1. **Auto-version bump**: If `src/` or `design-tokens/` changed, automatically bumps patch version
226
+ 2. **Build**: Generates fresh MCP data bundle with latest design system changes
227
+ 3. **Commit**: Pushes version bump back to master (with `[skip ci]` to avoid loops)
228
+ 4. **Publish**: Publishes new version to npm
229
+
230
+ **What this means for you:**
231
+
232
+ āœ… Update a component → Merge to master → New MCP version automatically published!
233
+
234
+ No need to manually bump versions when changing design system files. The workflow handles it for you.
235
+
236
+ ### Version Management
237
+
238
+ **Automatic (recommended):**
239
+
240
+ - Changes to `src/` or `design-tokens/` → patch version bumped automatically
241
+ - E.g., `1.0.5` → `1.0.6`
242
+
243
+ **Manual (for major/minor releases):**
244
+
245
+ ```bash
246
+ cd mcp
247
+ yarn version --minor # or --major
248
+ git push --follow-tags
249
+ ```
250
+
251
+ **Manual MCP-only changes:**
252
+
253
+ - If you only change files in `mcp/`, manually bump the version to publish
254
+
255
+ ## šŸ“ˆ Data Size
256
+
257
+ The bundled JSON includes:
258
+
259
+ - Full TypeScript component implementations
260
+ - Storybook stories
261
+ - CSS modules
262
+ - Design tokens
263
+ - Hook implementations
264
+
265
+ Typical size: 5-15 MB (cached by npm, so only downloaded once)
266
+
267
+ ## šŸ” Private Packages
268
+
269
+ If publishing to a private npm registry:
270
+
271
+ ```json
272
+ {
273
+ "publishConfig": {
274
+ "registry": "https://your-private-registry.com"
275
+ }
276
+ }
277
+ ```
278
+
279
+ Users configure their registry:
280
+
281
+ ```bash
282
+ npm config set @spaced-out:registry https://your-private-registry.com
283
+ # or with yarn
284
+ yarn config set registry https://your-private-registry.com
285
+ ```
286
+
287
+ ## šŸ¤ Contributing
288
+
289
+ When updating the design system:
290
+
291
+ 1. Changes to components/hooks/tokens are automatically detected
292
+ 2. CI/CD runs `npm run build` to regenerate data
293
+ 3. New version is published to npm
294
+ 4. Users get updates on next `npx` run (or immediately with `@latest`)
295
+
296
+ ## šŸ“š Additional Resources
297
+
298
+ - [MCP Documentation](https://modelcontextprotocol.io)
299
+
300
+ ## šŸ“„ License
301
+
302
+ UNLICENSED - Internal use only
303
+
304
+ ---
305
+
306
+ **Built with ā¤ļø by the Central UI team**
@@ -0,0 +1,247 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Build script to extract design system metadata into JSON
5
+ * This runs before publishing to npm to bundle all component/token data
6
+ */
7
+
8
+ import { readFileSync, writeFileSync, readdirSync, statSync, existsSync, mkdirSync } from 'fs';
9
+ import { join, resolve, dirname } from 'path';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+
15
+ // Design system is parent directory
16
+ const DESIGN_SYSTEM_PATH = resolve(__dirname, '..');
17
+ const OUTPUT_DIR = join(__dirname, 'data');
18
+ const OUTPUT_FILE = join(OUTPUT_DIR, 'design-system.json');
19
+
20
+ console.log('Building Genesis MCP data...\n');
21
+ console.log(`Design System: ${DESIGN_SYSTEM_PATH}`);
22
+ console.log(`Output: ${OUTPUT_FILE}\n`);
23
+
24
+ function safeReadFile(filePath) {
25
+ try {
26
+ return readFileSync(filePath, 'utf-8');
27
+ } catch (error) {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ function getDirectories(path) {
33
+ try {
34
+ return readdirSync(path).filter(f => {
35
+ const fullPath = join(path, f);
36
+ return statSync(fullPath).isDirectory() && !f.startsWith('.');
37
+ });
38
+ } catch (error) {
39
+ console.error(`āŒ Error reading directories in ${path}:`, error.message);
40
+ return [];
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Extract all components with their files
46
+ */
47
+ function buildComponentsData() {
48
+ console.log('šŸ“¦ Extracting components...');
49
+ const componentsPath = join(DESIGN_SYSTEM_PATH, 'src/components');
50
+ const components = {};
51
+
52
+ if (!existsSync(componentsPath)) {
53
+ console.warn('āš ļø Components path not found');
54
+ return components;
55
+ }
56
+
57
+ const componentDirs = getDirectories(componentsPath).filter(name => name !== 'index.ts');
58
+
59
+ for (const componentName of componentDirs) {
60
+ const componentDir = join(componentsPath, componentName);
61
+
62
+ const mainTsx = join(componentDir, `${componentName}.tsx`);
63
+ const mainTs = join(componentDir, `${componentName}.ts`);
64
+
65
+ const storyTsx = join(componentDir, `${componentName}.stories.tsx`);
66
+ const storyTs = join(componentDir, `${componentName}.stories.ts`);
67
+
68
+ const cssFile = join(componentDir, `${componentName}.module.css`);
69
+
70
+ const indexFile = join(componentDir, 'index.ts');
71
+
72
+ const mainContent = safeReadFile(mainTsx) || safeReadFile(mainTs);
73
+ const storyContent = safeReadFile(storyTsx) || safeReadFile(storyTs);
74
+ const cssContent = safeReadFile(cssFile);
75
+ const indexContent = safeReadFile(indexFile);
76
+
77
+ const allFiles = existsSync(componentDir)
78
+ ? readdirSync(componentDir).filter(f => !f.startsWith('.'))
79
+ : [];
80
+
81
+ components[componentName] = {
82
+ name: componentName,
83
+ path: `src/components/${componentName}`,
84
+ files: {
85
+ main: mainContent ? { path: `${componentName}.tsx`, content: mainContent } : null,
86
+ story: storyContent ? { path: `${componentName}.stories.tsx`, content: storyContent } : null,
87
+ css: cssContent ? { path: `${componentName}.module.css`, content: cssContent } : null,
88
+ index: indexContent ? { path: 'index.ts', content: indexContent } : null,
89
+ },
90
+ allFiles,
91
+ };
92
+ }
93
+
94
+ console.log(` āœ… Extracted ${Object.keys(components).length} components`);
95
+ return components;
96
+ }
97
+
98
+ /**
99
+ * Extract all hooks
100
+ */
101
+ function buildHooksData() {
102
+ console.log('šŸŖ Extracting hooks...');
103
+ const hooksPath = join(DESIGN_SYSTEM_PATH, 'src/hooks');
104
+ const hooks = {};
105
+
106
+ if (!existsSync(hooksPath)) {
107
+ console.warn('āš ļø Hooks path not found');
108
+ return hooks;
109
+ }
110
+
111
+ const hookDirs = getDirectories(hooksPath).filter(name => name !== 'index.ts');
112
+
113
+ for (const hookName of hookDirs) {
114
+ const hookDir = join(hooksPath, hookName);
115
+
116
+ // Read main hook file
117
+ const mainTs = join(hookDir, `${hookName}.ts`);
118
+ const mainTsx = join(hookDir, `${hookName}.tsx`);
119
+
120
+ // Read story file
121
+ const storyTsx = join(hookDir, `${hookName}.stories.tsx`);
122
+ const storyTs = join(hookDir, `${hookName}.stories.ts`);
123
+
124
+ // Read index file
125
+ const indexFile = join(hookDir, 'index.ts');
126
+
127
+ const mainContent = safeReadFile(mainTs) || safeReadFile(mainTsx);
128
+ const storyContent = safeReadFile(storyTsx) || safeReadFile(storyTs);
129
+ const indexContent = safeReadFile(indexFile);
130
+
131
+ const allFiles = existsSync(hookDir)
132
+ ? readdirSync(hookDir).filter(f => !f.startsWith('.'))
133
+ : [];
134
+
135
+ hooks[hookName] = {
136
+ name: hookName,
137
+ path: `src/hooks/${hookName}`,
138
+ files: {
139
+ main: mainContent ? { path: `${hookName}.ts`, content: mainContent } : null,
140
+ story: storyContent ? { path: `${hookName}.stories.tsx`, content: storyContent } : null,
141
+ index: indexContent ? { path: 'index.ts', content: indexContent } : null,
142
+ },
143
+ allFiles,
144
+ };
145
+ }
146
+
147
+ console.log(` āœ… Extracted ${Object.keys(hooks).length} hooks`);
148
+ return hooks;
149
+ }
150
+
151
+ /**
152
+ * Extract all design tokens
153
+ */
154
+ function buildTokensData() {
155
+ console.log('šŸŽØ Extracting design tokens...');
156
+ const tokensPath = join(DESIGN_SYSTEM_PATH, 'design-tokens');
157
+ const tokens = {};
158
+
159
+ if (!existsSync(tokensPath)) {
160
+ console.warn('āš ļø Design tokens path not found');
161
+ return tokens;
162
+ }
163
+
164
+ const categories = getDirectories(tokensPath);
165
+
166
+ for (const category of categories) {
167
+ tokens[category] = {};
168
+ const categoryPath = join(tokensPath, category);
169
+
170
+ try {
171
+ const files = readdirSync(categoryPath).filter(f => f.endsWith('.json'));
172
+
173
+ for (const file of files) {
174
+ const filePath = join(categoryPath, file);
175
+ try {
176
+ const content = readFileSync(filePath, 'utf-8');
177
+ const tokenData = JSON.parse(content);
178
+ tokens[category][file.replace('.json', '')] = tokenData;
179
+ } catch (error) {
180
+ console.warn(` āš ļø Error parsing ${file}:`, error.message);
181
+ }
182
+ }
183
+ } catch (error) {
184
+ console.warn(` āš ļø Error reading category ${category}:`, error.message);
185
+ }
186
+ }
187
+
188
+ const tokenCount = Object.values(tokens).reduce((sum, cat) => sum + Object.keys(cat).length, 0);
189
+ console.log(` āœ… Extracted ${tokenCount} token files across ${Object.keys(tokens).length} categories`);
190
+ return tokens;
191
+ }
192
+
193
+ /**
194
+ * Get package version
195
+ */
196
+ function getVersion() {
197
+ try {
198
+ const pkgPath = join(DESIGN_SYSTEM_PATH, 'package.json');
199
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
200
+ return pkg.version || '0.0.0';
201
+ } catch (error) {
202
+ return '0.0.0';
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Main build function
208
+ */
209
+ function build() {
210
+ try {
211
+ const data = {
212
+ metadata: {
213
+ buildDate: new Date().toISOString(),
214
+ version: getVersion(),
215
+ designSystemPath: DESIGN_SYSTEM_PATH,
216
+ },
217
+ components: buildComponentsData(),
218
+ hooks: buildHooksData(),
219
+ tokens: buildTokensData(),
220
+ };
221
+
222
+ // Create output directory if it doesn't exist
223
+ if (!existsSync(OUTPUT_DIR)) {
224
+ mkdirSync(OUTPUT_DIR, { recursive: true });
225
+ }
226
+
227
+ // Write to JSON file
228
+ writeFileSync(OUTPUT_FILE, JSON.stringify(data, null, 2));
229
+
230
+ console.log('\n✨ Build complete!');
231
+ console.log(`šŸ“Š Summary:`);
232
+ console.log(` - Components: ${Object.keys(data.components).length}`);
233
+ console.log(` - Hooks: ${Object.keys(data.hooks).length}`);
234
+ console.log(` - Token categories: ${Object.keys(data.tokens).length}`);
235
+ console.log(` - Version: ${data.metadata.version}`);
236
+ console.log(` - Output: ${OUTPUT_FILE}`);
237
+ console.log(` - Size: ${(Buffer.byteLength(JSON.stringify(data)) / 1024 / 1024).toFixed(2)} MB\n`);
238
+
239
+ } catch (error) {
240
+ console.error('āŒ Build failed:', error);
241
+ process.exit(1);
242
+ }
243
+ }
244
+
245
+ // Run the build
246
+ build();
247
+