Skip to content

Release v2.3.3

Latest

Choose a tag to compare

@neozhehan neozhehan released this 03 Aug 06:03
5d8489c

This release restores type-checking for the Figma plugin and closes the v2.3.3 safety-contract gaps across design-system writes, explicit placement, batch reporting, annotations, structured errors, peer-bound channels, and page-load isolation. It is a hard contract cutover: fail-closed safety repairs ship at patch level, and callers must migrate the request and response shapes below.

💥 Breaking changes (migration required)

  • Design-system writes require current-name/collection verification. UPDATE_VARIABLE needs currentVariableName; style_manage id-based updates need currentStyleName; CREATE_VARIABLE needs collectionName and an explicit scopes array.
  • Every caller-placed creator needs an explicit verified parent. create_shape, create_frame, create_text, create_svg, create_instance, and now create_component_set require parentId + parentNodeName.
  • annotation_set is a working append contract. Each row needs nodeId, nodeName, and a non-blank labelMarkdown; annotationId/status are gone; properties is an array of {type} entries.
  • annotation_list returns one grouped shape in both page and node modes — node mode no longer returns a flat owner-tagged list.
  • Nested inputs are strict. Unknown keys are rejected at any object depth (two intentional pass-throughs remain: style_manage.properties.paints[] and .layoutGrids[]).
  • Effect payloads are strict per variant. style_manage.properties.effects[] enumerates each Figma effect type with variant-specific fields and the exact BlendMode set; cross-variant keys are rejected instead of silently dropped.
  • Batch validation rejects empty/duplicate target sets up front, and the four batch aggregators (node_delete, text_set_content, annotation_set, instance_set_overrides) now share one result envelope: success, status, requestedCount, succeededCount, failedCount, skippedCount, and one row per input.
  • The socket handshake is role-declared, versioned, and peer-bound. Joins now send clientType and a version; the server can refuse a missing plugin, a duplicate MCP session, or a version mismatch.
  • The connector-visualization surface is removedcreate_connection and its FigJam-diagram output are gone; use reaction_list / reaction_update for native prototype metadata.
  • Empty-name assignment is rejected wherever Figma can't honor it truthfully — covers node_rename, node_group, the five creators, create_component_set, variable_manage, style_manage, and component_manage_property.
  • variable_delete in-use failures are a thrown, coded error (VARIABLE_IN_USE), with consumer evidence under error.details.variablesInUse, instead of a successful result carrying a prose string.
  • Page coverage is explicit. Every coverage object requires pagesAttempted; node_info adds pageFailedNodes; page_info.missingPageIds now includes load/read failures.

✨ Added

  • Structured {code, message, details} errors carried through the MCP boundary end-to-end.
  • create_instance responses include the resolved componentId.
  • Plugin type-check gate: strict TypeScript (no dom lib) plus a CI-enforced name-assignment oracle, so a new naming API can't be added without its empty-name contract.
  • Page-load isolation: destructive variable scans refuse with DOCUMENT_SCAN_INCOMPLETE unless every required page was inspected; successful page data survives partial failures elsewhere.
  • node_flatten reports the containing parentId, matching the other creators.
  • Script type-check gate in CI (check:types:scripts).

🩹 Fixed

  • annotation_set can append more than once to the same node — appending previously rewrote the existing entry into a shape Figma's own setter rejects, so only the first annotation on a node could ever be created.
  • variable_delete no longer false-positives DOCUMENT_SCAN_INCOMPLETE on a healthy document — its consumer scan now loads pages sequentially instead of concurrently.
  • Percent-encoded node-id scope links resolve in the plugin UI (the Figma sandbox has no URL global, so the parser's regex fallback never decoded them).
  • node_delete rows for a target already removed alongside its ancestor in the same batch now say so, instead of telling the caller to retry a call the dispatcher refuses.
  • annotation_set.properties[] rejections explain that validity is node-type-dependent and how to resend only the failed rows.

🔧 Changed

  • Implicit creators, clone, flatten, and component/component-set creation place results at the verified destination by an observable success boundary; cleanup after a later failure is best-effort, not an infallible rollback (supersedes v2.3.2's "no-orphan creation" wording).
  • Design-system updates validate their full plan before the first mutation and disclose unexpected mid-update mutations.
  • Effect numeric bounds now match what Figma actually preserves (e.g. NOISE density 0–1; noiseSize/TEXTURE radius 0–100; GLASS depth >= 1; at most one GLASS effect per node).

⚠️ Notes

  • Registered MCP inventory: 45 tools.
  • Backwards compatibility is not a concern for this project (no external end-users), so these contract changes ship without a deprecation cycle.

Details: see CHANGELOG.md and PR #49.

What's Changed

  • v2.3.3: Restore plugin type-checking; close safety-contract gaps by @neozhehan in #49
  • Update node version to 24 by @neozhehan in #50

Full Changelog: v2.3.2...v2.3.3