Skill: rasimme/figma-agent (version 0.3.0)

--- name: figma-agent description: Figma MCP integration for OpenClaw. Use when the user wants to read Figma designs, inspect design tokens/variables, work with Code Connect, or create/edit Figma designs. Requires one-time bootstrap setup. metadata: openclaw: requires: anyBins: ["node", "node18", "node20", "node22"] homepage: https://github.com/rasimme/figma-agent --- # Figma Agent Figma Remote MCP integration for OpenClaw. Reads designs via `figma__*` tools; creates and edits via CC sessions using `mcp__figma__*` tools. ## Route First **Read/Inspect** → call `figma__*` tools directly. No CC session needed. **Write/Create/Edit** → requires CC session via `mcp__figma__*` tools. Seeing or understanding a design? → Direct. Do not start a session. Changing or creating something? → Start a CC session. Follow the matching playbook below. **Execution model:** 1. **Route & Brief** — choose the workflow, identify the real risks, and write a lean execution brief 2. **Execute** — perform the direct read or CC-based write flow 3. **Done Gate** — do not report success until the applicable structural and visual validation checks pass ## Workflow Routing **First:** Determine the user's intent, then follow the matching path. This is an abridged routing summary. For the full matrix and decision logic, see [references/workflow-selection.md](references/workflow-selection.md). → Full routing matrix: [references/workflow-selection.md](references/workflow-selection.md) | Intent | Path | Route | |--------|------|-------| | Build a production screen | [Native Screen Generation](references/playbooks/native-screen-generation.md) | CC | | Create next step / state variant from an existing screen | [Native Screen Generation](references/playbooks/native-screen-generation.md) | CC | | Prototype from HTML/URL | [HTML-to-Figma Prototyping](references/playbooks/html-to-figma-prototyping.md) | CC | | Read-only inspection | `get_design_context` / `get_screenshot` | Direct | | Review + Edit a screen | [Screen Review Loop](references/playbooks/screen-review-loop.md) | CC | | Apply design tokens | [Color Tokenization](references/playbooks/color-tokenization.md) | CC | | Import + clean up Stitch export | [Stitch Import Cleanup](references/playbooks/stitch-import-cleanup.md) | CC | | Discover variables/styles | [Variable Discovery](references/playbooks/variable-discovery.md) | Direct | | Audit design system | [Design System Cleanup](references/playbooks/design-system-cleanup.md) | CC | | Inspect finished design | [Design Audit Review](references/playbooks/design-audit-review.md) | Direct | --- ## Image Delivery (MANDATORY after every write) Screenshots must reach the user as Telegram photo attachments — not as inline base64 text. **Workflow:** 1. **Save to file** — use `--out <path>` flag so the PNG is written directly: ```bash node scripts/figma-mcp-cli.mjs get_screenshot \ fileKey=<fileKey> nodeId=<nodeId> scale=2 \ --out ~/workspace-dev-botti/screenshots/<name>.png ``` 2. **Deliver to user** — put `MEDIA:<path>` on its own line in your reply: ``` Hier ist der aktuelle Stand: MEDIA:screenshots/validate.png ``` OpenClaw extracts every `MEDIA:` line and sends the file as a native Telegram photo. The path is workspace-relative (`screenshots/...`) or absolute. **Two different tools — don't mix them up:** | Tool | Purpose | Used for Image Delivery? | |------|---------|--------------------------| | `image` tool | Send to Vision model for analysis | ❌ No — analysis only | | `MEDIA:<path>` in reply text | Send as Telegram attachment | ✅ Yes — actual delivery | **Why `--out`?** Without it, the Figma API returns base64-encoded JSON → renders as inline text in Telegram, not a photo. The `--out` flag decodes and writes a real PNG file that `MEDIA:` sends as an attachment. **Screenshot directory:** `~/workspace-dev-botti/screenshots/` (create if missing) **Validation pattern (every write operation):** ```bash # After use_figma call — save screenshot → MEDIA: in reply node scripts/figma-mcp-cli.mjs get_screenshot fileKey=<key> nodeId=<id> scale=2 \ --out ~/workspace-dev-botti/screenshots/validate.png # Reply with: # MEDIA:screenshots/validate.png ``` **Stitch comparison:** Stitch delivers via `MEDIA:<screenshotUrl>` (HTTP URL). Figma delivers via `MEDIA:<local-path>` (file). Mechanism identical — only the source differs. --- ## Hard Rules (Top 6) These are non-negotiable. Full rule set: [references/core-rules.md](references/core-rules.md) 1. **Validate after every write** — `get_screenshot` or `get_metadata` after each `use_figma` call. Never assume success. 2. **Read → Understand → Fix → Retry** — never blindly retry failed code, never rebuild as first response. 3. **Explicit over implicit** — name exact variables, components, layout modes. Leave nothing to inference. 4. **Design-system-first** — check local variables, styles, Code Connect, then libraries before creating anything raw. 5. **Component-instance-first** — if a suitable existing design-system component exists, instantiate it instead of recreating it with local frames. 6. **Section-by-section** — one logical section per `use_figma` call, validate between sections. --- ## Known Gotchas Before writing any `use_figma` code, know these failure modes: → Full reference: [references/plugin-api-gotchas.md](references/plugin-api-gotchas.md) - **Paint binding:** `setBoundVariableForPaint` returns a new paint — reassign the fills/strokes array ([#paint-binding](references/plugin-api-gotchas.md#paint-binding)) - **Opacity reset:** Paint binding resets opacity to 1.0 — save and restore explicitly ([#opacity](references/plugin-api-gotchas.md#opacity)) - **Page context:** Always set page explicitly — `figma.currentPage` may reset between calls ([#page-context](references/plugin-api-gotchas.md#page-context)) - **FILL sizing:** appendChild to auto-layout parent before setting `layoutSizingHorizontal: "FILL"` ([#append-before-fill](references/plugin-api-gotchas.md#append-before-fill)) - **Async:** Always `await` async operations — `loadFontAsync`, `importComponentByKeyAsync`, etc. ([#promises](references/plugin-api-gotchas.md#promises)) --- ## Prompting Guidance → Full patterns: [references/prompting-patterns.md](references/prompting-patterns.md) Key patterns: variable-first code structure, section-by-section execution, explicit design-system usage, validation loops, error recovery framing. --- ## Install / Setup If dependencies are missing, install them from the skill repo root: ```bash npm install ``` Then run one-time bootstrap: ## Setup (One-Time Bootstrap) ```bash node ~/.openclaw/skills/figma-agent/scripts/auth.mjs ``` Reads CC OAuth token from `~/.claude/.credentials.json`, writes it into `~/.openclaw/openclaw.json`. Then restart the OpenClaw gateway. **Token check:** `node ~/.openclaw/skills/figma-agent/scripts/auth.mjs --check` **On 401 errors:** Open CC → use any Figma tool (auto-refreshes token) → re-run bootstrap script. --- ## URL Parsing ``` figma.com/design/:fileKey/:name?node-id=:nodeId ``` Convert `-` to `:` in nodeId (e.g. `123-456` → `123:456`). For FigJam: `figma.com/board/:fileKey/:name` → use `get_figjam`. --- ## Tool & Rate-Limit Reference → [references/figma-api.md](references/figma-api.md)

