gemini-design-mcp 3.3.0 → 3.4.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/build/index.js +41 -7
- package/build/lib/gemini.d.ts +2 -1
- package/build/lib/gemini.js +8 -8
- package/build/tools/create-frontend.js +1 -1
- package/build/tools/generate-vibes.d.ts +23 -0
- package/build/tools/generate-vibes.js +85 -0
- package/build/tools/modify-frontend.js +1 -1
- package/build/tools/snippet-frontend.js +1 -1
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { createFrontendSchema, createFrontend } from "./tools/create-frontend.js";
|
|
5
5
|
import { modifyFrontendSchema, modifyFrontend } from "./tools/modify-frontend.js";
|
|
6
6
|
import { snippetFrontendSchema, snippetFrontend } from "./tools/snippet-frontend.js";
|
|
7
|
+
import { generateVibesSchema, generateVibes } from "./tools/generate-vibes.js";
|
|
7
8
|
// Create MCP server
|
|
8
9
|
const server = new McpServer({
|
|
9
10
|
name: "gemini-design-mcp",
|
|
@@ -50,13 +51,8 @@ C) PROJECT EXISTS with existing frontend code:
|
|
|
50
51
|
|
|
51
52
|
STEP 2: VIBE SELECTION (Required for new designs)
|
|
52
53
|
──────────────────────────────────────────────────
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
🏛️ "Pristine Museum" - Ultra-clean, white-cube aesthetic...
|
|
56
|
-
⚡ "Technical Precision" - Grid-focused, architectural...
|
|
57
|
-
🌊 "Fluid & Organic" - Soft curves, flowing gradients...
|
|
58
|
-
🔥 "Bold & Unapologetic" - High contrast, oversized typography...
|
|
59
|
-
🌙 "Dark Luxe" - Deep darks, metallic accents...
|
|
54
|
+
Call generate_vibes tool FIRST to get 5 creative vibes from Gemini.
|
|
55
|
+
Present options to user, they select one, then pass it here via designSystem.vibe.
|
|
60
56
|
|
|
61
57
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
62
58
|
📤 OUTPUT
|
|
@@ -216,6 +212,44 @@ You (Claude) are responsible for:
|
|
|
216
212
|
- Merging new imports
|
|
217
213
|
- Inserting the snippet at the correct location`, snippetFrontendSchema, snippetFrontend);
|
|
218
214
|
// =============================================================================
|
|
215
|
+
// TOOL 4: GENERATE_VIBES
|
|
216
|
+
// =============================================================================
|
|
217
|
+
server.tool("generate_vibes", `Generate 5 unique design vibes for a new project using Gemini AI.
|
|
218
|
+
|
|
219
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
220
|
+
🎯 WHEN TO USE THIS TOOL
|
|
221
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
222
|
+
|
|
223
|
+
Use this tool BEFORE calling create_frontend on a NEW project that has no existing design.
|
|
224
|
+
|
|
225
|
+
This tool generates creative, unique design vibes tailored to the project context.
|
|
226
|
+
The user then selects one, and you pass it to create_frontend.
|
|
227
|
+
|
|
228
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
229
|
+
📋 FLOW
|
|
230
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
231
|
+
|
|
232
|
+
1. User asks to create frontend for a NEW project
|
|
233
|
+
2. Call generate_vibes with project description
|
|
234
|
+
3. Present the 5 vibes to the user
|
|
235
|
+
4. User selects their preferred vibe
|
|
236
|
+
5. Call create_frontend with the selected vibe in designSystem.vibe
|
|
237
|
+
|
|
238
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
239
|
+
📤 OUTPUT
|
|
240
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
241
|
+
|
|
242
|
+
Returns 5 vibes, each with:
|
|
243
|
+
- emoji: Visual identifier
|
|
244
|
+
- name: Memorable vibe name
|
|
245
|
+
- description: 2-3 evocative sentences
|
|
246
|
+
- keywords: Style keywords for the designer
|
|
247
|
+
|
|
248
|
+
Example vibe:
|
|
249
|
+
🏛️ "Pristine Museum"
|
|
250
|
+
An ultra-clean, 'white-cube' aesthetic focused on vast negative space.
|
|
251
|
+
Keywords: minimal, whitespace, gallery, clean, sophisticated`, generateVibesSchema, generateVibes);
|
|
252
|
+
// =============================================================================
|
|
219
253
|
// START SERVER
|
|
220
254
|
// =============================================================================
|
|
221
255
|
async function main() {
|
package/build/lib/gemini.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GoogleGenAI } from "@google/genai";
|
|
2
|
+
export type ThinkingMode = "minimal" | "low" | "medium" | "high";
|
|
2
3
|
export declare const ai: GoogleGenAI | null;
|
|
3
4
|
export declare const DEFAULT_MODEL = "gemini-3-flash-preview";
|
|
4
|
-
export declare function generateWithGemini(systemPrompt: string, userPrompt: string, model?: string): Promise<string>;
|
|
5
|
+
export declare function generateWithGemini(systemPrompt: string, userPrompt: string, model?: string, thinkingMode?: ThinkingMode): Promise<string>;
|
package/build/lib/gemini.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GoogleGenAI
|
|
1
|
+
import { GoogleGenAI } from "@google/genai";
|
|
2
2
|
const apiKey = process.env.API_KEY;
|
|
3
3
|
if (!apiKey) {
|
|
4
4
|
console.error("ERROR: API_KEY environment variable is required");
|
|
@@ -16,7 +16,7 @@ export const DEFAULT_MODEL = "gemini-3-flash-preview";
|
|
|
16
16
|
/**
|
|
17
17
|
* Generate content via the hosted proxy service
|
|
18
18
|
*/
|
|
19
|
-
async function generateViaProxy(systemPrompt, userPrompt, model) {
|
|
19
|
+
async function generateViaProxy(systemPrompt, userPrompt, model, thinkingMode = "minimal") {
|
|
20
20
|
const response = await fetch(PROXY_URL, {
|
|
21
21
|
method: "POST",
|
|
22
22
|
headers: {
|
|
@@ -37,7 +37,7 @@ async function generateViaProxy(systemPrompt, userPrompt, model) {
|
|
|
37
37
|
generationConfig: {
|
|
38
38
|
temperature: 1,
|
|
39
39
|
thinkingConfig: {
|
|
40
|
-
|
|
40
|
+
thinkingLevel: thinkingMode,
|
|
41
41
|
},
|
|
42
42
|
},
|
|
43
43
|
}),
|
|
@@ -57,7 +57,7 @@ async function generateViaProxy(systemPrompt, userPrompt, model) {
|
|
|
57
57
|
/**
|
|
58
58
|
* Generate content directly via Google SDK
|
|
59
59
|
*/
|
|
60
|
-
async function generateViaSdk(systemPrompt, userPrompt, model) {
|
|
60
|
+
async function generateViaSdk(systemPrompt, userPrompt, model, thinkingMode = "minimal") {
|
|
61
61
|
if (!ai) {
|
|
62
62
|
throw new Error("SDK not initialized");
|
|
63
63
|
}
|
|
@@ -68,19 +68,19 @@ async function generateViaSdk(systemPrompt, userPrompt, model) {
|
|
|
68
68
|
systemInstruction: systemPrompt,
|
|
69
69
|
temperature: 1,
|
|
70
70
|
thinkingConfig: {
|
|
71
|
-
thinkingLevel:
|
|
71
|
+
thinkingLevel: thinkingMode,
|
|
72
72
|
},
|
|
73
73
|
},
|
|
74
74
|
});
|
|
75
75
|
return response.text ?? "";
|
|
76
76
|
}
|
|
77
|
-
export async function generateWithGemini(systemPrompt, userPrompt, model = DEFAULT_MODEL) {
|
|
77
|
+
export async function generateWithGemini(systemPrompt, userPrompt, model = DEFAULT_MODEL, thinkingMode = "minimal") {
|
|
78
78
|
try {
|
|
79
79
|
if (isHostedKey) {
|
|
80
|
-
return await generateViaProxy(systemPrompt, userPrompt, model);
|
|
80
|
+
return await generateViaProxy(systemPrompt, userPrompt, model, thinkingMode);
|
|
81
81
|
}
|
|
82
82
|
else {
|
|
83
|
-
return await generateViaSdk(systemPrompt, userPrompt, model);
|
|
83
|
+
return await generateViaSdk(systemPrompt, userPrompt, model, thinkingMode);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
catch (error) {
|
|
@@ -64,7 +64,7 @@ TECH STACK: ${techStack}
|
|
|
64
64
|
FILE PATH: ${filePath}
|
|
65
65
|
|
|
66
66
|
Remember: Return a COMPLETE file ready to save at ${filePath}`.trim();
|
|
67
|
-
const result = await generateWithGemini(systemPrompt, request);
|
|
67
|
+
const result = await generateWithGemini(systemPrompt, request, undefined, "high");
|
|
68
68
|
return {
|
|
69
69
|
content: [{ type: "text", text: result }],
|
|
70
70
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const generateVibesSchema: {
|
|
3
|
+
projectDescription: z.ZodString;
|
|
4
|
+
projectType: z.ZodOptional<z.ZodString>;
|
|
5
|
+
targetAudience: z.ZodOptional<z.ZodString>;
|
|
6
|
+
};
|
|
7
|
+
export declare function generateVibes(params: {
|
|
8
|
+
projectDescription: string;
|
|
9
|
+
projectType?: string;
|
|
10
|
+
targetAudience?: string;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
content: {
|
|
13
|
+
type: "text";
|
|
14
|
+
text: string;
|
|
15
|
+
}[];
|
|
16
|
+
vibes: any;
|
|
17
|
+
} | {
|
|
18
|
+
content: {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
vibes?: undefined;
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { generateWithGemini } from "../lib/gemini.js";
|
|
3
|
+
export const generateVibesSchema = {
|
|
4
|
+
projectDescription: z.string().describe("Brief description of the project. " +
|
|
5
|
+
"Example: 'A SaaS dashboard for managing customer support tickets'"),
|
|
6
|
+
projectType: z.string().optional().describe("Type of project. " +
|
|
7
|
+
"Examples: 'landing page', 'dashboard', 'e-commerce', 'portfolio', 'blog', 'mobile app'"),
|
|
8
|
+
targetAudience: z.string().optional().describe("Who will use this app. " +
|
|
9
|
+
"Examples: 'developers', 'enterprise clients', 'young consumers', 'creative professionals'"),
|
|
10
|
+
};
|
|
11
|
+
const GENERATE_VIBES_PROMPT = `Generate 5 distinct mood/atmosphere options (VIBES) that match different interpretations of the project.
|
|
12
|
+
|
|
13
|
+
Each vibe should represent a unique visual direction and feeling.
|
|
14
|
+
Describe the mood, not specific colors - let the designer interpret creatively.
|
|
15
|
+
Think: "Corporate & Trustworthy" vs "Playful & Vibrant" vs "Dark & Techy"
|
|
16
|
+
|
|
17
|
+
OUTPUT FORMAT (JSON):
|
|
18
|
+
Return ONLY a valid JSON array with exactly 5 vibes. No markdown, no explanation.
|
|
19
|
+
|
|
20
|
+
[
|
|
21
|
+
{
|
|
22
|
+
"emoji": "🏛️",
|
|
23
|
+
"name": "Vibe Name",
|
|
24
|
+
"description": "2-3 sentences describing the mood and atmosphere.",
|
|
25
|
+
"keywords": ["keyword1", "keyword2", "keyword3"]
|
|
26
|
+
}
|
|
27
|
+
]`;
|
|
28
|
+
export async function generateVibes(params) {
|
|
29
|
+
const { projectDescription, projectType, targetAudience } = params;
|
|
30
|
+
// Build context for Gemini
|
|
31
|
+
let contextParts = [`Project: ${projectDescription}`];
|
|
32
|
+
if (projectType) {
|
|
33
|
+
contextParts.push(`Type: ${projectType}`);
|
|
34
|
+
}
|
|
35
|
+
if (targetAudience) {
|
|
36
|
+
contextParts.push(`Target Audience: ${targetAudience}`);
|
|
37
|
+
}
|
|
38
|
+
const userPrompt = contextParts.join("\n");
|
|
39
|
+
const result = await generateWithGemini(GENERATE_VIBES_PROMPT, userPrompt, undefined, // default model
|
|
40
|
+
"high" // high thinking for creative vibes
|
|
41
|
+
);
|
|
42
|
+
// Parse the JSON response
|
|
43
|
+
try {
|
|
44
|
+
// Clean the response (remove potential markdown code blocks)
|
|
45
|
+
let cleanedResult = result.trim();
|
|
46
|
+
if (cleanedResult.startsWith("```json")) {
|
|
47
|
+
cleanedResult = cleanedResult.slice(7);
|
|
48
|
+
}
|
|
49
|
+
if (cleanedResult.startsWith("```")) {
|
|
50
|
+
cleanedResult = cleanedResult.slice(3);
|
|
51
|
+
}
|
|
52
|
+
if (cleanedResult.endsWith("```")) {
|
|
53
|
+
cleanedResult = cleanedResult.slice(0, -3);
|
|
54
|
+
}
|
|
55
|
+
cleanedResult = cleanedResult.trim();
|
|
56
|
+
const vibes = JSON.parse(cleanedResult);
|
|
57
|
+
// Format vibes for display
|
|
58
|
+
const formattedVibes = vibes.map((vibe, index) => {
|
|
59
|
+
return `${index + 1}. ${vibe.emoji} "${vibe.name}"
|
|
60
|
+
${vibe.description}
|
|
61
|
+
Keywords: ${vibe.keywords.join(", ")}`;
|
|
62
|
+
}).join("\n\n");
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `Here are 5 design vibes generated for your project:\n\n${formattedVibes}\n\n---\n\nAsk the user to select one vibe. Once selected, pass it to create_frontend via the designSystem.vibe parameter with:\n- name: The vibe name (e.g., "Pristine Museum")\n- description: The full description\n- keywords: The array of keywords`,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
// Also return structured data for programmatic use
|
|
71
|
+
vibes,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// If parsing fails, return raw result
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: `Generated vibes:\n\n${result}\n\n---\n\nNote: Could not parse as JSON. Please extract the vibes manually.`,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -37,7 +37,7 @@ ${targetCode}
|
|
|
37
37
|
MODIFICATION REQUESTED: ${modification}
|
|
38
38
|
|
|
39
39
|
Remember: Return ONLY the find/replace block. ONE modification, surgical precision.`.trim();
|
|
40
|
-
const result = await generateWithGemini(systemPrompt, modification);
|
|
40
|
+
const result = await generateWithGemini(systemPrompt, modification, undefined, "minimal");
|
|
41
41
|
return {
|
|
42
42
|
content: [{ type: "text", text: result }],
|
|
43
43
|
};
|
|
@@ -39,7 +39,7 @@ INSERTION CONTEXT:
|
|
|
39
39
|
${insertionContext}
|
|
40
40
|
|
|
41
41
|
Generate a snippet that will integrate smoothly at this location.`.trim();
|
|
42
|
-
const result = await generateWithGemini(systemPrompt, request);
|
|
42
|
+
const result = await generateWithGemini(systemPrompt, request, undefined, "minimal");
|
|
43
43
|
return {
|
|
44
44
|
content: [{ type: "text", text: result }],
|
|
45
45
|
};
|