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.

License: MIT MCP Node.js Dependencies Tools Works on


Figma MCP Bridge Demo

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 ponteFigma Dev Mode MCP (oficial)FramelinkTalk 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 agentefigma_rollbackn/a
Funciona no plano gratuito do Figma❌ licença Dev/Full, plano pago¹
Cota de chamadas de ferramentasnenhuma — é local6 / mês em licenças Starter¹herda limites de taxa da RESTnenhuma
Leitura otimizada em tokens do documento ao vivo✅ 86–91% menor²⚠️ parcial❌ somente REST
Módulos de código persistentes no sandboxbridge.define/require
Trabalhos longos sobrevivem ao próprio timeoutjob_id assíncrono + progresson/a
Pegada de instalação0 dependências npm, npx ou git clone, apenas NodeFigma desktop + licença paganpx + token de acessoBun + 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:

  1. Plugins → Development → Import plugin from manifest… → escolha figma-plugin/manifest.json
  2. Pressione Ctrl + Alt + P (macOS: Cmd + Option + P) para iniciar o Antigravity Bridge
  3. 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:

CamadaQuantidadeRegistrada quando
Core8sempre
Estendida5sempre
REST7somente com FIGMA_PERSONAL_ACCESS_TOKEN definido
Legado3somente 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

FerramentaDescrição
figma_execute_codeExecuta JS no sandbox do Figma. Injeta figma, ensureFont, getFreePosition, progress, bridge. Suporta capture, capture_node_ids, diff, async, target.
figma_read_canvasLeitura otimizada em tokens do documento ao vivo (jsx / tree / json) com budget_tokens.
figma_screenshotPNG de node_ids específicos ou da seleção atual.
figma_find_componentsBusca de componentes em cache, tokenizada e difusa — variantes, propriedades, chaves.
figma_insert_component_instanceInstancia um componente/variante, aplica substituições de texto, coloca em AutoLayout.
figma_insert_svgInsere SVG bruto com escala proporcional, recolorização e encapsulamento opcional em componente.
figma_get_variablesColeções de variáveis, modos e valores de token.
figma_rollbackDesfaz o checkpoint de uma chamada de escrita anterior.

Estendida — canvas ao vivo

FerramentaDescrição
figma_get_selectionGeometria, preenchimentos hex compactos, página/pai, contexto de AutoLayout da seleção.
figma_get_canvas_layoutLimites do artboard + um suggestedNextPosition seguro. layout:"grid" empacota em prateleira.
figma_set_variables_modeAlterna o modo de tema (Dark/Light/Brand) em um frame ou página.
figma_job_statusConsulta um trabalho em segundo plano escalado.
figma_list_targetsLista documentos Figma conectados para segmentação de múltiplos arquivos.

REST — Nuvem do Figma (precisa de token)

FerramentaDescrição
get_file / get_nodeArquivo/subárvore de nuvem otimizado em tokens. Suporta budget_tokens.
get_imageRenderiza nós para PNG/SVG/PDF via renderizador do Figma.
get_styles / get_componentsEstilos publicados e componentes de design system.
get_comments / post_commentLê 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

  1. 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
  2. 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.js para 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