gemini-design-mcp 3.6.13 → 3.7.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/README.md CHANGED
@@ -1,100 +1,27 @@
1
1
  # gemini-design-mcp
2
2
 
3
- MCP server that uses **Google Gemini 3 Pro** for frontend/design code generation.
3
+ MCP server that uses **Google Gemini 3** for frontend/design code generation.
4
4
 
5
- ## Why?
5
+ Combine your favorite AI (Claude, GPT, etc.) with Gemini's design capabilities while keeping full conversation context.
6
6
 
7
- Gemini excels at UI/UX design and frontend code generation. This MCP lets any AI agent (Claude Code, Codex, etc.) delegate frontend tasks to Gemini.
7
+ ## Get Started
8
8
 
9
- **Key principle:** The calling agent manages context. No config files needed.
9
+ Visit **[gemini-design-mcp.com](https://gemini-design-mcp.com)** for:
10
10
 
11
- ## Installation
12
-
13
- Add to your Claude Desktop config (`~/.claude/claude_desktop_config.json`):
14
-
15
- ```json
16
- {
17
- "mcpServers": {
18
- "gemini-design": {
19
- "command": "npx",
20
- "args": ["-y", "gemini-design-mcp"],
21
- "env": {
22
- "GEMINI_API_KEY": "your-api-key-here"
23
- }
24
- }
25
- }
26
- }
27
- ```
28
-
29
- Get your Gemini API key at: https://aistudio.google.com/app/apikey
11
+ - Documentation & setup guide
12
+ - Create your free account
13
+ - Get your API key
14
+ - Pricing plans
30
15
 
31
16
  ## Tools
32
17
 
33
- ### generate_frontend
34
-
35
- Main tool for creating frontend code.
36
-
37
- **Parameters:**
38
- - `request` (required): What to create (e.g., "A hero section with gradient background")
39
- - `context` (nullable): Existing code for style reference. Pass `null` for new projects.
40
- - `techStack` (optional): Tech stack (e.g., "React + Tailwind", "Vue + CSS", "Next.js + shadcn")
41
- - `preferences` (optional): Style preferences (e.g., "dark mode", "glassmorphism", "minimal")
42
-
43
- ### improve_frontend
44
-
45
- Improve existing frontend code.
46
-
47
- **Parameters:**
48
- - `code` (required): Current code to improve
49
- - `feedback` (required): What to improve (e.g., "Make it more modern", "Add hover effects")
50
- - `context` (optional): Additional context for consistency
51
-
52
- ### suggest_design
53
-
54
- Get design suggestions without generating code. Saves tokens.
55
-
56
- **Parameters:**
57
- - `description` (required): What you're designing
58
- - `currentApproach` (optional): Current approach or constraints
59
-
60
- ## How Context Works
61
-
62
- The calling agent (Claude, Codex) is responsible for passing relevant context:
63
-
64
- - **New project**: Pass `context: null`, Gemini creates from scratch
65
- - **Existing project**: Agent reads relevant components and passes them as context
66
-
67
- This ensures design consistency without requiring any user configuration.
68
-
69
- ## Example Usage
70
-
71
- ```
72
- Agent: "Create a pricing card component"
73
-
74
- → Calls generate_frontend({
75
- request: "A pricing card with 3 tiers, toggle for monthly/yearly",
76
- context: "<existing Button and Card components>",
77
- techStack: "React + Tailwind",
78
- preferences: "glassmorphism, dark mode"
79
- })
80
-
81
- ← Returns complete, production-ready component code
82
- ```
83
-
84
- ## Development
85
-
86
- ```bash
87
- # Clone and install
88
- git clone <repo>
89
- cd gemini-design-mcp
90
- npm install
18
+ - **create_frontend** - Generate complete responsive views
19
+ - **modify_frontend** - Make surgical edits to existing components
20
+ - **snippet_frontend** - Create standalone UI components
91
21
 
92
- # Build
93
- npm run build
22
+ ## Compatibility
94
23
 
95
- # Test locally
96
- GEMINI_API_KEY=your-key node build/index.js
97
- ```
24
+ Works with Claude Code, Cursor, VS Code, Windsurf, Zed, JetBrains AI, Neovim, and more.
98
25
 
99
26
  ## License
100
27
 
package/build/index.js CHANGED
@@ -59,7 +59,7 @@ Present options to user, they select one, then pass it here via designSystem.vib
59
59
  📤 OUTPUT
60
60
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
61
61
 
62
- Returns a confirmation message after writing to disk (lightweight context).`, createFrontendSchema, createFrontend);
62
+ Returns the complete code. YOU (the agent) are responsible for writing it to disk.`, createFrontendSchema, createFrontend);
63
63
  // =============================================================================
