figma-mcp-bridge
Acceso de escritura a un lienzo de Figma en vivo con retroalimentación de capturas de pantalla con marco automático y reversión en una sola llamada. Cero dependencias, plan gratuito de Figma.
Documentación
⚡ Figma MCP Bridge
Permite que un agente de IA diseñe en Figma — y que realmente vea lo que dibujó.
Un servidor MCP sin dependencias que brinda a los agentes de codificación acceso de lectura y escritura a un lienzo de Figma en vivo, y luego cierra el ciclo entregando el PNG renderizado de vuelta al modelo para una autocrítica visual.

El agente genera el diseño, audita visualmente la salida renderizada, detecta defectos de maquetación y se autocorrige en tiempo real — cero intervención humana.
El problema
Los agentes de codificación son efectivamente ciegos dentro de Figma. La mayoría de los servidores MCP de Figma son de solo lectura — aplanan un diseño en texto para que un modelo pueda convertirlo en código, y el tráfico se detiene ahí. Los que pueden escribir están limitados por una licencia de Figma de pago con una cuota mensual de llamadas a herramientas, o exponen un vocabulario fijo de comandos (create_rectangle, set_fill) que se agota en el momento en que la tarea se vuelve específica.
Y ninguno responde la pregunta que realmente importa después de una escritura: ¿se veía bien? Un agente que dibuja una tarjeta, recibe {"ok": true} y continúa no tiene forma de notar que su texto se desbordó, que su contraste falló, o que su marco aterrizó encima del trabajo de otra persona.
Este puente cierra ese ciclo y añade las barreras de seguridad que un agente autónomo necesita para que se le confíe un archivo de diseño real.
En qué se diferencia
| Este puente | Figma Dev Mode MCP (oficial) | Framelink | Talk to Figma | |
|---|---|---|---|---|
| Escribe en el lienzo | ✅ JS arbitrario en el sandbox | ✅ código-a-lienzo | ❌ solo lectura | ✅ conjunto de comandos fijo |
| Ciclo de retroalimentación visual | ✅ PNG auto-enmarcado en cada escritura | ⚠️ llamada de captura separada | ❌ | ❌ |
| Deshacer el trabajo del agente | ✅ figma_rollback | ❌ | n/a | ❌ |
| Funciona en el plan gratuito de Figma | ✅ | ❌ Licencia Dev/Full, plan de pago¹ | ✅ | ✅ |
| Cuota de llamadas a herramientas | ninguna — es local | 6 / mes en licencias Starter¹ | hereda límites de tasa REST | ninguna |
| Lectura optimizada en tokens del documento en vivo | ✅ 86–91% más pequeño² | ⚠️ parcial | ❌ solo REST | ❌ |
| Módulos de código persistentes en el sandbox | ✅ bridge.define/require | ❌ | ❌ | ❌ |
| Trabajos largos que sobreviven su propio tiempo de espera | ✅ job_id asíncrono + progreso | ❌ | n/a | ❌ |
| Huella de instalación | 0 dependencias npm, npx o git clone, solo Node | Figma de escritorio + licencia de pago | npx + token de acceso | Bun + un segundo proceso de servidor |
La versión corta: Framelink es la mejor opción si solo quieres convertir un diseño existente en código. El servidor oficial es la opción más segura si ya estás en un plan de pago de Figma y quieres Code Connect. Este puente es para el caso en que el agente está haciendo el diseño — donde necesita escribir libremente, revisar su propio trabajo y ser deshacible cuando se equivoca.
Cómo funciona
flowchart LR
subgraph AI ["🤖 Coding Agent"]
LLM["Claude Code · Cursor<br/>Antigravity · Windsurf"]
end
subgraph Bridge ["⚡ MCP Server — Node.js, 0 deps"]
Router["Tool Router<br/>+ Job Ledger<br/>+ Target Router"]
Opt["Token Optimizer<br/>REST + LIVE"]
end
subgraph Figma ["🎨 Figma Desktop"]
Plugin["Bridge Plugin<br/>Checkpoint Journal<br/>Component Index"]
Canvas["Live Canvas"]
end
LLM -->|"stdio JSON-RPC"| Router
Router <-->|"WebSocket :8765"| Plugin
Plugin -->|"execute in sandbox"| Canvas
Canvas -->|"export PNG"| Plugin
Plugin -->|"raw node tree"| Opt
Opt -.->|"86% smaller"| LLM
Plugin ==>|"screenshot + warnings"| LLM
El agente habla MCP estándar a través de stdio. El servidor posee un WebSocket RFC 6455 hecho a mano en :8765, al que se conecta el plugin de Figma — así los comandos llegan al lienzo sin latencia de sondeo, y los resultados (incluyendo PNGs base64 de varios megabytes) fluyen directamente de vuelta.
Inicio rápido
npx @kolganovr/figma-mcp-bridge
O, para leer el instalador antes de que toque tu máquina — es el mismo script de cualquier manera, solo se obtiene de forma diferente:
git clone https://github.com/kolganovr/figma-mcp-bridge.git
cd figma-mcp-bridge
node install.mjs
Ambos copian el servidor y el plugin en su lugar y registran el servidor MCP en cada configuración de cliente de IA que encuentren — Claude Desktop, Claude Code, Cursor, Windsurf, Antigravity. El paquete npm existe puramente para un primer comando más corto; no añade una sola dependencia de ejecución — dependencies está vacío en su package.json también, y no se necesita Python. node es el único runtime que este proyecto necesita, tanto para el instalador como para el servidor.
Luego, en Figma Desktop:
- Plugins → Development → Import plugin from manifest… → elige
figma-plugin/manifest.json - Presiona
Ctrl + Alt + P(macOS:Cmd + Option + P) para lanzar Antigravity Bridge - El estado se vuelve verde —
CONNECTED
Reinicia tu cliente de IA para que detecte las nuevas herramientas. Verifica en cualquier momento con:
npx @kolganovr/figma-mcp-bridge --doctor # or: node install.mjs --doctor
Opcional: Acceso REST a Figma Cloud
Las herramientas del lienzo en vivo no necesitan token. Si también quieres leer archivos en la nube no abiertos (get_file, get_node, get_styles, …), proporciona un token de acceso personal:
npx @kolganovr/figma-mcp-bridge --token "your_figma_personal_access_token"
# or: node install.mjs --token "your_figma_personal_access_token"
Sin un token, estas 7 herramientas no se registran en absoluto — consulta Referencia de herramientas.
Lo que le da al agente
1. Escribe libremente, luego mira el resultado
capture: true devuelve un PNG de exactamente lo que la llamada acaba de crear o modificar — auto-enmarcado a los nodos cambiados, nunca a toda la página, y nunca secuestrando la selección del usuario.
// agent calls figma_execute_code
{ "code": "const f = figma.createFrame(); /* ... */ return f.id;", "capture": true }
// agent gets back — text + image in one response
{
"ok": true,
"created": ["12:34"],
"warnings": ["Text \"Total\" has ~3.1:1 contrast against its parent fill (WCAG AA wants 4.5:1)."],
"checkpoint_id": "cp_mfk3p2a_7",
"duration_ms": 840
}
El array warnings es un auto-lint barato sobre el subárbol tocado — desbordamiento de texto, bajo contraste, nodos de tamaño cero. Detecta los errores obvios sin gastar un viaje de ida y vuelta de captura de pantalla.
2. Deshacer cualquier cosa que hizo el agente
Cada escritura abre un punto de control automáticamente. Una llamada lo revierte — sin Ctrl+Z retrocediendo sobre el trabajo no relacionado del humano.
// agent calls figma_rollback
{ "checkpoint_id": "last" }
// gets back
{
"ok": true,
"checkpoint_id": "cp_mfk3p2a_7",
"label": "Generate checkout flow",
"removed": ["12:34", "12:35"], // nodes the call created — deleted
"restored": ["9:11"], // nodes it modified — properties put back
"missing": [] // ids that no longer exist
}
Los nodos creados se rastrean automáticamente mediante un Proxy alrededor de figma.create*(). Las ediciones de propiedades se rastrean cuando se capturan. Las eliminaciones se informan honestamente como irrecuperables en lugar de perderse silenciosamente.
3. Lee el lienzo en vivo por ~700 tokens en lugar de ~5,000
El mismo pipeline de poda + Pseudo-JSX que impulsa las herramientas en la nube, apuntado a lo que esté abierto ahora mismo. Medido en un diseño realista de 6 tarjetas: 20 KB de JSON crudo de API → 2.7 KB (86% más pequeño).
<Frame id="1:1" name="Landing" w="1440" h="900" row gap="20">
<Frame id="2:0" name="Card" w="300" h="200" col gap="12" pad="20" bg="#FFFFFF" radius="16">
<Icon id="3:0" name="ic_check" size="24" strokeWidth="2" />
<Text id="4:0" color="#1A1A1F" font="Inter 18px">Feature 0</Text>
</Frame>
</Frame>
budget_tokens limita la respuesta: si la profundidad solicitada se excede, el servidor re-serializa el árbol ya obtenido de forma más superficial — sin un segundo viaje de ida y vuelta — y añade un comentario diciendo qué hizo y qué id buscar para más.
4. Enseña al sandbox nuevos trucos que sobreviven reinicios
Cada llamada a figma_execute_code es un ámbito de función nuevo, así que los helpers normalmente mueren al instante. bridge.define compila y almacena un módulo dentro del documento .fig:
// once
bridge.define("kit", `
async function label(parent, text) { /* ... */ }
module.exports = { label };
`);
// in any later call — including next week, after a Figma restart
const { label } = bridge.require("kit");
5. Trabajos largos que no mueren en el tiempo de espera
Una generación que aún se está ejecutando después de 30s entrega un job_id en lugar de fallar mientras el plugin sigue trabajando. Consulta figma_job_status para progreso en vivo — el sandbox lo reporta a través de progress(step, of, note).
Referencia de herramientas
Las herramientas se sirven en niveles, para que la lista de esquemas enviada al modelo en cada turno sea proporcional a lo que realmente es utilizable:
| Nivel | Cantidad | Registrado cuando |
|---|---|---|
| Core | 8 | siempre |
| Extended | 5 | siempre |
| REST | 7 | solo con FIGMA_PERSONAL_ACCESS_TOKEN configurado |
| Legacy | 3 | solo con FIGMA_MCP_LEGACY_TOOLS=1 |
Sin un token REST, un agente ve 13 herramientas en lugar de 23 — y nunca desperdicia una llamada en algo que solo devolvería REST_TOKEN_MISSING.
Las 23 herramientas
Core — lienzo en vivo
| Herramienta | Descripción |
|---|---|
figma_execute_code | Ejecuta JS en el sandbox de Figma. Inyecta figma, ensureFont, getFreePosition, progress, bridge. Soporta capture, capture_node_ids, diff, async, target. |
figma_read_canvas | Lectura optimizada en tokens del documento en vivo (jsx / tree / json) con budget_tokens. |
figma_screenshot | PNG de node_ids específicos o de la selección actual. |
figma_find_components | Búsqueda de componentes en caché, tokenizada y difusa — variantes, propiedades, claves. |
figma_insert_component_instance | Instancia un componente/variante, aplica anulaciones de texto, coloca en AutoLayout. |
figma_insert_svg | Inserta SVG crudo con escalado proporcional, recoloración, envoltura de componente opcional. |
figma_get_variables | Colecciones de variables, modos y valores de token. |
figma_rollback | Deshace el punto de control de una llamada de escritura anterior. |
Extended — lienzo en vivo
| Herramienta | Descripción |
|---|---|
figma_get_selection | Geometría, rellenos hex compactos, padre/página, contexto AutoLayout de la selección. |
figma_get_canvas_layout | Límites del artboard + un suggestedNextPosition seguro. layout:"grid" empaqueta en estantes. |
figma_set_variables_mode | Cambia el modo de tema (Dark/Light/Brand) en un marco o página. |
figma_job_status | Consulta un trabajo en segundo plano escalado. |
figma_list_targets | Lista documentos de Figma conectados para apuntar a múltiples archivos. |
REST — Figma Cloud (necesita un token)
| Herramienta | Descripción |
|---|---|
get_file / get_node | Archivo/subárbol en la nube optimizado en tokens. Soporta budget_tokens. |
get_image | Renderiza nodos a PNG/SVG/PDF mediante el renderizador de Figma. |
get_styles / get_components | Estilos publicados y componentes del sistema de diseño. |
get_comments / post_comment | Lee y publica comentarios de archivo. |
Legacy — opt-in mediante FIGMA_MCP_LEGACY_TOOLS=1
figma_create_ui_card · get_me · get_image_fills
Notas de ingeniería
Las partes que fueron más difíciles de lo que parecen — y por qué el código tiene la forma que tiene.
`eval` es una trampa en el sandbox de Figma
La forma obvia de persistir helpers entre llamadas es "guardar la fuente, eval la la próxima vez". Falla silenciosamente: eval es una función vinculada en el sandbox de Figma, lo que por especificación hace que cada llamada sea una evaluación indirecta — las declaraciones dentro de ella no alcanzan ni el ámbito del llamador ni globalThis. Nada está definido, nada lanza.
bridge.define está construido sobre new Function en su lugar, cuyos cuerpos son ámbitos de función ordinarios. El bridge.info() del runtime reporta este contrato al agente cuando se le pide, para que pueda preguntar en lugar de adivinar.
Un WebSocket RFC 6455 hecho a mano, a propósito
Cero dependencias npm no es una métrica de vanidad aquí — significa que git clone && node install.mjs funciona en una máquina bloqueada sin acceso a registros, y no hay cadena de suministro que auditar para algo que ejecuta JS arbitrario dentro de tus archivos de diseño.
El costo es poseer el enmarcado: enmascaramiento, marcos de continuación fragmentados (una captura de pantalla de 4 MB llega dividida, y tratar cada fragmento como un mensaje completo la descartaba silenciosamente hasta que la llamada a la herramienta expiraba 40s después), liveness ping/pong, y un techo de 64 MB para que un solo marco no agote la memoria.
Fragmentación de `pluginData` por bytes UTF-8, no por longitud de cadena
Figma limita las entradas de pluginData a aproximadamente 100 KB — medido en bytes. El fragmentador original cortaba por longitud de cadena JS, así que un módulo escrito en cirílico (~2 bytes/carácter) producía fragmentos de "60,000 caracteres" que en realidad eran 120 KB, y setPluginData lanzaba un error. El divisor ahora recorre el presupuesto real de UTF-8 y nunca rompe un par de sustitutos.
La propiedad del puerto tiene que ser recuperable
Cada agente genera su propia copia del servidor; el primero en vincular :8765 posee el socket del plugin y el resto actúa como proxy hacia él. Cuando el propietario sale, un proxy tiene que poder tomar el control — de lo contrario, cada agente sobreviviente permanece permanentemente roto hasta reiniciarse. Un watchdog de 5 segundos reintenta la vinculación, y una llamada de proxy fallida desencadena un intento inmediato de toma de control.
Hacer la colocación O(vecinos) en lugar de O(n)
El motor de colisiones original volvía a escanear cada nodo de nivel superior para cada una de las hasta 200 posiciones candidatas, y luego lo hacía de nuevo en una segunda función dentro del mismo tick — y solo avanzaba a lo largo de un eje, por lo que 20 pantallas generadas se convertían en una cinta de una milla de largo que nadie podía alejar para ver.
Los límites ahora se calculan una vez y se comparten; las colisiones pasan por un hash de cuadrícula de 500px; y layout:"grid" se empaqueta en un rectángulo compacto.
Errores que le dicen al agente qué hacer en su lugar
Los errores crudos de la API del Plugin son notoriamente poco útiles. Los fallos se comparan con modos conocidos y se reescriben con una línea HINT: que nombra la API que realmente funciona, más un code estable y legible por máquina (FONT_NOT_LOADED, INSTANCE_TRANSFORM_LOCKED, STALE_NODE_ID, AUTOLAYOUT_HUG_RESIZE, AMBIGUOUS_TARGET, …) para que los agentes y las herramientas puedan ramificar según el tipo de fallo sin analizar prosa.
Seguridad: este endpoint ejecuta JS arbitrario en tu archivo de diseño
:8765 es loopback, pero loopback es alcanzable por cualquier página web que el usuario tenga abierta. Dos puertas independientes: una lista de permitidos de Origen (un navegador no puede falsificar Origin, por lo que una página en evil.com se rechaza en el handshake) y un token compartido que install.mjs genera e incorpora tanto en la configuración de MCP como en el plugin instalado — lo que también cubre el caso del iframe con sandbox, donde una página hostil puede presentar Origin: "null" también.
Al ejecutar directamente desde un clon sin token, la puerta de Origen sigue aplicándose y el servidor imprime una advertencia, por lo que degrada en lugar de abrirse silenciosamente.
La única llamada de red que este servidor hace por sí solo: una verificación de actualizaciones
Al iniciar, el servidor compara el commit desde el que se instaló (registrado por install.mjs en un version.json junto al código copiado) con el último commit en main mediante una solicitud a la API de GitHub, e imprime un aviso de una línea en stderr si difieren. Nunca aplica nada por sí mismo — node install.mjs --update sigue siendo un paso manual y deliberado.
Esta es la única cosa en este proyecto que asume acceso a la red, por lo que está diseñada para desaparecer limpiamente cuando no la hay: la verificación se dispara sin esperarse (nunca retrasa initialize ni la primera llamada a herramienta), una solicitud fallida/lenta/sin conexión se captura y se omite silenciosamente, y los resultados se cachean durante 24h para no golpear el límite de tasa no autenticado de GitHub ni ejecutarse una vez por copia generada del servidor. Establece FIGMA_MCP_NO_UPDATE_CHECK=1 para apagarlo por completo — vale la pena hacerlo en las máquinas bloqueadas de las que trata la nota anterior.
Pruebas
Cinco suites sin dependencias, todas ejecutables con node básico:
node tests/bridge-runtime.test.js # sandbox runtime, module persistence, checkpoint/rollback
node tests/layout-packer.test.js # row/grid packing, collision grid
node tests/optimizer.test.js # jsx/tree/json serialization, budget truncation
node tests/mcp-protocol.test.js # real server over stdio: initialize, tools/list, tiering
node tests/install.test.js # config merge/reuse, token persistence, stale-file cleanup
mcp-protocol.test.js genera el servidor real como un proceso hijo y le habla en NDJSON — el mismo transporte que usa un cliente MCP real — en lugar de importar internos.
Estructura del repositorio
figma-mcp-bridge/
├── figma/ # MCP server (Node.js, stdio + WebSocket)
│ ├── index.js # protocol, tool router, job ledger, target router
│ ├── optimizer/ # AST pruner, style collapser, JSX/tree serializers
│ ├── instructions.md # agent-facing protocol docs (served on `initialize`)
│ └── *.json # per-tool schemas
├── figma-plugin/ # Figma Desktop plugin
│ ├── code.js # sandbox executor, bridge runtime, checkpoints, capture
│ └── ui.html # HUD — stream, settings, control (pause / undo)
├── tests/ # 5 suites, 0 dependencies
├── install.mjs # cross-platform installer, updater, doctor (Node only)
└── AGENTS.md # onboarding protocol for AI agents
Fuentes
- Requisitos de asiento y cuota para el servidor oficial — Figma: Guía para el servidor MCP de Figma, Documentación para desarrolladores de Figma: Límites de tasa y acceso
- Reducción de tokens medida en un fixture de diseño de 6 tarjetas que imita la salida REST real: 20,587 B crudos → 2,862 B Pseudo-JSX (86.1%) / 1,912 B árbol (90.7%). El fixture está verificado y asegurado — ejecuta
node tests/optimizer.test.jspara reproducir las cifras exactas.
La comparación refleja el comportamiento documentado públicamente a agosto de 2026. Las alternativas se desarrollan activamente — verifica las capacidades actuales antes de tomar una decisión basada solo en esta tabla.
Licencia
MIT · Construido por Roman Kolganov