@gemini-designer/mcp-server 0.1.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.
Files changed (153) hide show
  1. package/.prettierrc +9 -0
  2. package/dist/components/catalog.d.ts +24 -0
  3. package/dist/components/catalog.d.ts.map +1 -0
  4. package/dist/components/catalog.js +186 -0
  5. package/dist/components/catalog.js.map +1 -0
  6. package/dist/config/index.d.ts +60 -0
  7. package/dist/config/index.d.ts.map +1 -0
  8. package/dist/config/index.js +199 -0
  9. package/dist/config/index.js.map +1 -0
  10. package/dist/context/builder.d.ts +32 -0
  11. package/dist/context/builder.d.ts.map +1 -0
  12. package/dist/context/builder.js +194 -0
  13. package/dist/context/builder.js.map +1 -0
  14. package/dist/context/filter.d.ts +28 -0
  15. package/dist/context/filter.d.ts.map +1 -0
  16. package/dist/context/filter.js +136 -0
  17. package/dist/context/filter.js.map +1 -0
  18. package/dist/context/grounding.d.ts +27 -0
  19. package/dist/context/grounding.d.ts.map +1 -0
  20. package/dist/context/grounding.js +162 -0
  21. package/dist/context/grounding.js.map +1 -0
  22. package/dist/context/guards.d.ts +31 -0
  23. package/dist/context/guards.d.ts.map +1 -0
  24. package/dist/context/guards.js +76 -0
  25. package/dist/context/guards.js.map +1 -0
  26. package/dist/context/repo-hints.d.ts +12 -0
  27. package/dist/context/repo-hints.d.ts.map +1 -0
  28. package/dist/context/repo-hints.js +40 -0
  29. package/dist/context/repo-hints.js.map +1 -0
  30. package/dist/generation/gemini-client.d.ts +27 -0
  31. package/dist/generation/gemini-client.d.ts.map +1 -0
  32. package/dist/generation/gemini-client.js +64 -0
  33. package/dist/generation/gemini-client.js.map +1 -0
  34. package/dist/generation/litellm-client.d.ts +16 -0
  35. package/dist/generation/litellm-client.d.ts.map +1 -0
  36. package/dist/generation/litellm-client.js +98 -0
  37. package/dist/generation/litellm-client.js.map +1 -0
  38. package/dist/generation/remote-client.d.ts +20 -0
  39. package/dist/generation/remote-client.d.ts.map +1 -0
  40. package/dist/generation/remote-client.js +69 -0
  41. package/dist/generation/remote-client.js.map +1 -0
  42. package/dist/index.d.ts +9 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +30 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/output/file-writer.d.ts +39 -0
  47. package/dist/output/file-writer.d.ts.map +1 -0
  48. package/dist/output/file-writer.js +153 -0
  49. package/dist/output/file-writer.js.map +1 -0
  50. package/dist/output/formatter.d.ts +26 -0
  51. package/dist/output/formatter.d.ts.map +1 -0
  52. package/dist/output/formatter.js +156 -0
  53. package/dist/output/formatter.js.map +1 -0
  54. package/dist/server.d.ts +9 -0
  55. package/dist/server.d.ts.map +1 -0
  56. package/dist/server.js +22 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/stack/detect.d.ts +49 -0
  59. package/dist/stack/detect.d.ts.map +1 -0
  60. package/dist/stack/detect.js +157 -0
  61. package/dist/stack/detect.js.map +1 -0
  62. package/dist/tokens/sync.d.ts +32 -0
  63. package/dist/tokens/sync.d.ts.map +1 -0
  64. package/dist/tokens/sync.js +188 -0
  65. package/dist/tokens/sync.js.map +1 -0
  66. package/dist/tools/analyze-screenshot-ui.d.ts +18 -0
  67. package/dist/tools/analyze-screenshot-ui.d.ts.map +1 -0
  68. package/dist/tools/analyze-screenshot-ui.js +133 -0
  69. package/dist/tools/analyze-screenshot-ui.js.map +1 -0
  70. package/dist/tools/analyze-tokens.d.ts +10 -0
  71. package/dist/tools/analyze-tokens.d.ts.map +1 -0
  72. package/dist/tools/analyze-tokens.js +107 -0
  73. package/dist/tools/analyze-tokens.js.map +1 -0
  74. package/dist/tools/catalog-components.d.ts +14 -0
  75. package/dist/tools/catalog-components.d.ts.map +1 -0
  76. package/dist/tools/catalog-components.js +85 -0
  77. package/dist/tools/catalog-components.js.map +1 -0
  78. package/dist/tools/create-ui.d.ts +10 -0
  79. package/dist/tools/create-ui.d.ts.map +1 -0
  80. package/dist/tools/create-ui.js +167 -0
  81. package/dist/tools/create-ui.js.map +1 -0
  82. package/dist/tools/detect-ui-stack.d.ts +15 -0
  83. package/dist/tools/detect-ui-stack.d.ts.map +1 -0
  84. package/dist/tools/detect-ui-stack.js +52 -0
  85. package/dist/tools/detect-ui-stack.js.map +1 -0
  86. package/dist/tools/generate-component-variants.d.ts +15 -0
  87. package/dist/tools/generate-component-variants.d.ts.map +1 -0
  88. package/dist/tools/generate-component-variants.js +199 -0
  89. package/dist/tools/generate-component-variants.js.map +1 -0
  90. package/dist/tools/generate-vibes.d.ts +10 -0
  91. package/dist/tools/generate-vibes.d.ts.map +1 -0
  92. package/dist/tools/generate-vibes.js +145 -0
  93. package/dist/tools/generate-vibes.js.map +1 -0
  94. package/dist/tools/index.d.ts +12 -0
  95. package/dist/tools/index.d.ts.map +1 -0
  96. package/dist/tools/index.js +36 -0
  97. package/dist/tools/index.js.map +1 -0
  98. package/dist/tools/modify-ui.d.ts +11 -0
  99. package/dist/tools/modify-ui.d.ts.map +1 -0
  100. package/dist/tools/modify-ui.js +207 -0
  101. package/dist/tools/modify-ui.js.map +1 -0
  102. package/dist/tools/scaffold-project.d.ts +10 -0
  103. package/dist/tools/scaffold-project.d.ts.map +1 -0
  104. package/dist/tools/scaffold-project.js +122 -0
  105. package/dist/tools/scaffold-project.js.map +1 -0
  106. package/dist/tools/snippet-ui.d.ts +11 -0
  107. package/dist/tools/snippet-ui.d.ts.map +1 -0
  108. package/dist/tools/snippet-ui.js +194 -0
  109. package/dist/tools/snippet-ui.js.map +1 -0
  110. package/dist/tools/sync-design-tokens.d.ts +14 -0
  111. package/dist/tools/sync-design-tokens.d.ts.map +1 -0
  112. package/dist/tools/sync-design-tokens.js +233 -0
  113. package/dist/tools/sync-design-tokens.js.map +1 -0
  114. package/dist/utils/walk.d.ts +15 -0
  115. package/dist/utils/walk.d.ts.map +1 -0
  116. package/dist/utils/walk.js +63 -0
  117. package/dist/utils/walk.js.map +1 -0
  118. package/eslint.config.js +37 -0
  119. package/package.json +56 -0
  120. package/src/__tests__/builder.test.ts +31 -0
  121. package/src/__tests__/config.test.ts +52 -0
  122. package/src/__tests__/filter.test.ts +109 -0
  123. package/src/components/catalog.ts +214 -0
  124. package/src/config/index.ts +237 -0
  125. package/src/context/builder.ts +233 -0
  126. package/src/context/filter.ts +164 -0
  127. package/src/context/grounding.ts +191 -0
  128. package/src/context/guards.ts +94 -0
  129. package/src/context/repo-hints.ts +43 -0
  130. package/src/generation/gemini-client.ts +94 -0
  131. package/src/generation/litellm-client.ts +121 -0
  132. package/src/generation/remote-client.ts +103 -0
  133. package/src/index.ts +36 -0
  134. package/src/output/file-writer.ts +181 -0
  135. package/src/output/formatter.ts +186 -0
  136. package/src/server.ts +28 -0
  137. package/src/stack/detect.ts +204 -0
  138. package/src/tokens/sync.ts +212 -0
  139. package/src/tools/analyze-screenshot-ui.ts +150 -0
  140. package/src/tools/analyze-tokens.ts +123 -0
  141. package/src/tools/catalog-components.ts +99 -0
  142. package/src/tools/create-ui.ts +194 -0
  143. package/src/tools/detect-ui-stack.ts +64 -0
  144. package/src/tools/generate-component-variants.ts +218 -0
  145. package/src/tools/generate-vibes.ts +177 -0
  146. package/src/tools/index.ts +42 -0
  147. package/src/tools/modify-ui.ts +230 -0
  148. package/src/tools/scaffold-project.ts +138 -0
  149. package/src/tools/snippet-ui.ts +222 -0
  150. package/src/tools/sync-design-tokens.ts +256 -0
  151. package/src/utils/walk.ts +75 -0
  152. package/tsconfig.json +34 -0
  153. package/vitest.config.ts +15 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Remote Gateway Client