64
64
  // TOOL 2: MODIFY_FRONTEND
65
65
  // =============================================================================
@@ -112,7 +112,7 @@ import { X } from "y";
112
112
  // REPLACE WITH:
113
113
  <new redesigned code>
114
114
 
115
- The modification is applied directly to disk by default.`, modifyFrontendSchema, modifyFrontend);
115
+ YOU (the agent) are responsible for applying this find/replace to the file.`, modifyFrontendSchema, modifyFrontend);
116
116
  // =============================================================================
117
117
  // TOOL 3: SNIPPET_FRONTEND
118
118
  // =============================================================================
@@ -234,11 +234,9 @@ import { Search } from "lucide-react";
234
234
  )}
235
235
  </div>
236
236
 
237
- You (Claude) are responsible for:
237
+ YOU (the agent) are responsible for:
238
238
  - Adding the logic (useState, handlers) BEFORE calling this tool
239
- - Providing insertAtLine or insertAfterPattern for direct insertion
240
-
241
- The snippet is inserted directly to disk by default (requires insertAtLine OR insertAfterPattern).`, snippetFrontendSchema, snippetFrontend);
239
+ - Inserting the returned snippet into the correct location in the file`, snippetFrontendSchema, snippetFrontend);
242
240
  // =============================================================================
243
241
  // TOOL 4: GENERATE_VIBES
244
242
  // =============================================================================
@@ -7,11 +7,11 @@ function getApiKey() {
7
7
  const apiKey = process.env.API_KEY;
8
8
  if (!apiKey) {
9
9
  throw new Error("Missing API_KEY environment variable. " +
10
- "Get your API key at https://gemini-design.com/dashboard/api-keys");
10
+ "Get your API key at https://gemini-design-mcp.com/dashboard/api-keys");
11
11
  }
12
12
  if (!apiKey.startsWith("gd_")) {
13
13
  throw new Error("Invalid API key format. API keys must start with 'gd_'. " +
14
- "Get your API key at https://gemini-design.com/dashboard/api-keys");
14
+ "Get your API key at https://gemini-design-mcp.com/dashboard/api-keys");
15
15
  }
16
16
  return apiKey;
17
17
  }
@@ -45,7 +45,17 @@ export async function generateWithGemini(systemPrompt, userPrompt, model = DEFAU
45
45
  });
46
46
  const result = await response.json();
47
47
  if (!response.ok) {
48
- throw new Error(result.error || `API error: ${response.status}`);
48
+ const errorMsg = result.error || `API error: ${response.status}`;
49
+ // Check for credit/quota related errors
50
+ if (errorMsg.toLowerCase().includes('credit') ||
51
+ errorMsg.toLowerCase().includes('quota') ||
52
+ errorMsg.toLowerCase().includes('insufficient') ||
53
+ errorMsg.toLowerCase().includes('limit') ||
54
+ response.status === 402 ||
55
+ response.status === 429) {
56
+ throw new Error(`${errorMsg}\n\n💳 Top up your credits here: https://gemini-design-mcp.com/settings/billing`);
57
+ }
58
+ throw new Error(errorMsg);
49
59
  }
50
60
  // Extract text from Gemini response format
51
61
  const text = result.candidates?.[0]?.content?.parts?.[0]?.text;
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { generateWithGemini } from "../lib/gemini.js";
3
3
  import { CREATE_FRONTEND_PROMPT } from "../prompts/system.js";
4
- import { writeFileWithDirs, getFileSize, stripCodeFences } from "../lib/filesystem.js";
4
+ import { stripCodeFences } from "../lib/filesystem.js";
5
5
  import { scaleSchema, scaleDescriptions } from "../lib/scale.js";
6
6
  export const createFrontendSchema = {
7
7
  request: z.string().describe("What to create: describe the page, component, or section. " +
@@ -24,12 +24,9 @@ export const createFrontendSchema = {
24
24
  }).describe("The selected design vibe"),
25
25
  }).optional().describe("Design system with selected vibe. REQUIRED for new projects without existing design. " +
26
26
  "Call generate_vibes first, user selects, then pass the selection here."),
27
- writeFile: z.boolean().default(false).describe("Write the file directly to disk instead of returning the code. " +
28
- "Returns a confirmation message instead of the full code, keeping context lightweight. " +
29
- "Defaults to false."),
30
27
  };
