#
tokens: 11015/50000 12/12 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── LICENSE
├── mcp-settings-example.json
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── accessibility.ts
│   ├── component-generator.ts
│   ├── figma-client.ts
│   ├── figma-react-tools.ts
│   ├── figma-tailwind-converter.ts
│   ├── figma-tools.ts
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependency directories
node_modules/

# Build output
dist/

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea/
.vscode/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
pnpm-lock.yaml

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# MCP Figma to React Converter

This is a Model Context Protocol (MCP) server that converts Figma designs to React components. It provides tools for fetching Figma designs and generating React components with TypeScript and Tailwind CSS.

## Features

- Fetch Figma designs using the Figma API
- Extract components from Figma designs
- Generate React components with TypeScript
- Apply Tailwind CSS classes based on Figma styles
- Enhance components with accessibility features
- Support for both stdio and SSE transports

## Prerequisites

- Node.js 18 or higher
- A Figma API token

## Installation

1. Clone the repository
2. Install dependencies:

```bash
npm install
```

3. Build the project:

```bash
npm run build
```

## Configuration

You need to set the `FIGMA_API_TOKEN` environment variable to your Figma API token. You can get a personal access token from the Figma account settings page.

## Usage

### Running as a local MCP server

```bash
FIGMA_API_TOKEN=your_token_here npm start
```

Or with explicit transport:

```bash
FIGMA_API_TOKEN=your_token_here node dist/index.js --transport=stdio
```

### Running as an HTTP server

```bash
FIGMA_API_TOKEN=your_token_here node dist/index.js --transport=sse
```

## Available Tools

### Figma Tools

- `getFigmaProject`: Get a Figma project structure
- `getFigmaComponentNodes`: Get component nodes from a Figma file
- `extractFigmaComponents`: Extract components from a Figma file
- `getFigmaComponentSets`: Get component sets from a Figma file

### React Tools

- `generateReactComponent`: Generate a React component from a Figma node
- `generateComponentLibrary`: Generate multiple React components from Figma components
- `writeComponentsToFiles`: Write generated components to files
- `figmaToReactWorkflow`: Complete workflow to convert Figma designs to React components

## Example Workflow

1. Get a Figma file key (the string after `figma.com/file/` in the URL)
2. Use the `figmaToReactWorkflow` tool with the file key and output directory
3. The tool will extract components, generate React code, and save the files

## Development

For development, you can use the watch mode:

```bash
npm run dev
```

## License

ISC
```

--------------------------------------------------------------------------------
/mcp-settings-example.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "figma-to-react": {
      "command": "node",
      "args": ["path/to/mcp-figma-to-react/dist/index.js", "--transport=stdio"],
      "env": {
        "FIGMA_API_TOKEN": "your_figma_api_token_here"
      },
      "disabled": false,
      "alwaysAllow": []
    }
  }
}
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist",
    "sourceMap": true,
    "declaration": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "mcp-figma-to-react",
  "version": "1.0.0",
  "description": "MCP server for converting Figma designs to React components",
  "main": "dist/index.js",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc -w & node --watch dist/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "figma",
    "react",
    "mcp",
    "component-generator"
  ],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@modelcontextprotocol/sdk": "1.10.2",
    "axios": "^1.8.4",
    "express": "5.1.0",
    "prettier": "^3.5.3",
    "typescript": "5.8.3",
    "ws": "^8.18.1",
    "zod": "3.24.3"
  },
  "devDependencies": {
    "@types/express": "^5.0.1",
    "@types/node": "22.14.1",
    "@types/ws": "8.18.1"
  }
}

```

--------------------------------------------------------------------------------
/src/figma-client.ts:
--------------------------------------------------------------------------------

