figma-mcp-bridge
Acesso de escrita a um canvas Figma ao vivo com feedback de screenshot com moldura automática e rollback em uma única chamada. Zero dependências, plano gratuito do Figma.
Documentação
⚡ Figma MCP Bridge
Deixe um agente de IA projetar no Figma — e realmente veja o que ele desenhou.
Um servidor MCP sem dependências que dá aos agentes de codificação acesso de leitura e escrita a um canvas Figma ao vivo, e então fecha o ciclo devolvendo o PNG renderizado ao modelo para autocrítica visual.

O agente gera o design, audita visualmente a saída renderizada, detecta defeitos de layout e se autocorrige em tempo real — zero intervenção humana no processo.
O problema
Agentes de codificação são efetivamente cegos dentro do Figma. A maioria dos servidores MCP do Figma são somente leitura —
eles achatam um design em texto para que um modelo possa transformá-lo em código, e o tráfego para por aí.
Os que podem escrever estão ou atrás de uma licença Figma paga com cota mensal de chamadas de ferramentas,
ou expõem um vocabulário fixo de comandos (create_rectangle, set_fill) que se esgota
no momento em que a tarefa fica específica.
E nenhum deles responde à pergunta que realmente importa após uma escrita: ficou certo?
Um agente que desenha um card, recebe de volta {"ok": true} e segue em frente não tem como perceber que seu
texto transbordou, que o contraste falhou, ou que o frame caiu em cima do trabalho de outra pessoa.
Esta ponte fecha esse ciclo e adiciona as proteções de segurança que um agente autônomo precisa para ser confiável com um arquivo de design real.
Como é diferente
| Esta ponte | Figma Dev Mode MCP (oficial) | Framelink | Talk to Figma | |
|---|---|---|---|---|
| Escreve no canvas | ✅ JS arbitrário no sandbox | ✅ código-para-canvas | ❌ somente leitura | ✅ conjunto fixo de comandos |
| Ciclo de feedback visual | ✅ PNG com moldura automática a cada escrita | ⚠️ chamada de captura de tela separada | ❌ | ❌ |
| Desfaz o trabalho do agente | ✅ figma_rollback | ❌ | n/a | ❌ |
| Funciona no plano gratuito do Figma | ✅ | ❌ licença Dev/Full, plano pago¹ | ✅ | ✅ |
| Cota de chamadas de ferramentas | nenhuma — é local | 6 / mês em licenças Starter¹ | herda limites de taxa da REST | nenhuma |
| Leitura otimizada em tokens do documento ao vivo | ✅ 86–91% menor² | ⚠️ parcial | ❌ somente REST | ❌ |
| Módulos de código persistentes no sandbox | ✅ bridge.define/require | ❌ | ❌ | ❌ |
| Trabalhos longos sobrevivem ao próprio timeout | ✅ job_id assíncrono + progresso | ❌ | n/a | ❌ |
| Pegada de instalação | 0 dependências npm, npx ou git clone, apenas Node | Figma desktop + licença paga | npx + token de acesso | Bun + um segundo processo de servidor |
A versão curta: Framelink é a melhor escolha se você só quer transformar um design existente em código. O servidor oficial é a escolha mais segura se você já está em um plano Figma pago e quer Code Connect. Esta ponte é para o caso em que o agente está fazendo o design — onde ele precisa escrever livremente, verificar o próprio trabalho e ser desfeito quando errar.
Como 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
O agente fala MCP puro via stdio. O servidor possui um WebSocket RFC 6455 feito à mão na
:8765, ao qual o plugin do Figma se conecta — então os comandos chegam ao canvas sem latência
de polling, e os resultados (incluindo PNGs base64 de vários megabytes) voltam direto.
Início rápido
npx @kolganovr/figma-mcp-bridge
Ou, para ler o instalador antes que ele toque na sua máquina — é o mesmo script de qualquer forma, apenas buscado de forma diferente:
git clone https://github.com/kolganovr/figma-mcp-bridge.git
cd figma-mcp-bridge
node install.mjs
Ambos copiam o servidor e o plugin para o lugar certo e registram o servidor MCP em toda configuração
de cliente de IA que encontrarem — Claude Desktop, Claude Code, Cursor, Windsurf, Antigravity. O pacote npm existe
puramente para um primeiro comando mais curto; ele não adiciona uma única dependência de runtime — dependencies
está vazio no package.json dele também, e nenhum Python é necessário. node é o único runtime
que este projeto sempre precisa, tanto para o instalador quanto para o servidor.
Depois, no Figma Desktop:
- Plugins → Development → Import plugin from manifest… → escolha
figma-plugin/manifest.json - Pressione
Ctrl + Alt + P(macOS:Cmd + Option + P) para iniciar o Antigravity Bridge - O status fica verde —
CONNECTED
Reinicie seu cliente de IA para que ele carregue as novas ferramentas. Verifique a qualquer momento com:
npx @kolganovr/figma-mcp-bridge --doctor # or: node install.mjs --doctor
Opcional: Acesso REST à nuvem do Figma
As ferramentas de canvas ao vivo não precisam de token. Se você também quiser ler arquivos na nuvem não abertos
(get_file, get_node, get_styles, …), forneça um token de acesso pessoal:
npx @kolganovr/figma-mcp-bridge --token "your_figma_personal_access_token"
# or: node install.mjs --token "your_figma_personal_access_token"
Sem um token, essas 7 ferramentas não são registradas — veja Referência de ferramentas.
O que isso dá ao agente
1. Escreva livremente e depois veja o resultado
capture: true retorna um PNG de exatamente o que a chamada acabou de criar ou modificar — com moldura automática
para os nós alterados, nunca a página inteira, e nunca sequestrando a seleção do usuário.
// 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
}
O array warnings é uma auto-verificação barata sobre a subárvore tocada — transbordamento de texto, baixo contraste,
nós de tamanho zero. Ele pega os erros óbvios sem gastar uma ida e volta de captura de tela.
2. Desfaça qualquer coisa que o agente fez
Cada escrita abre um checkpoint automaticamente. Uma chamada o reverte — sem Ctrl+Z rolando para trás
sobre o trabalho não relacionado do próprio 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
}
Nós criados são rastreados automaticamente via um Proxy em torno de figma.create*(). Edições de propriedades são
rastreadas quando há snapshot. Exclusões são honestamente relatadas como irrecuperáveis em vez de perdidas silenciosamente.
3. Leia o canvas ao vivo por ~700 tokens em vez de ~5.000
O mesmo pipeline de poda + Pseudo-JSX que alimenta as ferramentas de nuvem, apontado para o que está aberto agora. Medido em um layout realista de 6 cards: 20 KB de JSON bruto da API → 2,7 KB (86% menor).
<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 a resposta: se a profundidade solicitada ultrapassar, o servidor re-serializa a
árvore já buscada de forma mais rasa — sem segunda ida e volta — e anexa um comentário dizendo o que fez
e qual id buscar para mais.
4. Ensine novos truques ao sandbox que sobrevivem a reinícios
Toda chamada de figma_execute_code é um escopo de função novo, então helpers normalmente morrem instantaneamente.
bridge.define compila e armazena um módulo dentro do 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. Trabalhos longos que não morrem no timeout
Uma geração ainda em execução após 30s devolve um job_id em vez de falhar enquanto o plugin
continua trabalhando. Consulte figma_job_status para progresso ao vivo — o sandbox o reporta via
progress(step, of, note).
Referência de ferramentas
As ferramentas são servidas em camadas, para que a lista de esquemas enviada ao modelo a cada turno permaneça proporcional ao que é realmente utilizável:
| Camada | Quantidade | Registrada quando |
|---|---|---|
| Core | 8 | sempre |
| Estendida | 5 | sempre |
| REST | 7 | somente com FIGMA_PERSONAL_ACCESS_TOKEN definido |
| Legado | 3 | somente com FIGMA_MCP_LEGACY_TOOLS=1 |
Sem um token REST, um agente vê 13 ferramentas em vez de 23 — e nunca desperdiça uma chamada em
algo que só retornaria REST_TOKEN_MISSING.
Todas as 23 ferramentas
Core — canvas ao vivo
| Ferramenta | Descrição |
|---|---|
figma_execute_code | Executa JS no sandbox do Figma. Injeta figma, ensureFont, getFreePosition, progress, bridge. Suporta capture, capture_node_ids, diff, async, target. |
figma_read_canvas | Leitura otimizada em tokens do documento ao vivo (jsx / tree / json) com budget_tokens. |
figma_screenshot | PNG de node_ids específicos ou da seleção atual. |
figma_find_components | Busca de componentes em cache, tokenizada e difusa — variantes, propriedades, chaves. |
figma_insert_component_instance | Instancia um componente/variante, aplica substituições de texto, coloca em AutoLayout. |
figma_insert_svg | Insere SVG bruto com escala proporcional, recolorização e encapsulamento opcional em componente. |
figma_get_variables | Coleções de variáveis, modos e valores de token. |
figma_rollback | Desfaz o checkpoint de uma chamada de escrita anterior. |
Estendida — canvas ao vivo
| Ferramenta | Descrição |
|---|---|
figma_get_selection | Geometria, preenchimentos hex compactos, página/pai, contexto de AutoLayout da seleção. |
figma_get_canvas_layout | Limites do artboard + um suggestedNextPosition seguro. layout:"grid" empacota em prateleira. |
figma_set_variables_mode | Alterna o modo de tema (Dark/Light/Brand) em um frame ou página. |
figma_job_status | Consulta um trabalho em segundo plano escalado. |
figma_list_targets | Lista documentos Figma conectados para segmentação de múltiplos arquivos. |
REST — Nuvem do Figma (precisa de token)
| Ferramenta | Descrição |
|---|---|
get_file / get_node | Arquivo/subárvore de nuvem otimizado em tokens. Suporta budget_tokens. |
get_image | Renderiza nós para PNG/SVG/PDF via renderizador do Figma. |
get_styles / get_components | Estilos publicados e componentes de design system. |
get_comments / post_comment | Lê e publica comentários em arquivos. |
Legado — opt-in via FIGMA_MCP_LEGACY_TOOLS=1
figma_create_ui_card · get_me · get_image_fills
Notas de engenharia
As partes que foram mais difíceis do que parecem — e por que o código tem o formato que tem.
`eval` é uma armadilha no sandbox do Figma
A forma óbvia de persistir helpers entre chamadas é "guardar o código-fonte, eval na próxima vez." Isso
falha silenciosamente: eval é uma função ligada no sandbox do Figma, o que por especificação torna cada chamada
um eval indireto — declarações dentro dela não alcançam o escopo do chamador nem globalThis.
Nada é definido, nada lança erro.
bridge.define é construído sobre new Function em vez disso, cujos corpos são escopos de função comuns.
O bridge.info() do runtime relata esse contrato ao agente quando solicitado, para que ele possa perguntar em vez de adivinhar.
Um WebSocket RFC 6455 feito à mão, de propósito
Zero dependências npm não é uma métrica de vaidade aqui — significa que git clone && node install.mjs funciona
em uma máquina bloqueada sem acesso a registro, e não há cadeia de suprimentos para auditar em algo
que executa JS arbitrário dentro dos seus arquivos de design.
O custo é assumir o enquadramento: mascaramento, frames de continuação fragmentados (uma captura de tela de 4 MB chega dividida, e tratar cada fragmento como uma mensagem inteira a descartava silenciosamente até a chamada de ferramenta expirar 40s depois), liveness de ping/pong e um teto de 64 MB para que um frame não esgote a memória.
Dividindo `pluginData` por bytes UTF-8, não por comprimento de string
O Figma limita entradas de pluginData a aproximadamente 100 KB — medidos em bytes. O divisor original
cortava pelo comprimento da string JS, então um módulo escrito em cirílico (~2 bytes/char) produzia blocos de "60.000 caracteres"
que na verdade tinham 120 KB, e setPluginData lançava erro. O divisor agora percorre o orçamento real de UTF-8
e nunca separa um par substituto.
A propriedade da porta precisa ser recuperável
Cada agente inicia sua própria cópia do servidor; o primeiro a vincular :8765 é dono do socket do plugin e
os demais fazem proxy para ele. Quando o dono sai, um proxy precisa conseguir assumir — caso contrário, todo
agente sobrevivente fica permanentemente quebrado até ser reiniciado. Um watchdog de 5 segundos tenta o bind novamente, e
uma chamada de proxy com falha dispara uma tentativa imediata de assumir.
Tornando o posicionamento O(vizinhos) em vez de O(n)
O mecanismo de colisão original revasculhava cada nó de nível superior para cada uma das até 200 posições candidatas, e depois fazia isso novamente em uma segunda função no mesmo tick — e só avançava ao longo de um eixo, então 20 telas geradas viravam uma fita de um quilômetro que ninguém conseguia dar zoom para ver.
Os limites agora são calculados uma vez e compartilhados; colisões passam por um hash de grade de
500px; e layout:"grid" empacota em um retângulo compacto.
Erros que dizem ao agente o que fazer em vez disso
Erros brutos da Plugin API são notoriamente pouco úteis. Falhas são comparadas com modos conhecidos
e reescritas com uma linha HINT: nomeando a API que realmente funciona, além de um code
estável e legível por máquina (FONT_NOT_LOADED, INSTANCE_TRANSFORM_LOCKED, STALE_NODE_ID, AUTOLAYOUT_HUG_RESIZE,
AMBIGUOUS_TARGET, …) para que agentes e ferramentas possam ramificar com base no tipo de falha sem
precisar interpretar prosa.
Segurança: este endpoint executa JS arbitrário no seu arquivo de design
:8765 é loopback, mas loopback é acessível por qualquer página web que o usuário
por acaso tenha aberta. Duas portas independentes: uma lista de permissões de Origin (um navegador
não pode forjar Origin, então uma página em evil.com é rejeitada no handshake) e um
token compartilhado que install.mjs gera e incorpora tanto na configuração do MCP quanto no
plugin instalado — o que também cobre o caso do iframe com sandbox, onde uma página hostil pode
apresentar Origin: "null" também.
Executando direto de um clone sem token, a porta de Origin ainda se aplica e o servidor imprime um aviso, então ele degrada em vez de se abrir silenciosamente.
A única chamada de rede que este servidor faz por conta própria: uma verificação de atualização
Na inicialização, o servidor compara o commit do qual foi instalado (registrado por install.mjs em um
version.json ao lado do código copiado) com o commit mais recente em main via uma única
requisição à API do GitHub, e imprime um aviso de uma linha no stderr se forem diferentes. Ele nunca
aplica nada por conta própria — node install.mjs --update ainda é um passo manual e deliberado.
Esta é a única coisa neste projeto que assume acesso à rede, então foi construída para desaparecer
limpo quando não houver: a verificação é disparada sem ser aguardada (nunca atrasa initialize
ou a primeira chamada de ferramenta), uma requisição falha/lenta/offline é capturada e silenciosamente
ignorada, e os resultados são armazenados em cache por 24h para não atingir o limite de taxa não
autenticado do GitHub nem rodar uma vez por cópia gerada do servidor. Defina FIGMA_MCP_NO_UPDATE_CHECK=1 para
desligá-la completamente — vale a pena fazer nas máquinas bloqueadas sobre as quais a nota anterior
fala.
Testes
Cinco suítes sem dependências, todas executáveis com node puro:
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 inicia o servidor real como um processo filho e fala NDJSON com ele — o
mesmo transporte que um cliente MCP real usa — em vez de importar internos.
Estrutura do repositório
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
Fontes
- Requisitos de assento e cota para o servidor oficial — Figma: Guia para o servidor MCP do Figma, Figma Developer Docs: Limites de taxa e acesso
- Redução de tokens medida em um fixture de layout de 6 cartões que imita a saída REST real: 20.587 B brutos → 2.862 B Pseudo-JSX (86,1%) / 1.912 B árvore (90,7%). O fixture está versionado e verificado — execute
node tests/optimizer.test.jspara reproduzir os números exatos.
A comparação reflete o comportamento documentado publicamente em agosto de 2026. Alternativas estão em desenvolvimento ativo — verifique as capacidades atuais antes de tomar uma decisão com base apenas nesta tabela.
Licença
MIT · Criado por Roman Kolganov