path: CHANGELOG.md
content: # Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).

## [0.3.0] - 2026-04-09

### Added
- Explicit `Route & Brief -> Execute -> Done Gate` execution model in `SKILL.md` and `README.md`
- Validation Gates in `references/core-rules.md` with structural-first completion checks
- Task Ticket output in `references/workflow-selection.md` to carry execution mode, risks, and validation checks
- Execution Brief template and completion-criteria blocks in `references/prompting-patterns.md`

### Changed
- Hardened `screen-review-loop.md` so completion requires a real done gate instead of a soft final screenshot step
- Turned the state-variant decision in `native-screen-generation.md` into a hard fork instead of advisory guidance
- Reworked prompting guidance to separate always-in instructions, conditional inserts, and done criteria
- Clarified README execution principles around structural validation before visual confirmation

## [0.2.0] - 2026-04-09

### Added
- Structured workflow documentation for the current skill architecture, including workflow selection, prompting patterns, copy-and-edit guidance, and state-variant handling
- Image Delivery documentation for local screenshot output via `--out` and chat delivery via `MEDIA:<path>`
- Install note in `SKILL.md` and clearer routing guidance between abridged skill routing and full workflow selection docs

### Changed
- Rewrote `README.md` to reflect the current workflow-driven product surface instead of presenting the skill as a thin MCP wrapper
- Surfaced the state-variant decision earlier in the native screen generation flow
- Added dedicated Copy + Edit prompting guidance
- Strengthened routing and hard-rule language around component-instance-first behavior

## [0.1.2] - 2026-04-03

### Changed
- Republished `figma-agent` to ensure the ClawHub listing uses the display name **Figma Agent**

## [0.1.1] - 2026-04-03

### Fixed
- Removed legacy OAuth experiment scripts (`auth.mjs`, `sdk-auth-test.mjs`) from repo
- Corrected Figma Remote MCP link in README (→ official help docs)
- Made all ACP/write-path references provider-agnostic (Claude Code, Codex, or any supported ACP agent)
- Removed `.clawhubignore` from public git tracking (added to `.gitignore`)
- Added `displayName: "Figma Agent"` to `package.json` for correct ClawHub display name

## [0.1.0] - 2026-04-03

### Added
- **Hybrid architecture** — direct read/inspect via Figma Remote MCP + write/edit/create via Claude Code ACP sessions
- **Zero-dependency MCP client** (`scripts/figma-mcp.mjs`) — JSON-RPC 2.0 over HTTP POST, handles SSE and direct JSON responses, auto-Bearer-prefix, timeout support
- **Multi-client token bootstrap** (`scripts/bootstrap-token.mjs`) — scans Claude Code, Codex, and Windsurf credential stores, refreshes expired tokens, writes Bearer header to OpenClaw config
- **Full 17-tool coverage** — all official Figma Remote MCP tools documented with read/write split
- **Known limitations** — official Figma limitations (20 KB limit, no image import, no custom fonts, sandbox restrictions) documented in SKILL.md with sources
- **`references/figma-api.md`** — concise API reference for all MCP tools


path: README.md
content: <h1 align="center">Figma Agent</h1>

<p align="center">
  <strong>Workflow-driven Figma skill for OpenClaw — inspect designs directly, then create, review, and edit screens through structured Figma workflows.</strong><br>
  Built on the official <a href="https://help.figma.com/hc/en-us/articles/32132100833559">Figma Remote MCP server</a>.
</p>

<p align="center">
  <a href="https://github.com/rasimme/figma-agent/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License"></a>
  <a href="https://github.com/rasimme/figma-agent/blob/main/CHANGELOG.md"><img src="https://img.shields.io/badge/version-v0.3.0-blue.svg" alt="Version"></a>
  <a href="https://clawhub.ai"><img src="https://img.shields.io/badge/ClawHub-skill-purple.svg" alt="ClawHub"></a>
</p>

<p align="center">
  <a href="#what-this-skill-does">What this skill does</a> •
  <a href="#core-architecture">Core architecture</a> •
  <a href="#supported-workflows">Supported workflows</a> •
  <a href="#quick-start">Quick Start</a> •
  <a href="#image-delivery">Image Delivery</a> •
  <a href="#limitations">Limitations</a>
</p>

---

## What this skill does

Figma Agent is not just a raw MCP wrapper. It is a structured OpenClaw skill for working with Figma in two modes:

- **Direct read / inspect** for screenshots, metadata, design context, variables, and Code Connect inspection
- **Structured write / edit workflows** for building screens, reviewing existing work, applying tokens, and iterating on designs through ACP coding sessions

