Skip to content

feat(figma): add Figma MCP for design-to-code context - #558

Merged
yuriassuncx merged 4 commits into
mainfrom
feat/figma-mcp
Oct 13, 2026
Merged

yuriassuncx merged 4 commits into
mainfrom
feat/figma-mcp

Conversation

@yuriassuncx

@yuriassuncx yuriassuncx commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Why

Most Studio tasks link a Figma frame, but the board agents have no access to it. Figma only offers its official MCP to approved clients. So the agents guess, and the result often does not match the design. This MCP reads designs through the public REST API with each user's own Figma account (OAuth). It gives agents a real reference: a screenshot, the layout as CSS, the tokens and the designer's comments.

image

What

New figma/ MCP. Tool names follow Figma's official MCP server, so prompts and skills written for it carry over.

image
  • Design to code: get_design_context (start here), get_metadata, get_screenshot, download_assets, get_variable_defs, get_motion_context, get_figjam, get_comments
  • Design systems: get_libraries, search_design_system
  • Code Connect: get_code_connect_map, add_code_connect_map, get_code_connect_suggestions, get_context_for_code_connect. Mappings are stored as Figma dev resources, because Figma's own Code Connect data is not in the public API.
  • whoami, plus the create_design_system_rules prompt

get_design_context returns the PNG of the node and one line per layer:

