Skip to main content
Glama
martin-delivered

Figma Storybook Component Matching MCP Server

Servidor MCP de emparejamiento de componentes Figma → Storybook

Objetivo

Crear un servidor MCP remoto. El objetivo es recibir nodos de diseño de Figma, emparejarlos con los componentes de React de nuestro equipo (registrados en Storybook) y generar ejemplos de código de uso.

El LLM (Claude) debería poder procesar las siguientes solicitudes a través de este MCP:

  • "Analiza esta URL de Figma"

  • "Dime cómo implementar este nodo de Figma con nuestros componentes"

  • "Muéstrame 3 componentes candidatos"

Stack tecnológico

  • Runtime: Cloudflare Workers

  • Lenguaje: TypeScript (modo estricto)

  • MCP: Uso de los paquetes @modelcontextprotocol/sdk + agents

  • Transporte: HTTP transmitible (Streamable), el endpoint es /mcp

  • Validación: zod

  • Build/Despliegue: wrangler

  • Framework objetivo: React (salida JSX al generar código)

Autenticación (Opción A: Token Bearer)

  • Todas las solicitudes MCP requieren el encabezado Authorization: Bearer <token>

  • Se compara con env.MCP_AUTH_TOKEN, si no coincide se devuelve un 401

  • El fallo de autenticación debe devolver un mensaje de error claro ({"error": "invalid_token"})

