@gemini-designer/mcp-server 0.1.2 → 0.1.29

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.
Files changed (129) hide show
  1. package/dist/components/catalog.d.ts.map +1 -1
  2. package/dist/components/catalog.js +10 -4
  3. package/dist/components/catalog.js.map +1 -1
  4. package/dist/config/index.d.ts.map +1 -1
  5. package/dist/config/index.js +11 -6
  6. package/dist/config/index.js.map +1 -1
  7. package/dist/context/builder.d.ts.map +1 -1
  8. package/dist/context/builder.js.map +1 -1
  9. package/dist/context/filter.d.ts.map +1 -1
  10. package/dist/context/filter.js +5 -1
  11. package/dist/context/filter.js.map +1 -1
  12. package/dist/context/grounding.d.ts.map +1 -1
  13. package/dist/context/grounding.js +7 -3
  14. package/dist/context/grounding.js.map +1 -1
  15. package/dist/context/guards.d.ts.map +1 -1
  16. package/dist/context/guards.js +53 -0
  17. package/dist/context/guards.js.map +1 -1
  18. package/dist/context/repo-hints.js.map +1 -1
  19. package/dist/context/styling-detector.d.ts +24 -0
  20. package/dist/context/styling-detector.d.ts.map +1 -0
  21. package/dist/context/styling-detector.js +337 -0
  22. package/dist/context/styling-detector.js.map +1 -0
  23. package/dist/design/principles.js.map +1 -1
  24. package/dist/generation/gemini-client.d.ts.map +1 -1
  25. package/dist/generation/gemini-client.js.map +1 -1
  26. package/dist/generation/litellm-client.d.ts.map +1 -1
  27. package/dist/generation/litellm-client.js +14 -7
  28. package/dist/generation/litellm-client.js.map +1 -1
  29. package/dist/generation/remote-client.d.ts +10 -5
  30. package/dist/generation/remote-client.d.ts.map +1 -1
  31. package/dist/generation/remote-client.js +13 -2
  32. package/dist/generation/remote-client.js.map +1 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/output/file-writer.d.ts.map +1 -1
  35. package/dist/output/file-writer.js +4 -4
  36. package/dist/output/file-writer.js.map +1 -1
  37. package/dist/output/formatter.d.ts.map +1 -1
  38. package/dist/output/formatter.js +5 -2
  39. package/dist/output/formatter.js.map +1 -1
  40. package/dist/server.d.ts.map +1 -1
  41. package/dist/server.js +2 -1
  42. package/dist/server.js.map +1 -1
  43. package/dist/stack/detect.d.ts.map +1 -1
  44. package/dist/stack/detect.js +42 -9
  45. package/dist/stack/detect.js.map +1 -1
  46. package/dist/tokens/sync.d.ts.map +1 -1
  47. package/dist/tokens/sync.js +22 -5
  48. package/dist/tokens/sync.js.map +1 -1
  49. package/dist/tools/analyze-screenshot-ui.d.ts.map +1 -1
  50. package/dist/tools/analyze-screenshot-ui.js +5 -5
  51. package/dist/tools/analyze-screenshot-ui.js.map +1 -1
  52. package/dist/tools/analyze-tokens.d.ts.map +1 -1
  53. package/dist/tools/analyze-tokens.js +3 -1
  54. package/dist/tools/analyze-tokens.js.map +1 -1
  55. package/dist/tools/catalog-components.d.ts.map +1 -1
  56. package/dist/tools/catalog-components.js +1 -4
  57. package/dist/tools/catalog-components.js.map +1 -1
  58. package/dist/tools/create-ui.d.ts +3 -0
  59. package/dist/tools/create-ui.d.ts.map +1 -1
  60. package/dist/tools/create-ui.js +203 -75
  61. package/dist/tools/create-ui.js.map +1 -1
  62. package/dist/tools/detect-ui-stack.js.map +1 -1
  63. package/dist/tools/generate-component-variants.d.ts.map +1 -1
  64. package/dist/tools/generate-component-variants.js +15 -4
  65. package/dist/tools/generate-component-variants.js.map +1 -1
  66. package/dist/tools/generate-vibes.d.ts.map +1 -1
  67. package/dist/tools/generate-vibes.js +7 -3
  68. package/dist/tools/generate-vibes.js.map +1 -1
  69. package/dist/tools/index.js.map +1 -1
  70. package/dist/tools/modify-ui.d.ts.map +1 -1
  71. package/dist/tools/modify-ui.js +7 -2
  72. package/dist/tools/modify-ui.js.map +1 -1
  73. package/dist/tools/scaffold-project.d.ts.map +1 -1
  74. package/dist/tools/scaffold-project.js +3 -1
  75. package/dist/tools/scaffold-project.js.map +1 -1
  76. package/dist/tools/snippet-ui.d.ts +3 -1
  77. package/dist/tools/snippet-ui.d.ts.map +1 -1
  78. package/dist/tools/snippet-ui.js +219 -88
  79. package/dist/tools/snippet-ui.js.map +1 -1
  80. package/dist/tools/sync-design-tokens.d.ts.map +1 -1
  81. package/dist/tools/sync-design-tokens.js +26 -11
  82. package/dist/tools/sync-design-tokens.js.map +1 -1
  83. package/dist/utils/walk.d.ts.map +1 -1
  84. package/dist/utils/walk.js.map +1 -1
  85. package/dist/version.d.ts +2 -0
  86. package/dist/version.d.ts.map +1 -0
  87. package/dist/version.js +5 -0
  88. package/dist/version.js.map +1 -0
  89. package/package.json +55 -55
  90. package/src/__tests__/builder.test.ts +19 -19
  91. package/src/__tests__/config.test.ts +63 -31
  92. package/src/__tests__/filter.test.ts +98 -92
  93. package/src/__tests__/remote-client.test.ts +179 -0
  94. package/src/components/catalog.ts +170 -166
  95. package/src/config/index.ts +185 -177
  96. package/src/context/builder.ts +157 -157
  97. package/src/context/filter.ts +110 -104
  98. package/src/context/grounding.ts +143 -129
  99. package/src/context/guards.ts +97 -38
  100. package/src/context/repo-hints.ts +24 -24
  101. package/src/context/styling-detector.ts +460 -0
  102. package/src/design/principles.ts +14 -14
  103. package/src/generation/gemini-client.ts +53 -56
  104. package/src/generation/litellm-client.ts +102 -86
  105. package/src/generation/remote-client.ts +100 -77
  106. package/src/index.ts +16 -16
  107. package/src/output/file-writer.ts +123 -123
  108. package/src/output/formatter.ts +139 -132
  109. package/src/server.ts +12 -11
  110. package/src/stack/detect.ts +226 -175
  111. package/src/tokens/sync.ts +189 -155
  112. package/src/tools/analyze-screenshot-ui.ts +89 -88
  113. package/src/tools/analyze-tokens.ts +80 -78
  114. package/src/tools/catalog-components.ts +68 -68
  115. package/src/tools/create-ui.ts +295 -142
  116. package/src/tools/detect-ui-stack.ts +36 -36
  117. package/src/tools/generate-component-variants.ts +155 -135
  118. package/src/tools/generate-vibes.ts +121 -117
  119. package/src/tools/index.ts +14 -14
  120. package/src/tools/modify-ui.ts +170 -165
  121. package/src/tools/scaffold-project.ts +68 -66
  122. package/src/tools/snippet-ui.ts +323 -172
  123. package/src/tools/sync-design-tokens.ts +217 -195
  124. package/src/utils/walk.ts +47 -45
  125. package/src/version.ts +6 -0
  126. package/tsconfig.json +23 -33
  127. package/vitest.config.ts +10 -10
  128. package/.prettierrc +0 -9
  129. package/eslint.config.js +0 -37