FRAME "Card" [1:1] 320×200 styles{fill:"Surface/White"} { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 16px 24px; background: #FFFFFF; border-radius: 8px }
  TEXT "Title" [1:2] 272×24 { align-self: stretch; color: #000000; font-family: "Inter"; font-weight: 700; font-size: 20px; line-height: 24px } "Tênis 574"
  FRAME "Icon" [1:3] 24×24 asset=svg { width: 24px; height: 24px }
  INSTANCE "Buy" [1:6] 272×48 <Button Label="Comprar" Size="Large"> { align-self: stretch }

The output also lists the unresolved comments pinned inside the frame. Briefs often say these are part of the spec.

Not included, by design: use_figma, generate_figma_design, create_new_file, upload_assets and generate_diagram. Creating or editing layers needs the Plugin API inside an open Figma client, and REST does not offer it. generate_image already exists in Studio.

Gravacao.de.ecra.1.online-video-cutter.com.mp4

Deploy and auth (needs a reviewer)

  • Deploy: kubernetes-bun, entry added to deploy.json, served at https://sites-figma.deco.site/mcp.
  • Auth exemption: figma is added to auth-exemptions.json, which needs reviewer sign-off. The Authorization header carries the caller's own Figma token, and every tool forwards it to api.figma.com. There is no shared secret to check and no server-side credential exposed. This is the same situation as dynamic-yield.
  • OAuth hardening: runtime ^3.0.0 with stateSecret. The server refuses to boot without OAUTH_STATE_SECRET, so the code that carries the token is never plain base64. allowedRedirectHosts is pinned to decocms.com (plus EXTRA_ALLOWED_REDIRECT_HOSTS).
  • Personal tokens: a personal access token (figd_…) in the connection's Token field also works. It is sent as X-Figma-Token.

Before merging

  1. Create the Figma OAuth app with callback https://sites-figma.deco.site/oauth/callback and the scopes listed in figma/README.md. A private app only accepts users from the Figma org that owns it; a public app goes through Figma review.
  2. Provision these site secrets: FIGMA_CLIENT_ID, FIGMA_CLIENT_SECRET, OAUTH_STATE_SECRET.
  3. Rate limits follow the connected seat. View and Collab seats get only about 20 Tier-1 reads per month. Dev and Full seats get 10 to 20 per minute. See the README.

Verified

  • bun test in figma/: 10 tests pass (URL parsing, CSS conversion, truncation, XML output, comment scoping).
  • tsc --noEmit is clean for figma/server/**. The errors inside @decocms/runtime@3.0.0 sources also show up for github/ and come from how dependencies were installed in this environment.
  • check:auth, build:registry and the pre-commit format and lint checks pass.
  • Local boot of the built dist/server/main.js:
    • /.well-known/oauth-authorization-server is served.
    • tools/list returns the 15 tools and prompts/list returns the prompt.
    • /authorize redirects to figma.com/oauth with sealed state, and a non-deco redirect_uri is rejected.
    • A tool call reaches api.figma.com with the forwarded token.
  • Not yet verified against a real Figma file. That needs the OAuth app, or a personal token from a Dev or Full seat.

🤖 Generated with Claude Code


Summary by cubic

Adds a Figma MCP server so coding agents get real design context — screenshot, layout as flexbox CSS, tokens, comments, library search and Code Connect — read through the public REST API with each user's own Figma account (OAuth). Tool names mirror Figma's official MCP server, so prompts and skills written for it carry over.

The server is read-only: creating or editing layers is not included because the REST API doesn't offer it. Code Connect mappings are stored as Figma dev resources. get_design_context output was refined from a real run: LINE layers render as border-top dividers with the stroke color, text shows its content instead of stale layer names, fonts in use are listed so missing ones get flagged, and layout noise (gaps on single-child frames, missing position: relative) is dropped.

Reviewer notes

  • figma is added to auth-exemptions.json: every tool forwards the caller's own Figma token in the Authorization header, so there is no shared secret to check and no server-side credential to protect. OAuth state and code are sealed with OAUTH_STATE_SECRET, and redirect hosts are pinned to decocms.com.
  • Before merging, create the Figma OAuth app with the callback and scopes listed in figma/README.md and provision FIGMA_CLIENT_ID, FIGMA_CLIENT_SECRET and OAUTH_STATE_SECRET as site secrets. Until the app is approved, only its owner can authorize, so users connect with a personal access token (figd_…) through a Custom Connection; a Bearer prefix on the token is stripped before detection. Note rate limits: View and Collab seats get about 20 Tier-1 reads per month; Dev and Full seats get 10–20 per minute.
  • An explicit screenshot scale is honored even when the node size is unknown, and a failed token refresh from bad client credentials no longer evicts the stored token.

Written for commit 2f1748e. Summary will update on new commits.

View guided diff Turn on auto-fix

Read-only Figma MCP over the REST API with per-user OAuth, so coding
agents get a real reference to the design instead of guessing. Tool
names follow Figma's official MCP server:

- design to code: get_design_context (screenshot + one-line-per-layer
  outline with auto-layout as flexbox CSS, text, component props and
  the comments pinned in the frame), get_metadata, get_screenshot,
  download_assets, get_variable_defs, get_motion_context, get_figjam,
  get_comments
- design systems: get_libraries, search_design_system
- Code Connect via Figma dev resources: get_code_connect_map,
  add_code_connect_map, get_code_connect_suggestions,
  get_context_for_code_connect
- whoami and the create_design_system_rules prompt

Code-to-design tools are left out: creating layers needs the Plugin
API, which REST does not offer.

Deployed on kubernetes-bun at sites-figma.deco.site. Exempt from
withAuth because Authorization carries the caller's own Figma token;
OAuth state is sealed with OAUTH_STATE_SECRET and redirects are pinned
to decocms.com.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/airtable October 10, 2026 18:21 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/blog-generator October 10, 2026 18:21 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/content-scraper October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/deco-llm October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/deco-news-weekly-digest October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/discord-read October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/flux October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-analytics October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-big-query October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-calendar October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-calendar-sa October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-drive October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-meet October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-search-console October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-sheets October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-slides October 10, 2026 18:22 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-tag-manager October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-workspace October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/grafana October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/grain October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/hyperdx October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/magento October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/meta-ads October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/nanobanana October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/object-storage October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/pandadoc October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/perplexity October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/registry October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/shopify October 10, 2026 18:23 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-drive October 10, 2026 19:52 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-meet October 10, 2026 19:52 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-search-console October 10, 2026 19:52 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-sheets October 10, 2026 19:52 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-slides October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-tag-manager October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/google-workspace October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/grafana October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/grain October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/hyperdx October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/magento October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/meta-ads October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/nanobanana October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/object-storage October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/pandadoc October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/perplexity October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/registry October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/shopify October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/slack-mcp October 10, 2026 19:53 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/veo October 10, 2026 19:54 In progress
@deco-hosting
deco-hosting Bot requested a deployment to preview/pr-558/vtex October 10, 2026 19:54 In progress
… less noise

From a real get_design_context run on a FILA frame:
- LINE layers are dividers: emit border-top with the stroke color instead
  of asking for an SVG export
- text with Figma vertical trim gets text-box: trim-both cap alphabetic,
  which explains its cap-height box
- text layers show their content, not their name (names were stale:
  "Tamanho: 43" holding "Tamanho: GG")
- drop gap on single-child frames, a no-op in CSS
- containers with absolute children get position: relative
- notes list the fonts in use so the agent flags missing ones instead of
  substituting silently

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yuriassuncx

yuriassuncx commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor Author

@tlgimenes could you review and approve this when you have a moment? Once it's approved, I'll set up the secrets and publish the MCP in Studio.

Why: most task-board tasks link a Figma frame, but agents have no access to it. Figma's official MCP only accepts allowlisted clients. So agents guess the layout, and the result often doesn't match the design. This MCP reads designs through the public REST API, with each user's own Figma account. Tool names follow the official server.

OAuth app: created in the Figma developer portal. It's public, not listed in the Community, and currently under Figma review. Its production callback, https://sites-figma.deco.site/oauth/callback, only goes live once this PR is merged and the site deploys, and the Figma review depends on it. I tested the whole flow from a Studio connection through a temporary tunnel: authorize, token, and get_design_context on real frames.

Related: decocms/studio#7870, so the decopilot chat shows MCP images to the model instead of base64 text.

@JonasJesus42

Copy link
Copy Markdown
Contributor

Review rápida comparando com o #387 (um Figma MCP anterior, wrapper 1:1 da REST API, que vou fechar em favor deste). O #558 cobre bem melhor o caso de design-to-code; abaixo, só o que o #387 tinha e aqui falta, mais dois pontos do diff.

Sugestões (opcionais, nessa ordem de valor)

  1. post_comment (top-level ou reply, com client_meta): o agente consegue responder no frame ("implementado em X"). Exige o escopo file_comments:write; reações e delete eu deixaria de fora.
  2. get_team_projects / get_project_files (escopo projects:read): descobrir arquivos sem precisar de link.
  3. get_file_versions (escopo file_versions:read): baixa prioridade.

Pontos no diff

- tokenFrom strips a "Bearer " prefix if one reaches it, so a figd_
  personal token is always detected (the runtime already strips it;
  this keeps the tool layer from depending on that)
- reformat tools/shared.ts (stray blank lines inside the zod chains)
- app.json and README: until Figma approves the OAuth app, only its owner
  can authorize, so tell users to connect with a personal access token
  through a Custom Connection

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yuriassuncx

Copy link
Copy Markdown
Contributor Author

@JonasJesus42 valeu pela review! Resolvi os pontos do diff no 2f1748e:

  • shared.ts: removi as linhas em branco dentro das cadeias do zod. Rodei o oxfmt depois, e ele não voltou a inserir.
  • Bearer no token: o runtime v3 já entrega o token cru (authToken.split(" ")[1], em @decocms/runtime/src/index.ts). O teste pelo túnel confirmou isso: o token chegou à API do Figma sem prefixo. Mesmo assim, o tokenFrom agora remove um Bearer que chegue por engano, assim a detecção do figd_ não depende do runtime. Coloquei um teste para isso.
  • Escopos separados por espaço: a doc do OAuth do Figma aceita espaço ou vírgula no scope, e o fluxo real funcionou com espaço. Mantive.

Aproveitei para incluir no app.json e no README um aviso para usar token pessoal pela Conexão Personalizada enquanto o app OAuth não é aprovado, como a gente conversou.

Sobre as sugestões (post_comment, get_team_projects/get_project_files, get_file_versions): concordo com a ordem. Só que cada uma exige um escopo novo (file_comments:write, projects:read, file_versions:read). O app OAuth está em revisão no Figma, e o portal não deixa editar enquanto isso. Se o MCP pedir um escopo que o app não tem, o Figma recusa a autorização para todo mundo. A ideia é abrir um PR separado depois da aprovação: post_comment primeiro, depois projetos e arquivos.

This branch was successfully deployed

No deployments
preview/pr-558/youtube-search — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/youtube-channel-admin — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/whatsappagent — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/wake — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/vtex-docs — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/vtex — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/veo — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/slack-mcp — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/shopify — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/registry — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/perplexity — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/pandadoc — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/object-storage — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/nanobanana — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/meta-ads — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/magento — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/hyperdx — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/grain — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/grafana — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-workspace — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-tag-manager — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-slides — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-sheets — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-search-console — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-meet — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-drive — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-calendar-sa — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-calendar — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-big-query — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/google-analytics — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/flux — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/discord-read — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/deco-news-weekly-digest — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/deco-llm — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/content-scraper — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/blog-generator — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
preview/pr-558/airtable — 2f1748ec Deployed Oct 13, 2026 by deco-hosting[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants