read_my_design
Retrieve detailed information about selected elements in Figma designs, including node specifications and properties, for design analysis and integration workflows.
Instructions
Get detailed information about the current selection in Figma, including all node details
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/talk_to_figma_mcp/server.ts:127-154 (registration)MCP tool registration for 'read_my_design'. Defines the tool with no input parameters and handler that forwards the command to Figma plugin via sendCommandToFigma and returns the JSON result.server.tool( "read_my_design", "Get detailed information about the current selection in Figma, including all node details", {}, async () => { try { const result = await sendCommandToFigma("read_my_design", {}); return { content: [ { type: "text", text: JSON.stringify(result) } ] }; } catch (error) { return { content: [ { type: "text", text: `Error getting node info: ${error instanceof Error ? error.message : String(error) }`, }, ], }; } } );
- src/cursor_mcp_plugin/code.js:381-408 (handler)Core handler function 'readMyDesign' that processes the current Figma selection: loads nodes, exports them as JSON using Figma's JSON_REST_V1 format, filters with filterFigmaNode, and returns array of processed node documents.async function readMyDesign() { try { // Load all selected nodes in parallel const nodes = await Promise.all( figma.currentPage.selection.map((node) => figma.getNodeByIdAsync(node.id)) ); // Filter out any null values (nodes that weren't found) const validNodes = nodes.filter((node) => node !== null); // Export all valid nodes in parallel const responses = await Promise.all( validNodes.map(async (node) => { const response = await node.exportAsync({ format: "JSON_REST_V1", }); return { nodeId: node.id, document: filterFigmaNode(response.document), }; }) ); return responses; } catch (error) { throw new Error(`Error getting nodes info: ${error.message}`); } }
- Helper function filterFigmaNode recursively processes Figma node data: skips VECTOR nodes, converts RGBA colors to HEX using rgbaToHex, removes boundVariables/imageRef, extracts relevant properties like fills/strokes/children/style/bbox for serialization.function filterFigmaNode(node) { if (node.type === "VECTOR") { return null; } var filtered = { id: node.id, name: node.name, type: node.type, }; if (node.fills && node.fills.length > 0) { filtered.fills = node.fills.map((fill) => { var processedFill = Object.assign({}, fill); delete processedFill.boundVariables; delete processedFill.imageRef; if (processedFill.gradientStops) { processedFill.gradientStops = processedFill.gradientStops.map( (stop) => { var processedStop = Object.assign({}, stop); if (processedStop.color) { processedStop.color = rgbaToHex(processedStop.color); } delete processedStop.boundVariables; return processedStop; } ); } if (processedFill.color) { processedFill.color = rgbaToHex(processedFill.color); } return processedFill; }); } if (node.strokes && node.strokes.length > 0) { filtered.strokes = node.strokes.map((stroke) => { var processedStroke = Object.assign({}, stroke); delete processedStroke.boundVariables; if (processedStroke.color) { processedStroke.color = rgbaToHex(processedStroke.color); } return processedStroke; }); } if (node.cornerRadius !== undefined) { filtered.cornerRadius = node.cornerRadius; } if (node.absoluteBoundingBox) { filtered.absoluteBoundingBox = node.absoluteBoundingBox; } if (node.characters) { filtered.characters = node.characters; } if (node.style) { filtered.style = { fontFamily: node.style.fontFamily, fontStyle: node.style.fontStyle, fontWeight: node.style.fontWeight, fontSize: node.style.fontSize, textAlignHorizontal: node.style.textAlignHorizontal, letterSpacing: node.style.letterSpacing, lineHeightPx: node.style.lineHeightPx, }; } if (node.children) { filtered.children = node.children .map((child) => { return filterFigmaNode(child); }) .filter((child) => { return child !== null; }); } return filtered; }
- Helper function rgbaToHex converts Figma RGBA color objects to hexadecimal strings, omitting alpha if fully opaque.function rgbaToHex(color: any): string { // skip if color is already hex if (color.startsWith('#')) { return color; } const r = Math.round(color.r * 255); const g = Math.round(color.g * 255); const b = Math.round(color.b * 255); const a = Math.round(color.a * 255); return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}${a === 255 ? '' : a.toString(16).padStart(2, '0')}`; } function filterFigmaNode(node: any) { // Skip VECTOR type nodes if (node.type === "VECTOR") { return null; } const filtered: any = { id: node.id, name: node.name, type: node.type, }; if (node.fills && node.fills.length > 0) { filtered.fills = node.fills.map((fill: any) => { const processedFill = { ...fill }; // Remove boundVariables and imageRef delete processedFill.boundVariables; delete processedFill.imageRef; // Process gradientStops if present if (processedFill.gradientStops) { processedFill.gradientStops = processedFill.gradientStops.map((stop: any) => { const processedStop = { ...stop }; // Convert color to hex if present if (processedStop.color) { processedStop.color = rgbaToHex(processedStop.color); } // Remove boundVariables delete processedStop.boundVariables; return processedStop; }); } // Convert solid fill colors to hex if (processedFill.color) { processedFill.color = rgbaToHex(processedFill.color); } return processedFill; }); } if (node.strokes && node.strokes.length > 0) { filtered.strokes = node.strokes.map((stroke: any) => { const processedStroke = { ...stroke }; // Remove boundVariables delete processedStroke.boundVariables; // Convert color to hex if present
- src/cursor_mcp_plugin/code.js:122-182 (handler)Dispatch handler in handleCommand switch statement that routes 'read_my_design' commands to the readMyDesign function.return await readMyDesign(); case "create_rectangle": return await createRectangle(params); case "create_frame": return await createFrame(params); case "create_text": return await createText(params); case "set_fill_color": return await setFillColor(params); case "set_stroke_color": return await setStrokeColor(params); case "move_node": return await moveNode(params); case "resize_node": return await resizeNode(params); case "delete_node": return await deleteNode(params); case "delete_multiple_nodes": return await deleteMultipleNodes(params); case "get_styles": return await getStyles(); case "get_local_components": return await getLocalComponents(); // case "get_team_components": // return await getTeamComponents(); case "create_component_instance": return await createComponentInstance(params); case "export_node_as_image": return await exportNodeAsImage(params); case "set_corner_radius": return await setCornerRadius(params); case "set_text_content": return await setTextContent(params); case "clone_node": return await cloneNode(params); case "scan_text_nodes": return await scanTextNodes(params); case "set_multiple_text_contents": return await setMultipleTextContents(params); case "get_annotations": return await getAnnotations(params); case "set_annotation": return await setAnnotation(params); case "scan_nodes_by_types": return await scanNodesByTypes(params); case "set_multiple_annotations": return await setMultipleAnnotations(params); case "set_layout_mode": return await setLayoutMode(params); case "set_padding": return await setPadding(params); case "set_axis_align": return await setAxisAlign(params); case "set_layout_sizing": return await setLayoutSizing(params); case "set_item_spacing": return await setItemSpacing(params); default: throw new Error(`Unknown command: ${command}`); } }