UNPKG

cold-mcp-super-design

Version:

MCP server for SuperDesign integration with Cursor/Windsurf/Claude Code - Generate UI designs, components, and wireframes

390 lines (385 loc) • 16.8 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import fs from 'fs-extra'; import path from 'path'; import { v4 as uuidv4 } from 'uuid'; class SuperDesignMCPServer { server; designsPath; constructor() { this.server = new Server({ name: 'cold-mcp-super-design', version: '1.0.0', }, { capabilities: { tools: {}, }, }); // Find the workspace root (where the user is working) const workspaceRoot = process.env.PWD || process.cwd(); this.designsPath = path.join(workspaceRoot, '.superdesign', 'design_iterations'); this.setupToolHandlers(); } async ensureDesignsDirectory() { await fs.ensureDir(this.designsPath); } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'create_design', description: 'Create a new UI design with HTML and CSS', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the design (e.g., "login-form", "calculator")', }, description: { type: 'string', description: 'Description of the design', }, prompt: { type: 'string', description: 'The design prompt/requirements', }, html: { type: 'string', description: 'HTML content for the design', }, css: { type: 'string', description: 'CSS styles for the design', }, }, required: ['name', 'description', 'prompt', 'html', 'css'], }, }, { name: 'create_design_revision', description: 'Create a new revision of an existing design', inputSchema: { type: 'object', properties: { originalName: { type: 'string', description: 'Name of the original design to revise', }, revisionDescription: { type: 'string', description: 'Description of what changed in this revision', }, prompt: { type: 'string', description: 'The updated design prompt/requirements', }, html: { type: 'string', description: 'Updated HTML content', }, css: { type: 'string', description: 'Updated CSS styles', }, }, required: ['originalName', 'revisionDescription', 'prompt', 'html', 'css'], }, }, { name: 'list_designs', description: 'List all created designs and their revisions', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_design', description: 'Get details of a specific design', inputSchema: { type: 'object', properties: { designId: { type: 'string', description: 'ID of the design to retrieve', }, }, required: ['designId'], }, }, { name: 'generate_superdesign_prompt', description: 'Generate a prompt for SuperDesign canvas integration', inputSchema: { type: 'object', properties: { designType: { type: 'string', description: 'Type of design (login, calculator, dashboard, etc.)', }, colorScheme: { type: 'string', description: 'Color scheme (e.g., "gold, black, and white")', }, style: { type: 'string', description: 'Design style (modern, minimal, elegant, etc.)', }, additionalRequirements: { type: 'string', description: 'Any additional design requirements', }, }, required: ['designType'], }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'create_design': return await this.createDesign(args); case 'create_design_revision': return await this.createDesignRevision(args); case 'list_designs': return await this.listDesigns(); case 'get_design': return await this.getDesign(args); case 'generate_superdesign_prompt': return await this.generateSuperDesignPrompt(args); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { throw new McpError(ErrorCode.InternalError, `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`); } }); } async createDesign(args) { await this.ensureDesignsDirectory(); const design = { id: uuidv4(), name: args.name, description: args.description, prompt: args.prompt, html: args.html, css: args.css, timestamp: new Date().toISOString(), version: 1, }; const fileName = `${args.name}.json`; const filePath = path.join(this.designsPath, fileName); await fs.writeJson(filePath, design, { spaces: 2 }); // Also create an HTML preview file const htmlPreview = this.generateHTMLPreview(design); const htmlFileName = `${args.name}.html`; const htmlFilePath = path.join(this.designsPath, htmlFileName); await fs.writeFile(htmlFilePath, htmlPreview); return { content: [ { type: 'text', text: `āœ… Design "${args.name}" created successfully!\n\n` + `šŸ“ Saved to: ${filePath}\n` + `🌐 Preview: ${htmlFilePath}\n` + `šŸ†” Design ID: ${design.id}\n\n` + `To preview in SuperDesign canvas:\n` + `1. Press Cmd+Shift+P (or Ctrl+Shift+P)\n` + `2. Type "Superdesign: Open canvas view"\n` + `3. Open the HTML file: ${htmlFileName}`, }, ], }; } async createDesignRevision(args) { await this.ensureDesignsDirectory(); // Find the original design to get the next version number const originalPath = path.join(this.designsPath, `${args.originalName}.json`); let version = 1; if (await fs.pathExists(originalPath)) { const originalDesign = await fs.readJson(originalPath); version = originalDesign.version + 1; } const revision = { id: uuidv4(), name: `${args.originalName}-rev${version}`, description: args.revisionDescription, prompt: args.prompt, html: args.html, css: args.css, timestamp: new Date().toISOString(), version: version, }; const fileName = `${revision.name}.json`; const filePath = path.join(this.designsPath, fileName); await fs.writeJson(filePath, revision, { spaces: 2 }); // Create HTML preview const htmlPreview = this.generateHTMLPreview(revision); const htmlFileName = `${revision.name}.html`; const htmlFilePath = path.join(this.designsPath, htmlFileName); await fs.writeFile(htmlFilePath, htmlPreview); return { content: [ { type: 'text', text: `āœ… Design revision "${revision.name}" created successfully!\n\n` + `šŸ“ Saved to: ${filePath}\n` + `🌐 Preview: ${htmlFilePath}\n` + `šŸ†” Design ID: ${revision.id}\n` + `šŸ“Š Version: ${version}\n\n` + `To preview in SuperDesign canvas:\n` + `1. Press Cmd+Shift+P (or Ctrl+Shift+P)\n` + `2. Type "Superdesign: Open canvas view"\n` + `3. Open the HTML file: ${htmlFileName}`, }, ], }; } generateHTMLPreview(design) { return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>${design.name} - SuperDesign Preview</title> <style> ${design.css} </style> </head> <body> <!-- Design: ${design.name} --> <!-- Description: ${design.description} --> <!-- Generated: ${design.timestamp} --> <!-- Version: ${design.version} --> ${design.html} </body> </html>`; } async listDesigns() { await this.ensureDesignsDirectory(); const files = await fs.readdir(this.designsPath); const jsonFiles = files.filter(file => file.endsWith('.json')); if (jsonFiles.length === 0) { return { content: [ { type: 'text', text: 'šŸ“­ No designs found. Create your first design using the create_design tool!', }, ], }; } const designs = []; for (const file of jsonFiles) { try { const design = await fs.readJson(path.join(this.designsPath, file)); designs.push(design); } catch (error) { console.error(`Error reading design file ${file}:`, error); } } designs.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); const designList = designs.map(design => `šŸŽØ ${design.name} (v${design.version})\n` + ` šŸ“ ${design.description}\n` + ` šŸ†” ${design.id}\n` + ` šŸ“… ${new Date(design.timestamp).toLocaleString()}\n`).join('\n'); return { content: [ { type: 'text', text: `šŸ“š Found ${designs.length} design(s):\n\n${designList}`, }, ], }; } async getDesign(args) { await this.ensureDesignsDirectory(); const files = await fs.readdir(this.designsPath); const jsonFiles = files.filter(file => file.endsWith('.json')); for (const file of jsonFiles) { try { const design = await fs.readJson(path.join(this.designsPath, file)); if (design.id === args.designId) { return { content: [ { type: 'text', text: `šŸŽØ Design Details:\n\n` + `šŸ“› Name: ${design.name}\n` + `šŸ“ Description: ${design.description}\n` + `šŸ†” ID: ${design.id}\n` + `šŸ“Š Version: ${design.version}\n` + `šŸ“… Created: ${new Date(design.timestamp).toLocaleString()}\n\n` + `šŸ’­ Prompt:\n${design.prompt}\n\n` + `🌐 HTML:\n\`\`\`html\n${design.html}\n\`\`\`\n\n` + `šŸŽØ CSS:\n\`\`\`css\n${design.css}\n\`\`\``, }, ], }; } } catch (error) { console.error(`Error reading design file ${file}:`, error); } } return { content: [ { type: 'text', text: `āŒ Design with ID "${args.designId}" not found.`, }, ], }; } async generateSuperDesignPrompt(args) { const colorScheme = args.colorScheme || 'modern blue and white'; const style = args.style || 'modern and clean'; const additional = args.additionalRequirements || ''; const prompt = `Help me design a ${args.designType} UI with the following specifications: šŸŽØ **Design Type**: ${args.designType} 🌈 **Color Scheme**: ${colorScheme} ✨ **Style**: ${style} ${additional ? `šŸ“‹ **Additional Requirements**: ${additional}` : ''} Please create a ${style} ${args.designType} interface using ${colorScheme} colors. The design should be: - Responsive and mobile-friendly - Accessible with proper contrast ratios - Modern with clean typography - User-friendly with intuitive interactions ${additional ? `- ${additional}` : ''} Generate the HTML and CSS code, then use the create_design tool to save it to the SuperDesign canvas. To preview the design: 1. Press Cmd+Shift+P (or Ctrl+Shift+P) 2. Type "Superdesign: Open canvas view" 3. Open the generated HTML file`; return { content: [ { type: 'text', text: `šŸš€ **SuperDesign Prompt Generated:**\n\n${prompt}\n\n` + `šŸ’” **Next Steps:**\n` + `1. Copy this prompt to your AI assistant (Cursor/Windsurf/Claude Code)\n` + `2. Let the AI generate the HTML/CSS\n` + `3. Use the create_design tool to save the result\n` + `4. Preview in SuperDesign canvas with Cmd+Shift+P → "Superdesign: Open canvas view"`, }, ], }; } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Cold MCP SuperDesign server running on stdio'); } } const server = new SuperDesignMCPServer(); server.run().catch(console.error); //# sourceMappingURL=index.js.map