apply_variable_to_node
Bind a variable to a specific node property in Figma, such as color, opacity, width, or height. Call once per field to set multiple property bindings.
Instructions
Bind a variable to a node property in Figma. Call once per field — for multiple fields, call multiple times.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| nodeId | Yes | The ID of the node to bind the variable to | |
| variableId | Yes | The ID of the variable to bind | |
| field | Yes | The node property field to bind (e.g., 'fills/0/color', 'opacity', 'width', 'height') |
Implementation Reference
- src/talk_to_figma_mcp/tools/variable-tools.ts:85-121 (registration)Registration of the 'apply_variable_to_node' tool via server.tool() on the MCP server. Includes Zod schema for parameters (nodeId, variableId, field) and the handler function.
// Apply Variable to Node Tool server.tool( "apply_variable_to_node", "Bind a variable to a node property in Figma. Call once per field — for multiple fields, call multiple times.", { nodeId: z.string().describe("The ID of the node to bind the variable to"), variableId: z.string().describe("The ID of the variable to bind"), field: z.string().describe("The node property field to bind (e.g., 'fills/0/color', 'opacity', 'width', 'height')"), }, async ({ nodeId, variableId, field }) => { try { const result = await sendCommandToFigma("apply_variable_to_node", { nodeId, variableId, field, }); const typedResult = result as { nodeName: string; variableName: string; field: string }; return { content: [ { type: "text", text: `Bound variable "${typedResult.variableName}" to field "${typedResult.field}" on node "${typedResult.nodeName}"`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error applying variable to node: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } } ); - Handler function for 'apply_variable_to_node'. Sends the command to Figma via WebSocket with nodeId, variableId, and field, then returns a success message with bound variable name, field, and node name.
async ({ nodeId, variableId, field }) => { try { const result = await sendCommandToFigma("apply_variable_to_node", { nodeId, variableId, field, }); const typedResult = result as { nodeName: string; variableName: string; field: string }; return { content: [ { type: "text", text: `Bound variable "${typedResult.variableName}" to field "${typedResult.field}" on node "${typedResult.nodeName}"`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error applying variable to node: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } } ); - sendCommandToFigma is the helper that sends the command string 'apply_variable_to_node' via WebSocket to the Figma server, handling request tracking, timeouts, and promise resolution.
export function sendCommandToFigma( command: FigmaCommand, params: unknown = {}, timeoutMs: number = 300000 ): Promise<unknown> { return new Promise((resolve, reject) => { // If not connected, try to connect first if (!ws || ws.readyState !== WebSocket.OPEN) { connectToFigma(); reject(new Error("Not connected to Figma. Attempting to connect...")); return; } // Check if we need a channel for this command const requiresChannel = command !== "join"; if (requiresChannel && !currentChannel) { reject(new Error("Must join a channel before sending commands")); return; } const id = uuidv4(); const request = { id, type: command === "join" ? "join" : "message", ...(command === "join" ? { channel: (params as any).channel, sessionId: SESSION_ID } : { channel: currentChannel }), message: { id, command, params: { ...(params as any), commandId: id, // Include the command ID in params }, }, }; // Set timeout for request const timeout = setTimeout(() => { if (pendingRequests.has(id)) { pendingRequests.delete(id); logger.error(`Request ${id} to Figma timed out after ${timeoutMs / 1000} seconds`); reject(new Error('Request to Figma timed out')); } }, timeoutMs); // Store the promise callbacks to resolve/reject later pendingRequests.set(id, { resolve, reject, timeout, lastActivity: Date.now() }); // Send the request logger.info(`Sending command to Figma: ${command}`); logger.debug(`Request details: ${JSON.stringify(request)}`); ws.send(JSON.stringify(request)); }); } - FigmaCommand type definition that includes 'apply_variable_to_node' as a valid command string for the type system.
export type FigmaCommand = | "get_document_info" | "get_selection" | "get_node_info" | "create_rectangle" | "create_frame" | "create_text" | "create_ellipse" | "create_polygon" | "create_star" | "create_vector" | "create_line" | "set_fill_color" | "set_stroke_color" | "move_node" | "resize_node" | "delete_node" | "get_styles" | "get_local_components" | "get_team_components" | "create_component_instance" | "export_node_as_image" | "join" | "ping" | "set_corner_radius" | "clone_node" | "set_text_content" | "scan_text_nodes" | "set_multiple_text_contents" | "set_auto_layout" | "set_font_name" | "set_font_size" | "set_font_weight" | "set_letter_spacing" | "set_line_height" | "set_paragraph_spacing" | "set_text_case" | "set_text_decoration" | "get_styled_text_segments" | "load_font_async" | "get_remote_components" | "set_effects" | "set_effect_style_id" | "set_text_style_id" | "group_nodes" | "ungroup_nodes" | "flatten_node" | "insert_child" | "create_component_from_node" | "create_component_set" | "set_instance_variant" | "create_page" | "delete_page" | "rename_page" | "get_pages" | "set_current_page" | "rename_node" | "set_selection_colors" | "set_image_fill" | "get_image_from_node" | "replace_image_fill" // | "get_image_bytes" // COMMENTED OUT: Issues pending investigation | "apply_image_transform" | "set_image_filters" | "rotate_node" | "set_node_properties" | "reorder_node" | "duplicate_page" | "convert_to_frame" | "set_gradient" | "boolean_operation" | "set_svg" | "get_svg" | "set_image" | "set_grid" | "get_grid" | "set_guide" | "get_guide" | "set_annotation" | "get_annotation" | "get_variables" | "set_variable" | "apply_variable_to_node" | "switch_variable_mode" | "get_figjam_elements" | "create_sticky" | "set_sticky_text" | "create_shape_with_text" | "create_connector" | "create_section";