Skip to content
Design
Skill

/balise-affinity-mcp

Automate Affinity by Canva through the Affinity MCP and JavaScript SDK for manipulating open documents, selected objects, layers/nodes, geometry, text, fills, line styles, document commands, exports, and visual verification. Use when an agent needs to test or use the Affinity

BOOST
From plugin
balise-skills
1215 skills
Install
$ npx -y skills add mrstev3n/balise-skills --skill balise-affinity-mcp --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/balise-affinity-mcp

Context preview

The summary Claude sees to decide when to auto-load this skill.

Automate Affinity by Canva through the Affinity MCP and JavaScript SDK for manipulating open documents, selected objects, layers/nodes, geometry, text, fills, line styles, document commands, exports, and visual verification. Use when an agent needs to test or use the Affinity

SKILL.md

balise-affinity-mcp.SKILL.md
name: balise-affinity-mcp
description: Automate Affinity by Canva through the Affinity MCP and JavaScript SDK for manipulating open documents, selected objects, layers/nodes, geometry, text, fills, line styles, document commands, exports, and visual verification. Use when an agent needs to test or use the Affinity MCP, edit the current Affinity file, inspect or modify objects or selections, create or adjust Affinity elements, automate Affinity features, or capture reusable Affinity SDK lessons.

Affinity MCP Automation

Use the Affinity MCP as a live application-control surface. Work from the open document, inspect before editing, prefer small reversible commands, and verify visual results.

Standard Workflow

1. Load Affinity tools if they are not already present. 2. Read SDK documentation in this order:

  • Always read `preamble` first.
  • Call `list_sdk_documentation`.
  • Read only the topics needed for the task, such as `application.js`, `document.js`, `nodes.js`, `commands.js`, `geometry.js`, `colours.js`, `fills.js`, `linestyle.js`, `storybuilder.js`, `glyphatts.js`, `paragraphatts.js`, or `exportconfig.js`.

3. Inspect the current document before making changes. 4. Choose the narrowest edit: selected object first, current spread next, whole document only when requested. 5. Execute scripts with the Affinity MCP `execute_script` capability; a harness may expose a qualified tool name. Use `console.log(JSON.stringify(...))` for observable output. 6. Verify with `render_selection(document_session_uuid)` whenever the result is visual. 7. Do not save, export, flatten, delete broad selections, or resize the spread unless the user asked for that kind of change.

Document Inspection

Use this baseline probe before editing:

const {Document} = require('/document');
const doc = Document.current;
if (!doc) {
  console.log(JSON.stringify({ok: false, reason: 'NO_CURRENT_DOCUMENT'}));
} else {
  console.log(JSON.stringify({
    ok: true,
    title: doc.title,
    path: doc.path,
    sessionUuid: doc.sessionUuid,
    isReadOnly: doc.isReadOnly,
    isDirty: doc.isDirty,
    needsSaving: doc.needsSaving,
    pageCount: doc.pageCount,
    spreadCount: doc.spreadCount,
    selectedCount: doc.selection.nodes.length,
    currentSpreadBox: doc.currentSpread.baseBox
  }, null, 2));
}

Stop cleanly if `Document.current` is null or `doc.isReadOnly` is true.

Object Operations

Use node operations for visible document objects:

  • `doc.selection` returns the current selection.
  • `doc.selection.nodes` gives selected nodes.
  • `node.userDescription = '...'` is useful for naming automation-created objects.
  • `doc.selectAll(false)` selects all document objects; use it deliberately because it changes selection.
  • `doc.deleteSelection(selection)` deletes selected objects; only use when the user wants replacement or cleanup.
  • `doc.setVisible(visible, selection)`, `doc.lockSelection(selection)`, and `doc.unlockSelection(selection)` operate through document commands.
  • For new nodes, `doc.addNode(def, doc.currentSpread)` adds to the current spread and selects the new object.

Use descriptions and final selection intentionally so the user can inspect or undo the right thing.

Geometry And Appearance

For basic shapes:

  • Create a `ShapeNodeDefinition`.
  • Set a shape, such as `ShapeRectangle.create()` or `ShapeEllipse.create()`.
  • Set the bounding rectangle.
  • Add it to the current spread.
  • Apply fill and stroke to the resulting selection.

For exact freeform shapes:

  • Build a closed path with `CurveBuilder`.
  • Add it to a `PolyCurve`.
  • Put it into `PolyCurveNodeDefinition`.
  • Add it to the document.

For appearance:

  • Use `RGBA8(r,g,b,a)` from `/colours`.
  • Use `FillDescriptor.createSolid(colour)` for brush or pen fill.
  • Use `FillDescriptor.createNone()` to remove fill or stroke.
  • Use `/linestyle` and `doc.setLineStyleDescriptor(...)` when stroke weight, caps, joins, or alignment matter.

Read `references/affinity-js-patterns.md` for tested snippets. Read `references/affinity-concepts-and-nuances.md` when the task requires choosing between Affinity object types, text modes, non-destructive techniques, or verification methods.

Text Operations

Do not assume text node defaults exist. In current SDK usage, these fail:

  • `FrameTextNodeDefinition.createDefault()`
  • `ArtTextNodeDefinition.createDefault()`
  • `TextNodeDefinition.createDefault()`

Use `StoryBuilder` plus `FrameTextNodeDefinition.createFromStoryBuilder(frameBox, storyBuilder)` or `ArtTextNodeDefinition.createFromStoryBuilder(point, storyBuilder)`:

  • Set defaults with `sb.setToFrameTextDefaultStyle(doc.dpi, doc.format)`.
  • For Artistic Text, set defaults with `sb.setToArtisticTextDefaultStyle(doc.dpi, doc.format)`.
  • Configure `sb.glyphAtts` for font, height, fill, and optional spacing.
  • Configure `sb.paragraphAtts` for alignment and explicit leading.
  • Add text line by line.
  • Use generous frame bounds, then render-check for clipping.
  • Treat `glyph.characterSpacing` as an em-like value. Use small values such as `-0.02`, `0`, or `0.03`; values like `-1` or `-4` can collapse glyph advance and make characters appear stacked.

For existing selected text, read `commands.js` and `storydelta.js` before applying formatting commands such as `doc.formatText(...)`.

Artistic Text vs Frame Text

Choose the text object type intentionally:

  • Use Artistic Text for short display text, labels, numerals, titles, and poster-like fragments where the text box should hug the text content.
  • Use Frame Text for paragraphs, fixed-width copy blocks, multi-line text areas, and text that needs a deliberately sized container.
  • Frame Text has a container frame in the editing UI. That frame can be visually distracting while designing, but it is not part of the artwork/export unless an actual stroke/fill/decoration has been applied.
  • For rotated labels, create Artistic Text first, retrieve the created node, rotate it with `Docum
Read more
Ships withbalise-skills

Des skills portables pour Claude Code, Codex, Cursor, le Figma Agent, Figma Make et les autres outils compatibles avec le standard Agent Skills. Installez un skill précis ou une collection prête à l’emploi. Chaque skill utilise SKILL.md comme format canonique.

Get the whole plugin

Other skills on balise-skills.