@@ -14,11 +14,11 @@ import { sanitizeContent } from '../context/filter.js';
14
14
  import { generateWithGemini } from '../generation/gemini-client.js';
15
15
 
16
16
  const inputSchema = {
17
- paths: z.array(z.string()).describe('Paths to CSS, SCSS, or design token files to analyze'),
18
- format: z
19
- .enum(['json', 'css-vars', 'tailwind', 'summary'])
20
- .default('json')
21
- .describe('Output format for the extracted tokens'),
17
+ paths: z.array(z.string()).describe('Paths to CSS, SCSS, or design token files to analyze'),
18
+ format: z
19
+ .enum(['json', 'css-vars', 'tailwind', 'summary'])
20
+ .default('json')
21
+ .describe('Output format for the extracted tokens'),
22
22
  };
23
23
 
24
24
  const SYSTEM_PROMPT = `You are a design system analyst. Your task is to extract and structure design tokens from CSS/SCSS files.
@@ -42,82 +42,84 @@ Output rules:
42
42
  When naming tokens, prefer semantic names (primary, surface, text, border) rather than raw hex values.`;
43
43
 
44
44
  export function registerAnalyzeTokens(server: McpServer, config: Config): void {
45
- server.registerTool(
46
- 'analyze_tokens',
47
- {
48
- title: 'Analyze Design Tokens',
49
- description:
50
- 'Extract and structure design tokens from existing CSS/SCSS files. Use before create_ui to understand existing design systems.',
51
- inputSchema,
52
- },
53
- async (args) => {
54
- const paths = args.paths as string[];
55
- const format = (args.format as 'json' | 'css-vars' | 'tailwind' | 'summary') || 'json';
56
-
57
- // Read all specified files (with safety checks)
58
- const fileContents: string[] = [];
59
- const errors: string[] = [];
60
-
61
- for (const filePath of paths) {
62
- try {
63
- const safePath = assertReadablePath(filePath, config);
64
- const raw = fs.readFileSync(safePath, 'utf-8');
65
- const content = sanitizeContent(raw);
66
- fileContents.push(`/* File: ${filePath} */\n${content}`);
67
- } catch (error) {
68
- const message = error instanceof Error ? error.message : 'Could not read';
69
- errors.push(`${filePath}: ${message}`);
70
- }
71
- }
72
-
73
- if (fileContents.length === 0) {
74
- return {
75
- content: [
76
- {
77
- type: 'text' as const,
78
- text: `Error: Could not read any files.\n${errors.join('\n')}`,
79
- },
80
- ],
81
- isError: true,
82
- };
83
- }
84
-
85
- if (errors.length > 0 && config.debug) {
86
- console.error('[analyze_tokens] Warnings:\n' + errors.join('\n'));
87
- }
88
-
89
- const combinedContent = fileContents.join('\n\n');
90
-
91
- const userPrompt = `Analyze these CSS/SCSS files and extract design tokens.
45
+ server.registerTool(
46
+ 'analyze_tokens',
47
+ {
48
+ title: 'Analyze Design Tokens',
49
+ description:
50
+ 'Extract and structure design tokens from existing CSS/SCSS files. Use before create_ui to understand existing design systems.',
51
+ inputSchema,
52
+ },
53
+ async (args) => {
54
+ const paths = args.paths as string[];
55
+ const format = (args.format as 'json' | 'css-vars' | 'tailwind' | 'summary') || 'json';
56
+
57
+ // Read all specified files (with safety checks)
58
+ const fileContents: string[] = [];
59
+ const errors: string[] = [];
60
+
61
+ for (const filePath of paths) {
62
+ try {
63
+ const safePath = assertReadablePath(filePath, config);
64
+ const raw = fs.readFileSync(safePath, 'utf-8');
65
+ const content = sanitizeContent(raw);
66
+ fileContents.push(`/* File: ${filePath} */\n${content}`);
67
+ } catch (error) {
68
+ const message = error instanceof Error ? error.message : 'Could not read';
69
+ errors.push(`${filePath}: ${message}`);
70
+ }
71
+ }
72
+
73
+ if (fileContents.length === 0) {
74
+ return {
75
+ content: [
76
+ {
77
+ type: 'text' as const,
78
+ text: `Error: Could not read any files.\n${errors.join('\n')}`,
79
+ },
80
+ ],
81
+ isError: true,
82
+ };
83
+ }
84
+
85
+ if (errors.length > 0 && config.debug) {
86
+ console.error('[analyze_tokens] Warnings:\n' + errors.join('\n'));
87
+ }
88
+
89
+ const combinedContent = fileContents.join('\n\n');
90
+
91
+ const userPrompt = `Analyze these CSS/SCSS files and extract design tokens.
92
92
  Format: ${format}
93
93
 
94
94
  FILES:
95
95
  ${combinedContent}`;
96
96
 
97
- try {
98
- const response = await generateWithGemini(config, SYSTEM_PROMPT, userPrompt, { toolName: 'analyze_tokens' });
99
-
100
- // IMPORTANT: Return raw model output ONLY (no wrappers) so JSON / CSS output stays valid.
101
- return {
102
- content: [
103
- {
104
- type: 'text' as const,
105
- text: response.trim(),
106
- },
107
- ],
108
- };
109
- } catch (error) {
110
- const message = error instanceof Error ? error.message : 'Unknown error';
111
- return {
112
- content: [
113
- {
114
- type: 'text' as const,
115
- text: `Error analyzing tokens: ${message}`,
116
- },
117
- ],
118
- isError: true,
119
- };
120
- }
121
- }
122
- );
97
+ try {
98
+ const response = await generateWithGemini(config, SYSTEM_PROMPT, userPrompt, {
99
+ toolName: 'analyze_tokens',
100
+ });
101
+
102
+ // IMPORTANT: Return raw model output ONLY (no wrappers) so JSON / CSS output stays valid.
103
+ return {
104
+ content: [
105
+ {
106
+ type: 'text' as const,
107
+ text: response.trim(),
108
+ },
109
+ ],
110
+ };
111
+ } catch (error) {
112
+ const message = error instanceof Error ? error.message : 'Unknown error';
113
+ return {
114
+ content: [
115
+ {
116
+ type: 'text' as const,
117
+ text: `Error analyzing tokens: ${message}`,
118
+ },
119
+ ],
120
+ isError: true,
121
+ };
122
+ }
123
+ }
124
+ );
123
125
  }
@@ -18,82 +18,82 @@ import { walkFiles, toPosixPath } from '../utils/walk.js';
18
18
  import { buildComponentCatalog } from '../components/catalog.js';
19
19
 
20
20
  const inputSchema = {
21
- roots: z
22
- .array(z.string())
23
- .optional()
24
- .describe('Directories to scan (defaults to ["src"]).'),
25
- extensions: z
26
- .array(z.string())
27
- .optional()
28
- .describe('File extensions to include (defaults to [".tsx", ".jsx"]).'),
29
- maxFiles: z
30
- .number()
31
- .int()
32
- .min(1)
33
- .max(50_000)
34
- .default(2000)
35
- .describe('Safety limit to avoid scanning huge repos.'),
21
+ roots: z.array(z.string()).optional().describe('Directories to scan (defaults to ["src"]).'),
22
+ extensions: z
23
+ .array(z.string())
24
+ .optional()
25
+ .describe('File extensions to include (defaults to [".tsx", ".jsx"]).'),
26
+ maxFiles: z
27
+ .number()
28
+ .int()
29
+ .min(1)
30
+ .max(50_000)
31
+ .default(2000)
32
+ .describe('Safety limit to avoid scanning huge repos.'),
36
33
  };
37
34
 
38
35
  export function registerCatalogComponents(server: McpServer, config: Config): void {
39
- server.registerTool(
40
- 'catalog_components',
41
- {
42
- title: 'Catalog Components',
43
- description: 'Scan the repo and return a JSON catalog of exported components (no LLM).',
44
- inputSchema,
45
- },
46
- async (args) => {
47
- const roots = (args.roots as string[] | undefined) || ['src'];
48
- const extensions = (args.extensions as string[] | undefined) || ['.tsx', '.jsx'];
49
- const maxFiles = (args.maxFiles as number | undefined) || 2000;
36
+ server.registerTool(
37
+ 'catalog_components',
38
+ {
39
+ title: 'Catalog Components',
40
+ description: 'Scan the repo and return a JSON catalog of exported components (no LLM).',
41
+ inputSchema,
42
+ },
43
+ async (args) => {
44
+ const roots = (args.roots as string[] | undefined) || ['src'];
45
+ const extensions = (args.extensions as string[] | undefined) || ['.tsx', '.jsx'];
46
+ const maxFiles = (args.maxFiles as number | undefined) || 2000;
50
47
 
51
- const safeRoots: string[] = [];
52
- for (const r of roots) {
53
- try {
54
- safeRoots.push(assertReadableDir(r, config));
55
- } catch (error) {
56
- const message = error instanceof Error ? error.message : 'Invalid root';
57
- return {
58
- content: [{ type: 'text' as const, text: `Error: ${message}` }],
59
- isError: true,
60
- };
61
- }
62
- }
48
+ const safeRoots: string[] = [];
49
+ for (const r of roots) {
50
+ try {
51
+ safeRoots.push(assertReadableDir(r, config));
52
+ } catch (error) {
53
+ const message = error instanceof Error ? error.message : 'Invalid root';
54
+ return {
55
+ content: [{ type: 'text' as const, text: `Error: ${message}` }],
56
+ isError: true,
57
+ };
58
+ }
59
+ }
63
60
 
64
- const allFiles: string[] = [];
65
- for (const r of safeRoots) {
66
- const found = walkFiles(r, { includeExtensions: extensions, maxFiles });
67
- allFiles.push(...found);
68
- }
61
+ const allFiles: string[] = [];
62
+ for (const r of safeRoots) {
63
+ const found = walkFiles(r, { includeExtensions: extensions, maxFiles });
64
+ allFiles.push(...found);
65
+ }
69
66
 
70
- // Use cwd as catalog root so paths are stable for agents
71
- const root = path.resolve(process.cwd());
67
+ // Use cwd as catalog root so paths are stable for agents
68
+ const root = path.resolve(process.cwd());
72
69
 
73
- const catalog = await buildComponentCatalog(root, allFiles.map((f) => path.resolve(f)));
70
+ const catalog = await buildComponentCatalog(
71
+ root,
72
+ allFiles.map((f) => path.resolve(f))
73
+ );
74
74
 
75
- // Ensure POSIX paths
76
- catalog.components = catalog.components.map((c) => ({ ...c, file: toPosixPath(c.file) }));
77
- catalog.root = toPosixPath(catalog.root);
75
+ // Ensure POSIX paths
76
+ catalog.components = catalog.components.map((c) => ({ ...c, file: toPosixPath(c.file) }));
77
+ catalog.root = toPosixPath(catalog.root);
78
78
 
79
- // Small summary
80
- const summary = {
81
- totalFiles: catalog.filesScanned,
82
- totalComponents: catalog.components.length,
83
- byExportType: {
84
- named: catalog.components.filter((c) => c.exportType === 'named').length,
85
- default: catalog.components.filter((c) => c.exportType === 'default').length,
86
- },
87
- };
79
+ // Small summary
80
+ const summary = {
81
+ totalFiles: catalog.filesScanned,
82
+ totalComponents: catalog.components.length,
83
+ byExportType: {
84
+ named: catalog.components.filter((c) => c.exportType === 'named').length,
85
+ default: catalog.components.filter((c) => c.exportType === 'default').length,
86
+ },
87
+ };
88
88
 
89
- return {
90
- content: [
91
- {
92
- type: 'text' as const,
93
- text: JSON.stringify({ ...catalog, summary }, null, 2),
94
- },
95
- ],
96
- };
97
- }
98
- );
89
+ return {
90
+ content: [
91
+ {
92
+ type: 'text' as const,
93
+ text: JSON.stringify({ ...catalog, summary }, null, 2),
94
+ },
95
+ ],
96
+ };
97
+ }
98
+ );
99
99
  }