# Directory Structure ``` ├── .gitignore ├── dockerfile ├── package-lock.json ├── package.json ├── README.md ├── src │ ├── figma-api.ts │ ├── index.ts │ ├── prompts.ts │ ├── resources.ts │ └── tools.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Dependencies 2 | node_modules/ 3 | npm-debug.log 4 | yarn-debug.log 5 | yarn-error.log 6 | 7 | # Build outputs 8 | build/ 9 | dist/ 10 | *.tsbuildinfo 11 | 12 | # Environment variables 13 | .env 14 | .env.local 15 | .env.development 16 | .env.test 17 | .env.production 18 | 19 | # IDE and editor files 20 | .idea/ 21 | .vscode/ 22 | *.swp 23 | *.swo 24 | .DS_Store 25 | 26 | # Logs 27 | logs/ 28 | *.log 29 | 30 | # Coverage reports 31 | coverage/ 32 | 33 | # Temporary files 34 | tmp/ 35 | temp/ ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Figma MCP Server 2 | 3 | A Model Context Protocol (MCP) server that connects to Figma's API, allowing AI tools and LLMs to access and work with your Figma designs. 4 | 5 | 6 | 7 | ## Features 8 | 9 | - **Design Data Extraction**: Extract components, styles, and text from your Figma designs 10 | - **Design System Analysis**: Analyze design system consistency and patterns 11 | - **UI Content Management**: Extract and organize all UI copy from designs 12 | - **Development Handoff**: Generate comprehensive documentation for developers 13 | - **Seamless AI Integration**: Connect your designs to AI tools like Claude, Cursor, and other MCP-compatible clients 14 | 15 | ## Getting Started 16 | 17 | ### Prerequisites 18 | 19 | - Node.js 16 or higher 20 | - Figma Personal Access Token (Get it from your Figma account settings) 21 | 22 | ### Installation 23 | 24 | 1. Clone the repository: 25 | ```bash 26 | git clone https://github.com/yourusername/figma-mcp-server.git 27 | cd figma-mcp-server 28 | ``` 29 | 30 | 2. Install dependencies: 31 | ```bash 32 | npm install 33 | ``` 34 | 35 | 3. Create a `.env` file in the project root: 36 | ``` 37 | FIGMA_API_TOKEN=your_figma_personal_access_token 38 | API_KEY=your_secure_api_key 39 | TRANSPORT_TYPE=stdio 40 | ``` 41 | 42 | 4. Build the server: 43 | ```bash 44 | npm run build 45 | ``` 46 | 47 | 5. Start the server: 48 | ```bash 49 | npm start 50 | ``` 51 | 52 | 53 | 54 | ## Connecting to Clients 55 | 56 | ### Claude for Desktop 57 | 58 | 1. Open or create the Claude for Desktop configuration file: 59 | - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` 60 | - Windows: `%APPDATA%\Claude\claude_desktop_config.json` 61 | 62 | 2. Add the following configuration: 63 | ```json 64 | { 65 | "mcpServers": { 66 | "figma": { 67 | "command": "node", 68 | "args": ["/absolute/path/to/figma-mcp-server/build/index.js"], 69 | "env": { 70 | "FIGMA_API_TOKEN": "your_figma_personal_access_token", 71 | "TRANSPORT_TYPE": "stdio" 72 | } 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | 3. Restart Claude for Desktop 79 | 80 | ### Cursor 81 | 82 | #### Global Configuration 83 | 84 | Create or edit Cursor's MCP configuration file: 85 | - macOS: `~/Library/Application Support/Cursor/mcp.json` 86 | - Windows: `%APPDATA%\Cursor\mcp.json` 87 | 88 | ```json 89 | { 90 | "mcpServers": { 91 | "figma-mcp": { 92 | "url": "http://localhost:3000/sse", 93 | "env": { 94 | "API_KEY": "your_secure_api_key" 95 | } 96 | } 97 | } 98 | } 99 | ``` 100 | 101 | #### Project-Specific Configuration 102 | 103 | 1. Create a `.cursor` directory in your project root: 104 | ```bash 105 | mkdir -p .cursor 106 | ``` 107 | 108 | 2. Create an `mcp.json` file inside that directory: 109 | ```json 110 | { 111 | "mcpServers": { 112 | "figma-mcp": { 113 | "url": "http://localhost:3000/sse", 114 | "env": { 115 | "API_KEY": "your_secure_api_key" 116 | } 117 | } 118 | } 119 | } 120 | ``` 121 | 122 | ## Available Tools 123 | 124 | | Tool | Description | 125 | |------|-------------| 126 | | `get-file-info` | Get basic information about a Figma file | 127 | | `get-nodes` | Get specific nodes from a Figma file | 128 | | `get-components` | Get component information from a Figma file | 129 | | `get-styles` | Get style information from a Figma file | 130 | | `get-comments` | Get comments from a Figma file | 131 | | `search-file` | Search for elements in a Figma file by type, name, etc. | 132 | | `extract-text` | Extract all text elements from a Figma file | 133 | 134 | 135 | 136 | ## Available Prompts 137 | 138 | - `analyze-design-system` - Analyze design system components and styles for consistency 139 | - `extract-ui-copy` - Extract and organize all UI copy from designs 140 | - `generate-dev-handoff` - Generate development handoff documentation based on designs 141 | 142 | ## Usage Examples 143 | 144 | Using with Claude: 145 | ``` 146 | Can you analyze the design system in my Figma file with key abc123? Look for consistency in color usage and typography. 147 | ``` 148 | 149 | Using with Cursor: 150 | ``` 151 | Generate React components for the buttons from my Figma file with key abc123, using tailwind CSS. 152 | ``` 153 | 154 | ## Environment Variables 155 | 156 | | Variable | Description | Default | 157 | |----------|-------------|---------| 158 | | `FIGMA_API_TOKEN` | Your Figma Personal Access Token | (Required) | 159 | | `API_KEY` | Security key for API authentication | (Required) | 160 | | `TRANSPORT_TYPE` | Transport method (`stdio` or `sse`) | `stdio` | 161 | | `PORT` | Port for SSE transport | `3000` | 162 | 163 | ## Architecture 164 | 165 | This MCP server: 166 | 1. Connects to the Figma API using your personal access token 167 | 2. Exposes a standardized interface following the Model Context Protocol 168 | 3. Provides tools, resources, and prompts that LLMs can use to interact with your Figma designs 169 | 4. Supports both stdio transport (local connections) and SSE transport (remote connections) 170 | 171 | ## Contributing 172 | 173 | Contributions are welcome! Please feel free to submit a Pull Request. 174 | 175 | ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "./build", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules"] 15 | } ``` -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- ```dockerfile 1 | FROM node:18-alpine 2 | 3 | WORKDIR /app 4 | 5 | # Copy package files and install dependencies 6 | COPY package*.json ./ 7 | RUN npm ci --only=production 8 | 9 | # Copy the built app 10 | COPY build/ ./build/ 11 | 12 | # Set environment variables (placeholder values to be overridden at runtime) 13 | ENV FIGMA_API_TOKEN="" 14 | ENV TRANSPORT_TYPE="sse" 15 | ENV PORT=3000 16 | ENV API_KEY="" 17 | 18 | # Expose the port 19 | EXPOSE 3000 20 | 21 | # Run the server 22 | CMD ["node", "build/index.js"] ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "figma-mcp-server", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsc", 8 | "start": "node build/index.js", 9 | "dev": "tsc && node build/index.js", 10 | "start:stdio": "TRANSPORT_TYPE=stdio node build/index.js", 11 | "start:sse": "TRANSPORT_TYPE=sse node build/index.js" 12 | }, 13 | "keywords": [ 14 | "figma", 15 | "mcp", 16 | "design" 17 | ], 18 | "author": "Mohammed-uvaiz", 19 | "license": "ISC", 20 | "description": "", 21 | "dependencies": { 22 | "@modelcontextprotocol/sdk": "^1.7.0", 23 | "cors": "^2.8.5", 24 | "dotenv": "^16.4.7", 25 | "express": "^5.0.1", 26 | "node-fetch": "^3.3.2", 27 | "zod": "^3.24.2" 28 | }, 29 | "devDependencies": { 30 | "@types/cors": "^2.8.17", 31 | "@types/express": "^5.0.0", 32 | "@types/node": "^22.13.10", 33 | "typescript": "^5.8.2" 34 | } 35 | } 36 | ``` -------------------------------------------------------------------------------- /src/figma-api.ts: -------------------------------------------------------------------------------- ```typescript 1 | import fetch from "node-fetch"; 2 | import dotenv from "dotenv"; 3 | dotenv.config(); 4 | 5 | // Base URL for Figma API 6 | export const FIGMA_API_BASE_URL = "https://api.figma.com/v1"; 7 | 8 | 9 | // Check if Figma API token is available 10 | const FIGMA_API_TOKEN = process.env.FIGMA_API_TOKEN; 11 | if (!FIGMA_API_TOKEN) { 12 | console.error("Error: FIGMA_API_TOKEN environment variable is required"); 13 | process.exit(1); 14 | } 15 | 16 | /** 17 | * Helper function to make authenticated requests to Figma API 18 | * @param endpoint API endpoint path (without base URL) 19 | * @returns Promise with JSON response 20 | */ 21 | export async function fetchFigmaAPI(endpoint: string) { 22 | const response = await fetch(`${FIGMA_API_BASE_URL}${endpoint}`, { 23 | headers: { 24 | "X-Figma-Token": FIGMA_API_TOKEN as string 25 | } 26 | }); 27 | 28 | if (!response.ok) { 29 | throw new Error(`Figma API error: ${response.status} ${response.statusText}`); 30 | } 31 | 32 | return response.json(); 33 | } 34 | 35 | /** 36 | * Helper function to recursively search through nodes by criteria 37 | * @param node The node to search from 38 | * @param query The search query string 39 | * @returns Array of matching nodes 40 | */ 41 | export function searchNodes(node: any, query: string, results: any[] = []) { 42 | // Check if this node matches search criteria 43 | if (node.name && node.name.toLowerCase().includes(query.toLowerCase())) { 44 | results.push({ 45 | id: node.id, 46 | name: node.name, 47 | type: node.type, 48 | path: [node.name] 49 | }); 50 | } 51 | 52 | // Recursively search child nodes if they exist 53 | if (node.children && node.children.length > 0) { 54 | for (const child of node.children) { 55 | searchNodes(child, query, results); 56 | } 57 | } 58 | 59 | return results; 60 | } 61 | 62 | /** 63 | * Helper function to recursively find text nodes 64 | * @param node The node to search from 65 | * @returns Array of text nodes 66 | */ 67 | export function findTextNodes(node: any, results: any[] = []) { 68 | if (node.type === "TEXT") { 69 | results.push({ 70 | id: node.id, 71 | name: node.name, 72 | characters: node.characters, 73 | style: node.style 74 | }); 75 | } 76 | 77 | // Recursively search child nodes if they exist 78 | if (node.children && node.children.length > 0) { 79 | for (const child of node.children) { 80 | findTextNodes(child, results); 81 | } 82 | } 83 | 84 | return results; 85 | } ``` -------------------------------------------------------------------------------- /src/prompts.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { z } from "zod"; 3 | 4 | /** 5 | * Register all Figma-related prompts with the MCP server 6 | * @param server The MCP server instance 7 | */ 8 | export function registerPrompts(server: McpServer) { 9 | // Prompt to analyze design system consistency 10 | server.prompt( 11 | "analyze-design-system", 12 | "Analyze design system components and styles for consistency", 13 | { 14 | fileKey: z.string().describe("The Figma file key") 15 | }, 16 | ({ fileKey }) => ({ 17 | messages: [{ 18 | role: "user", 19 | content: { 20 | type: "text", 21 | text: `Please analyze the design system in this Figma file (${fileKey}). 22 | Focus on color consistency, typography usage, component variations, and 23 | any inconsistencies or opportunities for improvement. First list the components 24 | and styles using the appropriate tools, then provide your analysis.` 25 | } 26 | }] 27 | }) 28 | ); 29 | 30 | // Prompt to extract UI copy 31 | server.prompt( 32 | "extract-ui-copy", 33 | "Extract and organize all UI copy from designs", 34 | { 35 | fileKey: z.string().describe("The Figma file key") 36 | }, 37 | ({ fileKey }) => ({ 38 | messages: [{ 39 | role: "user", 40 | content: { 41 | type: "text", 42 | text: `Please extract all UI copy from this Figma file (${fileKey}). 43 | Organize the text by screens or components, and identify any potential 44 | inconsistencies in tone, terminology, or language. 45 | Use the extract-text tool to get the content.` 46 | } 47 | }] 48 | }) 49 | ); 50 | 51 | // Prompt to generate development handoff documentation 52 | server.prompt( 53 | "generate-dev-handoff", 54 | "Generate development handoff documentation based on designs", 55 | { 56 | fileKey: z.string().describe("The Figma file key") 57 | }, 58 | ({ fileKey }) => ({ 59 | messages: [{ 60 | role: "user", 61 | content: { 62 | type: "text", 63 | text: `Please create comprehensive development handoff documentation for this Figma file (${fileKey}). 64 | Include component specifications, style guides, interaction patterns, and responsive behavior descriptions. 65 | Use the appropriate tools to gather file data, components, and styles.` 66 | } 67 | }] 68 | }) 69 | ); 70 | } ``` -------------------------------------------------------------------------------- /src/resources.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { fetchFigmaAPI } from "./figma-api.js"; 3 | 4 | interface ComponentsResponse { 5 | meta: { 6 | components: unknown[]; 7 | }; 8 | } 9 | interface StylesResponse { 10 | meta: { 11 | styles: unknown[]; 12 | }; 13 | } 14 | 15 | /** 16 | * Register all Figma-related resources with the MCP server 17 | * @param server The MCP server instance 18 | */ 19 | export function registerResources(server: McpServer) { 20 | // Resource to list Figma files a user has access to 21 | server.resource( 22 | "user-files", 23 | "figma://files", 24 | async (uri) => { 25 | try { 26 | const filesData = await fetchFigmaAPI(`/me/files`); 27 | 28 | return { 29 | contents: [{ 30 | uri: uri.href, 31 | text: JSON.stringify(filesData, null, 2) 32 | }] 33 | }; 34 | } catch (error) { 35 | throw new Error(`Error fetching Figma files: ${error instanceof Error ? error.message : String(error)}`); 36 | } 37 | } 38 | ); 39 | 40 | // Resource to list projects 41 | server.resource( 42 | "projects", 43 | "figma://projects", 44 | async (uri) => { 45 | try { 46 | const projectsData = await fetchFigmaAPI(`/me/projects`); 47 | 48 | return { 49 | contents: [{ 50 | uri: uri.href, 51 | text: JSON.stringify(projectsData, null, 2) 52 | }] 53 | }; 54 | } catch (error) { 55 | throw new Error(`Error fetching Figma projects: ${error instanceof Error ? error.message : String(error)}`); 56 | } 57 | } 58 | ); 59 | 60 | // Resource to get file data 61 | server.resource( 62 | "file-data", 63 | new ResourceTemplate("figma://{fileKey}/data", { list: undefined }), 64 | async (uri, { fileKey }) => { 65 | try { 66 | const fileData = await fetchFigmaAPI(`/files/${fileKey}`); 67 | 68 | return { 69 | contents: [{ 70 | uri: uri.href, 71 | text: JSON.stringify(fileData, null, 2) 72 | }] 73 | }; 74 | } catch (error) { 75 | throw new Error(`Error fetching Figma file data: ${error instanceof Error ? error.message : String(error)}`); 76 | } 77 | } 78 | ); 79 | 80 | // Resource to get design system metadata 81 | server.resource( 82 | "design-system", 83 | new ResourceTemplate("figma://{fileKey}/design-system", { list: undefined }), 84 | async (uri, { fileKey }) => { 85 | try { 86 | // Get components and styles in parallel 87 | const [componentsData, stylesData] = await Promise.all([ 88 | fetchFigmaAPI(`/files/${fileKey}/components`) as Promise<ComponentsResponse>, 89 | fetchFigmaAPI(`/files/${fileKey}/styles`) as Promise<StylesResponse> 90 | ]); 91 | 92 | const designSystem = { 93 | components: componentsData.meta.components, 94 | styles: stylesData.meta.styles 95 | }; 96 | 97 | return { 98 | contents: [{ 99 | uri: uri.href, 100 | text: JSON.stringify(designSystem, null, 2) 101 | }] 102 | }; 103 | } catch (error) { 104 | throw new Error(`Error fetching design system data: ${error instanceof Error ? error.message : String(error)}`); 105 | } 106 | } 107 | ); 108 | } ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 3 | import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; 4 | import express from "express"; 5 | import cors from "cors"; 6 | import dotenv from "dotenv"; 7 | 8 | // Import our components 9 | import { registerTools } from "./tools.js"; 10 | import { registerResources } from "./resources.js"; 11 | import { registerPrompts } from "./prompts.js"; 12 | 13 | // Load environment variables 14 | console.error("Loading environment variables..."); 15 | dotenv.config(); 16 | console.error("Environment loaded. FIGMA_API_TOKEN present:", process.env.FIGMA_API_TOKEN ? "Yes" : "No"); 17 | 18 | // Check token before proceeding 19 | if (!process.env.FIGMA_API_TOKEN) { 20 | console.error("ERROR: FIGMA_API_TOKEN environment variable is required!"); 21 | process.exit(1); 22 | } 23 | 24 | async function main() { 25 | try { 26 | console.error("===== FIGMA MCP SERVER STARTING ====="); 27 | 28 | // Create the MCP server 29 | console.error("Creating MCP server instance..."); 30 | const server = new McpServer({ 31 | name: "figma-mcp-server", 32 | version: "1.0.0" 33 | }); 34 | console.error("MCP server instance created successfully"); 35 | 36 | // Register all components 37 | console.error("Registering tools..."); 38 | registerTools(server); 39 | console.error("Registering resources..."); 40 | registerResources(server); 41 | console.error("Registering prompts..."); 42 | registerPrompts(server); 43 | console.error("All components registered successfully"); 44 | 45 | // Determine transport type based on environment 46 | const transportType = process.env.TRANSPORT_TYPE || "stdio"; 47 | console.error(`Using transport type: ${transportType}`); 48 | 49 | if (transportType === "stdio") { 50 | // Use stdio transport for local connections 51 | console.error("Creating StdioServerTransport..."); 52 | const transport = new StdioServerTransport(); 53 | console.error("Connecting server to stdio transport..."); 54 | await server.connect(transport); 55 | console.error("Successfully connected to stdio transport"); 56 | } else if (transportType === "sse") { 57 | // Use SSE transport for remote connections 58 | console.error("Starting web server for SSE transport..."); 59 | 60 | const app = express(); 61 | const port = process.env.PORT || 3000; 62 | 63 | // Add CORS support 64 | app.use(cors()); 65 | // Parse JSON bodies 66 | app.use(express.json()); 67 | // ADD AUTHENTICATION HERE 68 | const API_KEY = process.env.API_KEY || "default-key-please-change"; 69 | 70 | // Middleware to check for API key 71 | const authenticateApiKey = (req: express.Request, res: express.Response, next: express.NextFunction): void => { 72 | const providedKey = req.headers['x-api-key']; 73 | 74 | if (!providedKey || providedKey !== API_KEY) { 75 | res.status(401).json({ error: "Unauthorized" }); 76 | return; 77 | } 78 | 79 | next(); 80 | }; 81 | // Add a basic home page 82 | app.get("/", (req, res) => { 83 | res.send("Figma MCP Server - Status: Running"); 84 | }); 85 | 86 | // Health check endpoint 87 | app.get("/health", (req, res) => { 88 | res.json({ status: "ok", version: "1.0.0" }); 89 | }); 90 | 91 | // Map to store active transports 92 | const activeTransports = new Map(); 93 | 94 | // SSE endpoint 95 | app.get("/sse",authenticateApiKey, (req, res) => { 96 | console.error("New SSE connection request received"); 97 | const clientId = Date.now().toString(); 98 | 99 | res.setHeader("Content-Type", "text/event-stream"); 100 | res.setHeader("Cache-Control", "no-cache"); 101 | res.setHeader("Connection", "keep-alive"); 102 | 103 | // Create a new transport for this connection 104 | const transport = new SSEServerTransport("/messages", res); 105 | activeTransports.set(clientId, transport); 106 | 107 | console.error(`SSE connection established for client ${clientId}`); 108 | 109 | // Connect the server to this transport 110 | server.connect(transport).catch(err => { 111 | console.error(`Error connecting to transport for client ${clientId}:`, err); 112 | }); 113 | 114 | // Handle client disconnect 115 | req.on("close", () => { 116 | console.error(`Client ${clientId} disconnected`); 117 | activeTransports.delete(clientId); 118 | }); 119 | }); 120 | 121 | // Message endpoint for client-to-server communication 122 | app.post("/messages", authenticateApiKey,async (req, res) => { 123 | console.error("Received message from client"); 124 | 125 | // Find the active transport 126 | // Note: In a real implementation, you'd need a way to identify 127 | // which transport to use based on the client 128 | 129 | if (activeTransports.size > 0) { 130 | const transport = Array.from(activeTransports.values())[0]; 131 | await transport.handlePostMessage(req, res); 132 | } else { 133 | res.status(400).json({ error: "No active connections" }); 134 | } 135 | }); 136 | 137 | // Start the server 138 | app.listen(port, () => { 139 | console.error(`Web server running on port ${port}`); 140 | }); 141 | } else { 142 | console.error(`Unknown transport type: ${transportType}`); 143 | process.exit(1); 144 | } 145 | 146 | console.error("===== FIGMA MCP SERVER RUNNING ====="); 147 | 148 | } catch (error) { 149 | console.error("FATAL SERVER ERROR:"); 150 | console.error(error); 151 | process.exit(1); 152 | } 153 | } 154 | 155 | // Run the server 156 | console.error("Calling main()..."); 157 | main().catch(error => { 158 | console.error("Unhandled error in main():", error); 159 | process.exit(1); 160 | }); ``` -------------------------------------------------------------------------------- /src/tools.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { z } from "zod"; 3 | import { fetchFigmaAPI, searchNodes, findTextNodes } from "./figma-api.js"; 4 | 5 | interface FigmaFileResponse { 6 | name: string; 7 | lastModified: string; 8 | version: string; 9 | document: { 10 | id: string; 11 | name: string; 12 | type: string; 13 | }; 14 | schemaVersion: number; 15 | thumbnailUrl: string; 16 | } 17 | 18 | 19 | export function registerTools(server: McpServer) { 20 | // Tool to get file information 21 | server.tool( 22 | "get-file-info", 23 | "Get basic information about a Figma file", 24 | { 25 | fileKey: z.string().describe("The Figma file key (found in the file URL)") 26 | }, 27 | async ({ fileKey }) => { 28 | try { 29 | const fileData = await fetchFigmaAPI(`/files/${fileKey}`) as FigmaFileResponse; 30 | return { 31 | content: [ 32 | { 33 | type: "text", 34 | text: JSON.stringify({ 35 | name: fileData.name, 36 | lastModified: fileData.lastModified, 37 | version: fileData.version, 38 | document: { 39 | id: fileData.document.id, 40 | name: fileData.document.name, 41 | type: fileData.document.type 42 | }, 43 | schemaVersion: fileData.schemaVersion, 44 | thumbnailUrl: fileData.thumbnailUrl 45 | }, null, 2) 46 | } 47 | ] 48 | }; 49 | } catch (error) { 50 | return { 51 | content: [ 52 | { 53 | type: "text", 54 | text: `Error fetching Figma file information: ${error instanceof Error ? error.message : String(error)}` 55 | } 56 | ], 57 | isError: true 58 | }; 59 | } 60 | } 61 | ); 62 | 63 | // Tool to get file nodes 64 | server.tool( 65 | "get-nodes", 66 | "Get specific nodes from a Figma file", 67 | { 68 | fileKey: z.string().describe("The Figma file key (found in the file URL)"), 69 | nodeIds: z.array(z.string()).describe("Array of node IDs to fetch") 70 | }, 71 | async ({ fileKey, nodeIds }) => { 72 | try { 73 | const nodeIdsParam = nodeIds.join(","); 74 | const nodesData = await fetchFigmaAPI(`/files/${fileKey}/nodes?ids=${nodeIdsParam}`); 75 | 76 | return { 77 | content: [ 78 | { 79 | type: "text", 80 | text: JSON.stringify(nodesData, null, 2) 81 | } 82 | ] 83 | }; 84 | } catch (error) { 85 | return { 86 | content: [ 87 | { 88 | type: "text", 89 | text: `Error fetching Figma nodes: ${error instanceof Error ? error.message : String(error)}` 90 | } 91 | ], 92 | isError: true 93 | }; 94 | } 95 | } 96 | ); 97 | 98 | // Tool to get file component sets and components 99 | server.tool( 100 | "get-components", 101 | "Get component information from a Figma file", 102 | { 103 | fileKey: z.string().describe("The Figma file key (found in the file URL)") 104 | }, 105 | async ({ fileKey }) => { 106 | try { 107 | const componentsData = await fetchFigmaAPI(`/files/${fileKey}/components`); 108 | 109 | return { 110 | content: [ 111 | { 112 | type: "text", 113 | text: JSON.stringify(componentsData, null, 2) 114 | } 115 | ] 116 | }; 117 | } catch (error) { 118 | return { 119 | content: [ 120 | { 121 | type: "text", 122 | text: `Error fetching Figma components: ${error instanceof Error ? error.message : String(error)}` 123 | } 124 | ], 125 | isError: true 126 | }; 127 | } 128 | } 129 | ); 130 | 131 | // Tool to get design system styles 132 | server.tool( 133 | "get-styles", 134 | "Get style information from a Figma file", 135 | { 136 | fileKey: z.string().describe("The Figma file key (found in the file URL)") 137 | }, 138 | async ({ fileKey }) => { 139 | try { 140 | const stylesData = await fetchFigmaAPI(`/files/${fileKey}/styles`); 141 | 142 | return { 143 | content: [ 144 | { 145 | type: "text", 146 | text: JSON.stringify(stylesData, null, 2) 147 | } 148 | ] 149 | }; 150 | } catch (error) { 151 | return { 152 | content: [ 153 | { 154 | type: "text", 155 | text: `Error fetching Figma styles: ${error instanceof Error ? error.message : String(error)}` 156 | } 157 | ], 158 | isError: true 159 | }; 160 | } 161 | } 162 | ); 163 | 164 | // Tool to get comments from a Figma file 165 | server.tool( 166 | "get-comments", 167 | "Get comments from a Figma file", 168 | { 169 | fileKey: z.string().describe("The Figma file key (found in the file URL)") 170 | }, 171 | async ({ fileKey }) => { 172 | try { 173 | const commentsData = await fetchFigmaAPI(`/files/${fileKey}/comments`); 174 | 175 | return { 176 | content: [ 177 | { 178 | type: "text", 179 | text: JSON.stringify(commentsData, null, 2) 180 | } 181 | ] 182 | }; 183 | } catch (error) { 184 | return { 185 | content: [ 186 | { 187 | type: "text", 188 | text: `Error fetching Figma comments: ${error instanceof Error ? error.message : String(error)}` 189 | } 190 | ], 191 | isError: true 192 | }; 193 | } 194 | } 195 | ); 196 | 197 | // Tool to search for specific elements in a Figma file 198 | server.tool( 199 | "search-file", 200 | "Search for elements in a Figma file by type, name, etc.", 201 | { 202 | fileKey: z.string().describe("The Figma file key (found in the file URL)"), 203 | query: z.string().describe("Search query") 204 | }, 205 | async ({ fileKey, query }) => { 206 | try { 207 | // Fetch all file data first 208 | const fileData = await fetchFigmaAPI(`/files/${fileKey}`) as FigmaFileResponse; 209 | const searchResults = searchNodes(fileData.document, query); 210 | 211 | return { 212 | content: [ 213 | { 214 | type: "text", 215 | text: JSON.stringify(searchResults, null, 2) 216 | } 217 | ] 218 | }; 219 | } catch (error) { 220 | return { 221 | content: [ 222 | { 223 | type: "text", 224 | text: `Error searching Figma file: ${error instanceof Error ? error.message : String(error)}` 225 | } 226 | ], 227 | isError: true 228 | }; 229 | } 230 | } 231 | ); 232 | 233 | // Tool to extract text from a Figma file 234 | server.tool( 235 | "extract-text", 236 | "Extract all text elements from a Figma file", 237 | { 238 | fileKey: z.string().describe("The Figma file key (found in the file URL)") 239 | }, 240 | async ({ fileKey }) => { 241 | try { 242 | // Fetch all file data first 243 | const fileData = await fetchFigmaAPI(`/files/${fileKey}`) as FigmaFileResponse; 244 | const textNodes = findTextNodes(fileData.document); 245 | 246 | return { 247 | content: [ 248 | { 249 | type: "text", 250 | text: JSON.stringify(textNodes, null, 2) 251 | } 252 | ] 253 | }; 254 | } catch (error) { 255 | return { 256 | content: [ 257 | { 258 | type: "text", 259 | text: `Error extracting text from Figma file: ${error instanceof Error ? error.message : String(error)}` 260 | } 261 | ], 262 | isError: true 263 | }; 264 | } 265 | } 266 | ); 267 | } ```