31
28
  export async function createFrontend(params) {
32
- const { request, filePath, techStack, context, designSystem, writeFile } = params;
29
+ const { request, filePath, techStack, context, designSystem } = params;
33
30
  // Build design system instructions if provided
34
31
  let designSystemInstructions = '';
35
32
  if (designSystem?.vibe) {
@@ -74,18 +71,7 @@ Remember: Return a COMPLETE file ready to save at ${filePath}`.trim();
74
71
  const rawResult = await generateWithGemini(systemPrompt, request, undefined, "high", "create_frontend");
75
72
  // Strip markdown code fences from the result
76
73
  const result = stripCodeFences(rawResult);
77
- // Write file directly if requested
78
- if (writeFile) {
79
- writeFileWithDirs(filePath, result);
80
- const size = getFileSize(result);
81
- return {
82
- content: [{
83
- type: "text",
84
- text: `✅ Created: ${filePath} (${size})`,
85
- }],
86
- };
87
- }
88
- // Default: return the code
74
+ // Return the code for the agent to write
89
75
  return {
90
76
  content: [{ type: "text", text: result }],
91
77
  };
@@ -1,7 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { generateWithGemini } from "../lib/gemini.js";
3
3
  import { MODIFY_FRONTEND_PROMPT } from "../prompts/system.js";
4
- import { readFileIfExists, writeFileWithDirs, parseFindReplace, applyFindReplace, mergeImports, } from "../lib/filesystem.js";
5
4
  import { scaleSchema, getScaleInstructions } from "../lib/scale.js";
6
5
  export const modifyFrontendSchema = {
7
6
  modification: z.string().describe("The SINGLE design modification to make. Be specific. " +
@@ -20,13 +19,9 @@ export const modifyFrontendSchema = {
20
19
  "Example: 'Project uses: var(--font-heading), var(--bg-primary), .section-padding class'"),
21
20
  scale: scaleSchema.optional().describe("Element sizing: 'refined' (small, elegant), 'balanced' (standard), 'zoomed' (large). " +
22
21
  "Controls button sizes, typography, spacing, icons."),
23
- writeFile: z.boolean().default(true).describe("Apply the modification directly to the file on disk. " +
24
- "Reads the file, applies find/replace, and writes back. " +
25
- "Returns a confirmation instead of the find/replace instructions. " +
26
- "Defaults to true."),
27
22
  };
28
23
  export async function modifyFrontend(params) {
29
- const { modification, targetCode, filePath, context, scale, writeFile } = params;
24
+ const { modification, targetCode, filePath, context, scale } = params;
30
25
  // Build context instructions
31
26
  let contextInstructions = '';
32
27
  if (context) {
@@ -51,52 +46,7 @@ MODIFICATION REQUESTED: ${modification}
51
46
 
52
47
  Remember: Return ONLY the find/replace block. ONE modification, surgical precision.`.trim();
53
48
  const result = await generateWithGemini(systemPrompt, modification, undefined, "minimal", "modify_frontend");
54
- // Apply modification directly if requested
55
- if (writeFile) {
56
- const fileContent = readFileIfExists(filePath);
57
- if (!fileContent) {
58
- return {
59
- content: [{
60
- type: "text",
61
- text: `❌ Error: File not found: ${filePath}`,
62
- }],
63
- };
64
- }
65
- const parsed = parseFindReplace(result);
66
- if (!parsed) {
67
- // Fallback: return raw result if parsing fails
68
- return {
69
- content: [{
70
- type: "text",
71
- text: `⚠️ Could not parse Gemini output. Raw result:\n\n${result}`,
72
- }],
73
- };
74
- }
75
- // Apply the find/replace
76
- const { success, newContent, error } = applyFindReplace(fileContent, parsed.find, parsed.replace);
77
- if (!success) {
78
- return {
79
- content: [{
80
- type: "text",
81
- text: `❌ Error: ${error}\n\nExpected to find:\n${parsed.find.slice(0, 200)}...`,
82
- }],
83
- };
84
- }
85
- // Merge imports if any
86
- let finalContent = newContent;
87
- if (parsed.imports) {
88
- finalContent = mergeImports(newContent, parsed.imports);
89
- }
90
- // Write the file
91
- writeFileWithDirs(filePath, finalContent);
92
- return {
93
- content: [{
94
- type: "text",
95
- text: `✅ Modified: ${filePath}${parsed.imports ? `\n Added imports` : ""}`,
96
- }],
97
- };
98
- }
99
- // Default: return the find/replace instructions
49
+ // Return the find/replace instructions for the agent to apply
100
50
  return {
101
51
  content: [{ type: "text", text: result }],
102
52
  };
@@ -1,7 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { generateWithGemini } from "../lib/gemini.js";
3
3
  import { SNIPPET_FRONTEND_PROMPT } from "../prompts/system.js";
4
- import { readFileIfExists, writeFileWithDirs, parseSnippet, mergeImports, insertAfterLine, insertAfterPattern, } from "../lib/filesystem.js";
5
4
  import { scaleSchema, getScaleInstructions } from "../lib/scale.js";
6
5
  export const snippetFrontendSchema = {
7
6
  request: z.string().describe("What code snippet to generate. Be specific about what you need. " +
@@ -23,18 +22,9 @@ export const snippetFrontendSchema = {
23
22
  "Without this, Gemini will create standalone styles that won't match your design system."),
24
23
  scale: scaleSchema.optional().describe("Element sizing: 'refined' (small, elegant), 'balanced' (standard), 'zoomed' (large). " +
25
24
  "Controls button sizes, typography, spacing, icons."),
26
- writeFile: z.boolean().default(true).describe("Insert the snippet directly into the file on disk. " +
27
- "Requires insertAtLine OR insertAfterPattern to know where to insert. " +
28
- "Defaults to true."),
29
- insertAtLine: z.number().optional().describe("Line number (1-indexed) where to insert the snippet. " +
30
- "The snippet will be inserted AFTER this line. " +
31
- "Required if writeFile is true and insertAfterPattern is not provided."),
32
- insertAfterPattern: z.string().optional().describe("A string pattern to find in the file. The snippet will be inserted after this pattern. " +
33
- "Example: 'return (' or '<main>' or '</header>'. " +
34
- "Required if writeFile is true and insertAtLine is not provided."),
35
25
  };
36
26
  export async function snippetFrontend(params) {
37
- const { request, targetFile, techStack, insertionContext, context, scale, writeFile, insertAtLine: lineNumber, insertAfterPattern: pattern, } = params;
27
+ const { request, targetFile, techStack, insertionContext, context, scale } = params;
38
28
  // Build context instructions
39
29
  let contextInstructions = '';
40
30
  if (context) {
@@ -56,69 +46,7 @@ ${insertionContext}
56
46
 
57
47
  Generate a snippet that will integrate smoothly at this location.`.trim();
58
48
  const result = await generateWithGemini(systemPrompt, request, undefined, "minimal", "snippet_frontend");
59
- // Insert snippet directly if requested
60
- if (writeFile) {
61
- if (!lineNumber && !pattern) {
62
- return {
63
- content: [{
64
- type: "text",
65
- text: `❌ Error: writeFile requires either insertAtLine or insertAfterPattern`,
66
- }],
67
- };
68
- }
69
- const fileContent = readFileIfExists(targetFile);
70
- if (!fileContent) {
71
- return {
72
- content: [{
73
- type: "text",
74
- text: `❌ Error: File not found: ${targetFile}`,
75
- }],
76
- };
77
- }
78
- const parsed = parseSnippet(result);
79
- if (!parsed) {
80
- return {
81
- content: [{
82
- type: "text",
83
- text: `⚠️ Could not parse snippet. Raw result:\n\n${result}`,
84
- }],
85
- };
86
- }
87
- let newContent;
88
- // Insert using line number or pattern
89
- if (lineNumber) {
90
- newContent = insertAfterLine(fileContent, parsed.snippet, lineNumber);
91
- }
92
- else if (pattern) {
93
- const insertResult = insertAfterPattern(fileContent, parsed.snippet, pattern);
94
- if (!insertResult.success) {
95
- return {
96
- content: [{
97
- type: "text",
98
- text: `❌ Error: ${insertResult.error}`,
99
- }],
100
- };
101
- }
102
- newContent = insertResult.newContent;
103
- }
104
- else {
105
- // Should never reach here due to earlier check
106
- newContent = fileContent;
107
- }
108
- // Merge imports if any
109
- if (parsed.imports) {
110
- newContent = mergeImports(newContent, parsed.imports);
111
- }
112
- // Write the file
113
- writeFileWithDirs(targetFile, newContent);
114
- return {
115
- content: [{
116
- type: "text",
117
- text: `✅ Inserted snippet in: ${targetFile}${parsed.imports ? `\n Added imports` : ""}${lineNumber ? `\n After line ${lineNumber}` : ""}${pattern ? `\n After pattern: ${pattern}` : ""}`,
118
- }],
119
- };
120
- }
121
- // Default: return the snippet
49
+ // Return the snippet for the agent to insert
122
50
  return {
123
51
  content: [{ type: "text", text: result }],
124
52
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gemini-design-mcp",
3
- "version": "3.6.13",
3
+ "version": "3.7.0",
4
4
  "description": "MCP server that uses Gemini 3 Pro for frontend/design code generation",
5
5
  "main": "build/index.js",
6
6
  "bin": {
@@ -11,7 +11,7 @@
11
11
  "build": "tsc",
12
12
  "dev": "tsc --watch",
13
13
  "start": "node build/index.js",
14
- "prepublishOnly": "npm run build"
14
+ "prepublishOnly": "echo 'Publishing...'"
15
15
  },
16
16
  "keywords": [
17
17
  "mcp",