```typescript
import axios from 'axios';

// Define types for Figma API responses
export interface FigmaNode {
  id: string;
  name: string;
  type: string;
  [key: string]: any;
}

export interface GetFileResponse {
  document: {
    children: FigmaNode[];
    [key: string]: any;
  };
  components: Record<string, any>;
  styles: Record<string, any>;
  [key: string]: any;
}

export interface GetFileNodesResponse {
  nodes: Record<string, {
    document: FigmaNode;
    components?: Record<string, any>;
    styles?: Record<string, any>;
    [key: string]: any;
  }>;
}

export class FigmaClient {
  private token: string;
  private baseUrl = 'https://api.figma.com/v1';
  
  constructor(token: string) {
    this.token = token;
  }
  
  async getFile(fileKey: string): Promise<GetFileResponse> {
    try {
      const response = await axios.get<GetFileResponse>(
        `${this.baseUrl}/files/${fileKey}`,
        {
          headers: {
            'X-Figma-Token': this.token
          }
        }
      );
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        throw new Error(`Figma API error: ${error.response?.data?.message || error.message}`);
      }
      throw error;
    }
  }
  
  async getFileNodes(fileKey: string, nodeIds: string[]): Promise<GetFileNodesResponse> {
    try {
      const response = await axios.get<GetFileNodesResponse>(
        `${this.baseUrl}/files/${fileKey}/nodes`,
        {
          params: { ids: nodeIds.join(',') },
          headers: {
            'X-Figma-Token': this.token
          }
        }
      );
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        throw new Error(`Figma API error: ${error.response?.data?.message || error.message}`);
      }
      throw error;
    }
  }

  async getImageFills(fileKey: string, nodeIds: string[]): Promise<Record<string, string>> {
    try {
      const response = await axios.get(
        `${this.baseUrl}/images/${fileKey}`,
        {
          params: { ids: nodeIds.join(','), format: 'png' },
          headers: {
            'X-Figma-Token': this.token
          }
        }
      );
      return response.data.images || {};
    } catch (error) {
      if (axios.isAxiosError(error)) {
        throw new Error(`Figma API error: ${error.response?.data?.message || error.message}`);
      }
      throw error;
    }
  }

  async getComponentSets(fileKey: string): Promise<any> {
    try {
      const response = await axios.get(
        `${this.baseUrl}/files/${fileKey}/component_sets`,
        {
          headers: {
            'X-Figma-Token': this.token
          }
        }
      );
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        throw new Error(`Figma API error: ${error.response?.data?.message || error.message}`);
      }
      throw error;
    }
  }
}
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express';
import { FigmaClient } from './figma-client.js';
import { registerFigmaTools } from './figma-tools.js';
import { ComponentGenerator } from './component-generator.js';
import { registerReactTools } from './figma-react-tools.js';

// Get Figma API token from environment variable
const FIGMA_API_TOKEN = process.env.FIGMA_API_TOKEN;
if (!FIGMA_API_TOKEN) {
  console.error('FIGMA_API_TOKEN environment variable is required');
  process.exit(1);
}

// Create the MCP server
const server = new McpServer({
  name: 'Figma to React Converter',
  version: '1.0.0',
  description: 'MCP server for converting Figma designs to React components'
});

// Initialize Figma client and component generator
const figmaClient = new FigmaClient(FIGMA_API_TOKEN);
const componentGenerator = new ComponentGenerator();

// Register tools with the server
registerFigmaTools(server, figmaClient);
registerReactTools(server, componentGenerator, figmaClient);

// Determine the transport to use based on command-line arguments
const transportArg = process.argv.find(arg => arg.startsWith('--transport='));
const transportType = transportArg ? transportArg.split('=')[1] : 'stdio';

async function main() {
  try {
    if (transportType === 'stdio') {
      // Use stdio transport for local MCP server
      const transport = new StdioServerTransport();
      await server.connect(transport);
      console.error('Figma to React MCP server running on stdio');
    } else if (transportType === 'sse') {
      // Set up Express server
      const app = express();
      const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
      
      app.use(express.json());
      
      // Health check endpoint
      app.get('/health', (_req, res) => {
        res.status(200).send('OK');
      });
      
      // SSE endpoint
      app.get('/sse', async (req: express.Request, res: express.Response) => {
        res.setHeader('Content-Type', 'text/event-stream');
        res.setHeader('Cache-Control', 'no-cache');
        res.setHeader('Connection', 'keep-alive');
        
        const transport = new SSEServerTransport('/messages', res);
        await server.connect(transport);
        
        req.on('close', async () => {
          await server.close();
        });
      });
      
      // Message endpoint
      app.post('/messages', express.json(), async (req: express.Request, res: express.Response) => {
        // This endpoint would be used by the client to send messages to the server
        res.status(200).json({ status: 'ok' });
      });
      
      // Start the Express server
      const httpServer = app.listen(port, () => {
        console.error(`Figma to React MCP server running on port ${port}`);
      });
      
      // Handle server shutdown
      process.on('SIGINT', async () => {
        console.error('Shutting down server...');
        await server.close();
        httpServer.close();
        process.exit(0);
      });
    } else {
      console.error(`Unsupported transport type: ${transportType}`);
      process.exit(1);
    }
  } catch (error) {
    console.error('Error starting server:', error);
    process.exit(1);
  }
}

// Start the server
main().catch(console.error);
```

--------------------------------------------------------------------------------
/src/figma-tools.ts:
--------------------------------------------------------------------------------

