genable-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| jsxA | Create design trees with nested JSX markup. One jsx call builds a complete subtree atomically — nesting is the hierarchy. Keep a single logical unit inside one call; the returned root's children are already built, not stubs to be filled in later. Examples: jsx({markup: ""}) jsx({markup: "Account"}) Elements: frame, text, rect, ellipse, line, icon, image, instance, component, group, section, vector Attributes (frame): layout, justify, items, wrap, w, h, minW, maxW, p, gap, bg, fill, rounded, stroke, shadow, blur, bgblur, opacity, layoutPositioning Attributes (text): size, weight, lineHeight, font, fill, w (w="fill" for wrap), maxLines, textTruncation Effects: shadow="0,8,32,0,#0006" or shadow={shadow(0,8,32,0,'#0006')}; blur={10} for layer blur; bgblur={20} for frosted-glass/glassmorphism background blur. Multiple effects merge automatically. Decoration in auto-layout: floating orbs/blobs/decorative shapes inside a row/column parent need layoutPositioning="absolute" so they don't get stacked into the main-axis flow. Full-frame backgrounds: set the parent frame's bg directly. Don't add a separate backdrop. Supported gradient strings (CSS-like subset, not full CSS):
Swap an existing subtree: jsx({replaceId: "", markup: "..."}) replaces the old node at the same parent and sibling index atomically, preserving position in one call. Markup must have a single root. Use jsx for tree creation; edit for property updates on known nodes. |
| inspectA | Read design node(s) — choose what to surface with Default (no facets) returns a skeleton: id, name, type, role, children. For anything else, list the facets you need — nothing else is included. Facets: structure name, type, size, layout shorthand — cheap overview layout layoutMode/gap/padding/align/sizing (row/column, fill/hug, etc.) paint|fill fills + Paint.boundVariables.color (see bound tokens) stroke strokes, strokeWeight, strokeAlign, dashPattern effects shadows, blurs typography|text fontFamily, fontSize, fontWeight, lineHeight, letterSpacing appearance opacity, visible, blendMode, cornerRadius, clipsContent variables node-level boundVariables + explicitVariableModes (token bindings) all everything Parameters: node "/" for page root, or node ID from jsx/inspect results (e.g. "100:5"). facets array of facet names listed above. depth Max tree depth (default: 5, max: 10). Examples: inspect({node: "/"}) → page skeleton inspect({node: "100:5"}) → one-node skeleton inspect({node: "100:5", facets: ["variables"]}) → token bindings only inspect({node: "100:5", facets: ["layout", "paint"]}) → layout + fills inspect({node: "100:5", facets: ["all"]}) → full properties Use |
| describeA | Validate a design subtree — semantic description, role detection, and lint rules. Checks for layout conflicts, overflow, missing properties, and structural issues. Parameters: node: Node ID to describe (e.g. "100:5"). Required. depth: How deep to check children (default: 3, max: 8). Returns per-node: role, visual summary, layout summary, and issues (severity: error/warning/info). Examples: describe({node: "100:5"}) → validate subtree, depth 3 describe({node: "100:5", depth: 1}) → shallow check (root + direct children only) |
| editA | Batch update properties on multiple nodes. For single-property changes, prefer focused setters: set_text — text content set_fill — fill/background color set_stroke — border set_layout — padding, gap, direction Use edit for batch fixes or properties not covered by setters (sizing, radius, opacity, effects, component props): edit({nodes: [ {node: "1:1", props: {w: "fill", corner: 8}}, // Figma native props {node: "1:2", props: {opacity: 0.6}}, {node: "1:3", props: {Label: "Sign In"}}, // instance TEXT prop (by display name) ]}) For instances, use component property DISPLAY NAMES (e.g. "Label") — edit resolves them to Figma's internal keys automatically. Component props can be mixed with Figma props in the same call. |
| find_nodesA | Search nodes by name or type. Scoped to the current page — call switch_page first if your target lives on a different page. Examples: find_nodes({query: "Button"}) find_nodes({query: "frame", scope: "1:2"}) |
| discover_propsA | Discover unique property values in a subtree. Examples: discover_props({node: "1:2", props: ["fillColor", "fontSize"]}) Searchable properties: fillColor, textColor, strokeColor, strokeWeight, opacity, cornerRadius, gap, fontSize, fontFamily, fontWeight. |
| replace_propsA | Bulk find-and-replace property values across a subtree (target node + all descendants). Destructive batch mutation — no preview, no undo across many nodes. Returns per-rule match counts. Use when:
Returns: { data: { replacements: [{ rule: 0, matched: 12 }, { rule: 1, matched: 0 }] } } Parameters beyond schema:
Skip when:
Examples: // single rule, white -> black replace_props({node: "1:2", rules: [{prop: "fillColor", from: "#FFF", to: "#000"}]}) // batch theme update — both rules applied in one pass replace_props({node: "1:2", rules: [ {prop: "fillColor", from: "#FFF", to: "#000"}, {prop: "fontSize", from: "14", to: "16"} ]}) |
| find_referencesA | Find every node on the current page that references a given variable. This is the REVERSE of inspect: inspect asks "what does this node bind?", find_references asks "who uses this variable?". Use it when renaming, auditing, or swapping tokens — you need to know all the binding sites before you touch the variable. Scan scope: currentPage only. Invisible nodes are skipped by default. Node-level bindings (e.g. boundVariables.paddingLeft) and per-paint color bindings (fills[i].boundVariables.color, strokes[i].boundVariables.color) are both returned. Parameters: variable — VariableID (e.g. "VariableID:1:5"). Required. Returns: {variable, variableName, variableType, referenceCount, references: [{nodeId, nodeName, nodeType, path}, ...]} path values look like: "boundVariables.paddingLeft" "fills[0].boundVariables.color" "strokes[2].boundVariables.color" Examples: find_references({variable: "VariableID:1:5"}) |
| delete_nodeB | Delete a node and its children. Examples: delete_node({node: "1:2"}) |
| move_nodeA | Relocate a node without recreating it. Preserves IDs, bound variables, and component instances across the move, so callers tracking the node by ID never need to re-discover it. Use for: (a) changing child order within a container, (b) moving a subtree into a different parent, (c) fixing a placement mistake after jsx. Examples: move_node({node: "1:3", name: "NewTitle"}) — rename in place move_node({node: "1:3", parent: "1:4"}) — move into parent 1:4 move_node({node: "1:5", index: 0}) — reorder within current parent |
| clone_nodeA | Deep-copy a node with optional property overrides. Examples: clone_node({node: "1:2"}) — clone to page root, same name clone_node({node: "1:2", parent: "/"}) — clone to page root explicitly clone_node({node: "1:2", parent: "/", name: "Hero Copy"}) — clone to root with custom name clone_node({node: "1:2", parent: "1:4"}) — clone into parent node 1:4 clone_node({node: "1:2", parent: "1:4", overrides: {"bg": "#D9D9D9"}}) |
| skillA | Load a procedural skill — workflow + tool sequence + anti-patterns. Use FIRST when the user is changing/adjusting existing canvas, OR before creating a new design if a matching skill exists. Example: skill({ name: "restyle" }) |
| styleA | Load a visual style preset as INSPIRATION — color tokens, typography, shape, depth. Treat as a reference library, not a cage: pick one wholesale, mix elements, or invent your own. You are not restricted to the menu. Example: style({ name: "neon-cyber" }) |
| guidelineA | Load a page-type design guideline — layout patterns for landing pages, dashboards, login flows, forms, etc. Example: guideline({ name: "form" }) |
| helpB | Load narrow how-to / process help — tool usage rules, edge cases, naming conventions. Example: help({ name: "interaction-model" }) |
| list_variablesA | List variables as a flat array with referenced collections. Returns {data: {variables[], collections[], nextCursor?}}. Each variable carries its full Figma shape: id, name, variableCollectionId, resolvedType, valuesByMode. collections[] only includes collections referenced by the returned variables (use for mode-name resolution). Parameters: collection — VariableCollectionId to filter by filter — substring match on variable name (case-insensitive) cursor — opaque pagination cursor from a previous call limit — max variables per page (default 100) Examples: list_variables() list_variables({collection: "VariableCollectionId:1:2"}) list_variables({filter: "bg"}) list_variables({cursor: "100"}) |
| create_collectionA | Create a VariableCollection with named modes. The first mode in the array becomes the default mode. Returns {data: {id, modes: [{modeId, name}]}} — use those modeIds with set_variable_value and set_variable_mode. Examples: create_collection({name: "Theme", modes: ["Light", "Dark"]}) create_collection({name: "Device", modes: ["Desktop", "Tablet", "Mobile"]}) |
| create_variableA | Create a variable in an existing collection. No value is set here — use set_variable_value after. Returns {data: {id}}. Examples: create_variable({collection: "VariableCollectionId:1:2", name: "Theme/bg", type: "COLOR"}) create_variable({collection: "VariableCollectionId:1:2", name: "spacing/md", type: "FLOAT"}) |
| ensure_collectionA | Idempotent VariableCollection creation — safe to retry. Returns existing collection if one with the same name + identical mode list already exists, otherwise creates a new one. Spec §3.1. Prefer this over create_collection — re-running with the same name + modes returns the existing collection instead of creating a duplicate. Omit idempotency_key — the handler computes it canonically from (name, modes). Pass it only if you need strict concurrency-safety validation (LLMs should not try to compute SHA-256 inline; placeholder strings are rejected). Returns {data: {collection_id, modes: [{modeId, name}], reused?: true}}. Examples: ensure_collection({name: "Theme", modes: ["Light", "Dark"]}) |
| ensure_variableA | Idempotent variable creation — populates values_by_mode in one shot. Prefer this over create_variable — re-running with the same args returns the existing variable instead of creating a duplicate. Behavior (spec §3.1):
values_by_mode keys can be either mode NAMES (e.g. "Light") or modeIds (e.g. "1:0"). Each value must match the variable type (hex/RGBA for COLOR, number for FLOAT, string for STRING, boolean for BOOLEAN). Omit idempotency_key — the handler computes it canonically from (collection_id, name, type, values_by_mode). Pass it only if you need strict concurrency-safety validation (LLMs should not try to compute SHA-256 inline; placeholder strings are rejected). Mode coverage policy (spec §6.2):
Returns {data: {variable_id, name, type, collection_id, mode_coverage[], mode_coverage_required, reused?: true}, warnings?: [...]}. Examples: ensure_variable({collection_id: "VariableCollectionId:1:2", name: "Text/Primary", type: "COLOR", values_by_mode: {Light: "#111", Dark: "#EEE"}}) ensure_variable({collection_id: "VariableCollectionId:1:2", name: "Spacing/desktop", type: "FLOAT", values_by_mode: {Desktop: 24}, mode_coverage_required: "opt-in-fallback", fallback_reason: "Desktop-only metric; fallback to Desktop in Mobile mode."}) |
| set_variable_valueA | Set a variable's value for a specific mode. Thin wrapper over Figma's variable.setValueForMode(modeId, value). Call once per mode. Value is a raw value (COLOR/FLOAT/STRING/BOOLEAN) OR an alias object {type: "VARIABLE_ALIAS", id: "VariableID:x:y"}. Hex strings are accepted for COLOR and normalized to {r,g,b,a} in 0-1 range. Examples: set_variable_value({variable: "VariableID:1:5", mode: "1:0", value: "#FFFFFF"}) set_variable_value({variable: "VariableID:1:5", mode: "1:1", value: {r: 0.1, g: 0.1, b: 0.1, a: 1}}) set_variable_value({variable: "VariableID:1:6", mode: "1:0", value: 16}) set_variable_value({variable: "VariableID:1:7", mode: "1:0", value: {type: "VARIABLE_ALIAS", id: "VariableID:1:9"}}) |
| bind_variableA | Bind a FLOAT, BOOLEAN, or STRING variable to a node property. prop is a flat Figma bindable field (e.g. fontSize, itemSpacing, paddingTop, cornerRadius, opacity, visible, width, height, characters). Shorthands: gap → itemSpacing, padding → paddingTop, corner → cornerRadius, font-size → fontSize. COLOR variables are NOT bound here — they live inside Paint objects. To apply a color token, specify it at the source instead: • At creation: jsx <frame bg="$TokenName" ...> or fill="$TokenName" • Post-hoc: set_fill({node, bg: "$TokenName"}) or set_stroke When selecting which variable to bind: if the node is a Tablet or Mobile variant (name or variant property contains "Tablet"/"Mobile"), match the node's property value against the Tablet/Mobile mode column from list_variables — not Desktop. Examples: bind_variable({node: "1:2", prop: "fontSize", variable: "VariableID:1:6"}) bind_variable({node: "1:3", prop: "paddingTop", variable: "VariableID:1:7"}) bind_variable({node: "1:4", prop: "visible", variable: "VariableID:1:8"}) bind_variable({node: "1:5", prop: "characters", variable: "VariableID:1:9"}) |
| set_variable_modeA | Set a node to use a specific mode of a variable collection. This controls which variable values the node displays. For example, set a frame to use "Dark" mode of the "Theme" collection so all bound variables show dark values. Examples: set_variable_mode({node: "1:2", collection: "VariableCollectionId:1:2", mode: "1:1"}) set_variable_mode({node: "1:5", collection: "VariableCollectionId:1:3", mode: "1:2"}) |
| create_componentC | Convert a frame or group to a Figma component. Examples: create_component({node: "1:2"}) |
| combine_componentsB | Combine multiple components into a variant set (ComponentSet). Examples: combine_components({nodes: ["1:2", "1:3", "1:4"], name: "Button"}) |
| add_component_propA | Add a component property and bind it to a child node. For TEXT properties: binds to the target text node's characters, so instances can override the text content. For BOOLEAN properties: binds to the target node's visibility. Parameters: node: The component node ID (must be COMPONENT or COMPONENT_SET). name: Property display name. type: TEXT, BOOLEAN, or INSTANCE_SWAP. default: Default value. bind: Child node ID to bind this property to. For TEXT, binds to text content. For BOOLEAN, binds to visibility. Examples: add_component_prop({node: "1:2", name: "Label", type: "TEXT", default: "Click me", bind: "1:5"}) add_component_prop({node: "1:2", name: "Show Icon", type: "BOOLEAN", default: "true", bind: "1:6"}) |
| list_component_propsA | List properties and variants of a component, component set, or instance. Examples: list_component_props({node: "1:2"}) |
| create_instanceA | Create an instance of an existing component. Mutates the canvas — appends a new InstanceNode as the last child of Use when:
Returns: { data: { id: "5:42", name: "Button", componentId: "1:2" } } Parameters beyond schema:
Skip when:
Examples: create_instance({node: "1:2"}) // at page root create_instance({node: "1:2", parent: "1:4"}) // inside frame 1:4 |
| set_textA | Set text content on one or more nodes. set_text({node: "1:2", text: "Hello World"}) set_text({nodes: [{node: "1:2", text: "Title"}, {node: "1:3", text: "Subtitle"}]}) Use this when changing what text says. For text styling (font, size, weight), use edit. |
| set_fillA | Set fill or background color on a node. set_fill({node: "1:2", bg: "#F5F5F5"}) set_fill({node: "1:2", fill: "#333333"}) set_fill({node: "1:2", bg: "linear-gradient(135deg, #8B5CF6 0%, #F97316 100%)"}) // Batch — bulk paint update in one call: set_fill({nodes: [{node: "1:2", bg: "#FFF"}, {node: "1:3", bg: "#F5F5F5"}]}) fill = text color or shape fill. bg = frame background. For stroke color, use set_stroke. Accepted color formats (for fill or bg): hex "#FFF", "#F5F5F5" gradient string CSS-like subset, not full CSS: "linear-gradient(deg, <#hex> %, ...)" "linear-gradient(to , ...)" directions: top/right/bottom/left + corners "radial-gradient()" centered, no position/shape modifiers "radial-gradient(circle, )" circle shape only "conic-gradient(from deg, ...)" Rejected: "circle at X% Y%", "ellipse at ...", named colors, hsl(). variable token qualified bare name "$Surface/Card" transparent "transparent" (bg only) |
| set_strokeA | Set stroke (border) on a node. set_stroke({node: "1:2", stroke: "1 #E0E0E0"}) set_stroke({node: "1:2", stroke: "2 #333 inside"}) set_stroke({node: "1:2", color: "#E0E0E0", weight: 1, align: "inside"}) set_stroke({node: "1:2", color: "linear-gradient(90deg, #8B5CF6 0%, #F97316 100%)", weight: 1.5, align: "inside"}) // Batch — bulk stroke update in one call: set_stroke({nodes: [{node: "1:2", color: "#E0E0E0", weight: 1}, {node: "1:3", color: "#333", weight: 2}]}) Shorthand: "weight color align" (e.g. "1 #E0E0E0 inside"). Hex only in shorthand. Accepted color formats (for the explicit To bind a variable to the stroke color, use the explicit |
| set_layoutB | Set auto-layout properties on a container. set_layout({node: "1:2", gap: 16, p: 24}) set_layout({node: "1:2", layout: "row", justify: "space-between"}) set_layout({node: "1:2", layout: "column", gap: 8, p: "16 24", align: "center"}) set_layout({node: "1:2", layout: "grid", cols: 3, rows: 2, gap: 16}) // Batch — bulk update in one call: set_layout({nodes: [{node: "1:2", gap: 16, p: 24}, {node: "1:3", gap: 8, p: 12}]}) Controls spacing, padding, direction, and alignment of a container's children. Grid: use layout:"grid" with cols/rows + gap (or rowGap/colGap for asymmetric). Children fill the grid in insertion order. |
| get_selectionA | Get the user's currently selected nodes in Figma. Returns node names, types, and IDs of selected elements. Call this when the user's intent involves modifying existing elements:
Skip for fresh design requests ("design a login page", "create a dashboard") — a new canvas has no selection to read, so the call returns nothing and burns an iteration. Examples: get_selection() |
| switch_pageA | Navigate between pages in the Figma file. ID-driven — names are not addressable (they can collide and change). Two modes:
Pages are top-level containers under the file root. Most read/write operations default to figma.currentPage. Call this when you need to operate on nodes that live on a different page than the current one. Returns:
Typical flow:
When to call:
Don't call:
|
| read_plugin_dataA | Read plugin data (private or shared) from a Figma node. Use for: i18n metadata, design-system tags, custom plugin annotations, anything stored via setPluginData / setSharedPluginData. If Returns Examples: read_plugin_data({node_id: "1:5", key: "ref"}) read_plugin_data({node_id: "1:5", namespace: "i18n", key: "ref"}) |
| write_plugin_dataA | Write plugin data (private or shared) to a Figma node. If Pass an empty string as Examples: write_plugin_data({node_id: "1:5", key: "ref", value: "home_title"}) write_plugin_data({node_id: "1:5", namespace: "i18n", key: "ref", value: "home.welcome_title"}) |
| create_vectorA | Create a vector node from SVG path data or a list of points. Use for chart lines, custom icon paths, freeform curves, or any shape that needs path data. Examples: // Polyline (chart trend line) create_vector({ parent: "1:23", name: "TrendLine", x: 40, y: 20, width: 550, height: 240, points: [[0,144],[90,96],[180,120],[270,64],[360,80],[450,40],[540,72]], stroke: "#6366F1", strokeWeight: 2 }) // Raw SVG path (custom shape) create_vector({ parent: "1:23", name: "Wave", width: 200, height: 60, data: "M 0 30 Q 50 0 100 30 T 200 30", stroke: "linear-gradient(90deg, #8B5CF6 0%, #F97316 100%)", strokeWeight: 1.5 }) Path input — provide ONE of: points: [[x,y], ...] compiled to "M x0 y0 L x1 y1 ..." (polyline shortcut) data: "M ... L ..." raw SVG path (LLM-native; supports M, L, C, Q, A, Z) Stroke / fill (same formats as set_stroke / set_fill): hex "#6366F1" gradient "linear-gradient(angle, #color stop%, ...)" variable qualified bare name "$Brand/Primary" Default fill is "transparent" so the vector shows only its stroke. Pass an explicit fill if you want it filled. When NOT to use:
|
| get_screenshotA | Capture a PNG screenshot of a node. Use after style changes to visually verify the result instead of reading properties back. Returns base64 PNG data embedded in the response. Parameters: node: Node ID from jsx/inspect results (e.g. "100:5"). Page root ("/") is not supported. scale: Export scale 0.5–2 (default 1). Higher = larger file. padding: Reserved for future use — currently ignored. Examples: get_screenshot({node: "100:5"}) → PNG at 1x get_screenshot({node: "100:5", scale: 2}) → PNG at 2x (sharper) |
| ask_userA | Ask the user 1-3 questions in a single form. Each question has its own options and can be single- or multi-select. Bundle related decisions in ONE call instead of multiple turns. Use when:
Returns one of:
Each question:
Conventions:
Example: ask_user({ questions: [ { question: "Who is this for?", options: [{label:"B2B SaaS"},{label:"Consumer"},{label:"Developer tool"}] }, { question: "What visual direction?", options: [{label:"Minimal"},{label:"Bold/Brutalist"},{label:"Neon/Cyber"}] } ]}) Skip when the prompt is already actionable — asking adds a turn and costs momentum. |
| subtaskA | Delegate a focused sub-task to a typed child agent. Each type has its own tools, iteration budget, and behavioral constraints. Available agent types:
Use when the prompt names 3+ distinct regions (e.g. header, sidebar, main) that share no nodes, or when specialized behavior is needed (audit, token ops). For 1-2 tool-call operations, inline calls finish faster than the subtask spin-up cost. |
| session_noteA | Read / write your own session scratchpad. Persists across turns within one design session ("New Design" resets it). Notes are how state carries from this turn into the next — the next session loads them as context. Actions: action: "read" → read({key}) returns the current value (empty string if unset) action: "write" → write({key, value}) replaces or deletes (pass value:"" to delete) action: "list" → list({}) returns [{key, chars}] for all existing notes Slots — FORWARD-LOOKING (what we plan / commit to):
Slots — BACKWARD-LOOKING (what happened, write AT TURN END — AUTO-MERGE on write):
BACKWARD-LOOKING slots auto-merge: writing to REQUIRED behavior:
Examples: session_note({action: "write", key: "decisions", value: "Style: fintech-dark.\nAccent: #3B82F6 (style.accent — NOT indigo).\nFont: Space Grotesk display / Inter body.\nHero treatment: split (overrides anatomy VERTICAL — desktop convention).\nH1 size: 32 (style.display)."}) session_note({action: "write", key: "failures", value: "jsx #6 align='stretch' rejected (DSL valid: center|start|end|space-between|baseline) — retried with 'center'. Same CSS-prior class as gradient: model has CSS values that DSL doesn't accept."}) session_note({action: "write", key: "gotchas", value: "8 LOW_CONTRAST warnings on nav links + secondary CTA (2.5:1 against dark bg). Left as-is — user prompt didn't require WCAG pass; flagging in case next iteration tightens contrast."}) session_note({action: "write", key: "learnings", value: "layoutPositioning='absolute' children render correctly inside auto-layout frame, but parent needs clipsContent=true to suppress overflow on big glow ellipses."}) session_note({action: "read", key: "decisions"}) session_note({action: "list"}) |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/musepy/genable'
If you have feedback or need assistance with the MCP directory API, please join our Discord server