| name | cursor-talk-to-figma-mcp |
| description | MCP server enabling AI agents to read and modify Figma designs programmatically through WebSocket communication |
| triggers | ["interact with figma designs","read figma document structure","modify figma components programmatically","automate figma design tasks","sync code with figma designs","extract design information from figma","create figma elements via ai","update figma text content in bulk"] |
Cursor Talk to Figma MCP
Skill by ara.so — Design Skills collection.
This skill enables AI agents to communicate with Figma through the Model Context Protocol (MCP), allowing programmatic reading and modification of design files. The integration uses a WebSocket server to bridge AI agents (Cursor, Claude Code) with a Figma plugin.
Installation
Prerequisites
Install Bun runtime:
curl -fsSL https://bun.sh/install | bash
Quick Setup
Run the automated setup which installs the MCP server in your Cursor configuration:
bun setup
Manual MCP Configuration
Add to ~/.cursor/mcp.json:
{
"mcpServers": {
"TalkToFigma": {
"command": "bunx",
"args": ["cursor-talk-to-figma-mcp@latest"]
}
}
}
For local development, use direct file path:
{
"mcpServers": {
"TalkToFigma": {
"command": "bun",
"args": ["/absolute/path/to/src/talk_to_figma_mcp/server.ts"]
}
}
}
Start WebSocket Server
bun socket
Figma Plugin Installation
- Install from Figma Community
- Or develop locally: Plugins > Development > New Plugin > Link existing plugin > Select
src/cursor_mcp_plugin/manifest.json
Core Workflow
1. Establish Connection
await use_mcp_tool("TalkToFigma", "join_channel", {
channelId: "my-design-session"
});
2. Read Design Information
const docInfo = await use_mcp_tool("TalkToFigma", "get_document_info", {});
const selection = await use_mcp_tool("TalkToFigma", "get_selection", {});
const nodeInfo = await use_mcp_tool("TalkToFigma", "get_node_info", {
nodeId: "123:456"
});
const selectionDetails = await use_mcp_tool("TalkToFigma", "read_my_design", {});
3. Create Elements
const frame = await use_mcp_tool("TalkToFigma", "create_frame", {
x: 0,
y: 0,
width: 375,
height: 812,
name: "iPhone Frame"
});
const rect = await use_mcp_tool("TalkToFigma", "create_rectangle", {
x: 20,
y: 20,
width: 335,
height: 200,
name: "Header Background"
});
const text = await use_mcp_tool("TalkToFigma", "create_text", {
x: 40,
y: 100,
characters: "Welcome to the App",
fontSize: 24,
fontName: { family: "Inter", style: "Bold" },
name: "Header Title"
});
4. Modify Existing Elements
await use_mcp_tool("TalkToFigma", "set_fill_color", {
nodeId: "123:456",
r: 0.2,
g: 0.4,
b: 0.8,
a: 1.0
});
await use_mcp_tool("TalkToFigma", "set_stroke_color", {
nodeId: "123:456",
r: 0.1,
g: 0.1,
b: 0.1,
a: 1.0,
strokeWeight: 2
});
await use_mcp_tool("TalkToFigma", "set_corner_radius", {
nodeId: "123:456",
radius: 12
});
await use_mcp_tool("TalkToFigma", "move_node", {
nodeId: "123:456",
x: 100,
y: 200
});
await use_mcp_tool(, , {
: ,
: ,
:
});
Auto Layout Configuration
await use_mcp_tool("TalkToFigma", "set_layout_mode", {
nodeId: "123:456",
layoutMode: "VERTICAL",
layoutWrap: "NO_WRAP"
});
await use_mcp_tool("TalkToFigma", "set_padding", {
nodeId: "123:456",
paddingTop: 24,
paddingRight: 16,
paddingBottom: 24,
paddingLeft: 16
});
await use_mcp_tool("TalkToFigma", "set_axis_align", {
nodeId: "123:456",
primaryAxisAlignItems: "CENTER",
counterAxisAlignItems: "CENTER"
});
await use_mcp_tool("TalkToFigma", "set_layout_sizing", {
nodeId: "123:456",
layoutSizingHorizontal: "HUG",
:
});
(, , {
: ,
:
});
Bulk Text Operations
Scan Text Nodes
const textNodes = await use_mcp_tool("TalkToFigma", "scan_text_nodes", {
chunkSize: 50,
startIndex: 0,
parentNodeId: null
});
Update Single Text
await use_mcp_tool("TalkToFigma", "set_text_content", {
nodeId: "123:456",
characters: "Updated heading text"
});
Batch Update Multiple Texts
await use_mcp_tool("TalkToFigma", "set_multiple_text_contents", {
updates: [
{ nodeId: "123:456", characters: "New Title" },
{ nodeId: "123:789", characters: "New Description" },
{ nodeId: "123:101", characters: "Updated Label" }
]
});
Annotations
Get Annotations
const annotations = await use_mcp_tool("TalkToFigma", "get_annotations", {});
const nodeAnnotations = await use_mcp_tool("TalkToFigma", "get_annotations", {
nodeId: "123:456"
});
Create/Update Annotation
await use_mcp_tool("TalkToFigma", "set_annotation", {
nodeId: "123:456",
text: "This component needs **accessibility** improvements:\n- Add ARIA labels\n- Improve color contrast",
category: "DESIGN"
});
Batch Create Annotations
await use_mcp_tool("TalkToFigma", "set_multiple_annotations", {
annotations: [
{
nodeId: "123:456",
text: "Update to new brand colors",
category: "DESIGN"
},
{
nodeId: "123:789",
text: "Implement loading state",
category: "ENGINEERING"
}
]
});
Components and Instances
Get Components
const components = await use_mcp_tool("TalkToFigma", "get_local_components", {});
Create Component Instance
const instance = await use_mcp_tool("TalkToFigma", "create_component_instance", {
componentKey: "abc123def456",
x: 100,
y: 100
});
Instance Override Management
const overrides = await use_mcp_tool("TalkToFigma", "get_instance_overrides", {
sourceInstanceId: "123:456"
});
await use_mcp_tool("TalkToFigma", "set_instance_overrides", {
targetInstanceIds: ["123:789", "123:101"],
overrides: overrides.overrides
});
Prototyping and Connections
Get Prototype Reactions
const reactions = await use_mcp_tool("TalkToFigma", "get_reactions", {});
Create FigJam Connectors
await use_mcp_tool("TalkToFigma", "set_default_connector", {});
await use_mcp_tool("TalkToFigma", "create_connections", {
connections: [
{ from: "123:456", to: "123:789" },
{ from: "123:789", to: "123:101" }
]
});
Selection and Focus
await use_mcp_tool("TalkToFigma", "set_focus", {
nodeId: "123:456"
});
await use_mcp_tool("TalkToFigma", "set_selections", {
nodeIds: ["123:456", "123:789", "123:101"]
});
Node Management
Clone Nodes
const clone = await use_mcp_tool("TalkToFigma", "clone_node", {
nodeId: "123:456",
offsetX: 20,
offsetY: 20
});
Delete Nodes
await use_mcp_tool("TalkToFigma", "delete_node", {
nodeId: "123:456"
});
await use_mcp_tool("TalkToFigma", "delete_multiple_nodes", {
nodeIds: ["123:456", "123:789", "123:101"]
});
Get Multiple Nodes Info
const nodesInfo = await use_mcp_tool("TalkToFigma", "get_nodes_info", {
nodeIds: ["123:456", "123:789", "123:101"]
});
Export
const imageData = await use_mcp_tool("TalkToFigma", "export_node_as_image", {
nodeId: "123:456",
format: "PNG",
scale: 2
});
Scan Nodes by Type
const frames = await use_mcp_tool("TalkToFigma", "scan_nodes_by_types", {
types: ["FRAME", "COMPONENT", "INSTANCE"],
parentNodeId: null
});
Common Patterns
Pattern: Create a Card Component
const card = await use_mcp_tool("TalkToFigma", "create_frame", {
x: 0, y: 0, width: 300, height: 400, name: "Card"
});
await use_mcp_tool("TalkToFigma", "set_layout_mode", {
nodeId: card.id, layoutMode: "VERTICAL"
});
await use_mcp_tool("TalkToFigma", "set_padding", {
nodeId: card.id,
paddingTop: 20, paddingRight: 20,
paddingBottom: 20, paddingLeft: 20
});
await use_mcp_tool("TalkToFigma", "set_item_spacing", {
nodeId: card.id, itemSpacing: 16
});
await use_mcp_tool("TalkToFigma", "set_fill_color", {
nodeId: card.id, : , : , : , :
});
(, , {
: card., :
});
(, , {
: card., : , : , : , : , :
});
Pattern: Bulk Content Replacement
let allTextNodes = [];
let hasMore = true;
let nextIndex = 0;
while (hasMore) {
const result = await use_mcp_tool("TalkToFigma", "scan_text_nodes", {
chunkSize: 50,
startIndex: nextIndex
});
allTextNodes = allTextNodes.concat(result.nodes);
hasMore = result.hasMore;
nextIndex = result.nextIndex;
}
const updates = allTextNodes
.filter(node => node.characters.includes("old-text"))
.map(node => ({
nodeId: node.id,
characters: node.characters.replace("old-text", "new-text")
}));
await use_mcp_tool("TalkToFigma", "set_multiple_text_contents", {
updates: updates
});
Pattern: Convert Legacy Annotations
const textNodes = await use_mcp_tool("TalkToFigma", "scan_text_nodes", {});
const markers = textNodes.nodes.filter(n => /^\d+\./.test(n.characters));
const targets = await use_mcp_tool("TalkToFigma", "scan_nodes_by_types", {
types: ["FRAME", "COMPONENT", "INSTANCE", "RECTANGLE"]
});
const annotations = markers.map(marker => {
const targetNode = findClosestTarget(marker, targets.nodes);
return {
nodeId: targetNode.id,
text: marker.characters.replace(/^\d+\.\s*/, ""),
category: "DESIGN"
};
});
await use_mcp_tool("TalkToFigma", "set_multiple_annotations", {
annotations: annotations
});
(, , {
: markers.( m.)
});
Pattern: Prototype to Connector Visualization
const reactions = await use_mcp_tool("TalkToFigma", "get_reactions", {});
await use_mcp_tool("TalkToFigma", "set_default_connector", {});
const connections = reactions.reactions.map(r => ({
from: r.node.id,
to: r.action.destinationId
}));
await use_mcp_tool("TalkToFigma", "create_connections", {
connections: connections
});
Windows + WSL Configuration
For Windows with WSL, uncomment the hostname in src/socket.ts:
hostname: "0.0.0.0",
Install Bun via PowerShell:
irm bun.sh/install.ps1|iex
Troubleshooting
WebSocket Connection Issues
- Ensure WebSocket server is running:
bun socket
- Verify Figma plugin is active and connected
- Check that
join_channel was called successfully
- For WSL, ensure hostname is set to
0.0.0.0
MCP Server Not Found
- Verify
~/.cursor/mcp.json configuration is correct
- Restart Cursor after configuration changes
- Check that Bun is installed and in PATH
- For local development, use absolute paths
Commands Fail Silently
- Always join a channel first with
join_channel
- Verify node IDs exist using
get_document_info or get_selection
- Check WebSocket server logs for errors
- Ensure Figma file is open and plugin is running
Text Update Failures
- Verify node is actually a TEXT node type
- Check that text layer is not locked
- For batch updates, process in smaller chunks if needed
- Missing fonts may cause failures - ensure fonts are available
Color Values
All color values use 0-1 range (not 0-255):
- Convert:
rgbValue / 255
- Example: RGB(51, 102, 204) = r: 0.2, g: 0.4, b: 0.8
Auto Layout Constraints
set_layout_mode must be called before other auto-layout properties
- Cannot set padding on non-auto-layout frames
- Item spacing only works in auto-layout frames
- Some sizing modes require specific layout configurations
Best Practices
- Always establish connection first - Call
join_channel before any other commands
- Get context before modifying - Use
get_document_info and get_selection to understand structure
- Use batch operations - Prefer
set_multiple_text_contents over multiple set_text_content calls
- Chunk large operations - Use chunking parameters for designs with many nodes
- Verify changes - Use
get_node_info after modifications to confirm success
- Handle errors gracefully - All commands can throw exceptions, implement proper error handling
- Use component instances - Prefer instances over duplicating elements for consistency
- Organize with frames - Use frames with auto-layout for structured, responsive designs
- Clean up legacy elements - Delete old nodes after migrating to new patterns
- Test incrementally - Verify each step when building complex automation workflows