3
+ *
4
+ * Handles communication with the VPS gateway for remote mode.
5
+ * Transmits only sanitized context to protect user secrets.
6
+ */
7
+
8
+ import { Config } from '../config/index.js';
9
+ import type { GeminiUserContent } from './gemini-client.js';
10
+
11
+ interface GatewayRequest {
12
+ systemPrompt: string;
13
+ userPrompt?: string;
14
+ userParts?: Array<{ text: string } | { inlineData: { mimeType: string; data: string } }>;
15
+ toolName?: string;
16
+ }
17
+
18
+ interface GatewayResponse {
19
+ content: string;
20
+ tokensUsed: number;
21
+ inputTokens?: number;
22
+ model?: string;
23
+ costMicrodollars?: number;
24
+ error?: string;
25
+ }
26
+
27
+ /**
28
+ * Generate content via remote gateway
29
+ */
30
+ export async function generateWithRemote(
31
+ config: Config,
32
+ systemPrompt: string,
33
+ userPrompt: GeminiUserContent,
34
+ toolName?: string
35
+ ): Promise<string> {
36
+ if (!config.remoteEndpoint) {
37
+ throw new Error('Remote endpoint not configured');
38
+ }
39
+
40
+ if (!config.remoteApiKey) {
41
+ throw new Error('Remote API key not configured');
42
+ }
43
+
44
+ const request: GatewayRequest = {
45
+ systemPrompt,
46
+ toolName,
47
+ };
48
+
49
+ if (typeof userPrompt === 'string') {
50
+ request.userPrompt = userPrompt;
51
+ } else {
52
+ request.userParts = userPrompt as any;
53
+ }
54
+
55
+ const response = await fetch(`${config.remoteEndpoint}/generate`, {
56
+ method: 'POST',
57
+ headers: {
58
+ 'Content-Type': 'application/json',
59
+ Authorization: `Bearer ${config.remoteApiKey}`,
60
+ },
61
+ body: JSON.stringify(request),
62
+ });
63
+
64
+ if (!response.ok) {
65
+ const errorText = await response.text();
66
+ throw new Error(`Gateway error (${response.status}): ${errorText}`);
67
+ }
68
+
69
+ const data = (await response.json()) as GatewayResponse;
70
+
71
+ if (data.error) {
72
+ throw new Error(`Gateway error: ${data.error}`);
73
+ }
74
+
75
+ if (config.debug) {
76
+ console.error('[remote] Tokens used:', data.tokensUsed);
77
+ if (typeof data.inputTokens === 'number') console.error('[remote] Input tokens:', data.inputTokens);
78
+ if (data.model) console.error('[remote] Model:', data.model);
79
+ }
80
+
81
+ return data.content;
82
+ }
83
+
84
+ /**
85
+ * Check remaining quota with the gateway
86
+ */
87
+ export async function checkQuota(config: Config): Promise<{ remaining: number; limit: number }> {
88
+ if (!config.remoteEndpoint || !config.remoteApiKey) {
89
+ throw new Error('Remote configuration missing');
90
+ }
91
+
92
+ const response = await fetch(`${config.remoteEndpoint}/quota`, {
93
+ headers: {
94
+ Authorization: `Bearer ${config.remoteApiKey}`,
95
+ },
96
+ });
97
+
98
+ if (!response.ok) {
99
+ throw new Error(`Failed to check quota: ${response.status}`);
100
+ }
101
+
102
+ return (await response.json()) as { remaining: number; limit: number };
103
+ }
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Gemini Designer MCP - Entry Point
4
+ *
5
+ * Open-source MCP server for design-to-code generation.
6
+ * Supports both local (BYO API key) and remote gateway modes.
7
+ */
8
+
9
+ import { createServer } from './server.js';
10
+ import { loadConfig } from './config/index.js';
11
+
12
+ async function main() {
13
+ const config = loadConfig();
14
+
15
+ console.error(`[gemini-designer-mcp] Starting in ${config.mode} mode...`);
16
+
17
+ const server = await createServer(config);
18
+
19
+ // Handle graceful shutdown
20
+ process.on('SIGINT', async () => {
21
+ console.error('[gemini-designer-mcp] Shutting down...');
22
+ await server.close();
23
+ process.exit(0);
24
+ });
25
+
26
+ process.on('SIGTERM', async () => {
27
+ console.error('[gemini-designer-mcp] Shutting down...');
28
+ await server.close();
29
+ process.exit(0);
30
+ });
31
+ }
32
+
33
+ main().catch((error) => {
34
+ console.error('[gemini-designer-mcp] Fatal error:', error);
35
+ process.exit(1);
36
+ });
@@ -0,0 +1,181 @@
1
+ /**
2
+ * File Writer
3
+ *
4
+ * Writes generated code to files with safety checks.
5
+ * Supports backup creation and git-aware operations.
6
+ */
7
+
8
+ import * as fs from 'node:fs';
9
+ import * as path from 'node:path';
10
+ import { execSync } from 'node:child_process';
11
+ import { formatCode } from './formatter.js';
12
+
13
+ export interface WriteOptions {
14
+ backup?: boolean;
15
+ format?: boolean;
16
+ createDirs?: boolean;
17
+ gitAdd?: boolean;
18
+ }
19
+
20
+ export interface WriteResult {
21
+ success: boolean;
22
+ filePath: string;
23
+ backupPath?: string;
24
+ error?: string;
25
+ }
26
+
27
+ /**
28
+ * Write content to file with safety features
29
+ */
30
+ export async function writeFile(
31
+ filePath: string,
32
+ content: string,
33
+ options: WriteOptions = {}
34
+ ): Promise<WriteResult> {
35
+ const { backup = true, format = true, createDirs = true, gitAdd = false } = options;
36
+
37
+ const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
38
+
39
+ try {
40
+ // Create parent directories if needed
41
+ if (createDirs) {
42
+ const dir = path.dirname(absPath);
43
+ if (!fs.existsSync(dir)) {
44
+ fs.mkdirSync(dir, { recursive: true });
45
+ }
46
+ }
47
+
48
+ // Create backup if file exists
49
+ let backupPath: string | undefined;
50
+ if (backup && fs.existsSync(absPath)) {
51
+ backupPath = `${absPath}.bak`;
52
+ fs.copyFileSync(absPath, backupPath);
53
+ }
54
+
55
+ // Format content if requested
56
+ let finalContent = content;
57
+ if (format) {
58
+ try {
59
+ finalContent = await formatCode(content, { filePath: absPath });
60
+ } catch {
61
+ // Keep original if formatting fails
62
+ }
63
+ }
64
+
65
+ // Write the file
66
+ fs.writeFileSync(absPath, finalContent, 'utf-8');
67
+
68
+ // Git add if requested and in a git repo
69
+ if (gitAdd && isGitRepo(absPath)) {
70
+ try {
71
+ execSync(`git add "${absPath}"`, { cwd: path.dirname(absPath), stdio: 'ignore' });
72
+ } catch {
73
+ // Ignore git errors
74
+ }
75
+ }
76
+
77
+ return {
78
+ success: true,
79
+ filePath: absPath,
80
+ backupPath,
81
+ };
82
+ } catch (error) {
83
+ return {
84
+ success: false,
85
+ filePath: absPath,
86
+ error: error instanceof Error ? error.message : 'Unknown error',
87
+ };
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Restore file from backup
93
+ */
94
+ export function restoreFromBackup(filePath: string): boolean {
95
+ const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
96
+ const backupPath = `${absPath}.bak`;
97
+
98
+ if (!fs.existsSync(backupPath)) {
99
+ return false;
100
+ }
101
+
102
+ try {
103
+ fs.copyFileSync(backupPath, absPath);
104
+ fs.unlinkSync(backupPath);
105
+ return true;
106
+ } catch {
107
+ return false;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Check if path is in a git repository
113
+ */
114
+ function isGitRepo(filePath: string): boolean {
115
+ try {
116
+ execSync('git rev-parse --git-dir', {
117
+ cwd: path.dirname(filePath),
118
+ stdio: 'ignore',
119
+ });
120
+ return true;
121
+ } catch {
122
+ return false;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Get git status for a file
128
+ */
129
+ export function getGitStatus(filePath: string): 'modified' | 'untracked' | 'clean' | 'not-git' {
130
+ const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
131
+
132
+ if (!isGitRepo(absPath)) {
133
+ return 'not-git';
134
+ }
135
+
136
+ try {
137
+ const status = execSync(`git status --porcelain "${absPath}"`, {
138
+ cwd: path.dirname(absPath),
139
+ encoding: 'utf-8',
140
+ }).trim();
141
+
142
+ if (!status) return 'clean';
143
+ if (status.startsWith('??')) return 'untracked';
144
+ return 'modified';
145
+ } catch {
146
+ return 'not-git';
147
+ }
148
+ }
149
+
150
+ /**
151
+ * List all backup files in a directory
152
+ */
153
+ export function listBackups(directory: string): string[] {
154
+ try {
155
+ const entries = fs.readdirSync(directory, { withFileTypes: true });
156
+ return entries
157
+ .filter((e) => e.isFile() && e.name.endsWith('.bak'))
158
+ .map((e) => path.join(directory, e.name));
159
+ } catch {
160
+ return [];
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Clean up old backup files
166
+ */
167
+ export function cleanBackups(directory: string): number {
168
+ const backups = listBackups(directory);
169
+ let cleaned = 0;
170
+
171
+ for (const backup of backups) {
172
+ try {
173
+ fs.unlinkSync(backup);
174
+ cleaned++;
175
+ } catch {
176
+ // Ignore errors
177
+ }
178
+ }
179
+
180
+ return cleaned;
181
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Output Formatter
3
+ *
4
+ * Uses Prettier to format generated code for consistency.
5
+ * Auto-detects language from content or file extension.
6
+ */
7
+
8
+ import * as prettier from 'prettier';
9
+
10
+ export type SupportedLanguage = 'typescript' | 'javascript' | 'css' | 'html' | 'json' | 'markdown';
11
+
12
+ interface FormatOptions {
13
+ language?: SupportedLanguage;
14
+ filePath?: string;
15
+ printWidth?: number;
16
+ tabWidth?: number;
17
+ useTabs?: boolean;
18
+ semi?: boolean;
19
+ singleQuote?: boolean;
20
+ }
21
+
22
+ /**
23
+ * Format code using Prettier
24
+ */
25
+ export async function formatCode(code: string, options: FormatOptions = {}): Promise<string> {
26
+ const language = options.language || detectLanguage(code, options.filePath);
27
+ const parser = getParser(language);
28
+
29
+ try {
30
+ const formatted = await prettier.format(code, {
31
+ parser,
32
+ printWidth: options.printWidth ?? 100,
33
+ tabWidth: options.tabWidth ?? 2,
34
+ useTabs: options.useTabs ?? false,
35
+ semi: options.semi ?? true,
36
+ singleQuote: options.singleQuote ?? true,
37
+ trailingComma: 'es5',
38
+ bracketSpacing: true,
39
+ arrowParens: 'always',
40
+ });
41
+
42
+ return formatted;
43
+ } catch (error) {
44
+ // If Prettier fails, return original code
45
+ console.error('[formatter] Prettier error, returning unformatted:', error);
46
+ return code;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Extract code blocks from LLM response and format them
52
+ */
53
+ export async function formatLLMResponse(response: string): Promise<string> {
54
+ // Match code blocks with language identifier
55
+ const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
56
+ let result = response;
57
+ let match;
58
+
59
+ const replacements: Array<{ original: string; formatted: string }> = [];
60
+
61
+ while ((match = codeBlockRegex.exec(response)) !== null) {
62
+ const [fullMatch, lang, code] = match;
63
+ const language = mapLanguageIdentifier(lang);
64
+
65
+ if (language) {
66
+ try {
67
+ const formatted = await formatCode(code.trim(), { language });
68
+ replacements.push({
69
+ original: fullMatch,
70
+ formatted: `\`\`\`${lang}\n${formatted}\`\`\``,
71
+ });
72
+ } catch {
73
+ // Keep original if formatting fails
74
+ }
75
+ }
76
+ }
77
+
78
+ // Apply replacements
79
+ for (const { original, formatted } of replacements) {
80
+ result = result.replace(original, formatted);
81
+ }
82
+
83
+ return result;
84
+ }
85
+
86
+ /**
87
+ * Detect language from code content or file path
88
+ */
89
+ function detectLanguage(code: string, filePath?: string): SupportedLanguage {
90
+ if (filePath) {
91
+ const ext = filePath.split('.').pop()?.toLowerCase();
92
+ switch (ext) {
93
+ case 'ts':
94
+ case 'tsx':
95
+ return 'typescript';
96
+ case 'js':
97
+ case 'jsx':
98
+ return 'javascript';
99
+ case 'css':
100
+ case 'scss':
101
+ case 'less':
102
+ return 'css';
103
+ case 'html':
104
+ return 'html';
105
+ case 'json':
106
+ return 'json';
107
+ case 'md':
108
+ return 'markdown';
109
+ }
110
+ }
111
+
112
+ // Detect from content
113
+ if (code.includes('import React') || code.includes('from "react"') || code.includes("from 'react'")) {
114
+ return code.includes(': React.') || code.includes('<') ? 'typescript' : 'javascript';
115
+ }
116
+
117
+ if (code.includes('interface ') || code.includes(': string') || code.includes(': number')) {
118
+ return 'typescript';
119
+ }
120
+
121
+ if (code.includes('function ') || code.includes('const ') || code.includes('let ')) {
122
+ return 'javascript';
123
+ }
124
+
125
+ if (code.includes('{') && (code.includes('color:') || code.includes('margin:') || code.includes('display:'))) {
126
+ return 'css';
127
+ }
128
+
129
+ if (code.startsWith('{') || code.startsWith('[')) {
130
+ try {
131
+ JSON.parse(code);
132
+ return 'json';
133
+ } catch {
134
+ // Not JSON
135
+ }
136
+ }
137
+
138
+ if (code.includes('<html') || code.includes('<!DOCTYPE')) {
139
+ return 'html';
140
+ }
141
+
142
+ // Default to TypeScript for better JSX support
143
+ return 'typescript';
144
+ }
145
+
146
+ /**
147
+ * Get Prettier parser for language
148
+ */
149
+ function getParser(language: SupportedLanguage): string {
150
+ const parsers: Record<SupportedLanguage, string> = {
151
+ typescript: 'typescript',
152
+ javascript: 'babel',
153
+ css: 'css',
154
+ html: 'html',
155
+ json: 'json',
156
+ markdown: 'markdown',
157
+ };
158
+
159
+ return parsers[language];
160
+ }
161
+
162
+ /**
163
+ * Map common language identifiers to supported types
164
+ */
165
+ function mapLanguageIdentifier(identifier?: string): SupportedLanguage | null {
166
+ if (!identifier) return null;
167
+
168
+ const lower = identifier.toLowerCase();
169
+ const mapping: Record<string, SupportedLanguage> = {
170
+ ts: 'typescript',
171
+ tsx: 'typescript',
172
+ typescript: 'typescript',
173
+ js: 'javascript',
174
+ jsx: 'javascript',
175
+ javascript: 'javascript',
176
+ css: 'css',
177
+ scss: 'css',
178
+ less: 'css',
179
+ html: 'html',
180
+ json: 'json',
181
+ md: 'markdown',
182
+ markdown: 'markdown',
183
+ };
184
+
185
+ return mapping[lower] || null;
186
+ }
package/src/server.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * MCP Server Factory
3
+ *
4
+ * Creates and configures the MCP server with all tools registered.
5
+ */
6
+
7
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
9
+ import { Config } from './config/index.js';
10
+ import { registerTools } from './tools/index.js';
11
+
12
+ export async function createServer(config: Config) {
13
+ const server = new McpServer({
14
+ name: 'gemini-designer-mcp',
15
+ version: '0.1.0',
16
+ });
17
+
18
+ // Register all MCP tools
19
+ registerTools(server, config);
20
+
21
+ // Use stdio transport (standard for MCP)
22
+ const transport = new StdioServerTransport();
23
+ await server.connect(transport);
24
+
25
+ console.error('[gemini-designer-mcp] Server connected via stdio');
26
+
27
+ return server;
28
+ }