gemini-design-mcp 3.8.0 → 3.10.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 CHANGED
@@ -1,22 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
4
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
5
6
  import express from "express";
6
7
  import cors from "cors";
8
+ import { randomUUID } from "crypto";
7
9
  import { createFrontendSchema, createFrontend } from "./tools/create-frontend.js";
8
10
  import { modifyFrontendSchema, modifyFrontend } from "./tools/modify-frontend.js";
9
11
  import { snippetFrontendSchema, snippetFrontend } from "./tools/snippet-frontend.js";
10
12
  import { generateVibesSchema, generateVibes } from "./tools/generate-vibes.js";
11
- // Create MCP server
12
- const server = new McpServer({
13
- name: "gemini-design-mcp",
14
- version: "3.8.0",
15
- });
13
+
16
14
  // =============================================================================
17
- // TOOL 1: CREATE_FRONTEND
15
+ // HELPER: Create and configure MCP server with all tools
18
16
  // =============================================================================
19
- server.tool("create_frontend", `Create a NEW, complete frontend file with PREMIUM DESIGN quality.
17
+ function createMcpServer() {
18
+ const server = new McpServer({
19
+ name: "gemini-design-mcp",
20
+ version: "3.10.0",
21
+ });
22
+
23
+ // TOOL 1: CREATE_FRONTEND
24
+ server.tool("create_frontend", `Create a NEW, complete frontend file with PREMIUM DESIGN quality.
20
25
 
21
26
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
22
27
  🎯 WHEN TO USE THIS TOOL
@@ -63,10 +68,9 @@ Present options to user, they select one, then pass it here via designSystem.vib
63
68
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
64
69
 
65
70
  Returns the complete code. YOU (the agent) are responsible for writing it to disk.`, createFrontendSchema, createFrontend);
66
- // =============================================================================
67
- // TOOL 2: MODIFY_FRONTEND
68
- // =============================================================================
69
- server.tool("modify_frontend", `Redesign a SINGLE UI element. Returns ONLY the changed code (find/replace format).
71
+
72
+ // TOOL 2: MODIFY_FRONTEND
73
+ server.tool("modify_frontend", `Redesign a SINGLE UI element. Returns ONLY the changed code (find/replace format).
70
74
 
71
75
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
72
76
  ⚠️ CRITICAL: ONE MODIFICATION PER CALL
@@ -116,10 +120,9 @@ import { X } from "y";
116
120
  <new redesigned code>
117
121
 
118
122
  YOU (the agent) are responsible for applying this find/replace to the file.`, modifyFrontendSchema, modifyFrontend);
119
- // =============================================================================
120
- // TOOL 3: SNIPPET_FRONTEND
121
- // =============================================================================
122
- server.tool("snippet_frontend", `Generate the JSX/HTML for a NEW UI component to INSERT into an existing file.
123
+
124
+ // TOOL 3: SNIPPET_FRONTEND
125
+ server.tool("snippet_frontend", `Generate the JSX/HTML for a NEW UI component to INSERT into an existing file.
123
126
 
124
127
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
125
128
  ⚠️ CRITICAL: SEPARATION OF CONCERNS
@@ -240,10 +243,9 @@ import { Search } from "lucide-react";
240
243
  YOU (the agent) are responsible for:
241
244
  - Adding the logic (useState, handlers) BEFORE calling this tool
242
245
  - Inserting the returned snippet into the correct location in the file`, snippetFrontendSchema, snippetFrontend);
243
- // =============================================================================
244
- // TOOL 4: GENERATE_VIBES
245
- // =============================================================================
246
- server.tool("generate_vibes", `Generate 5 unique design vibes for a new project using Gemini AI.
246
+
247
+ // TOOL 4: GENERATE_VIBES
248
+ server.tool("generate_vibes", `Generate 5 unique design vibes for a new project using Gemini AI.
247
249
 
248
250
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
249
251
  🎯 WHEN TO USE THIS TOOL
@@ -278,94 +280,213 @@ Example vibe:
278
280
  🏛️ "Pristine Museum"
279
281
  An ultra-clean, 'white-cube' aesthetic focused on vast negative space.
280
282
  Keywords: minimal, whitespace, gallery, clean, sophisticated`, generateVibesSchema, generateVibes);
283
+
284
+ return server;
285
+ }
286
+
281
287
  // =============================================================================
282
288
  // START SERVER
283
289
  // =============================================================================
284
290
  async function main() {
285
291
  const transportType = process.env.TRANSPORT || "stdio";
286
292
 
287
- if (transportType === "sse") {
288
- // SSE/HTTP mode for remote connections
293
+ if (transportType === "http" || transportType === "streamable-http") {
294
+ // Streamable HTTP mode (NEW - recommended for remote connections)
289
295
  const app = express();
290
296
 
291
297
  app.use(cors({
292
298
  origin: "*",
293
- methods: ["GET", "POST", "OPTIONS"],
299
+ methods: ["GET", "POST", "DELETE", "OPTIONS"],
294
300
  allowedHeaders: ["Content-Type", "Authorization", "mcp-session-id"],
301
+ exposedHeaders: ["mcp-session-id"],
295
302
  credentials: true,
296
303
  }));
297
304
  app.use(express.json());
298
305
 
299
- // Store active transports by session
306
+ // Store active transports and servers by session
300
307
  const transports = new Map();
308
+ const servers = new Map();
301
309
 
302
310
  // Health check endpoint
303
311
  app.get("/health", (req, res) => {
304
312
  res.json({
305
313
  status: "ok",
306
314
  service: "gemini-design-mcp",
307
- version: "3.8.0",
308
- transport: "sse",
315
+ version: "3.10.0",
316
+ transport: "streamable-http",
309
317
  timestamp: Date.now(),
310
318
  });
311
319
  });
312
320
 
313
- // SSE endpoint - establishes connection
321
+ // Main MCP endpoint - handles all communication
322
+ app.post("/mcp", async (req, res) => {
323
+ const sessionId = req.headers["mcp-session-id"];
324
+ let transport = sessionId ? transports.get(sessionId) : undefined;
325
+
326
+ if (transport) {
327
+ // Existing session - reuse transport
328
+ console.log(`[MCP] Reusing session: ${sessionId}`);
329
+ await transport.handleRequest(req, res, req.body);
330
+ } else if (!sessionId && isInitializeRequest(req.body)) {
331
+ // New session - create transport and server
332
+ console.log("[MCP] New session initialization");
333
+
334
+ // Extract API key from Authorization header
335
+ const authHeader = req.headers.authorization;
336
+ const apiKey = authHeader?.replace("Bearer ", "");
337
+
338
+ if (apiKey && (apiKey.startsWith("gd_") || apiKey.startsWith("AIza"))) {
339
+ process.env.API_KEY = apiKey;
340
+ }
341
+
342
+ const newTransport = new StreamableHTTPServerTransport({
343
+ sessionIdGenerator: () => randomUUID(),
344
+ onsessioninitialized: (sid) => {
345
+ transports.set(sid, newTransport);
346
+ console.log(`[MCP] Session initialized: ${sid}`);
347
+ },
348
+ });
349
+
350
+ // Clean up on close
351
+ newTransport.onclose = () => {
352
+ if (newTransport.sessionId) {
353
+ console.log(`[MCP] Session closed: ${newTransport.sessionId}`);
354
+ transports.delete(newTransport.sessionId);
355
+ servers.delete(newTransport.sessionId);
356
+ }
357
+ };
358
+
359
+ // Create new server instance for this session
360
+ const server = createMcpServer();
361
+ servers.set(newTransport.sessionId, server);
362
+
363
+ // Connect server to transport
364
+ await server.connect(newTransport);
365
+
366
+ // Handle the initialization request
367
+ await newTransport.handleRequest(req, res, req.body);
368
+ } else {
369
+ // Invalid request
370
+ console.warn("[MCP] Bad request - no session ID or not an init request");
371
+ res.status(400).json({
372
+ error: "Bad Request: No valid session ID provided and not an initialization request",
373
+ });
374
+ }
375
+ });
376
+
377
+ // GET for server-to-client streaming (optional, for long-running operations)
378
+ app.get("/mcp", async (req, res) => {
379
+ const sessionId = req.headers["mcp-session-id"];
380
+ const transport = sessionId ? transports.get(sessionId) : undefined;
381
+
382
+ if (!transport) {
383
+ return res.status(404).json({ error: "Session not found" });
384
+ }
385
+
386
+ await transport.handleRequest(req, res);
387
+ });
388
+
389
+ // DELETE for explicit session termination
390
+ app.delete("/mcp", async (req, res) => {
391
+ const sessionId = req.headers["mcp-session-id"];
392
+ const transport = sessionId ? transports.get(sessionId) : undefined;
393
+
394
+ if (!transport) {
395
+ return res.status(404).json({ error: "Session not found" });
396
+ }
397
+
398
+ await transport.handleRequest(req, res);
399
+ });
400
+
401
+ // =========================================================================
402
+ // LEGACY SSE ENDPOINTS (for backwards compatibility with mcp-remote)
403
+ // =========================================================================
404
+ // Keep SSE support for clients that haven't migrated yet
405
+ const { SSEServerTransport } = await import("@modelcontextprotocol/sdk/server/sse.js");
406
+ const sseTransports = new Map();
407
+
314
408
  app.get("/sse", async (req, res) => {
315
409
  const authHeader = req.headers.authorization;
316
410
  const apiKey = authHeader?.replace("Bearer ", "");
317
411
 
318
- if (!apiKey || !apiKey.startsWith("gd_")) {
412
+ const isValidKey = apiKey && (apiKey.startsWith("gd_") || apiKey.startsWith("AIza"));
413
+ if (!isValidKey) {
319
414
  return res.status(401).json({
320
- error: "Missing or invalid API key. Use Authorization: Bearer gd_xxx",
415
+ error: "Missing or invalid API key. Use Authorization: Bearer <key>",
321
416
  });
322
417
  }
323
418
 
324
- // Set API_KEY env var for this request (tools read from process.env)
325
419
  process.env.API_KEY = apiKey;
326
420
 
327
- // Create SSE transport
328
421
  const transport = new SSEServerTransport("/messages", res);
329
- const sessionId = `sess_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
330
- transports.set(sessionId, transport);
422
+ const sessionId = transport.sessionId;
423
+ sseTransports.set(sessionId, transport);
331
424
 
332
- // Send session ID to client
333
425
  res.setHeader("mcp-session-id", sessionId);
334
426
 
335
- // Clean up on disconnect
336
427
  res.on("close", () => {
337
- transports.delete(sessionId);
428
+ console.log("[SSE] Connection closed for session:", sessionId);
429
+ sseTransports.delete(sessionId);
338
430
  });
339
431
 
432
+ const server = createMcpServer();
340
433
  await server.connect(transport);
434
+
435
+ // Keepalive every 5s
436
+ const keepAlive = setInterval(() => {
437
+ try {
438
+ res.write(":keepalive\n\n");
439
+ } catch {
440
+ // Ignore errors
441
+ }
442
+ }, 5000);
443
+ res.on("close", () => clearInterval(keepAlive));
341
444
  });
342
445
 
343
- // Messages endpoint - receives JSON-RPC from client
344
446
  app.post("/messages", async (req, res) => {
345
- const sessionId = req.query.sessionId || req.headers["mcp-session-id"];
346
- const transport = transports.get(sessionId);
447
+ const headerSession = req.headers["mcp-session-id"];
448
+ const headerSessionId = Array.isArray(headerSession) ? headerSession[0] : headerSession;
449
+ let sessionId = req.query.sessionId || headerSessionId;
450
+ let transport = sessionId ? sseTransports.get(sessionId) : undefined;
451
+
452
+ if (!transport && !sessionId && sseTransports.size === 1) {
453
+ const onlyEntry = sseTransports.entries().next().value;
454
+ if (onlyEntry) {
455
+ sessionId = onlyEntry[0];
456
+ transport = onlyEntry[1];
457
+ }
458
+ }
347
459
 
348
460
  if (!transport) {
349
461
  return res.status(400).json({ error: "Invalid or expired session" });
350
462
  }
351
463
 
352
- await transport.handlePostMessage(req, res);
464
+ try {
465
+ await transport.handlePostMessage(req, res, req.body);
466
+ } catch (error) {
467
+ console.error("[SSE] Error handling message:", error);
468
+ if (!res.headersSent) {
469
+ res.status(400).json({ error: "Invalid message" });
470
+ }
471
+ }
353
472
  });
354
473
 
355
474
  const port = parseInt(process.env.PORT || "3000", 10);
356
475
  app.listen(port, "0.0.0.0", () => {
357
- console.log(`gemini-design-mcp v3.8.0 running on SSE transport`);
358
- console.log(` SSE endpoint: http://0.0.0.0:${port}/sse`);
359
- console.log(` Messages endpoint: http://0.0.0.0:${port}/messages`);
476
+ console.log(`gemini-design-mcp v3.10.0 running on Streamable HTTP transport`);
477
+ console.log(` MCP endpoint: http://0.0.0.0:${port}/mcp`);
478
+ console.log(` Legacy SSE: http://0.0.0.0:${port}/sse`);
360
479
  console.log(` Health check: http://0.0.0.0:${port}/health`);
361
480
  });
362
481
  } else {
363
482
  // Stdio mode (default, for local IDE usage)
483
+ const server = createMcpServer();
364
484
  const transport = new StdioServerTransport();
365
485
  await server.connect(transport);
366
- console.error("gemini-design-mcp v3.8.0 running on stdio");
486
+ console.error("gemini-design-mcp v3.10.0 running on stdio");
367
487
  }
368
488
  }
489
+
369
490
  main().catch((error) => {
370
491
  console.error("Fatal error:", error);
371
492
  process.exit(1);
@@ -2,71 +2,105 @@
2
2
  const PROXY_URL = "https://dashing-alpaca-785.convex.site/v1/generate";
3
3
  // Default model - Gemini 3 Flash Preview
4
4
  export const DEFAULT_MODEL = "gemini-3-flash-preview";
5
- // Get and validate API key
5
+ // Import Google Generative AI SDK for BYOK (Bring Your Own Key)
6
+ import { GoogleGenAI } from "@google/genai";
7
+ // Get and validate API key - supports both platform keys (gd_) and Google API keys (AIza)
6
8
  function getApiKey() {
7
9
  const apiKey = process.env.API_KEY;
8
10
  if (!apiKey) {
9
11
  throw new Error("Missing API_KEY environment variable. " +
10
- "Get your API key at https://gemini-design-mcp.com/dashboard/api-keys");
12
+ "Use either a platform key (gd_xxx) from https://gemini-design-mcp.com/dashboard/api-keys " +
13
+ "or a Google API key (AIza...) from https://aistudio.google.com/apikey");
11
14
  }
12
- if (!apiKey.startsWith("gd_")) {
13
- throw new Error("Invalid API key format. API keys must start with 'gd_'. " +
14
- "Get your API key at https://gemini-design-mcp.com/dashboard/api-keys");
15
+ // Accept both platform keys (gd_) and Google API keys (AIza)
16
+ if (!apiKey.startsWith("gd_") && !apiKey.startsWith("AIza")) {
17
+ throw new Error("Invalid API key format. Use either:\n" +
18
+ "- Platform key starting with 'gd_' from https://gemini-design-mcp.com/dashboard/api-keys\n" +
19
+ "- Google API key starting with 'AIza' from https://aistudio.google.com/apikey");
15
20
  }
16
21
  return apiKey;
17
22
  }
23
+ // Generate via platform proxy (for gd_ keys)
24
+ async function generateViaProxy(apiKey, systemPrompt, userPrompt, model, thinkingMode, endpoint) {
25
+ const response = await fetch(PROXY_URL, {
26
+ method: "POST",
27
+ headers: {
28
+ "Content-Type": "application/json",
29
+ "Authorization": `Bearer ${apiKey}`,
30
+ },
31
+ body: JSON.stringify({
32
+ model,
33
+ endpoint,
34
+ contents: [
35
+ {
36
+ role: "user",
37
+ parts: [{ text: userPrompt }],
38
+ },
39
+ ],
40
+ systemInstruction: {
41
+ parts: [{ text: systemPrompt }],
42
+ },
43
+ generationConfig: {
44
+ temperature: 1,
45
+ },
46
+ // Pass thinking mode for billing/logging purposes
47
+ thinkingMode,
48
+ }),
49
+ });
50
+ const result = await response.json();
51
+ if (!response.ok) {
52
+ const errorMsg = result.error || `API error: ${response.status}`;
53
+ // Check for credit/quota related errors
54
+ if (errorMsg.toLowerCase().includes('credit') ||
55
+ errorMsg.toLowerCase().includes('quota') ||
56
+ errorMsg.toLowerCase().includes('insufficient') ||
57
+ errorMsg.toLowerCase().includes('limit') ||
58
+ response.status === 402 ||
59
+ response.status === 429) {
60
+ throw new Error(`${errorMsg}\n\n Top up your credits here: https://gemini-design-mcp.com/settings/billing`);
61
+ }
62
+ throw new Error(errorMsg);
63
+ }
64
+ // Extract text from Gemini response format
65
+ const text = result.candidates?.[0]?.content?.parts?.[0]?.text;
66
+ if (!text) {
67
+ throw new Error("No content in response");
68
+ }
69
+ return text;
70
+ }
71
+ // Generate via direct Google API (for AIza keys - BYOK)
72
+ async function generateViaDirect(apiKey, systemPrompt, userPrompt, model) {
73
+ const ai = new GoogleGenAI({ apiKey });
74
+ const response = await ai.models.generateContent({
75
+ model,
76
+ contents: userPrompt,
77
+ config: {
78
+ systemInstruction: systemPrompt,
79
+ temperature: 1,
80
+ },
81
+ });
82
+ const text = response.text;
83
+ if (!text) {
84
+ throw new Error("No content in response from Google API");
85
+ }
86
+ return text;
87
+ }
18
88
  export async function generateWithGemini(systemPrompt, userPrompt, model = DEFAULT_MODEL, thinkingMode = "minimal", endpoint = "generate") {
19
89
  const apiKey = getApiKey();
90
+ const isGoogleKey = apiKey.startsWith("AIza");
20
91
  try {
21
- const response = await fetch(PROXY_URL, {
22
- method: "POST",
23
- headers: {
24
- "Content-Type": "application/json",
25
- "Authorization": `Bearer ${apiKey}`,
26
- },
27
- body: JSON.stringify({
28
- model,
29
- endpoint,
30
- contents: [
31
- {
32
- role: "user",
33
- parts: [{ text: userPrompt }],
34
- },
35
- ],
36
- systemInstruction: {
37
- parts: [{ text: systemPrompt }],
38
- },
39
- generationConfig: {
40
- temperature: 1,
41
- },
42
- // Pass thinking mode for billing/logging purposes
43
- thinkingMode,
44
- }),
45
- });
46
- const result = await response.json();
47
- if (!response.ok) {
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);
59
- }
60
- // Extract text from Gemini response format
61
- const text = result.candidates?.[0]?.content?.parts?.[0]?.text;
62
- if (!text) {
63
- throw new Error("No content in response");
92
+ if (isGoogleKey) {
93
+ // BYOK: Direct call to Google API
94
+ return await generateViaDirect(apiKey, systemPrompt, userPrompt, model);
95
+ } else {
96
+ // Platform key: Use proxy
97
+ return await generateViaProxy(apiKey, systemPrompt, userPrompt, model, thinkingMode, endpoint);
64
98
  }
65
- return text;
66
99
  }
67
100
  catch (error) {
68
101
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
69
- console.error("Gemini Design API error:", errorMessage);
70
- throw new Error(`Gemini Design API error: ${errorMessage}`);
102
+ const source = isGoogleKey ? "Google Gemini API" : "Gemini Design API";
103
+ console.error(`${source} error:`, errorMessage);
104
+ throw new Error(`${source} error: ${errorMessage}`);
71
105
  }
72
106
  }
@@ -2,66 +2,295 @@
2
2
  // CREATE_FRONTEND PROMPT
3
3
  // Used for creating NEW complete files (pages, components, sections)
4
4
  // =============================================================================
5
- export const CREATE_FRONTEND_PROMPT = `You are an elite UI/UX Designer creating production-ready, visually stunning interfaces.
6
-
7
- YOUR TASK: Create a COMPLETE, NEW frontend file.
8
-
9
- DESIGN EXCELLENCE REQUIREMENTS:
10
-
11
- 1. **MAXIMUM DETAIL AND DENSITY**
12
- - The interface MUST be densely populated and feature-rich
13
- - NEVER produce simple wireframes, empty containers, or placeholder content
14
- - Fill the space with intricate details, realistic content, background elements, and meaningful UI components
15
- - Every section should feel like a fully finished, polished product
16
-
17
- 2. **PREMIUM VISUAL STYLING**
18
- - Apply high-end design principles throughout
19
- - Use sophisticated styling: nuanced shadows, subtle gradients, refined borders, micro-textures
20
- - Add depth and dimension with layered elements
21
- - The aesthetic MUST feel modern, premium, and visually captivating
22
- - Use professional color palettes with proper contrast and hierarchy
23
-
24
- 3. **COMPREHENSIVE INTERACTIVITY**
25
- - Design every element to look tangible and interactive
26
- - Include visual states: hover effects, focus rings, active states, transitions
27
- - Buttons should look clickable, inputs should look fillable
28
- - The interface should feel alive, not static
29
-
30
- 4. **LAYOUT EXCELLENCE**
31
- - Use proper visual hierarchy with clear focal points
32
- - Apply consistent spacing rhythm throughout
33
- - Ensure the layout breathes - balance density with whitespace
34
- - Full viewport height designs (min-height: 100vh on body)
35
- - No awkward gaps or orphaned elements
36
-
37
- 5. **RESPONSIVE BY DEFAULT**
38
- - Designs MUST work flawlessly on mobile and desktop
39
- - Touch-friendly sizing for interactive elements on mobile
40
- - Stackable grids that adapt naturally to screen size
41
-
42
- 6. **REALISTIC CONTENT**
43
- - Use believable placeholder content (real-looking names, dates, numbers)
44
- - Include appropriate icons, avatars, and imagery
45
- - Data visualizations should have realistic data points
46
- - Text should be contextually appropriate, not "Lorem ipsum"
47
-
48
- 7. **DESIGN SYSTEM INTEGRATION (CRITICAL WHEN CONTEXT IS PROVIDED)**
49
- When existing project context is provided, you MUST:
50
- - NEVER add inline <style> tags - use ONLY the styling approach from the context
51
- - NEVER hardcode pixel values - use the project's existing variables, tokens, or classes
52
- - NEVER introduce new fonts - use the project's existing font definitions
53
- - NEVER hardcode colors - use the project's existing color tokens/variables/classes
54
- - Match the EXACT spacing scale, typography scale, and color palette
55
- - The new file must look like it belongs to the same project, not a "foreign" design
56
- - Reuse existing component patterns, class naming conventions, and styling architecture
57
- - Adapt to whatever styling system the project uses (analyze the context)
5
+ export const CREATE_FRONTEND_PROMPT = `# Frontend Design Skill - Enhanced Prompt
6
+
7
+ This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
8
+
9
+ The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.
10
+
11
+ ---
12
+
13
+ ## Design Thinking
14
+
15
+ Before coding, understand the context and commit to a BOLD aesthetic direction:
16
+
17
+ **Purpose & Context**: What problem does this interface solve? Who uses it? Adapt the "volume" of creativity to the intent.
18
+
19
+ - **Trust Tier** (Banks, SaaS, Enterprise): Innovation lies in subtlety—perfect typography, "glass" materials, and silky smooth interactions.
20
+ - **Disrupt Tier** (Fashion, Portfolio, Event): Innovation lies in shock—brutalism, WebGL distortion, and aggressive layouts.
21
+
22
+ **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian.
23
+
24
+ **Differentiation**: What makes this UNFORGETTABLE? Is it a physics-based interaction? A seamless video background? A shader effect?
25
+
26
+ **CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality.
27
+
28
+ ---
29
+
30
+ ## Aesthetic References (Non-Exhaustive Inspiration)
31
+
32
+ Sites to study for different vibes—don't copy, extract the *principle* that makes them work:
33
+
34
+ | Vibe | References |
35
+ |------|------------|
36
+ | **Liquid/Organic** | Linear.app, Stripe.com, Raycast.com |
37
+ | **Brutalist/Raw** | Balenciaga, Bloomberg Businessweek |
38
+ | **Editorial/Magazine** | Pudding.cool, The Outline (RIP), NYT Interactive |
39
+ | **3D/Immersive** | Apple product pages, Porsche.com, Rivian.com |
40
+ | **Retro-Web/Y2K** | Poolsuite.net, Superbad.com |
41
+ | **Neo-Corporate** | Vercel, Supabase, Planetscale, Railway.app |
42
+ | **Luxury/Fashion** | Bottega Veneta, Acne Studios, SSENSE |
43
+ | **Experimental** | Awwwards winners, Hoverstat.es, Brutalist Websites |
44
+
45
+ ---
46
+
47
+ ## Frontend Aesthetics Guidelines
48
+
49
+ ### 1. Visual Fidelity & Alchemy
50
+
51
+ Go beyond standard CSS. Layer lighting, depth, and texture to create objects that feel tactile.
52
+
53
+ **The "Hyper-Border"**: Instead of simple borders, consider mask-composite or pseudo-elements with conic-gradients to create flowing light. Use \`box-shadow: inset\` for inner glows that create depth (e.g., buttons that feel carved into the screen).
54
+
55
+ **Backgrounds & Atmosphere**: Create atmosphere rather than solid colors. Use gradient meshes, noise textures to prevent banding, geometric patterns, or layered transparencies.
56
+
57
+ **Cinematic Materials**: Treat video as a texture. Don't just embed a player; blend video footage using \`mix-blend-mode\`, mask it inside typography, or use it as a blurred atmospheric layer.
58
+
59
+ ### 2. Motion & Physics
60
+
61
+ Motion should feel alive, not just "animated."
62
+
63
+ **Tools**: Prioritize CSS-only solutions for simple movement. For complex interactions, feel free to use libraries like Framer Motion, GSAP (ScrollTrigger), or Lenis (for smooth scrolling).
64
+
65
+ **Interaction**: Use magnetic buttons that track the cursor, scroll-linked animations (scrubbing), or parallax effects.
66
+
67
+ **The "Woah" Moment**: Focus on one high-impact centerpiece. A well-orchestrated hero section with staggered reveals is better than chaotic noise.
68
+
69
+ ### 3. Advanced Tech (Optional)
70
+
71
+ If the aesthetic demands "God-tier" visuals (and performance permits), do not hesitate to suggest or implement:
72
+
73
+ - **WebGL / Shaders**: Use Three.js / React Three Fiber for liquid distortions, particle systems, or 3D displacement maps.
74
+ - **Canvas**: For fluid simulations or interactive grain.
75
+
76
+ ### 4. Typography & Layout
77
+
78
+ **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts (Arial, Inter). Pair a distinctive display font with a refined body font.
79
+
80
+ **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
81
+
82
+ ---
83
+
84
+ ## Details That Elevate
85
+
86
+ Small touches that separate good from unforgettable:
87
+
88
+ - Custom cursor that reacts to context (pointer changes near interactive elements, grows on hover)
89
+ - Text that reveals on scroll with \`clip-path\` or mask
90
+ - Hover states that feel physical (\`scale(0.98)\` + shadow reduction = "pressed")
91
+ - Loading states that are actually delightful (skeleton with shimmer, not spinner)
92
+ - Scroll progress indicators (subtle bar, dot navigation, or percentage)
93
+ - Easter eggs (Konami code, hidden interactions, developer console messages)
94
+ - Sound design (subtle click/hover sounds for immersive experiences)
95
+ - View Transitions API for seamless page changes
96
+ - Staggered animations with variable delays (not uniform timing)
97
+ - "Breathing" elements (subtle scale oscillation on idle states)
98
+ - Focus states that are beautiful, not just functional
99
+ - Selection highlight with custom \`::selection\` colors
100
+ - Smooth scroll behavior with momentum
101
+ - Magnetic snap points on scroll
102
+ - Cursor trails or custom pointer effects
103
+
104
+ ---
105
+
106
+ ## Creative Moves Toolkit
107
+
108
+ A palette of techniques to draw from—mix and match based on your aesthetic direction:
109
+
110
+ ### Typography Moves
111
+
112
+ - Massive display type (200px+) cropped by viewport edge
113
+ - Mixed weights in same headline ("Build **FAST**")
114
+ - Vertical text for navigation or accents
115
+ - Variable fonts animated on scroll/hover (weight, width, slant)
116
+ - Text stroke / outline only (no fill)
117
+ - Kinetic typography (letters that move independently)
118
+ - Highlighted text with custom underlines or background marks
119
+ - Rotated or angled headlines
120
+ - Text masks revealing images or video
121
+ - Stacked/layered text shadows for depth
122
+ - Justified text with careful hyphenation
123
+ - Pull quotes that break the grid
124
+
125
+ ### Layout Moves
126
+
127
+ - Bento grid (varied cell sizes like Apple)
128
+ - Overlap everything (z-index chaos, controlled)
129
+ - Sticky elements that transform as you scroll
130
+ - Split screen with independent scroll speeds
131
+ - Cards that "fan out" or "stack" on hover
132
+ - Masonry that reflows dynamically
133
+ - Off-canvas elements peeking in
134
+ - Full-bleed sections alternating with contained
135
+ - Diagonal dividers between sections
136
+ - Floating elements that respond to scroll/cursor
137
+ - Horizontal scroll sections within vertical page
138
+ - Asymmetric hero with off-center focal point
139
+
140
+ ### Color Moves
141
+
142
+ - Monochrome + ONE violent accent color
143
+ - Dark mode with glowing/neon edges
144
+ - Gradient text (\`background-clip: text\`)
145
+ - Color that shifts with scroll position
146
+ - Inverted sections (dark/light alternating dramatically)
147
+ - Duotone images (two-color treatment)
148
+ - Color theming that changes per section
149
+ - Complementary color clashes (intentional tension)
150
+ - Single hue with many tints/shades
151
+ - Black & white with selective color pops
152
+ - Iridescent/holographic effects
153
+ - Color that responds to time of day
154
+
155
+ ### Texture Moves
156
+
157
+ - Grain overlay (SVG noise filter or CSS)
158
+ - Glassmorphism done right (\`backdrop-blur\` + subtle border glow)
159
+ - Halftone patterns for retro feel
160
+ - Scan lines (CRT/retro screen effect)
161
+ - Paper texture for editorial authenticity
162
+ - Metallic/chrome reflections (\`conic-gradient\`)
163
+ - Mesh gradients for organic depth
164
+ - Dot patterns or grids as backgrounds
165
+ - Frosted glass panels
166
+ - Subtle shadows with colored tints
167
+ - Glow effects (outer and inner)
168
+ - Noise dithering on gradients
169
+
170
+ ---
171
+
172
+ ## Proven Aesthetic Combos
173
+
174
+ Pre-validated combinations that work—use as starting points, then make them your own:
175
+
176
+ | Vibe | Typography | Colors | Motion | Layout |
177
+ |------|-----------|--------|--------|--------|
178
+ | **Luxury Tech** | Thin serif + monospace | Black + gold/cream accent | Slow, smooth reveals | Generous whitespace |
179
+ | **Y2K Revival** | Bubble/rounded + pixel fonts | Chrome + hot pink/lime | Bouncy, elastic easing | Chaotic overlap |
180
+ | **Editorial** | High-contrast serif (Playfair, etc.) | B&W + single red accent | Subtle, purposeful fades | Grid with intentional breaks |
181
+ | **Neo-Brutalist** | Heavy grotesque (bold sans) | Primary colors + pure black | Instant, no easing (steps) | Rigid grid, raw borders |
182
+ | **Organic/Calm** | Rounded sans + hand-drawn elements | Earth tones + warm cream | Gentle, breathing motion | Flowing, asymmetric curves |
183
+ | **Cyberpunk** | Condensed industrial + glitch | Neon on dark, high contrast | Flicker, glitch effects | Dense, information-heavy |
184
+ | **Minimal Luxury** | Ultra-thin sans + elegant serif | Muted neutrals, subtle warmth | Almost invisible, precise | Extreme whitespace |
185
+ | **Playful/Toy** | Rounded, chunky, friendly | Bright primaries, pastels | Bouncy, squash & stretch | Loose, scattered, fun |
186
+ | **Art Deco** | Geometric display, gold accents | Black + gold + cream | Elegant reveals, symmetric | Strong geometry, patterns |
187
+ | **Swiss/International** | Helvetica-adjacent, grid-locked | Limited palette, functional | Minimal, functional only | Strict grid system |
188
+
189
+ ---
190
+
191
+ ## The "AI Slop" Detector
192
+
193
+ You've made AI slop if you see any of these patterns. Stop immediately and pick a weirder direction:
194
+
195
+ ### Visual Red Flags
196
+ - Purple-to-blue gradient on white background
197
+ - Generic 3D blob/abstract illustrations
198
+ - Card grid with identical border-radius and shadows
199
+ - Perfectly centered everything with no tension
200
+ - Stock photos of people smiling at laptops
201
+ - Gradient orbs floating in the background
202
+ - The same "glass card" component everywhere
203
+
204
+ ### Typography Red Flags
205
+ - Inter, Roboto, Poppins, or system fonts as primary
206
+ - Generic font pairing with no character
207
+ - All text perfectly aligned and safe
208
+ - No hierarchy risks taken
209
+
210
+ ### Layout Red Flags
211
+ - "Hero section with headline, subtext, two buttons" (the holy trinity of boring)
212
+ - Three-column feature grid
213
+ - Testimonial carousel
214
+ - "Clean and modern" as the only design rationale
215
+ - Footer with 4 equal columns of links
216
+
217
+ ### Motion Red Flags
218
+ - \`transition: all 0.3s ease\` on everything
219
+ - Fade-in-up on scroll for every element
220
+ - No motion at all (static = forgettable)
221
+ - Overused bounce effects
222
+
223
+ **If you catch yourself here**: STOP. Go back to the aesthetic combos. Pick something with actual personality. Ask "what would make someone screenshot this?"
224
+
225
+ ---
226
+
227
+ ## Before You Ship, Ask Yourself
228
+
229
+ Gut-check questions to ensure you're not shipping mediocrity:
230
+
231
+ 1. **Would this make someone stop scrolling?**
232
+ 2. **If I remove the logo, is this still recognizable as this brand/product?**
233
+ 3. **What's the ONE thing people will screenshot or share?**
234
+ 4. **Does this feel like a human made it, or an algorithm?**
235
+ 5. **Is there any moment of genuine surprise or delight?**
236
+ 6. **Would I put this in my portfolio?**
237
+ 7. **Can I describe the aesthetic in one evocative phrase?** (If you can only say "clean and modern," try again)
238
+ 8. **Did I take at least ONE real risk?**
239
+ 9. **Is the typography doing heavy lifting or just existing?**
240
+ 10. **Will this be remembered tomorrow?**
241
+
242
+ ---
243
+
244
+ ## Implementation Rules
245
+
246
+ **Code Quality**: Production-grade, functional, and clean. No placeholder content unless explicitly temporary.
247
+
248
+ **Styling**: Use Tailwind CSS (with arbitrary values \`[]\` for precision) or custom CSS/SCSS as appropriate for the project.
249
+
250
+ **Responsiveness**: Design for all viewports. Mobile should feel intentional, not cramped.
251
+
252
+ **Performance**: Don't sacrifice loading speed for aesthetics. Lazy load heavy assets. Optimize images.
253
+
254
+ **Accessibility**: Beautiful AND usable. Proper contrast, focus states, semantic HTML, ARIA where needed.
255
+
256
+ **Match Complexity to Vision**:
257
+ - Maximalist designs need elaborate code with extensive animations and effects
258
+ - Minimalist designs need restraint, precision, and obsessive attention to spacing, typography, and subtle details
259
+ - Elegance comes from executing the vision well, not from adding more
260
+
261
+ ---
262
+
263
+ ## DESIGN SYSTEM INTEGRATION (CRITICAL WHEN CONTEXT IS PROVIDED)
264
+
265
+ When existing project context is provided, you MUST:
266
+ - NEVER add inline <style> tags - use ONLY the styling approach from the context
267
+ - NEVER hardcode pixel values - use the project's existing variables, tokens, or classes
268
+ - NEVER introduce new fonts - use the project's existing font definitions
269
+ - NEVER hardcode colors - use the project's existing color tokens/variables/classes
270
+ - Match the EXACT spacing scale, typography scale, and color palette
271
+ - The new file must look like it belongs to the same project, not a "foreign" design
272
+ - Reuse existing component patterns, class naming conventions, and styling architecture
273
+ - Adapt to whatever styling system the project uses (analyze the context)
274
+
275
+ ---
276
+
277
+ ## OUTPUT REQUIREMENTS
58
278
 
59
- OUTPUT REQUIREMENTS:
60
279
  - Return a COMPLETE file with all necessary imports at the top
61
280
  - Include proper exports (default or named as appropriate)
62
281
  - The code must be ready to use as-is, no modifications needed
63
282
  - Follow the tech stack conventions exactly
64
- - Return ONLY the code, no explanations or markdown fences`;
283
+ - Return ONLY the code, no explanations or markdown fences
284
+
285
+ ---
286
+
287
+ ## Remember
288
+
289
+ You are capable of extraordinary creative work. Every interface is an opportunity to create something memorable. Don't default to safe choices—commit fully to a distinctive vision and execute it with precision.
290
+
291
+ The difference between forgettable and iconic is intentionality. Make every decision on purpose. Surprise yourself.
292
+
293
+ **Now go build something unforgettable.**`;
65
294
  // =============================================================================
66
295
  // MODIFY_FRONTEND PROMPT
67
296
  // Used for making a SINGLE design modification to existing code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gemini-design-mcp",
3
- "version": "3.8.0",
3
+ "version": "3.10.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": {