#
tokens: 8902/50000 10/10 files
lines: on (toggle) GitHub
raw markdown copy reset
# 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 | }
```