read_my_design
Extract detailed node information from the current selection in Figma to streamline design analysis and collaboration.
Instructions
Get detailed information about the current selection in Figma, including all node details
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {},
"type": "object"
}
Implementation Reference
- src/cursor_mcp_plugin/code.js:381-408 (handler)Core handler implementation in Figma plugin: Exports current selection nodes using Figma's JSON_REST_V1 format, filters with filterFigmaNode, returns array of detailed node representationsasync 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}`); } }
- src/talk_to_figma_mcp/server.ts:127-154 (registration)MCP tool registration: Registers 'read_my_design' with no input params, forwards execution to Figma plugin via sendCommandToFigma, returns JSON stringified resultserver.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) }`, }, ], }; } } );
- Helper function used by handler: Recursively processes Figma node JSON - skips VECTOR nodes, converts RGBA colors to hex strings, removes boundVariables/imageRef, extracts relevant properties for MCP responsefunction 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 utility: Converts Figma RGBA color objects to hexadecimal color strings (with alpha if not opaque), used in node filtering (note: duplicated in plugin code.js)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