Typical use cases:

- "Show me this screen and tell me what is off"
- "Build the next step based on this existing screen"
- "Replace hardcoded colors with design-system tokens"
- "Inspect local variables and Code Connect mappings"
- "Create a production-ready screen using existing components"

---

## Core architecture

The skill is intentionally split into four layers:

1. **`SKILL.md`** — routing surface for deciding direct read vs ACP write/edit
2. **`references/workflow-selection.md`** — workflow and strategy selection
3. **`references/core-rules.md`** — global execution rules and constraints
4. **`references/prompting-patterns.md` + playbooks** — prompt structure and step-by-step execution

### Runtime model

- **Read / inspect** goes directly to Figma MCP
- **Write / create / edit** goes through an ACP coding session with Figma MCP available

This hybrid model keeps inspection fast while preserving a stronger execution path for canvas changes.

### Execution model

The controller should operate in three phases:

1. **Route & Brief** — choose the workflow, identify the actual risks, and produce a lean execution brief
2. **Execute** — perform the direct read or ACP write/edit task
3. **Done Gate** — require structural checks first and screenshot confirmation second before reporting success

---

## Supported workflows

### Read / Inspect

- **Read-only inspection** — screenshots, metadata, design context, variables, Code Connect context
- **Variable Discovery** — inspect local tokens and styles before searching externally
- **Design Audit Review** — review a finished design and identify issues or cleanup opportunities

### Create / Edit

- **Native Screen Generation** — production-ready, design-system-aligned screen creation
- **Screen Review Loop** — screenshot, isolate issues, apply targeted fixes, validate
- **Color Tokenization** — replace hardcoded values with variable bindings
- **HTML-to-Figma Prototyping** — rapid exploration from HTML/CSS, then cleanup
- **Stitch Import Cleanup** — fix imported Stitch output for native Figma quality
- **State Variants / Next Steps** — prefer **Copy + Edit** over rebuild when most of the screen already exists

### Execution principles

- **Design-system-first** — use local variables, styles, components, and Code Connect before creating anything raw
- **Component-instance-first** — if a suitable component exists, instantiate it instead of rebuilding it visually
- **Section-by-section** — build in small validated steps, not giant write calls
- **Validate after every write** — structural checks first, screenshots second

---

## Quick Start

### Prerequisites

