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+agentsTransporte: HTTP transmitible (Streamable), el endpoint es
/mcpValidació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 401El 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 clienteCOMPONENT_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:
Analizar
fileKeyynodeIdde la URL (decodificar?node-id=1%3A2→1: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}
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,
characterse información de fuenteEstructura 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:
Hacer fetch de
${env.STORYBOOK_URL}/index.json(En caso de fallo) intentar
${env.STORYBOOK_URL}/stories.jsonExtraer del objeto
entriessolo aquellos contype: "story"(excluyendo páginas docs)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:
Buscar el ID correspondiente en
index.jsonSi es posible, extraer
argTypesde${STORYBOOK_URL}/stories.jsono de los metadatos basados en IDOrganizar 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:
Obtener todos los componentes con
list_storiesCalcular la puntuación de coincidencia para cada componente:
Similitud de nombre (peso 0.5): Nombre del nodo de Figma vs
componentNameCoincidencia 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
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:
Obtener la firma de props con
get_story_detailsIntentar mapear el texto, estilos e información de variantes del nodo de Figma a las props
Texto de Figma → prop
childrenolabelNombre de variante de Figma → valor de prop coincidente
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.mdRequisitos de implementación
Seguridad de tipos: Definir explícitamente el esquema zod de entrada de todas las herramientas y los tipos de salida.
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.
Logging:
console.logpara el inicio/fin de la llamada a la herramienta,console.errorpara errores. Visible en el panel de control de Workers.Pruebas: Pruebas unitarias de lógica central con vitest (análisis de URL, puntuación de coincidencia, lógica de depuración).
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.jsonVerificar 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 unitariasfigma/client.ts(llamada real a la API)figma/normalizer.ts(depuración de respuesta)Registro de la herramienta
get_figma_nodeVerificar 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 unitariasRegistro de
match_figma_to_components
Fase 6: Generación de código
codegen/react.tsRegistro de
generate_component_usage
Fase 7: Finalización
Añadir
get_figma_subtreeEscribir 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 enfetch.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
The Figma MCP server brings Figma design context directly into your AI workflow.
Serves your design system and coding standards to coding agents, so they stop guessing.
Build and manage your design system with AI: tokens, themes, components, icons, Figma and code.
Live React design-system APIs, patterns, and code validation so AI agents build real UI, not slop.