Variables de entorno (definidas en wrangler)

  • FIGMA_TOKEN: Token de acceso personal de Figma (almacenado por el servidor)

  • STORYBOOK_URL: URL base de Storybook (ej: https://storybook.example.com)

  • MCP_AUTH_TOKEN: Token para la autenticación del cliente

  • COMPONENT_IMPORT_PREFIX: Ruta de importación al generar código (por defecto @/components)

Para desarrollo local usar .dev.vars, para producción gestionar con wrangler secret put. Mantener solo marcadores de posición ficticios en wrangler.toml.

Herramientas a exponer (Tools)

1. get_figma_node

Descripción: Recibe una URL de Figma y devuelve la información clave del nodo en un formato depurado.

Entrada (zod):

{
  url: string  // Figma 노드 URL (예: https://www.figma.com/file/XXX/...?node-id=1%3A2)
}

Funcionamiento:

  1. Analizar fileKey y nodeId de la URL (decodificar ?node-id=1%3A2 → 1:2)

  2. Llamada a la API de Figma: GET https://api.figma.com/v1/files/{fileKey}/nodes?ids={nodeId}

    • Encabezado: X-Figma-Token: {env.FIGMA_TOKEN}

  3. Extraer solo lo siguiente (la respuesta de Figma es demasiado verbosa, así que se depura):

    • Nombre del nodo (name)

    • Tipo de nodo (type: FRAME, INSTANCE, TEXT, ...)

    • Si es un componente, nombre del componente (componentId → componentName)

    • Estilos: color de fondo, borde, radio de borde, relleno, modo de diseño (dirección autolayout), gap

    • Si es texto, characters e información de fuente

    • Estructura de hijos: solo nombres/tipos de nodos hijos con 1 nivel de profundidad (sin recursión, se vuelve demasiado largo)

    • Información de variables/variantes de componentes (si existe)

Salida: Objeto JSON depurado con la información anterior.

Errores: Distinguir entre fallo de análisis de URL, API de Figma 4xx/5xx, token expirado, etc., y devolver mensajes de error.

2. get_figma_subtree

Descripción: Obtiene el árbol completo del nodo de forma recursiva (para analizar páginas/marcos completos).

Entrada:

{
  url: string,
  maxDepth?: number  // 기본 3, 너무 깊으면 토큰 폭발
}

Funcionamiento: Similar a get_figma_node pero recursivo hasta maxDepth para los hijos. Cada hijo también en formato depurado.

3. list_stories

Descripción: Devuelve la lista de componentes de nuestro Storybook.

Entrada: Ninguna (o { filter?: string } para búsqueda)

Funcionamiento:

  1. Hacer fetch de ${env.STORYBOOK_URL}/index.json

  2. (En caso de fallo) intentar ${env.STORYBOOK_URL}/stories.json

  3. Extraer del objeto entries solo aquellos con type: "story" (excluyendo páginas docs)

  4. Convertir al siguiente formato:

{
  id: string,
  componentName: string,  // title에서 마지막 "/" 뒤 부분 (예: "Forms/Button" → "Button")
  storyName: string,      // name 필드
  fullTitle: string,      // 원본 title
  tags: string[]
}[]

Caché: Caché en memoria de la respuesta durante 5 minutos (no es necesario usar KV, usar una variable simple). Como las instancias de Workers viven poco tiempo, no establecer un tiempo demasiado largo.

4. get_story_details

Descripción: Información detallada de una historia específica (props, args).

Entrada:

{ storyId: string }

Funcionamiento:

  1. Buscar el ID correspondiente en index.json

  2. Si es posible, extraer argTypes de ${STORYBOOK_URL}/stories.json o de los metadatos basados en ID

  3. Organizar la firma de props:

{
  id: string,
  componentName: string,
  description?: string,
  props: {
    name: string,
    type: string,
    required: boolean,
    description?: string,
    defaultValue?: any
  }[]
}

Si no se pueden obtener argTypes, dejar props como un array vacío y añadir note: "argTypes unavailable".

5. match_figma_to_components

Descripción: Devuelve candidatos a componentes que coinciden con los datos del nodo de Figma junto con una puntuación (herramienta principal).

Entrada:

{
  figmaNode: <get_figma_node 출력 형식>,
  topK?: number  // 기본 3
}

Funcionamiento:

  1. Obtener todos los componentes con list_stories

  2. Calcular la puntuación de coincidencia para cada componente:

    • Similitud de nombre (peso 0.5): Nombre del nodo de Figma vs componentName

      • Coincidencia exacta: 1.0

      • Coincidencia ignorando mayúsculas: 0.9

      • Relación de inclusión: 0.6

      • Basado en distancia de Levenshtein: 0~0.5

    • Coincidencia de estructura (peso 0.3): Inferencia de patrones de hijos

      • Hijos de Figma solo texto → Candidatos "Button", "Label" +

      • Icono + texto → Candidatos "Button", "Tag", "Chip" +

      • Varios hijos tipo tarjeta → Candidatos "List", "Grid" +

    • Coincidencia de etiquetas (peso 0.2): Las etiquetas de la historia de Storybook incluyen palabras clave del nombre del nodo de Figma

  3. Devolver los K mejores (por defecto 3):

{
  storyId: string,
  componentName: string,
  score: number,  // 0~1
  reasons: string[]  // 왜 매칭됐는지 사람이 읽을 수 있게
}[]

Descartar coincidencias con puntuación inferior a 0.3 (filtrar coincidencias sin sentido).

6. generate_component_usage

Descripción: Genera un ejemplo de código React JSX con el componente emparejado + información del nodo de Figma.

Entrada:

{
  storyId: string,
  figmaNode: <get_figma_node 출력 형식>
}

Funcionamiento:

  1. Obtener la firma de props con get_story_details

  2. Intentar mapear el texto, estilos e información de variantes del nodo de Figma a las props

    • Texto de Figma → prop children o label

    • Nombre de variante de Figma → valor de prop coincidente

  3. Generar cadena de código JSX

Salida:

{
  code: string,        // <Button variant="primary">Click me</Button>
  importStatement: string,  // import { Button } from "@/components/Button"
  notes: string[]      // 매핑 추측이나 빠진 정보 안내
}

La ruta de importación se basa en la variable de entorno env.COMPONENT_IMPORT_PREFIX (valor por defecto "@/components").

Estructura del proyecto

figma-storybook-mcp/
├── src/
│   ├── index.ts              # Worker 진입점, 인증 미들웨어, MCP 라우팅
│   ├── mcp.ts                # MyMCP 클래스 (도구 등록)
│   ├── auth.ts               # Bearer 토큰 검증
│   ├── figma/
│   │   ├── client.ts         # Figma REST API 호출
│   │   ├── url-parser.ts     # URL → fileKey + nodeId
│   │   └── normalizer.ts     # Figma 응답 → 정제된 형식
│   ├── storybook/
│   │   ├── client.ts         # index.json fetch + 캐싱
│   │   └── types.ts
│   ├── matching/
│   │   ├── scorer.ts         # 매칭 점수 계산
│   │   └── name-similarity.ts # Levenshtein 등
│   ├── codegen/
│   │   └── react.ts          # JSX 코드 생성
│   └── types.ts              # 공통 타입
├── tests/
│   ├── url-parser.test.ts
│   ├── normalizer.test.ts
│   └── scorer.test.ts
├── wrangler.toml
├── .dev.vars.example         # 실제 .dev.vars는 gitignore
├── package.json
├── tsconfig.json
├── vitest.config.ts
└── README.md

Requisitos de implementación

  1. Seguridad de tipos: Definir explícitamente el esquema zod de entrada de todas las herramientas y los tipos de salida.

  2. Manejo de errores:

    • Figma 401 → "Token de Figma expirado/incorrecto"

    • Figma 404 → "Nodo no encontrado"

    • Fallo de fetch de Storybook → Mensaje claro

    • Todos los errores deben devolverse en un formato que el MCP pueda entender.

  3. Logging: console.log para el inicio/fin de la llamada a la herramienta, console.error para errores. Visible en el panel de control de Workers.

  4. Pruebas: Pruebas unitarias de lógica central con vitest (análisis de URL, puntuación de coincidencia, lógica de depuración).

  5. Actualización del README:

    • Qué hace la herramienta

    • Explicación de variables de entorno

    • Ejecución local (npm run dev)

    • Despliegue (npm run deploy)

    • Cómo conectar a Claude Desktop / Claude.ai

    • Ejemplos de entrada/salida de cada herramienta

Orden de trabajo (proceder informando paso a paso)

Fase 1: Configuración

  • Inicialización del proyecto, instalación de dependencias

  • Creación de wrangler.toml, tsconfig.json

  • Verificar que el servidor MCP vacío responda en /mcp (incluso con 0 herramientas está bien)

Fase 2: Autenticación

  • Middleware de validación de token Bearer

  • Verificar 401 al llamar con un token incorrecto

Fase 3: Herramientas de Figma

  • figma/url-parser.ts + pruebas unitarias

  • figma/client.ts (llamada real a la API)

  • figma/normalizer.ts (depuración de respuesta)

  • Registro de la herramienta get_figma_node

  • Verificar funcionamiento con una URL real de Figma

Fase 4: Herramientas de Storybook

  • storybook/client.ts (fetch de index.json + caché)

  • Registro de list_stories, get_story_details

Fase 5: Emparejamiento

  • matching/scorer.ts + pruebas unitarias

  • Registro de match_figma_to_components

Fase 6: Generación de código

  • codegen/react.ts

  • Registro de generate_component_usage

Fase 7: Finalización

  • Añadir get_figma_subtree

  • Escribir README

  • Proporcionar .dev.vars.example

Al finalizar cada fase, informar brevemente "he hecho esto y haré esto a continuación" y proceder.

Notas

  • Cloudflare Workers solo admite parte de la API de Node.js. No admite fs, child_process, etc. Escribir basado en fetch.

  • Usar la última versión estable de @modelcontextprotocol/sdk.

  • El estándar MCP cambia rápidamente, seguir los patrones más recientes del paquete agents.

  • No construir todo a la vez, proceder verificando por fases.

  • Código claro, comentarios solo en la lógica de negocio (como la puntuación de coincidencia).

Inicio

Por favor, comienza por la Fase 1.

Related MCP Connectors