- [OpenClaw](https://github.com/openclaw/openclaw) installed
- [Node.js](https://nodejs.org) available
- Figma account connected to a supported MCP client
- **Full Seat** in Figma for write operations (`use_figma`)

### Install

**Via ClawHub (recommended):**

```bash
clawhub install figma-agent
```

**Manual install:**

```bash
cd ~/.openclaw/skills
git clone https://github.com/rasimme/figma-agent.git
cd figma-agent
npm install
```

### Bootstrap authentication

```bash
node ~/.openclaw/skills/figma-agent/scripts/bootstrap-token.mjs
```

The bootstrap script extracts or refreshes the Figma MCP token from a supported MCP client and writes the Figma server config into your OpenClaw config.

Then restart OpenClaw:

```bash
openclaw gateway restart
```

### Verify

Check that Figma MCP is configured in `~/.openclaw/openclaw.json` under `mcp.servers.figma`, then try a simple read action such as `get_screenshot` or `get_metadata`.

---

## Image Delivery

For chat delivery, screenshots should be sent as actual image attachments, not inline base64 blobs.

### Recommended pattern

1. Save the screenshot to disk with `--out`
2. Reply with `MEDIA:<path>`

Example:

```bash
node scripts/figma-mcp-cli.mjs get_screenshot \
  fileKey=<fileKey> nodeId=<nodeId> scale=2 \
  --out ~/workspace-dev-botti/screenshots/validate.png
```

Then in chat:

```text
MEDIA:screenshots/validate.png
```

### Important distinction

- `image` tool = image analysis / vision input
- `MEDIA:<path>` = actual chat attachment delivery

This matters especially on Telegram, where inline base64 is not the same as a native image attachment.

---

## Tool coverage

The skill supports the official Figma Remote MCP toolset for inspection and canvas operations. Commonly used tools include:

- `get_design_context`
- `get_screenshot`
- `get_metadata`
- `get_variable_defs`
- `search_design_system`
- `get_code_connect_map`
- `get_figjam`
- `whoami`
- `use_figma`
- `create_new_file`
- `generate_figma_design`
- `generate_diagram`
- Code Connect mapping helpers

For full tool-level details, see [`references/figma-api.md`](references/figma-api.md).

---

## Limitations

Current known constraints:

- **Write operations require a Full Seat** in Figma
- **Large write calls are fragile** — split work into smaller sections
- **Remote MCP write behavior still has edge cases** — validate after each change
- **Some Plugin API behavior differs in Remote MCP** — see [`references/plugin-api-gotchas.md`](references/plugin-api-gotchas.md)
- **Image delivery works best through local file output + `MEDIA:`**
- **State variants should usually duplicate and edit existing screens rather than rebuild from scratch**
- **Playbooks guide the agent, not ACP automatically** — critical execution constraints must still be injected into ACP prompts when relevant

---

## Project structure

```text
figma-agent/
├── SKILL.md
├── README.md
├── CHANGELOG.md
├── LICENSE
├── package.json
├── references/
│   ├── core-rules.md
│   ├── figma-api.md
│   ├── plugin-api-gotchas.md
│   ├── prompting-patterns.md
│   └── workflow-selection.md
└── scripts/
    ├── bootstrap-token.mjs
    ├── figma-mcp-cli.mjs
    ├── figma-mcp.mjs
    └── token-scanner.mjs
```

---

## License

MIT — see [LICENSE](LICENSE).


path: package.json
content: {
  "name": "figma-agent",
  "displayName": "Figma Agent",
  "version": "0.3.0",
  "repository": {
    "type": "git",
    "url": "[REDACTED_SECRET]:rasimme/figma-agent.git"
  },
  "type": "module",
  "description": "Unified OpenClaw Figma skill with direct inspect/read flows and a heavier write path when needed",
  "dependencies": {},
  "keywords": [
    "openclaw",
    "figma",
    "mcp",
    "design",
    "agent-skill"
  ]
}


path: references/core-rules.md
content: # Core Rules

Universal rules that apply across all Figma Agent workflows. These are non-negotiable unless explicitly overridden for exploratory/prototype work.

---

## Always-On Rules

### 1. Validate After Write

Every meaningful `use_figma` write must be followed by validation — either `get_screenshot` or `get_metadata` on the affected nodes. Never assume a write succeeded; confirm visually or structurally.

### 2. Read -> Understand -> Fix -> Retry

When a `use_figma` call fails or produces unexpected results:
1. **Read** the error message or screenshot carefully.
2. **Understand** what went wrong — consult [plugin-api-gotchas.md](plugin-api-gotchas.md) for known failure patterns.
3. **Fix** the specific issue in the code.
4. **Retry** with the corrected code.

Never blindly retry the same code. Never rebuild from scratch as a first response to a targeted issue.

### 3. Explicit Over Implicit

State everything explicitly in `use_figma` calls:
- Which variables/tokens to use (by name)
- Which components to instantiate (by key/name)
- Layout mode (auto-layout direction, padding, gap)
- Color values as variable bindings, not raw hex

The model performs best when nothing is left to inference.

### 4. Design-System-First

Before creating any visual element, check what already exists:
- Search local variables and styles in the current file
- Check Code Connect mappings if available
- Search connected libraries via `search_design_system`
- Only create raw/hardcoded values as a last resort

### 5. Component Instance First

If a suitable existing design-system component already exists, use it as a **real instance** instead of visually recreating it with local frames.

**Required behavior:**
- Prefer existing component instances for tabs, buttons, radios, alerts, cards, icons, typography, and similar UI primitives
- Do not build local replacement frames for components that already exist in the file or connected design system
- Only build locally when no suitable component exists
- When unsure, inspect/search first, then build

This rule applies across build, edit, cleanup, and state-variant workflows whenever the design system already provides the needed UI.

### 6. Section-by-Section Execution

Large screens and complex layouts must be built incrementally, not in a single monolithic call. Break work into logical sections (header, content area, sidebar, footer) and build/validate each before moving on.

---

## ⚠️ Conditional Rules

These rules apply only when their trigger condition is met — not on every workflow.

---

### Section-Relative Positioning

**Trigger:** Building a new screen that is based on / extrapolated from an existing screen, where positioning values are derived from the source screen.

**Trigger examples:**
- Clone screen A and place it below A
- Create "Step 2" based on "Step 1" layout
- Duplicate a card layout to create a variant

**Non-trigger examples:**
- Building a new screen from scratch with no reference screen
- Inspecting/reading an existing screen without creating new nodes

**Rule:**
> Figma coordinates are always relative to the **direct parent container**. If the source screen is inside a Section or Frame, the new screen must use coordinates relative to that same Section — not absolute page coordinates.

**When triggered, before building:**
1. Identify the parent container of the source screen (Section/Frame)
2. Get the container's absolute page position
3. Calculate local offset: `localX = sourceX - containerX`, `localY = sourceY - containerY`
4. Apply the intended positioning (e.g., +100px below) as an offset from the container, not from page origin
5. **Explicitly state the parent container ID and local coordinates in the `use_figma` prompt**

**Check:** After creation — is the new screen actually inside/child-of the same section as the source, not on the page?

---

## Validation Gates

Validation is not a vibe check. Before reporting success, run the applicable checks in order.

### Check Order

1. **Structural checks first** — confirm the build is technically correct
2. **Visual confirmation second** — use screenshots to confirm the intended appearance
3. **Only then report success**

Do not rely on screenshots alone when structural checks are possible.

### Structural Checks

Use the lightest structural read that can prove the requirement:

- `get_metadata` — verify node hierarchy, parent container, layout settings, instance status, text content
- `get_variable_defs` — verify variable bindings where token usage matters
- local node inspection via `use_figma` — verify that duplicated/copied nodes remained real instances when relevant

### Common Validation Gates

Apply only the checks that match the workflow and task.

#### 1. Parent / Placement Gate
- Is the new node inside the intended Section or Frame?
- Does the parent container match the expected container?
- If this was a relative-placement task, are local coordinates being interpreted relative to the correct parent?

#### 2. Component Integrity Gate
- Are required UI primitives still real component instances instead of detached or local recreations?
- If this was a Copy + Edit task, did duplication preserve working component structure?
- Was `clone()` / duplication behavior used rather than reconstructing the shell manually?

#### 3. Content Completeness Gate
- Do text nodes contain real intended content rather than placeholder/default text?
- Were expected slot contents actually replaced?
- Are state-specific labels, CTA text, and body copy correct?

#### 4. Token / Variable Gate
- Are the required fills, borders, or effects bound to variables where expected?
- Were hardcoded values removed when tokenization or design-system alignment was required?

#### 5. State Accuracy Gate
- Does the requested UI state actually match the spec?
- Examples: active tab, expanded accordion, selected control, disabled button, current step

#### 6. Visual Confirmation Gate
- After structural checks pass, does `get_screenshot` confirm that the layout, spacing, alignment, and visual state match the intended result?
- Use screenshot review as confirmation, not as the sole source of truth.

### If a Gate Fails

Do not jump straight to rebuild.

- **Cheap failures** — content swaps, wrong labels, missed state toggles, local spacing issues -> apply targeted fix and re-run the relevant gates
- **Expensive failures** — fake component reconstruction, wrong parent container, structurally broken shell -> stop and decide whether a structural redo is cheaper than patching

See [screen-review-loop.md](playbooks/screen-review-loop.md) for the operational review/fix cycle.

---

## Batch-Write Heuristic

One logical section per `use_figma` call.

| Approach | Assessment | Why |
|----------|-----------|-----|
| Full screen in one call | **Too large** | Fragile, hard to debug, partial failures waste everything |
| Individual nodes one by one | **Too small** | High MCP overhead, slow, unnecessary round-trips |
| **1-3 semantically related components/groups** per call | **Sweet spot** | Debuggable, recoverable, efficient |

After each meaningful write: **always validate**, never proceed blind.

**Grouping examples:**
- A card component with its icon, title, and body = 1 call
- A navigation bar with logo, links, and actions = 1 call
- Header section + hero section = 2 calls (separate validation points)

---

## SVG Decision Matrix

| Situation | Action | Rationale |
|-----------|--------|-----------|
| SVG from external tool (logo, icon font) | **Preserve as-is** | Original fidelity matters more than editability |
| SVG as layout proxy (HTML-to-Figma output) | **Evaluate** - often replace with native nodes | Auto-layout compatibility, editability, variable binding |
| SVG in design-system context | **Flatten** or convert to component | Reusability, variable binding support |
| Animation/interaction needed | **Use native nodes** | SVG cannot participate in Figma prototyping |

When in doubt: native nodes are safer for production-quality work. SVG is acceptable for speed in exploratory contexts.

---

## Local-Context-First

Context preference order — always exhaust higher-priority sources before reaching outward:

1. **Current frame/selection** — nodes, styles, and variables already present
2. **Current file** — local variables, local styles, other pages/frames in the same file
3. **Code Connect mappings** — component-to-code mappings where configured
4. **Library search** — external/connected libraries via `search_design_system` (only when 1-3 are insufficient)

This reduces unnecessary API calls, avoids duplicate definitions, and stays consistent with the file's existing design decisions.

---

## Cost Awareness

MCP calls carry token and latency overhead. Minimize unnecessary calls:

- Batch semantically related operations (see Batch-Write Heuristic above)
- Use `get_metadata` for structural inspection before `get_design_context` on large trees
- Cache knowledge from earlier reads within the same session — don't re-fetch what you already know
- Avoid speculative `search_design_system` calls; search with specific intent

---

## Code Connect Awareness

When Code Connect mappings exist for a file:
- `get_design_context` will return mapped codebase components — use them directly
- Respect the mapping: instantiate the mapped component, don't recreate it from scratch
- When creating new components, consider whether a Code Connect mapping should be added
- See [figma-api.md](figma-api.md) for Code Connect tool details

---

## Native vs HTML-to-Figma Decision

See [workflow-selection.md](workflow-selection.md) for the full routing matrix. Quick rule:

- **Native Figma** (default): production-ready, design-system-aligned, variable-bound, fully editable
- **HTML-to-Figma**: rapid exploration, layout speed, complex CSS layouts that are tedious to replicate natively

HTML-to-Figma output requires cleanup for production use. It does not automatically use design-system variables or components. See [playbooks/html-to-figma-prototyping.md](playbooks/html-to-figma-prototyping.md) for the full workflow.


path: references/figma-api.md
content: # Figma Remote MCP — Tool Reference

**Endpoint:** `https://mcp.figma.com/mcp`
**Auth:** OAuth 2.0 (Bearer token via CC Token Bootstrap)
**In OpenClaw:** Read tools as `figma__*`, write tools via CC as `mcp__figma__*`

---

## Read Tools

### `get_design_context` / `figma__get_design_context`

Returns React+Tailwind code, screenshot URL, and contextual hints for a Figma node.

**Parameters:**
- `fileKey` (string) — Figma file key from URL
- `nodeId` (string) — Node ID, use `:` separator (e.g. `123:456`)

**Returns:** code snippet, screenshot, Code Connect mappings if configured, design annotations

**Notes:**
- Primary tool for design-to-code workflows
- Output varies based on Figma setup: raw code, Code Connect snippets, or annotated references
- Adapt output to project's actual stack — not final code

---

### `get_screenshot` / `figma__get_screenshot`

Returns a screenshot image of the specified Figma node.

**Parameters:**
- `fileKey`, `nodeId`

**Use when:** layout fidelity is important, design context code alone is insufficient

---

### `get_metadata` / `figma__get_metadata`

Returns sparse XML with layer IDs, names, types, positions, and sizes.

**Parameters:**
- `fileKey`, `nodeId`

**Use when:** inspecting large designs where `get_design_context` would be too heavy; good for understanding layer structure before drilling into specific nodes

---

### `get_variable_defs` / `figma__get_variable_defs`

Returns variables and styles scoped to a selection: colors, spacing, typography.

**Parameters:**
- `fileKey`, `nodeId`

**Use when:** extracting design tokens for a component or page section

---

### `search_design_system` / `figma__search_design_system`

Searches all connected Figma libraries for components, variables, and styles.

**Parameters:**
- `query` (string) — search term
- `fileKey` (string) — context file

**Use when:** finding existing components before generating new ones; always check this first

---

### `get_figjam` / `figma__get_figjam`

Returns a FigJam board as XML with embedded screenshots.

**Parameters:**
- `fileKey`, `nodeId`

**File type:** FigJam only (`figma.com/board/...`)

---

### `get_code_connect_map` / `figma__get_code_connect_map`

Returns existing mappings from Figma node IDs to codebase components.

**Parameters:**
- `fileKey`, `nodeId`

---

### `get_code_connect_suggestions` / `figma__get_code_connect_suggestions`

Returns Figma-generated suggestions for mapping components to code.

**Parameters:**
- `fileKey`

---

### `get_context_for_code_connect` / `figma__get_context_for_code_connect`

Returns context needed to set up Code Connect for a component.

**Parameters:**
- `fileKey`, `nodeId`

---

### `whoami` / `figma__whoami`

Returns authenticated user info, plan, and seat type.

**Parameters:** none

**Use for:** verifying the token is valid; debugging auth issues

---

## Write Tools (CC session only)

### `use_figma` / `mcp__figma__use_figma`

General-purpose write tool — executes JavaScript via Figma's Plugin API.

**Parameters:**
- `code` (string) — JavaScript to execute in Figma plugin context
- `fileKey` (string)

**Limits:**
- 20KB response limit per call
- No image/asset import (no `loadImageAsync`, no custom fonts)
- Components must be published before Code Connect works
- Beta — output needs manual review

**IMPORTANT:** Always load `figma:figma-use` skill before calling this tool.

---

### `generate_figma_design` / `mcp__figma__generate_figma_design`

Generates UI layers from live web pages or code into a Figma file.

**Parameters:**
- `url` or code source
- `fileKey` (target file, optional — creates new if omitted)

**Notes:**
- Exempt from rate limits
- Only available in certain clients (CC, Cursor, Codex)
- Best with `figma:figma-generate-design` skill

---

### `create_new_file` / `mcp__figma__create_new_file`

Creates a new empty design or FigJam file in Drafts.

**Parameters:**
- `name` (string)
- `fileType`: `"design"` | `"figjam"`

**Returns:** `file_key`, `file_url`

---

### `generate_diagram` / `mcp__figma__generate_diagram`

Creates a FigJam diagram from Mermaid syntax.

**Parameters:**
- `mermaid` (string) — Mermaid diagram source
- `fileKey` (FigJam file)

**Supported types:** flowchart, sequence diagram, state diagram, Gantt chart

---

### `add_code_connect_map` / `mcp__figma__add_code_connect_map`

Adds a mapping from a Figma node to a codebase component.

**Parameters:**
- `fileKey`, `nodeId`, `componentPath`, `componentName`

---

### `send_code_connect_mappings` / `mcp__figma__send_code_connect_mappings`

Confirms and sends Code Connect mappings to Figma.

**Parameters:**
- `fileKey`, `mappings` (array)

---

### `create_design_system_rules` / `mcp__figma__create_design_system_rules`

Generates a rule file for design-system-aware code generation.

**Parameters:**
- `fileKey`, `codebasePath`

**Notes:** Load `figma:figma-create-design-system-rules` skill first.

---

## Rate Limits

| Plan | Read Tools (daily) | Write Tools |
|------|-------------------|-------------|
| Enterprise | 600/day, 20/min | Exempt |
| Org/Pro Full/Dev | 200/day, 10-15/min | Exempt |
| Starter | 6/month | Exempt |

Rate limits apply per workspace/team, not just per user seat.

---

## URL Format

```
figma.com/design/:fileKey/:fileName?node-id=:nodeId
```

- `nodeId` in URL uses `-` separator → convert to `:` for API calls
- Branch files: use `branchKey` as `fileKey`
- FigJam: `figma.com/board/:fileKey/:fileName`
- Figma Make: `figma.com/make/:makeFileKey/:makeFileName`


path: references/playbooks/color-tokenization.md
content: # Playbook: Color Tokenization

Audit unbound colors, map them to design-system variables, and apply bindings safely. Use after HTML-to-Figma import, design-system migration, or whenever hardcoded colors need to be replaced with variable bindings.

**When to use:** A frame or file contains hardcoded hex colors that should be bound to design-system variables. See [workflow-selection.md](../workflow-selection.md).

---

## Step 1: Discover Available Variables

Before mapping colors, know what variables exist.

### Local Variables First
```js
const collections = await figma.variables.getLocalVariableCollectionsAsync();
for (const c of collections) {
  const vars = [];
  for (const id of c.variableIds) {
    vars.push(await figma.variables.getVariableByIdAsync(id));
  }
  // Filter to COLOR type
  const colorVars = vars.filter(v => v.resolvedType === "COLOR");
  return colorVars.map(v => ({ name: v.name, id: v.id }));
}
```

### Library Variables (Only If Local Is Insufficient)
Use `search_design_system(query, fileKey)` to find variables from connected libraries. Library variables can be imported via `figma.variables.importVariableByKeyAsync(key)`.

**Important:** Local and library variables are distinct. Local variables are resolved via `getLocalVariableCollectionsAsync`. Library variables require import. Do not mix the discovery methods.

---

## Step 2: Audit Unbound Fills

Scan target nodes for fills that are not bound to variables. Process fills and strokes **separately** — they have different semantic meaning and may map to different tokens.

### Fills Audit
```js
const target = await figma.getNodeByIdAsync("TARGET_NODE_ID");
const nodes = target.findAll(n => n.fills && n.fills.length > 0);
const unboundFills = [];
for (const n of nodes) {
  for (let i = 0; i < n.fills.length; i++) {
    const fill = n.fills[i];
    if (fill.type === "SOLID" && !fill.boundVariables?.color) {
      unboundFills.push({
        nodeId: n.id,
        nodeName: n.name,
        fillIndex: i,
        hex: figma.util.rgba(fill.color, fill.opacity),
        opacity: fill.opacity
      });
    }
  }
}
return unboundFills;
```

### Strokes Audit
Same pattern, but iterate `n.strokes` instead of `n.fills`. Strokes often map to border/outline tokens which differ from fill tokens.

**Output:** A list of `{ nodeId, hex, opacity, fillIndex/strokeIndex }` tuples ready for mapping.

---

## Step 3: Map Colors to Variables

Match each unbound hex to the closest design-system variable. This is a judgment step:

| Hex Value | Likely Variable | Confidence |
|-----------|----------------|------------|
| `#1A1A1A` | `colors/neutral/900` | High — exact match |
| `#1B1B1F` | `colors/neutral/900` | Medium — close but not exact |
| `#FF6B35` | `colors/accent/500` | High — matches brand accent |
| `#E8E8E8` | `colors/neutral/200` | Medium — could be 100 or 200 |

**Rules for mapping:**
- Exact hex match = bind immediately
- Close match (within a few shades) = bind to nearest token, note the delta
- No reasonable match = leave unbound; flag for design review
- **Never invent new variables** during tokenization — only bind to what exists

---

## Step 4: Apply Bindings Safely {#apply-bindings}

Critical: `setBoundVariableForPaint` has two known sharp edges. Both must be handled in every binding operation.

### The Paint-Binding Pattern {#paint-binding-pattern}

`setBoundVariableForPaint` returns a **new paint object** — it does not mutate in place. You must reassign the fills/strokes array. See [plugin-api-gotchas.md#paint-binding](../plugin-api-gotchas.md#paint-binding).

### The Opacity Preservation Pattern {#opacity-preservation}

`setBoundVariableForPaint` **resets opacity to 1.0**. You must save and restore opacity explicitly. See [plugin-api-gotchas.md#opacity](../plugin-api-gotchas.md#opacity).

### Combined Correct Pattern

```js
const node = await figma.getNodeByIdAsync("NODE_ID");
const variable = await figma.variables.getVariableByIdAsync("VAR_ID");

// Bind fill at index 0
const fills = [...node.fills];
const originalOpacity = fills[0].opacity;  // 1. Save opacity
fills[0] = figma.variables.setBoundVariableForPaint(fills[0], variable, "color");  // 2. Bind
fills[0] = { ...fills[0], opacity: originalOpacity };  // 3. Restore opacity
node.fills = fills;  // 4. Reassign array
```

### Batching Strategy

Bind 5-10 nodes per `use_figma` call when applying the same variable or same category of variables. Group by variable, not by node proximity.

```js
// Good: all primary-fill bindings in one call
const nodesToBind = ["1:23", "1:45", "1:67"];
const variable = await figma.variables.getVariableByIdAsync("VAR_ID");
for (const id of nodesToBind) {
  const node = await figma.getNodeByIdAsync(id);
  const fills = [...node.fills];
  const origOpacity = fills[0].opacity;
  fills[0] = figma.variables.setBoundVariableForPaint(fills[0], variable, "color");
  fills[0] = { ...fills[0], opacity: origOpacity };
  node.fills = fills;
}
```

---

## Step 5: Validate Bindings

After each batch of bindings:

1. **`get_screenshot`** — visual check that colors haven't shifted unexpectedly
2. **`get_variable_defs`** on the target frame — confirm variables are bound
3. **Spot-check opacity** — verify nodes that had non-1.0 opacity still look correct

If a color looks wrong after binding:
- Check the variable resolves to the expected hex in the current mode (light/dark)
- Check opacity was preserved (most common issue)
- Check the correct fill/stroke index was targeted

---

## Step 6: Handle Strokes

Repeat Steps 4-5 for strokes. The pattern is identical but uses `node.strokes` instead of `node.fills`:

```js
const strokes = [...node.strokes];
const origOpacity = strokes[0].opacity;
strokes[0] = figma.variables.setBoundVariableForPaint(strokes[0], variable, "color");
strokes[0] = { ...strokes[0], opacity: origOpacity };
node.strokes = strokes;
```

---

## Anti-Patterns

| Anti-Pattern | Why It Fails |
|-------------|-------------|
| Binding fills and strokes in the same audit pass | Different semantic meaning, different tokens |
| Forgetting to preserve opacity | Silent visual regression — colors appear correct but transparency is lost |
| Mutating `fills[0]` directly without array reassignment | Figma ignores in-place mutations; the binding is silently dropped |
| Creating new variables during tokenization | Scope creep — tokenization binds to existing tokens, variable creation is a separate workflow |
| Binding without validating via screenshot | You won't catch opacity bugs or wrong-mode color resolution |


path: references/playbooks/design-audit-review.md
content: # Playbook: Design Audit & Review

Read-only inspection workflow for evaluating design-system compliance, component usage, variable coverage, and accessibility basics. Produces a structured audit report without modifying the file.

**When to use:** Before a cleanup migration, as a periodic health check, or when a user asks "how well does this screen follow the design system?" See [workflow-selection.md](../workflow-selection.md).

---

## Step 1: Visual Baseline

```
get_screenshot(fileKey, nodeId)
```

Note first impressions:
- Overall visual consistency (colors, spacing, typography feel cohesive?)
- Obvious outliers (one-off colors, inconsistent card styles, alignment breaks)
- Accessibility red flags (very small text, low-contrast areas)

---

## Step 2: Structural Inspection

```
get_metadata(fileKey, nodeId)
```

From the metadata, evaluate:

| Aspect | What to Check |
|--------|--------------|
| **Auto-layout usage** | Are sections using auto-layout or manual positioning? |
| **Naming** | Descriptive node names or generic "Frame 42" / "Group 17"? |
| **Nesting depth** | Excessive nesting (>5-6 levels) suggests structural issues |
| **Sizing modes** | Mix of FIXED/HUG/FILL — is it intentional or inconsistent? |

---

## Step 3: Variable Binding Coverage {#binding-audit}

Measure what percentage of fills and strokes are bound to variables vs hardcoded.

```js
const frame = await figma.getNodeByIdAsync("FRAME_ID");
const nodes = frame.findAll(n =>
  (n.fills?.length > 0) || (n.strokes?.length > 0)
);

let fills = { bound: 0, unbound: 0 };
let strokes = { bound: 0, unbound: 0 };

for (const n of nodes) {
  if (n.fills) {
    for (const f of n.fills) {
      if (f.type === "SOLID") {
        f.boundVariables?.color ? fills.bound++ : fills.unbound++;
      }
    }
  }
  if (n.strokes) {
    for (const s of n.strokes) {
      if (s.type === "SOLID") {
        s.boundVariables?.color ? strokes.bound++ : strokes.unbound++;
      }
    }
  }
}

return {
  fills: { ...fills, coverage: `${Math.round(fills.bound / (fills.bound + fills.unbound) * 100)}%` },
  strokes: { ...strokes, coverage: `${Math.round(strokes.bound / (strokes.bound + strokes.unbound) * 100)}%` }
};
```

**Interpretation:**
- **>80%** — well-tokenized, minor cleanup needed
- **40-80%** — partial adoption, section-by-section cleanup recommended
- **<40%** — largely hardcoded, full [design-system-cleanup](design-system-cleanup.md) needed

---

## Step 4: Component Usage Audit {#component-audit}

Check whether the frame uses component instances vs manual constructions.

```js
const frame = await figma.getNodeByIdAsync("FRAME_ID");
const instances = frame.findAll(n => n.type === "INSTANCE");
const allNodes = frame.findAll(() => true);

const componentNames = {};
for (const inst of instances) {
  const name = inst.mainComponent?.name || "unknown";
  componentNames[name] = (componentNames[name] || 0) + 1;
}

return {
  totalNodes: allNodes.length,
  instanceCount: instances.length,
  instanceRatio: `${Math.round(instances.length / allNodes.length * 100)}%`,
  components: componentNames
};
```

**What to flag:**
- Repeated visual patterns that are NOT instances → candidate for component extraction or replacement
- Detached instances (instances that were detached from their main component) → may need re-linking
- Low instance ratio on screens that should be component-heavy (lists, cards, navigation)

---

## Step 5: Unique Color Inventory {#color-inventory}

List all unique colors used in the frame, distinguishing bound from unbound.

```js
const frame = await figma.getNodeByIdAsync("FRAME_ID");
const nodes = frame.findAll(n => n.fills?.length > 0);
const colors = {};

for (const n of nodes) {
  for (const f of n.fills) {
    if (f.type === "SOLID") {
      const hex = `#${Math.round(f.color.r*255).toString(16).padStart(2,'0')}${Math.round(f.color.g*255).toString(16).padStart(2,'0')}${Math.round(f.color.b*255).toString(16).padStart(2,'0')}`;
      if (!colors[hex]) colors[hex] = { count: 0, bound: 0, unbound: 0 };
      colors[hex].count++;
      f.boundVariables?.color ? colors[hex].bound++ : colors[hex].unbound++;
    }
  }
}

// Sort by count descending
const sorted = Object.entries(colors).sort((a,b) => b[1].count - a[1].count);
return sorted.slice(0, 25); // Top 25 colors
```

This reveals:
- **Dominant colors** — should all be tokenized
- **One-off colors** — potential inconsistencies or intentional accents
- **Near-duplicates** — e.g., `#1A1A1A` and `#1B1B1F` that should be the same token

---

## Step 6: Accessibility Basics {#accessibility}

Quick checks that don't require external tools.

### Text Size Check

```js
const frame = await figma.getNodeByIdAsync("FRAME_ID");
const textNodes = frame.findAll(n => n.type === "TEXT");
const small = textNodes.filter(n => n.fontSize < 12);
const sizes = {};
for (const t of textNodes) {
  const s = `${t.fontSize}px`;
  sizes[s] = (sizes[s] || 0) + 1;
}
return {
  totalText: textNodes.length,
  belowMinimum: small.map(t => ({ name: t.name, size: t.fontSize })),
  sizeDistribution: sizes
};
```

**Guidelines:**
- Body text below 12px is generally problematic for readability
- Below 10px is a red flag in almost all contexts
- Font size distribution should show a limited set of sizes (consistent type scale)

### Contrast Awareness

Full WCAG contrast calculation is complex in plugin code, but flag obvious risks:
- Light text on light backgrounds (both fills with high lightness)
- Very thin font weights (<300) at small sizes
- Text over images without overlay/scrim

Report these as "flagged for manual contrast check" — automated WCAG scoring is beyond the scope of a quick audit.

---

## Step 7: Generate Audit Report

Compile findings into a structured summary for the user:

```markdown
## Audit Report: [Frame Name]

### Variable Coverage
- Fills: X% bound (Y/Z)
- Strokes: X% bound (Y/Z)
- Assessment: [well-tokenized / partial / needs migration]

### Component Usage
- X instances out of Y nodes (Z%)
- Top components: [list]
- Manual patterns flagged: [list or "none"]

### Color Inventory
- X unique colors found
- Top unbound: [hex, count] (suggest token mapping)
- Near-duplicates: [pairs]

### Typography
- X text nodes, Y unique sizes
- Below minimum (12px): [list or "none"]
- Type scale consistency: [consistent / scattered]

### Accessibility Flags
- [list of flagged items or "no flags"]

### Recommendations
1. [Prioritized next steps — link to relevant playbooks]
```

---

## Linking to Action Playbooks

Based on audit findings, recommend the appropriate next workflow:

| Finding | Recommended Playbook |
|---------|---------------------|
| Low variable coverage | [Color Tokenization](color-tokenization.md) → [Design System Cleanup](design-system-cleanup.md) |
| Stitch artifacts (fixed text, decimal dims) | [Stitch Import Cleanup](stitch-import-cleanup.md) |
| Missing components | [Design System Cleanup](design-system-cleanup.md) |
| Visual issues | [Screen Review Loop](screen-review-loop.md) |
| Need to understand available tokens first | [Variable Discovery](variable-discovery.md) |

---

## Anti-Patterns

| Anti-Pattern | Why It Fails |
|-------------|-------------|
| Making changes during an audit | Audit is read-only — mixing reads and writes confuses the baseline |
| Reporting raw numbers without interpretation | "42% coverage" means nothing without context — say what to do about it |
| Auditing an entire file at once | Audit per frame/screen — file-level aggregation loses actionable detail |
| Skipping the screenshot | Numbers alone miss visual inconsistencies that metadata can't reveal |
| Running full audit when user asked a specific question | Adapt scope — a "check colors" request doesn't need a typography audit |


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)