# Mercury Gotchas

> Reference for chaining Mercury MCP calls safely. Covers batch sizing limits, auto-layout sizing modes, text style application order, off-canvas scaffolding, corner radius quirks, and the "verify visually" discipline. Consult when composing multi-step Mercury operations, when a batch is failing, when auto-layout output looks wrong, or before declaring a design complete.

- Skill: `egoisutolabs/mercury-gotchas` (Agent Skill)
- Install (CLI): `npx skillmds@latest add egoisutolabs/mercury-gotchas`
- Raw SKILL.md: https://api.skillmd.com/api/skills/egoisutolabs/mercury-gotchas/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: egoisutolabs (https://skillmd.com/u/egoisutolabs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/egoisutolabs/mercury-gotchas

---


# Mercury Gotchas

Cross-cutting lessons from building real Figma designs via Mercury. These are
*not* specific to any one tool — they apply whenever you're stringing calls
together. Read the rule, the reason, the fix.

## 1. Verify visually, not by tool results

**Rule:** A batch that returns `ok` does not mean the screen looks right.

**Why:** Mercury's tools return structural success (the node exists, the fill
applied). They do not render. You can get a "successful" screen where the
hero number is off-frame, the auto-layout collapsed, or two elements overlap.

**Fix:** At least once per design phase, call `mcp__mercury__export_node` on
the parent frame (PNG, 2x scale) and *look* at the image before continuing.
Treat the export as a required checkpoint, not a nice-to-have.

## 2. Auto-layout sizing modes are the #1 bug source

**Rule:** Know the difference between `primaryAxisSizingMode`,
`counterAxisSizingMode`, and `layoutSizingHorizontal` / `layoutSizingVertical`.

**Why:** Figma has two parallel systems. Sizing mode values (`FIXED`, `AUTO`)
describe the frame itself. Layout sizing values (`FIXED`, `FILL`, `HUG`)
describe how a child behaves inside its parent. They're easy to confuse.

**Concrete traps:**

- `primaryAxisSizingMode: "FIXED"` on create without an explicit `width`/
  `height` defaults the length to **100px**. Your "full-width" hero ends up
  100px wide. Always pass explicit dimensions when using FIXED.
- `layoutSizingHorizontal: "FILL"` often cannot be set at creation time. Set
  it via a follow-up `mcp__mercury__patch` after the node exists.
- A child with `layoutSizingHorizontal: "FILL"` inside a parent with
  `primaryAxisSizingMode: "AUTO"` produces nothing — FILL needs a defined
  parent length to fill.

## 3. Batch size cap: ~40 ops

**Rule:** Keep `mcp__mercury__batch` calls at or under 40 sub-operations.
Hard ceiling around 55–60.

**Why:** The WebSocket bridge to the Figma plugin has timeout behavior around
60 ops. You'll see partial success with no clear error.

**Fix:** Chunk larger work into multiple batches. You lose single-undo
atomicity across chunks, but Cmd+Z still works per chunk. For truly atomic
multi-phase builds, export between chunks and verify — don't rely on one
gigantic batch.

## 4. Use `${idx}.id` to chain ops in one batch

**Rule:** `batch` takes `{ ops: [{ tool, params }, ...] }`. When a later op
needs an id produced by an earlier op, reference it as `${idx}.id` inside
any string param, where `idx` is the zero-based op index.

**Why:** Without this, you'd need a round-trip per dependent op — 65 calls
instead of 12 for a typical screen build. Mercury resolves `${idx}.field`
placeholders server-side before executing.

**Example:**

```
batch {
  ops: [
    { tool: "create", params: { kind: "frame", name: "Card", width: 343, height: 120 } },
    { tool: "patch",  params: { id: "${0}.id", autoLayout: { direction: "VERTICAL", itemSpacing: 8 } } }
  ]
}
```

## 5. Text styles overwrite everything — apply first, tune second

**Rule:** If you're applying a text style (`mcp__mercury__style op:"apply"`)
AND setting per-node overrides (`mcp__mercury__set_text_style`), apply the
style first, THEN set overrides.

**Why:** Applying a style resets the node's text properties to the style's
values. If you set font size first and then apply the style, your size is
gone.

## 6. Off-canvas scaffolding appears in exports — use a separate page

**Rule:** If you're building helper frames, reference components, or
intermediate scaffolding, put them on a separate Figma page, not off to the
side of the working screen.

**Why:** Setting `x: -2000` hides scaffolding from view but
`mcp__mercury__export_node` on the containing frame will still render the
full node tree. Off-canvas ≠ off-page.

**Fix:** `mcp__mercury__page op:"create" name:"Components"` or `"Scratch"`,
then `op:"set-current"` when you want to build there. Switch back to the
working page for the real design.

## 7. Corner radius differs by node type

**Rule:** `cornerRadius` is set differently on rectangle vs. frame vs.
ellipse.

**Why:** Figma's API exposes corner radius inline on rectangles (with an
optional `rectangleCornerRadii` for per-corner), but frames require it as a
patch field. Ellipses accept it but it has no visual effect unless the
ellipse is actually a rounded shape (which is rare — you want a rectangle
with matching corners).

**Fix:** Use `mcp__mercury__patch` with the appropriate field name. If in
doubt, inspect the node via `mcp__mercury__query op:"subtree" depth:1` after
creation and check which corner fields are populated.

## 8. `icon create` returns a frame component, not a paint surface

**Rule:** `mcp__mercury__icon op:"create"` produces a component/frame. It is
not a fill, not a vector, not something you can directly recolor by setting
`paints` on it like a rectangle.

**Why:** Mercury bundles ~1943 Lucide icons as component-like frames so they
can be instanced and recolored via component overrides.

**Fix:** If you need a raw vector, use `mcp__mercury__create kind:"vector"`
with an SVG path string. If you need an icon, use `icon create`, instantiate
it, and style via instance overrides (`mcp__mercury__component op:"bind"` or
`mcp__mercury__patch` on the instance's fills).

## 9. Atomic undo: one tool call = one Cmd+Z

**Rule:** Every single Mercury tool invocation wraps `figma.commitUndo()`.
One tool call = one undo step in Figma.

**Why:** Lets designers recover cleanly from an agent mistake. A 40-op
`batch` is still one Cmd+Z.

**Fix:** Communicate this to the designer explicitly when reporting. "I
applied 12 changes in one batch — Cmd+Z once will revert the whole thing."
Don't say "press Cmd+Z 12 times".

## 10. Absolute positioning escapes auto-layout

**Rule:** To pin a child to a specific corner of an auto-layout parent (e.g.,
a FAB pinned to bottom-right, a badge on a tab icon), set
`layoutPositioning: "ABSOLUTE"` on the child.

**Why:** Otherwise auto-layout packs the child into the flex flow and the
fixed coordinates you set are overridden.

**Fix:** On create, pass `layoutPositioning: "ABSOLUTE"`. On existing nodes,
patch it. Then `x` and `y` become meaningful relative to the parent.

---

## Quick checklist before declaring a design "done"

- [ ] Exported the top-level frame(s) as PNG and visually reviewed.
- [ ] No hidden collapsed auto-layout frames (width/height = 100 or 0).
- [ ] Batches stayed under 40 ops each.
- [ ] Scaffolding is on a different page, not just off-canvas.
- [ ] Text styles applied before per-node tweaks.
- [ ] Atomic undo story communicated to the designer.

