-
Notifications
You must be signed in to change notification settings - Fork 7
Feature w 20796448 figma mcps migration #212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
c3be6cc
dcc6b12
18a4480
649a962
1dc964e
9fa1c1a
61eaa94
38eab96
da0bf04
d7d2328
80c9dad
2d859b2
9be0116
cd94041
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| /* | ||
| * Copyright (c) 2025, Salesforce, Inc. | ||
| * SPDX-License-Identifier: Apache-2 | ||
| * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 | ||
| */ | ||
|
|
||
| export interface FigmaParams { | ||
| fileKey: string; | ||
| nodeId: string; | ||
| } | ||
|
|
||
| /** | ||
| * Parses a Figma URL to extract fileKey and nodeId | ||
| * | ||
| * Supported URL formats: | ||
| * - https://figma.com/design/:fileKey/:fileName?node-id=1-2 | ||
| * - https://www.figma.com/design/:fileKey/:fileName?node-id=1-2 | ||
| * - https://figma.com/file/:fileKey/:fileName?node-id=1-2 | ||
| * | ||
| * @param figmaUrl - The Figma URL to parse | ||
| * @returns Object with fileKey and nodeId, or throws error if invalid | ||
| */ | ||
| export function parseFigmaUrl(figmaUrl: string): FigmaParams { | ||
| try { | ||
| const url = new URL(figmaUrl); | ||
|
|
||
| // Validate it's a Figma URL | ||
| if (!url.hostname.includes('figma.com')) { | ||
| throw new Error('URL must be from figma.com'); | ||
| } | ||
|
|
||
| // Extract fileKey from pathname | ||
| // Pattern: /design/:fileKey/:fileName or /file/:fileKey/:fileName | ||
| const pathMatch = url.pathname.match(/\/(design|file)\/([^/]+)/); | ||
| if (!pathMatch || !pathMatch[2]) { | ||
| throw new Error( | ||
| 'Could not extract fileKey from URL. Expected format: https://figma.com/design/:fileKey/:fileName', | ||
| ); | ||
| } | ||
|
|
||
| const fileKey = pathMatch[2]; | ||
|
|
||
| // Extract nodeId from query params | ||
| // Pattern: ?node-id=1-2 or ?node-id=1:2 | ||
| const nodeIdParam = url.searchParams.get('node-id'); | ||
| if (!nodeIdParam) { | ||
| throw new Error('Could not extract node-id from URL. Expected query parameter: ?node-id=1-2'); | ||
| } | ||
|
|
||
| // Convert node-id format from "1-2" to "1:2" (Figma MCP expects colon format) | ||
| const nodeId = nodeIdParam.replaceAll('-', ':'); | ||
|
|
||
| return { | ||
| fileKey, | ||
| nodeId, | ||
| }; | ||
| } catch (error) { | ||
| if (error instanceof TypeError) { | ||
| throw new TypeError(`Invalid URL format: ${figmaUrl}`); | ||
| } | ||
| throw error; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,310 @@ | ||
| /* | ||
| * Copyright (c) 2025, Salesforce, Inc. | ||
| * SPDX-License-Identifier: Apache-2 | ||
| * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 | ||
| */ | ||
|
|
||
| import {z} from 'zod'; | ||
| import {readFileSync, existsSync} from 'node:fs'; | ||
| import type {McpTool} from '../../../../utils/index.js'; | ||
| import type {Services} from '../../../../services.js'; | ||
| import {createToolAdapter, textResult} from '../../../adapter.js'; | ||
| import {parseFigmaUrl, type FigmaParams} from './figma-url-parser.js'; | ||
|
|
||
| // prettier-ignore | ||
| const DEFAULT_WORKFLOW_CONTENT = `--- | ||
| description: Figma to StorefrontNext component conversion workflow | ||
| taskType: component | ||
| --- | ||
| # Figma to StorefrontNext Component Workflow | ||
|
|
||
| IMPORTANT: The figma_to_component tool is a WORKFLOW ORCHESTRATOR that provides instructions only. It does NOT fetch design data or generate components. | ||
|
|
||
| After calling figma_to_component, you MUST: | ||
| 1. Call Figma MCP tools to fetch design data | ||
| 2. Discover similar components using Glob/Grep/Read | ||
| 3. Call generate-component tool for REUSE/EXTEND/CREATE recommendation | ||
| 4. Call map-tokens tool for token mapping | ||
| 5. Implement the recommended approach | ||
|
|
||
| DO NOT STOP after receiving workflow instructions. Execute all steps to complete the conversion. | ||
|
|
||
| ## WORKFLOW_GUIDELINES | ||
|
|
||
| ### Overview | ||
| This workflow guides through converting a Figma design into a StorefrontNext-compliant component. | ||
|
|
||
| ### Key Principles | ||
| 1. Review workflow plan and list out todos to user before fetching designs | ||
| 2. ALWAYS fetch design context and visual reference from Figma MCP tools first. Do not attempt to generate code without retrieving at minimum the design context and screenshot. Metadata is optional | ||
| 3. Only call the Figma MCP tools listed in this workflow. If a tool is not available or not enabled, inform the user | ||
| 4. ALWAYS discover similar components before creating new ones. Use Glob/Grep/Read to search the codebase | ||
| 5. ALWAYS call generate-component tool with discovered components to get REUSE/EXTEND/CREATE recommendation | ||
| 6. ALWAYS call map-tokens tool to map Figma tokens to existing theme variables rather than hardcoding values | ||
| 7. Follow StorefrontNext patterns. All components must adhere to StorefrontNext architecture | ||
| 8. Present a detailed plan to the user and wait for approval before implementing | ||
| 9. Validate thoroughly. Always run validation checks before presenting the final component to the developer | ||
|
|
||
| ### Figma MCP Tools | ||
| When calling these tools, always include: clientLanguages="typescript", clientFrameworks="react" | ||
| - mcp__figma__get_design_context (REQUIRED): Generates UI code and returns asset URLs | ||
| - mcp__figma__get_screenshot (REQUIRED): Provides visual reference of the design | ||
| - mcp__figma__get_metadata (OPTIONAL): Retrieves node hierarchy, layer types, names, positions, and sizes. Use only if you need detailed structural information | ||
|
|
||
| ### StorefrontNext MCP Tools (REQUIRED) | ||
| - generate-component: Analyzes Figma design and discovered components, recommends REUSE/EXTEND/CREATE strategy. MUST be called with discoveredComponents parameter | ||
| - map-tokens: Maps Figma design tokens to existing theme tokens. MUST be called to avoid hardcoded values | ||
| - validate_component: Validates component against StorefrontNext patterns (optional, not yet implemented) | ||
|
|
||
| ### AI-Driven Component Discovery (Before calling generate-component) | ||
| Before calling generate-component, you must discover similar components using your tools: | ||
|
|
||
| **Discovery Strategy:** | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to provide detailed discovery strategy? Will it better if we only provide the expected result and let Agentic IDE uses its build in approaches to find what we need? It should already indexed files and content and might be faster. Can we compact and simplify the flow? Too much information might confuse agent and consume more token.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tools were demoed by Emmy Zhang under multiple audiences, I will provide some links to the demos. I also think that we can implement improvements in subsequent updates to these tools and just focus on the migration right now as agreed so we can meet the migration timeline of 2/27. |
||
| 1. **Name-Based Search (Primary):** | ||
| - Use Glob to find component files: \`**/components/**/*.tsx\`, \`**/src/**/*.tsx\` | ||
| - Exclude: \`**/node_modules/**\`, \`**/dist/**\`, \`**/*.test.tsx\`, \`**/*.stories.tsx\` | ||
| - Use Grep to search for component names similar to the Figma component name | ||
| - Look in: export statements, function names, interface names | ||
|
|
||
| 2. **Structure-Based Search (Secondary):** | ||
| - If name search yields poor results, search by code structure | ||
| - Look for similar hooks (useState, useEffect, etc.) | ||
| - Look for similar element patterns (buttons, forms, layouts) | ||
| - Search for 'use client' directive if Figma code is client-side | ||
|
|
||
| 3. **Read and Score Components:** | ||
| - Read each promising match | ||
| - Score similarity (0-100) based on: | ||
| * Name similarity: How close is the name? | ||
| * Purpose similarity: Does it serve the same function? | ||
| * Structure similarity: Similar JSX structure, hooks, props? | ||
| * Styling similarity: Similar Tailwind classes or theme usage? | ||
| - Assign match type: 'name', 'structure', or 'visual' | ||
|
|
||
| 4. **Select Top Matches:** | ||
| - Select top 1-3 matches with similarity >= 50% | ||
| - Sort by similarity score (highest first) | ||
| - If no matches found, pass empty array to generate-component | ||
|
|
||
| **Discovery Tips:** | ||
| - Be semantic: "PrimaryButton" and "CallToAction" might serve the same purpose | ||
| - Consider component purpose and context, not just file names | ||
| - Check common directories first: components/ui/, components/shared/ | ||
| - Read component code to understand structure, props, and behavior | ||
| - Trust your judgment using React patterns knowledge | ||
|
|
||
| ### Component Requirements | ||
| - Use React Server Components (RSC) pattern by default | ||
| - Use Tailwind CSS classes with theme tokens, no inline styles or hardcoded values | ||
| - Follow TypeScript strict mode conventions | ||
| - Include proper accessibility attributes | ||
| - Follow existing file naming conventions | ||
| - Use absolute imports from '@/components', '@/lib', etc. | ||
|
|
||
| ## General Development Guidelines | ||
|
|
||
| ### Core Principles | ||
| - Thoroughly analyze requests and the existing project for successful implementation | ||
| - Promptly clarify ambiguous requirements | ||
|
|
||
| ### Development Workflow | ||
| - **Analyze Requirements** - Clearly define the objectives and functionalities required | ||
| - **Review Existing Code** - Examine the current codebase to identify similar solutions and potentially reusable components | ||
| - **Understand Existing Hooks and Utilities** - Familiarize with hooks and utility functions available within the project | ||
| - **Plan Implementation** - Design component structure before coding | ||
| - **Implement Incrementally** - Develop and test the service in small, manageable steps | ||
| - **Test Thoroughly** - Ensure comprehensive testing | ||
|
|
||
| ### After Generation | ||
| - Present the component code to the developer for review | ||
| - Provide file path suggestions based on component type | ||
| - Highlight any design tokens that don't have existing mappings | ||
| - List any validation warnings or suggestions | ||
|
|
||
| ## WORKFLOW_STEPS | ||
|
|
||
| **Create and present to the user a task plan that reflects these steps while keeping the Workflow Guidelines in mind. Wait for approval before proceeding.** | ||
|
|
||
| 1. REQUIRED: Retrieve design context and generated code using mcp__figma__get_design_context with the provided fileKey and nodeId | ||
| 2. REQUIRED: Retrieve visual reference using mcp__figma__get_screenshot with the provided fileKey and nodeId | ||
| 3. OPTIONAL: Retrieve design metadata using mcp__figma__get_metadata with the provided fileKey and nodeId (only if you need detailed structural information about the design hierarchy) | ||
| 4. REQUIRED: Discover similar components in the codebase: | ||
| - Use Glob to find component files in common directories | ||
| - Use Grep to search for components with similar names or structure | ||
| - Use Read to examine promising matches | ||
| - Score similarity (0-100) and select top 1-3 matches | ||
| - Prepare discoveredComponents array for next step | ||
| 5. REQUIRED: Analyze component generation strategy using generate-component tool with discovered components. This provides REUSE/EXTEND/CREATE recommendation. Wait for user approval of strategy before code changes | ||
| 6. REQUIRED: Map Figma design tokens to existing StorefrontNext theme tokens using the map-tokens tool. Extract color, spacing, and other design tokens from Figma data and pass them to this tool for matching | ||
| 7. OPTIONAL (not implemented): Validate the generated component against StorefrontNext patterns using the validate_component tool | ||
| 8. REQUIRED: Implement the recommended approach and present the final component code to the developer for review`; | ||
|
|
||
| export const figmaToComponentSchema = z | ||
| .object({ | ||
| figmaUrl: z | ||
| .string() | ||
| .url() | ||
| .describe('The Figma design URL to convert to a StorefrontNext component. Must include node-id parameter.'), | ||
| workflowFilePath: z | ||
| .string() | ||
| .optional() | ||
| .describe('Optional absolute path to custom workflow .md file. If not provided, uses default built-in workflow.'), | ||
| }) | ||
| .strict(); | ||
|
|
||
| export type FigmaToComponentInput = z.infer<typeof figmaToComponentSchema>; | ||
|
|
||
| export interface WorkflowConfig { | ||
| /** YAML frontmatter key-value pairs. Parsed for future use (e.g., taskType-specific behavior). */ | ||
| metadata: Record<string, string>; | ||
| content: string; | ||
| } | ||
|
|
||
| function extractWorkflowContent(content: string): {metadata: Record<string, string>; body: string} { | ||
| const metadata: Record<string, string> = {}; | ||
| let body = content; | ||
|
|
||
| const metadataMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/); | ||
| if (metadataMatch) { | ||
| const metadataText = metadataMatch[1]; | ||
| body = metadataMatch[2]; | ||
|
|
||
| for (const line of metadataText.split('\n')) { | ||
| const match = line.match(/^(.+?):\s*(.+)$/); | ||
| if (match) { | ||
| metadata[match[1].trim()] = match[2].trim(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return {metadata, body: body.trim()}; | ||
| } | ||
|
|
||
| function parseWorkflowFile(filePath?: string): WorkflowConfig { | ||
| let fileContent: string; | ||
|
|
||
| if (filePath) { | ||
| if (!existsSync(filePath)) { | ||
| throw new Error(`Workflow file not found: ${filePath}`); | ||
| } | ||
| fileContent = readFileSync(filePath, 'utf8'); | ||
| } else { | ||
| fileContent = DEFAULT_WORKFLOW_CONTENT; | ||
| } | ||
|
|
||
| const {metadata, body} = extractWorkflowContent(fileContent); | ||
|
|
||
| return {metadata, content: body}; | ||
| } | ||
|
|
||
| function formatFigmaParams(params: FigmaParams, originalUrl: string): string { | ||
| let section = '## Figma Design Parameters\n\n'; | ||
| section += '```json\n'; | ||
| section += JSON.stringify( | ||
| { | ||
| fileKey: params.fileKey, | ||
| nodeId: params.nodeId, | ||
| originalUrl, | ||
| }, | ||
| null, | ||
| 2, | ||
| ); | ||
| section += '\n```\n\n'; | ||
| section += | ||
| 'IMPORTANT: Use these exact parameters when calling Figma MCP tools. The `clientLanguages` parameter should be set to "typescript" and `clientFrameworks` should be set to "react".\n\n'; | ||
| return section; | ||
| } | ||
|
|
||
| function formatWorkflowContent(content: string): string { | ||
| return `${content}\n\n`; | ||
| } | ||
|
|
||
| function formatNextStepsReminder(): string { | ||
| let reminder = '---\n\n'; | ||
| reminder += '## CRITICAL: Next Steps Required\n\n'; | ||
sf-cboscenco marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| reminder += 'This tool has provided workflow instructions only. You MUST now execute ALL steps below.\n\n'; | ||
| reminder += | ||
| '**EXPECTED FINAL OUTPUT:** A recommendation with confidence score from generate_component tool AND a token mapping summary from map_tokens_to_theme tool.\n\n'; | ||
| reminder += '### Step 1: Fetch Figma Design Data (Parallel Calls)\n'; | ||
| reminder += 'Call these Figma MCP tools with the parameters above:\n'; | ||
| reminder += '- `mcp__figma__get_design_context` (REQUIRED) - Get generated React code\n'; | ||
| reminder += '- `mcp__figma__get_screenshot` (REQUIRED) - Get visual reference\n'; | ||
| reminder += '- `mcp__figma__get_metadata` (OPTIONAL) - Get node structure and hierarchy if needed\n\n'; | ||
| reminder += '### Step 2: Discover Similar Components\n'; | ||
| reminder += 'Use your tools to find existing components:\n'; | ||
| reminder += '- Use `Glob` to find component files: `**/components/**/*.tsx`\n'; | ||
| reminder += '- Use `Grep` to search for similar names or patterns\n'; | ||
| reminder += '- Use `Read` to examine promising matches\n'; | ||
| reminder += '- Score each match (0-100) based on similarity\n\n'; | ||
| reminder += '### Step 3: Analyze Component Strategy (CRITICAL - DO NOT SKIP)\n'; | ||
| reminder += | ||
| 'You MUST call `generate_component` tool with:\n- figmaMetadata (from step 1, or empty string if not fetched)\n- figmaCode (from step 1)\n- componentName (extracted from Figma)\n- discoveredComponents (from step 2)\n\n'; | ||
| reminder += 'This tool returns the recommendation with confidence score that MUST be shown to the user.\n\n'; | ||
| reminder += '### Step 4: Map Design Tokens (CRITICAL - DO NOT SKIP)\n'; | ||
| reminder += 'You MUST call `map_tokens_to_theme` tool with tokens extracted from Figma design.\n\n'; | ||
| reminder += 'This tool returns the token mapping summary that MUST be shown to the user.\n\n'; | ||
| reminder += '### Step 5: Implement\n'; | ||
| reminder += | ||
| 'After showing the recommendation and token mapping to the user, wait for approval then implement the code changes.\n\n'; | ||
| reminder += | ||
| '**DO NOT STOP until you have called generate_component AND map_tokens_to_theme and shown their outputs to the user.**\n\n'; | ||
| return reminder; | ||
| } | ||
|
|
||
| function formatErrorResponse(details: string): string { | ||
| let response = `# Error: Invalid Figma URL\n\n${details}\n\n`; | ||
| response += 'Please provide a valid Figma URL in the format:\n'; | ||
| response += 'https://figma.com/design/:fileKey/:fileName?node-id=1-2\n\n'; | ||
| response += 'Example:\nhttps://figma.com/design/abc123/MyDesign?node-id=1-2\n'; | ||
| return response; | ||
| } | ||
|
|
||
| export function generateWorkflowResponse(figmaUrl: string, workflowFilePath?: string): string { | ||
| let figmaParams: FigmaParams; | ||
| try { | ||
| figmaParams = parseFigmaUrl(figmaUrl); | ||
| } catch (error) { | ||
| const errorMessage = error instanceof Error ? error.message : String(error); | ||
| return formatErrorResponse(errorMessage); | ||
| } | ||
|
|
||
| let workflowConfig: WorkflowConfig; | ||
| try { | ||
| workflowConfig = parseWorkflowFile(workflowFilePath); | ||
| } catch (error) { | ||
| const errorMessage = error instanceof Error ? error.message : String(error); | ||
| return `# Error: Workflow File Not Found\n\n${errorMessage}\n\nPlease provide a valid workflow file path or omit the parameter to use the default workflow.\n`; | ||
| } | ||
|
|
||
| let response = '# Figma to StorefrontNext Workflow Guide\n\n'; | ||
| response += formatFigmaParams(figmaParams, figmaUrl); | ||
| response += formatWorkflowContent(workflowConfig.content); | ||
| response += formatNextStepsReminder(); | ||
|
|
||
| return response; | ||
| } | ||
|
|
||
| export function createFigmaToComponentTool(loadServices: () => Services): McpTool { | ||
| return createToolAdapter<FigmaToComponentInput, string>( | ||
| { | ||
| name: 'storefront_next_figma_to_component_workflow', | ||
| description: | ||
| 'WORKFLOW ORCHESTRATOR: Call this tool FIRST when converting Figma designs. ' + | ||
| 'Parses Figma URL to extract fileKey and nodeId, returns step-by-step workflow instructions ' + | ||
| 'and parameters for subsequent tool calls. ' + | ||
| 'CRITICAL: This is only the FIRST step. After calling this tool, you MUST continue executing ' + | ||
| 'the complete workflow: 1) Call Figma MCP tools, 2) Discover similar components, ' + | ||
| '3) Call generate_component tool, 4) Call map_tokens_to_theme tool, ' + | ||
| '5) Show both outputs to the user then implement the recommended approach.', | ||
| toolsets: ['STOREFRONTNEXT'], | ||
| isGA: false, | ||
| requiresInstance: false, | ||
| inputSchema: figmaToComponentSchema.shape, | ||
| async execute(args) { | ||
| return generateWorkflowResponse(args.figmaUrl, args.workflowFilePath); | ||
| }, | ||
| formatOutput: (output) => textResult(output), | ||
| }, | ||
| loadServices, | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.