```typescript
import { FigmaClient } from './figma-client.js';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

export function registerFigmaTools(server: McpServer, figmaClient: FigmaClient): void {
  // Register getFigmaProject tool
  server.tool(
    'getFigmaProject',
    {
      fileKey: z.string().describe('Figma file key')
    },
    async ({ fileKey }) => {
      try {
        const fileData = await figmaClient.getFile(fileKey);
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({
                name: fileData.name,
                documentId: fileData.document.id,
                lastModified: fileData.lastModified,
                version: fileData.version,
                componentCount: Object.keys(fileData.components || {}).length,
                styleCount: Object.keys(fileData.styles || {}).length
              }, null, 2)
            }
          ]
        };
      } catch (error) {
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Failed to get Figma project: ${error instanceof Error ? error.message : String(error)}`
            }
          ]
        };
      }
    }
  );
  
  // Register getFigmaComponentNodes tool
  server.tool(
    'getFigmaComponentNodes',
    {
      fileKey: z.string().describe('Figma file key'),
      nodeIds: z.array(z.string()).describe('Node IDs to fetch')
    },
    async ({ fileKey, nodeIds }) => {
      try {
        const nodesData = await figmaClient.getFileNodes(fileKey, nodeIds);
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(nodesData.nodes, null, 2)
            }
          ]
        };
      } catch (error) {
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Failed to get Figma component nodes: ${error instanceof Error ? error.message : String(error)}`
            }
          ]
        };
      }
    }
  );
  
  // Register extractFigmaComponents tool
  server.tool(
    'extractFigmaComponents',
    {
      fileKey: z.string().describe('Figma file key')
    },
    async ({ fileKey }) => {
      try {
        const fileData = await figmaClient.getFile(fileKey);
        
        // Find all components in the file
        const components: Array<{ id: string, name: string, type: string }> = [];
        
        // Helper function to recursively traverse the document
        function findComponents(node: any) {
          if (node.type === 'COMPONENT' || node.type === 'COMPONENT_SET') {
            components.push({
              id: node.id,
              name: node.name,
              type: node.type
            });
          }
          
          if (node.children) {
            for (const child of node.children) {
              findComponents(child);
            }
          }
        }
        
        // Start traversal from the document root
        findComponents(fileData.document);
        
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({ components }, null, 2)
            }
          ]
        };
      } catch (error) {
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Failed to extract Figma components: ${error instanceof Error ? error.message : String(error)}`
            }
          ]
        };
      }
    }
  );
  
  // Register getFigmaComponentSets tool
  server.tool(
    'getFigmaComponentSets',
    {
      fileKey: z.string().describe('Figma file key')
    },
    async ({ fileKey }) => {
      try {
        const componentSets = await figmaClient.getComponentSets(fileKey);
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(componentSets, null, 2)
            }
          ]
        };
      } catch (error) {
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Failed to get Figma component sets: ${error instanceof Error ? error.message : String(error)}`
            }
          ]
        };
      }
    }
  );
}
```

--------------------------------------------------------------------------------
/src/accessibility.ts:
--------------------------------------------------------------------------------

```typescript
import { FigmaNode } from './figma-client.js';

// Determine the likely heading level based on font size and other properties
function determineLikelyHeadingLevel(figmaNode: any): number {
  if (!figmaNode.style) return 2; // Default to h2 if no style information

  const { fontSize, fontWeight } = figmaNode.style;
  
  // Use font size as the primary indicator
  if (fontSize >= 32) return 1;
  if (fontSize >= 24) return 2;
  if (fontSize >= 20) return 3;
  if (fontSize >= 18) return 4;
  if (fontSize >= 16) return 5;
  return 6;
}

// Check if a node is likely a button based on its properties
function isLikelyButton(figmaNode: FigmaNode): boolean {
  // Check name for button-related keywords
  const nameLower = figmaNode.name.toLowerCase();
  if (nameLower.includes('button') || nameLower.includes('btn')) return true;
  
  // Check for common button styles
  if (figmaNode.cornerRadius && figmaNode.cornerRadius > 0) {
    // Buttons often have rounded corners
    if (figmaNode.fills && figmaNode.fills.length > 0) {
      // Buttons typically have a background fill
      return true;
    }
  }
  
  return false;
}

// Check if a node is likely an image
function isLikelyImage(figmaNode: FigmaNode): boolean {
  if (figmaNode.type === 'IMAGE') return true;
  
  const nameLower = figmaNode.name.toLowerCase();
  if (nameLower.includes('image') || nameLower.includes('img') || nameLower.includes('icon')) return true;
  
  // Check for image fills
  if (figmaNode.fills) {
    for (const fill of figmaNode.fills) {
      if (fill.type === 'IMAGE') return true;
    }
  }
  
  return false;
}

// Check if a node is likely an input field
function isLikelyInputField(figmaNode: FigmaNode): boolean {
  const nameLower = figmaNode.name.toLowerCase();
  return nameLower.includes('input') || 
         nameLower.includes('field') || 
         nameLower.includes('text field') ||
         nameLower.includes('form field');
}

// Generate appropriate alt text for an image
function generateAltText(figmaNode: FigmaNode): string {
  // Start with the node name
  let altText = figmaNode.name;
  
  // Remove common prefixes/suffixes that aren't useful in alt text
  altText = altText.replace(/^(img|image|icon|pic|picture)[-_\s]*/i, '');
  altText = altText.replace(/[-_\s]*(img|image|icon|pic|picture)$/i, '');
  
  // If the alt text is empty or just contains "image" or similar, use a more generic description
  if (!altText || /^(img|image|icon|pic|picture)$/i.test(altText)) {
    altText = 'Image';
  }
  
  return altText;
}

export function enhanceWithAccessibility(jsx: string, figmaNode: FigmaNode): string {
  let enhancedJsx = jsx;
  
  // Add appropriate ARIA attributes based on component type
  if (figmaNode.type === 'TEXT' || (figmaNode.characters && figmaNode.characters.length > 0)) {
    // For text elements, check if they might be headings
    if (figmaNode.style && figmaNode.style.fontSize >= 16) {
      const headingLevel = determineLikelyHeadingLevel(figmaNode);
      enhancedJsx = enhancedJsx.replace(/<div([^>]*)>(.*?)<\/div>/g, `<h${headingLevel}$1>$2</h${headingLevel}>`);
    }
  }
  
  // Add alt text to images
  if (isLikelyImage(figmaNode)) {
    const altText = generateAltText(figmaNode);
    if (enhancedJsx.includes('<img')) {
      enhancedJsx = enhancedJsx.replace(/<img([^>]*)>/g, `<img$1 alt="${altText}">`);
    } else if (enhancedJsx.includes('<Image')) {
      enhancedJsx = enhancedJsx.replace(/<Image([^>]*)>/g, `<Image$1 alt="${altText}">`);
    } else {
      // If it's a div with a background image, add role="img" and aria-label
      enhancedJsx = enhancedJsx.replace(
        /<div([^>]*)>/g, 
        `<div$1 role="img" aria-label="${altText}">`
      );
    }
  }
  
  // Add appropriate role attributes for interactive elements
  if (isLikelyButton(figmaNode)) {
    if (!enhancedJsx.includes('<button')) {
      enhancedJsx = enhancedJsx.replace(
        /<div([^>]*)>/g, 
        '<div$1 role="button" tabIndex={0} onKeyDown={(e) => e.key === "Enter" && onClick && onClick(e)}>'
      );
      
      // If there's an onClick handler, make sure it's keyboard accessible
      enhancedJsx = enhancedJsx.replace(
        /onClick={([^}]+)}/g,
        'onClick={$1}'
      );
    }
  }
  
  // Add label and appropriate attributes for input fields
  if (isLikelyInputField(figmaNode)) {
    const inputId = `input-${figmaNode.id.replace(/[^a-zA-Z0-9]/g, '-')}`;
    const labelText = figmaNode.name.replace(/input|field|text field|form field/gi, '').trim() || 'Input';
    
    if (enhancedJsx.includes('<input')) {
      enhancedJsx = enhancedJsx.replace(
        /<input([^>]*)>/g,
        `<label htmlFor="${inputId}">${labelText}<input$1 id="${inputId}" aria-label="${labelText}"></label>`
      );
    } else {
      // If it's a div that should be an input, transform it
      enhancedJsx = enhancedJsx.replace(
        /<div([^>]*)>(.*?)<\/div>/g,
        `<label htmlFor="${inputId}">${labelText}<input$1 id="${inputId}" aria-label="${labelText}" /></label>`
      );
    }
  }
  
  return enhancedJsx;
}
```

--------------------------------------------------------------------------------
/src/figma-tailwind-converter.ts:
--------------------------------------------------------------------------------

```typescript
// Helper functions for color conversion
function rgbToHex(r: number, g: number, b: number): string {
  return '#' + [r, g, b]
    .map(x => Math.round(x).toString(16).padStart(2, '0'))
    .join('');
}

// Map RGB color to closest Tailwind color
function mapToTailwindColor(hexColor: string): string {
  // This is a simplified implementation
  // In a real-world scenario, you would have a more comprehensive mapping
  const tailwindColors: Record<string, string> = {
    '#000000': 'text-black',
    '#ffffff': 'text-white',
    '#ef4444': 'text-red-500',
    '#3b82f6': 'text-blue-500',
    '#10b981': 'text-green-500',
    '#f59e0b': 'text-yellow-500',
    '#6366f1': 'text-indigo-500',
    '#8b5cf6': 'text-purple-500',
    '#ec4899': 'text-pink-500',
    '#6b7280': 'text-gray-500',
    // Add more color mappings as needed
  };

  // Find the closest color by calculating the distance in RGB space
  let minDistance = Number.MAX_VALUE;
  let closestColor = 'text-black';

  const r1 = parseInt(hexColor.slice(1, 3), 16);
  const g1 = parseInt(hexColor.slice(3, 5), 16);
  const b1 = parseInt(hexColor.slice(5, 7), 16);

  for (const [color, className] of Object.entries(tailwindColors)) {
    const r2 = parseInt(color.slice(1, 3), 16);
    const g2 = parseInt(color.slice(3, 5), 16);
    const b2 = parseInt(color.slice(5, 7), 16);

    const distance = Math.sqrt(
      Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) + Math.pow(b1 - b2, 2)
    );

    if (distance < minDistance) {
      minDistance = distance;
      closestColor = className;
    }
  }

  return closestColor;
}

// Map font size to Tailwind class
function mapFontSizeToTailwind(fontSize: number): string {
  if (fontSize <= 12) return 'text-xs';
  if (fontSize <= 14) return 'text-sm';
  if (fontSize <= 16) return 'text-base';
  if (fontSize <= 18) return 'text-lg';
  if (fontSize <= 20) return 'text-xl';
  if (fontSize <= 24) return 'text-2xl';
  if (fontSize <= 30) return 'text-3xl';
  if (fontSize <= 36) return 'text-4xl';
  if (fontSize <= 48) return 'text-5xl';
  return 'text-6xl';
}

// Map font weight to Tailwind class
function mapFontWeightToTailwind(fontWeight: number): string {
  if (fontWeight < 400) return 'font-light';
  if (fontWeight < 500) return 'font-normal';
  if (fontWeight < 600) return 'font-medium';
  if (fontWeight < 700) return 'font-semibold';
  return 'font-bold';
}

// Map size values to Tailwind size classes
function mapToTailwindSize(size: number): string {
  // This is a simplified implementation
  if (size <= 4) return '1';
  if (size <= 8) return '2';
  if (size <= 12) return '3';
  if (size <= 16) return '4';
  if (size <= 20) return '5';
  if (size <= 24) return '6';
  if (size <= 32) return '8';
  if (size <= 40) return '10';
  if (size <= 48) return '12';
  if (size <= 64) return '16';
  if (size <= 80) return '20';
  if (size <= 96) return '24';
  if (size <= 128) return '32';
  if (size <= 160) return '40';
  if (size <= 192) return '48';
  if (size <= 256) return '64';
  if (size <= 320) return '80';
  if (size <= 384) return '96';
  return 'full';
}

export function convertFigmaStylesToTailwind(figmaStyles: any): string[] {
  const tailwindClasses: string[] = [];
  
  // Convert colors
  if (figmaStyles.fills && figmaStyles.fills.length > 0) {
    const fill = figmaStyles.fills[0];
    if (fill && fill.type === 'SOLID') {
      const { r, g, b } = fill.color;
      // Convert RGB to hex and find closest Tailwind color
      const hexColor = rgbToHex(r * 255, g * 255, b * 255);
      const tailwindColor = mapToTailwindColor(hexColor);
      tailwindClasses.push(tailwindColor);
      
      // Add opacity if needed
      if (fill.opacity && fill.opacity < 1) {
        const opacityValue = Math.round(fill.opacity * 100);
        tailwindClasses.push(`opacity-${opacityValue}`);
      }
    }
  }
  
  // Convert typography
  if (figmaStyles.style) {
    const { fontSize, fontWeight, lineHeight, letterSpacing } = figmaStyles.style;
    
    // Map font size to Tailwind classes
    if (fontSize) {
      tailwindClasses.push(mapFontSizeToTailwind(fontSize));
    }
    
    // Map font weight
    if (fontWeight) {
      tailwindClasses.push(mapFontWeightToTailwind(fontWeight));
    }
    
    // Map line height
    if (lineHeight) {
      // Simplified mapping
      if (lineHeight <= 1) tailwindClasses.push('leading-none');
      else if (lineHeight <= 1.25) tailwindClasses.push('leading-tight');
      else if (lineHeight <= 1.5) tailwindClasses.push('leading-normal');
      else if (lineHeight <= 1.75) tailwindClasses.push('leading-relaxed');
      else tailwindClasses.push('leading-loose');
    }
    
    // Map letter spacing
    if (letterSpacing) {
      // Simplified mapping
      if (letterSpacing <= -0.05) tailwindClasses.push('tracking-tighter');
      else if (letterSpacing <= 0) tailwindClasses.push('tracking-tight');
      else if (letterSpacing <= 0.05) tailwindClasses.push('tracking-normal');
      else if (letterSpacing <= 0.1) tailwindClasses.push('tracking-wide');
      else tailwindClasses.push('tracking-wider');
    }
  }
  
  // Convert layout properties
  if (figmaStyles.absoluteBoundingBox) {
    const { width, height } = figmaStyles.absoluteBoundingBox;
    tailwindClasses.push(`w-${mapToTailwindSize(width)}`);
    tailwindClasses.push(`h-${mapToTailwindSize(height)}`);
  }
  
  // Convert border radius
  if (figmaStyles.cornerRadius) {
    if (figmaStyles.cornerRadius <= 2) tailwindClasses.push('rounded-sm');
    else if (figmaStyles.cornerRadius <= 4) tailwindClasses.push('rounded');
    else if (figmaStyles.cornerRadius <= 6) tailwindClasses.push('rounded-md');
    else if (figmaStyles.cornerRadius <= 8) tailwindClasses.push('rounded-lg');
    else if (figmaStyles.cornerRadius <= 12) tailwindClasses.push('rounded-xl');
    else if (figmaStyles.cornerRadius <= 16) tailwindClasses.push('rounded-2xl');
    else if (figmaStyles.cornerRadius <= 24) tailwindClasses.push('rounded-3xl');
    else tailwindClasses.push('rounded-full');
  }
  
  // Convert borders
  if (figmaStyles.strokes && figmaStyles.strokes.length > 0) {
    const stroke = figmaStyles.strokes[0];
    if (stroke && stroke.type === 'SOLID') {
      const { r, g, b } = stroke.color;
      const hexColor = rgbToHex(r * 255, g * 255, b * 255);
      const tailwindColor = mapToTailwindColor(hexColor).replace('text-', 'border-');
      tailwindClasses.push(tailwindColor);
      
      // Border width
      if (figmaStyles.strokeWeight) {
        if (figmaStyles.strokeWeight <= 1) tailwindClasses.push('border');
        else if (figmaStyles.strokeWeight <= 2) tailwindClasses.push('border-2');
        else if (figmaStyles.strokeWeight <= 4) tailwindClasses.push('border-4');
        else tailwindClasses.push('border-8');
      }
    }
  }
  
  // Convert shadows
  if (figmaStyles.effects) {
    const shadowEffect = figmaStyles.effects.find((effect: any) => effect.type === 'DROP_SHADOW');
    if (shadowEffect) {
      if (shadowEffect.offset.x === 0 && shadowEffect.offset.y === 1 && shadowEffect.radius <= 2) {
        tailwindClasses.push('shadow-sm');
      } else if (shadowEffect.offset.y <= 3 && shadowEffect.radius <= 4) {
        tailwindClasses.push('shadow');
      } else if (shadowEffect.offset.y <= 8 && shadowEffect.radius <= 10) {
        tailwindClasses.push('shadow-md');
      } else if (shadowEffect.offset.y <= 15 && shadowEffect.radius <= 15) {
        tailwindClasses.push('shadow-lg');
      } else {
        tailwindClasses.push('shadow-xl');
      }
    }
  }
  
  return tailwindClasses;
}
```

--------------------------------------------------------------------------------
/src/figma-react-tools.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { ComponentGenerator } from './component-generator.js';
import { FigmaClient, FigmaNode } from './figma-client.js';
import * as fs from 'fs/promises';
import * as path from 'path';

export function registerReactTools(server: McpServer, componentGenerator: ComponentGenerator, figmaClient: FigmaClient): void {
  // Register generateReactComponent tool
  server.tool(
    'generateReactComponent',
    {
      componentName: z.string().describe('Name for the React component'),
      figmaNodeId: z.string().describe('Figma node ID'),
      fileKey: z.string().describe('Figma file key')
    },
    async ({ componentName, figmaNodeId, fileKey }) => {
      try {
        // Get the Figma node data
        const nodeData = await figmaClient.getFileNodes(fileKey, [figmaNodeId]);
        const figmaNode = nodeData.nodes[figmaNodeId]?.document;
        
        if (!figmaNode) {
          return {
            isError: true,
            content: [
              {
                type: 'text',
                text: `Figma node with ID ${figmaNodeId} not found`
              }
            ]
          };
        }
        
        // Generate the React component
        const componentCode = await componentGenerator.generateReactComponent(componentName, figmaNode);
        
        return {
          content: [
            {
              type: 'text',
              text: componentCode
            }
          ]
        };
      } catch (error) {
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Failed to generate React component: ${error instanceof Error ? error.message : String(error)}`
            }
          ]
        };
      }
    }
  );
  
  // Register generateComponentLibrary tool
  server.tool(
    'generateComponentLibrary',
    {
      components: z.array(
        z.object({
          name: z.string(),
          nodeId: z.string()
        })
      ).describe('Components to generate'),
      fileKey: z.string().describe('Figma file key')
    },
    async ({ components, fileKey }) => {
      try {
        // Get all node IDs
        const nodeIds = components.map(comp => comp.nodeId);
        
        // Get the Figma node data for all components
        const nodesData = await figmaClient.getFileNodes(fileKey, nodeIds);
        
        // Prepare the components for generation
        const componentsForGeneration = components
          .filter(comp => nodesData.nodes[comp.nodeId]?.document)
          .map(comp => ({
            name: comp.name,
            node: nodesData.nodes[comp.nodeId].document
          }));
        
        // Generate the component library
        const generatedComponents = await componentGenerator.generateComponentLibrary(componentsForGeneration);
        
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(
                Object.entries(generatedComponents).map(([name, code]) => ({
                  name,
                  code
                })),
                null,
                2
              )
            }
          ]
        };
      } catch (error) {
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Failed to generate component library: ${error instanceof Error ? error.message : String(error)}`
            }
          ]
        };
      }
    }
  );
  
  // Register writeComponentsToFiles tool
  server.tool(
    'writeComponentsToFiles',
    {
      components: z.array(
        z.object({
          name: z.string(),
          code: z.string()
        })
      ).describe('Components to write to files'),
      outputDir: z.string().describe('Output directory')
    },
    async ({ components, outputDir }) => {
      try {
        // Create the output directory if it doesn't exist
        await fs.mkdir(outputDir, { recursive: true });
        
        // Write each component to a file
        const results = await Promise.all(
          components.map(async (component) => {
            const fileName = `${component.name}.tsx`;
            const filePath = path.join(outputDir, fileName);
            
            await fs.writeFile(filePath, component.code, 'utf-8');
            
            return {
              name: component.name,
              path: filePath
            };
          })
        );
        
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({ results }, null, 2)
            }
          ]
        };
      } catch (error) {
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Failed to write components to files: ${error instanceof Error ? error.message : String(error)}`
            }
          ]
        };
      }
    }
  );
  
  // Register figmaToReactWorkflow tool
  server.tool(
    'figmaToReactWorkflow',
    {
      fileKey: z.string().describe('Figma file key'),
      outputDir: z.string().describe('Output directory for components')
    },
    async ({ fileKey, outputDir }) => {
      try {
        // Step 1: Extract components from Figma file
        const fileData = await figmaClient.getFile(fileKey);
        
        // Find all components in the file
        const components: Array<{ id: string, name: string, type: string }> = [];
        
        // Helper function to recursively traverse the document
        function findComponents(node: any) {
          if (node.type === 'COMPONENT' || node.type === 'COMPONENT_SET') {
            components.push({
              id: node.id,
              name: node.name,
              type: node.type
            });
          }
          
          if (node.children) {
            for (const child of node.children) {
              findComponents(child);
            }
          }
        }
        
        // Start traversal from the document root
        findComponents(fileData.document);
        
        // Step 2: Get the Figma node data for all components
        const nodeIds = components.map(comp => comp.id);
        const nodesData = await figmaClient.getFileNodes(fileKey, nodeIds);
        
        // Step 3: Prepare the components for generation
        const componentsForGeneration = components
          .filter(comp => nodesData.nodes[comp.id]?.document)
          .map(comp => ({
            name: comp.name,
            node: nodesData.nodes[comp.id].document
          }));
        
        // Step 4: Generate the component library
        const generatedComponents = await componentGenerator.generateComponentLibrary(componentsForGeneration);
        
        // Step 5: Create the output directory if it doesn't exist
        await fs.mkdir(outputDir, { recursive: true });
        
        // Step 6: Write each component to a file
        const results = await Promise.all(
          Object.entries(generatedComponents).map(async ([name, code]) => {
            const fileName = `${name}.tsx`;
            const filePath = path.join(outputDir, fileName);
            
            await fs.writeFile(filePath, code, 'utf-8');
            
            return {
              name,
              path: filePath
            };
          })
        );
        
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({ 
                componentsFound: components.length,
                componentsGenerated: results.length,
                results 
              }, null, 2)
            }
          ]
        };
      } catch (error) {
        return {
          isError: true,
          content: [
            {
              type: 'text',
              text: `Failed to execute Figma to React workflow: ${error instanceof Error ? error.message : String(error)}`
            }
          ]
        };
      }
    }
  );
}
```

--------------------------------------------------------------------------------
/src/component-generator.ts:
--------------------------------------------------------------------------------

```typescript
import * as prettier from 'prettier';
import { FigmaNode } from './figma-client.js';
import { convertFigmaStylesToTailwind } from './figma-tailwind-converter.js';
import { enhanceWithAccessibility } from './accessibility.js';

interface PropDefinition {
  name: string;
  type: string;
  defaultValue?: string;
  description?: string;
}

interface ReactComponentParts {
  jsx: string;
  imports: string[];
  props: PropDefinition[];
  styles?: Record<string, any>;
}

// Helper function to convert Figma node name to a valid React component name
function toComponentName(name: string): string {
  // Remove invalid characters and convert to PascalCase
  return name
    .replace(/[^\w\s-]/g, '')
    .split(/[-_\s]+/)
    .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
    .join('');
}

// Helper function to convert Figma node name to a valid prop name
function toPropName(name: string): string {
  // Convert to camelCase
  const parts = name
    .replace(/[^\w\s-]/g, '')
    .split(/[-_\s]+/);
  
  return parts[0].toLowerCase() + 
    parts.slice(1)
      .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
      .join('');
}

// Extract potential props from Figma node
function extractProps(node: FigmaNode): PropDefinition[] {
  const props: PropDefinition[] = [];
  
  // Text content could be a prop
  if (node.type === 'TEXT' && node.characters) {
    const propName = toPropName(node.name) || 'text';
    props.push({
      name: propName,
      type: 'string',
      defaultValue: JSON.stringify(node.characters),
      description: `Text content for ${node.name}`
    });
  }
  
  // If node has a "variant" property, it could be a prop
  if (node.name.toLowerCase().includes('variant')) {
    props.push({
      name: 'variant',
      type: "'primary' | 'secondary' | 'outline' | 'text'",
      defaultValue: "'primary'",
      description: 'Visual variant of the component'
    });
  }
  
  // If node looks like a button, add onClick prop
  if (node.name.toLowerCase().includes('button') || node.name.toLowerCase().includes('btn')) {
    props.push({
      name: 'onClick',
      type: '() => void',
      description: 'Function called when button is clicked'
    });
  }
  
  // If node has children that could be dynamic, add children prop
  if (node.children && node.children.length > 0) {
    // Check if it has a container-like name
    if (
      node.name.toLowerCase().includes('container') || 
      node.name.toLowerCase().includes('wrapper') ||
      node.name.toLowerCase().includes('layout') ||
      node.name.toLowerCase().includes('section')
    ) {
      props.push({
        name: 'children',
        type: 'React.ReactNode',
        description: 'Child elements to render inside the component'
      });
    }
  }
  
  // Add className prop for styling customization
  props.push({
    name: 'className',
    type: 'string',
    description: 'Additional CSS classes to apply'
  });
  
  return props;
}

// Convert a Figma node to JSX
function figmaNodeToJSX(node: FigmaNode, level = 0): ReactComponentParts {
  const imports: string[] = [];
  const allProps: PropDefinition[] = [];
  
  // Default result
  let result: ReactComponentParts = {
    jsx: '',
    imports: [],
    props: []
  };
  
  // Handle different node types
  switch (node.type) {
    case 'TEXT':
      // Extract text content and convert to JSX
      const textContent = node.characters || '';
      const tailwindClasses = convertFigmaStylesToTailwind(node);
      const textProps = extractProps(node);
      
      result = {
        jsx: `<p className="${tailwindClasses.join(' ')}">{${textProps[0]?.name || 'text'}}</p>`,
        imports: [],
        props: textProps
      };
      break;
      
    case 'RECTANGLE':
    case 'ELLIPSE':
    case 'POLYGON':
    case 'STAR':
    case 'VECTOR':
    case 'LINE':
      // Convert to a div with appropriate styling
      const shapeClasses = convertFigmaStylesToTailwind(node);
      
      result = {
        jsx: `<div className="${shapeClasses.join(' ')} ${node.type.toLowerCase()} ${node.name.toLowerCase().replace(/\s+/g, '-')}"></div>`,
        imports: [],
        props: []
      };
      break;
      
    case 'COMPONENT':
    case 'INSTANCE':
    case 'FRAME':
    case 'GROUP':
      // These are container elements that might have children
      const containerClasses = convertFigmaStylesToTailwind(node);
      const containerProps = extractProps(node);
      
      // Process children if they exist
      let childrenJSX = '';
      if (node.children && node.children.length > 0) {
        for (const child of node.children) {
          const childResult = figmaNodeToJSX(child, level + 1);
          childrenJSX += `\n${'  '.repeat(level + 1)}${childResult.jsx}`;
          
          // Collect imports and props from children
          imports.push(...childResult.imports);
          allProps.push(...childResult.props);
        }
        childrenJSX += `\n${'  '.repeat(level)}`;
      }
      
      // If this is a component that looks like a button
      if (node.name.toLowerCase().includes('button') || node.name.toLowerCase().includes('btn')) {
        result = {
          jsx: `<button 
  className="${containerClasses.join(' ')} ${node.name.toLowerCase().replace(/\s+/g, '-')}"
  onClick={onClick}
>${childrenJSX}</button>`,
          imports: imports,
          props: [...containerProps, ...allProps]
        };
      } else {
        // Check if we should use children prop
        const hasChildrenProp = containerProps.some(p => p.name === 'children');
        
        result = {
          jsx: `<div 
  className="${containerClasses.join(' ')} ${node.name.toLowerCase().replace(/\s+/g, '-')}"
>${hasChildrenProp ? '{children}' : childrenJSX}</div>`,
          imports: imports,
          props: [...containerProps, ...(hasChildrenProp ? [] : allProps)]
        };
      }
      break;
      
    case 'IMAGE':
      // Convert to an img tag
      const imageClasses = convertFigmaStylesToTailwind(node);
      
      result = {
        jsx: `<img 
  src="${node.name.toLowerCase().replace(/\s+/g, '-')}.png" 
  className="${imageClasses.join(' ')}" 
  alt="${node.name}"
/>`,
        imports: [],
        props: [{
          name: 'src',
          type: 'string',
          description: 'Image source URL'
        }]
      };
      break;
      
    default:
      // Default to a simple div
      result = {
        jsx: `<div className="${node.name.toLowerCase().replace(/\s+/g, '-')}"></div>`,
        imports: [],
        props: []
      };
  }
  
  return result;
}

export class ComponentGenerator {
  async generateReactComponent(componentName: string, figmaNode: FigmaNode): Promise<string> {
    // Extract styles, structure, and props from Figma node
    const componentParts = figmaNodeToJSX(figmaNode);
    
    // Enhance with accessibility features
    const enhancedJSX = enhanceWithAccessibility(componentParts.jsx, figmaNode);
    
    // Deduplicate props
    const uniqueProps = componentParts.props.filter((prop, index, self) => 
      index === self.findIndex(p => p.name === prop.name)
    );
    
    // Create component template
    const componentCode = `
import React from 'react';
${componentParts.imports.join('\n')}

interface ${componentName}Props {
  ${uniqueProps.map(prop => 
    `/** ${prop.description || ''} */
  ${prop.name}${prop.type.includes('?') || prop.defaultValue ? '?' : ''}: ${prop.type};`
  ).join('\n  ')}
}

export const ${componentName} = ({ 
  ${uniqueProps.map(p => 
    p.defaultValue 
      ? `${p.name} = ${p.defaultValue}` 
      : p.name
  ).join(', ')} 
}: ${componentName}Props) => {
  return (
    ${enhancedJSX}
  );
};
`;
    
    // Format the code
    try {
      return await prettier.format(componentCode, {
        parser: 'typescript',
        singleQuote: true,
        trailingComma: 'es5',
        tabWidth: 2
      });
    } catch (error) {
      console.error('Error formatting component code:', error);
      return componentCode;
    }
  }

  async generateComponentLibrary(components: Array<{ name: string, node: FigmaNode }>): Promise<Record<string, string>> {
    const generatedComponents: Record<string, string> = {};
    
    for (const { name, node } of components) {
      const componentName = toComponentName(name);
      generatedComponents[componentName] = await this.generateReactComponent(componentName, node);
    }
    
    return generatedComponents;
  }
}
```