| name | figma-pilot-mcp |
| description | Control Figma through code execution with MCP - create, modify, and query design elements programmatically via AI agents |
| triggers | ["create a figma design","modify figma elements","query figma components","generate figma design system","check figma accessibility","export figma assets","create figma components","batch update figma layers"] |
Figma Pilot MCP Skill
Skill by ara.so — Design Skills collection.
Overview
Figma Pilot is an MCP (Model Context Protocol) server that enables AI agents to control Figma through code execution. Instead of exposing dozens of individual tools, it provides only 3 MCP tools with full Figma API access through JavaScript code execution.
Key advantages:
- 90%+ fewer tokens in tool definitions
- Batch operations (modify 100 elements in one call)
- Data filtering before returning to context
- Complex workflows with loops, conditionals, error handling
Installation
1. Install MCP Server
claude mcp add figma-pilot -- npx @youware-labs/figma-pilot-mcp
Claude Desktop (~/.config/claude/claude_desktop_config.json):
{
"mcpServers": {
"figma-pilot": {
"command": "npx",
"args": ["@youware-labs/figma-pilot-mcp"]
}
}
}
Cursor (~/.cursor/mcp.json):
{
"mcpServers": {
"figma-pilot": {
"command": "npx",
"args": ["@youware-labs/figma-pilot-mcp"]
}
}
}
2. Install Figma Plugin
- Download
figma-pilot-plugin-vX.X.X.zip from GitHub Releases
- Unzip the file
- In Figma: Plugins > Development > Import plugin from manifest...
- Select
manifest.json from unzipped folder
- Run: Plugins > Development > figma-pilot
3. Verify Connection
Check the Figma connection status
The plugin should show "Connected" in Figma.
MCP Tools
| Tool | Purpose |
|---|
figma_status | Check if plugin is connected |
figma_execute | Execute JavaScript code with Figma API |
figma_get_api_docs | Get API documentation for reference |
Core API Reference
Query Operations
const { nodes } = await figma.query({ target: 'selection' });
const { nodes } = await figma.query({ target: 'nodeId:123:456' });
const { nodes } = await figma.query({ target: 'page' });
const { nodes } = await figma.query({ target: 'document' });
Create Operations
await figma.create({
type: 'rectangle',
name: 'My Rectangle',
width: 200,
height: 100,
fill: '#FF6B6B',
cornerRadius: 8
});
await figma.create({
type: 'text',
content: 'Hello World',
fontSize: 24,
fontWeight: 600,
fill: '#333333'
});
await figma.create({
type: 'frame',
name: 'Card',
width: 300,
height: 200,
children: [
{
type: 'text',
content: 'Title',
fontSize: 18,
fontWeight: 600
},
{
type: 'text',
content: 'Description text',
fontSize: 14,
fill:
}
]
});
figma.({
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: [
{
: ,
: ,
: ,
:
}
]
});
Modify Operations
await figma.modify({
target: 'selection',
fill: '#4CAF50',
cornerRadius: 12,
opacity: 0.9
});
await figma.modify({
target: 'nodeId:123:456',
width: 300,
height: 150
});
await figma.modify({
target: 'selection',
content: 'Updated text',
fontSize: 16,
fontWeight: 700
});
Delete Operations
await figma.delete({ target: 'selection' });
await figma.delete({ target: 'nodeId:123:456' });
Append Operations
await figma.append({
target: 'selection',
parent: 'nodeId:789:012'
});
Component Operations
List Components
const { components } = await figma.listComponents();
Create Component Instance
await figma.instantiate({
component: 'abc123componentkey',
name: 'Button Instance'
});
await figma.instantiate({
component: 'abc123componentkey',
x: 100,
y: 200
});
Convert to Component
await figma.toComponent({
target: 'selection',
name: 'My Component'
});
Create Variants
await figma.createVariants({
name: 'Button',
variants: [
{
properties: { size: 'small', state: 'default' },
node: 'nodeId:123:456'
},
{
properties: { size: 'small', state: 'hover' },
node: 'nodeId:123:457'
},
{
properties: { size: 'large', state: 'default' },
node: 'nodeId:123:458'
}
]
});
Design Tokens
Create Token
await figma.createToken({
name: 'colors/primary',
value: '#0066FF',
type: 'color',
description: 'Primary brand color'
});
await figma.createToken({
name: 'spacing/md',
value: 16,
type: 'number',
description: 'Medium spacing'
});
Bind Token to Element
await figma.bindToken({
target: 'selection',
property: 'fill',
token: 'colors/primary'
});
await figma.bindToken({
target: 'selection',
property: 'paddingLeft',
token: 'spacing/md'
});
Accessibility Operations
const result = await figma.accessibility({
target: 'page',
level: 'AA'
});
const result = await figma.accessibility({
target: 'selection',
level: 'AA',
autoFix: true
});
Export Operations
const { data } = await figma.export({
target: 'selection',
format: 'png',
scale: 2
});
const { data } = await figma.export({
target: 'nodeId:123:456',
format: 'svg'
});
const { data } = await figma.export({
target: 'selection',
format: 'jpg',
quality: 0.9
});
Common Patterns
Batch Modify Elements
const { nodes } = await figma.query({ target: 'selection' });
const rectangles = nodes.filter(n => n.type === 'RECTANGLE');
for (const rect of rectangles) {
await figma.modify({
target: `nodeId:${rect.id}`,
fill: '#FF6B6B',
cornerRadius: 8
});
}
console.log(`Modified ${rectangles.length} rectangles`);
Create Design System Components
const sizes = ['small', 'medium', 'large'];
const variants = [];
for (const size of sizes) {
const buttonNode = await figma.create({
type: 'frame',
name: `Button/${size}`,
layoutMode: 'horizontal',
primaryAxisAlignItems: 'center',
counterAxisAlignItems: 'center',
paddingLeft: size === 'small' ? 12 : size === 'medium' ? 16 : 20,
paddingRight: size === 'small' ? 12 : size === 'medium' ? 16 : 20,
paddingTop: size === 'small' ? 6 : size === 'medium' ? 8 : 10,
paddingBottom: size === 'small' ? 6 : size === 'medium' ? 8 : 10,
fill: '#0066FF',
cornerRadius: 6,
children: [{
type: ,
: ,
: size === ? : size === ? : ,
:
}]
});
variants.({
: { size },
: buttonNode.
});
}
figma.({
: ,
variants
});
Generate Card Layout
await figma.create({
type: 'frame',
name: 'Card',
width: 320,
height: 400,
layoutMode: 'vertical',
primaryAxisAlignItems: 'min',
counterAxisAlignItems: 'min',
paddingLeft: 0,
paddingRight: 0,
paddingTop: 0,
paddingBottom: 0,
itemSpacing: 0,
fill: '#FFFFFF',
cornerRadius: 12,
effects: [{
type: 'DROP_SHADOW',
color: { r: 0, g: 0, b: 0, a: 0.1 },
offset: { x: 0, y: 2 },
radius: 8,
visible: true
}],
children: [
{
type: 'rectangle',
name: ,
: ,
: ,
:
},
{
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: [
{
: ,
: ,
: ,
: ,
:
},
{
: ,
: ,
: ,
: ,
: { : , : }
},
{
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: ,
: [{
: ,
: ,
: ,
:
}]
}
]
}
]
});
Filter and Process Elements
const { nodes } = await figma.query({ target: 'page' });
const headings = nodes.filter(n =>
n.type === 'TEXT' &&
n.fontSize >= 24 &&
n.fontWeight >= 600
);
for (const heading of headings) {
await figma.modify({
target: `nodeId:${heading.id}`,
fill: '#1A1A1A',
fontFamily: 'Inter',
fontWeight: 700
});
}
console.log(`Updated ${headings.length} headings`);
Create Responsive Grid
await figma.create({
type: 'frame',
name: 'Grid',
width: 1200,
layoutMode: 'horizontal',
primaryAxisAlignItems: 'min',
counterAxisAlignItems: 'stretch',
itemSpacing: 24,
paddingLeft: 24,
paddingRight: 24,
paddingTop: 24,
paddingBottom: 24,
children: Array(3).fill(null).map((_, i) => ({
type: 'frame',
name: `Column ${i + 1}`,
layoutMode: 'vertical',
primaryAxisSizingMode: 'FIXED',
counterAxisSizingMode: 'FIXED',
width: 376,
primaryAxisAlignItems: 'min',
counterAxisAlignItems: 'stretch',
itemSpacing: 16,
fill: '#F5F5F5',
: ,
: ,
: ,
: ,
: ,
: [
{
: ,
: ,
: ,
:
}
]
}))
});
Troubleshooting
Plugin Not Connecting
- Check MCP server status in your AI client
- Verify plugin shows "Connected" in Figma
- Ensure port 38451 is not blocked
- Try reopening the plugin
lsof -i :38451
kill <PID>
Code Execution Errors
If you get errors like "Cannot read property 'type' of undefined":
const { nodes } = await figma.query({ target: 'selection' });
if (!nodes || nodes.length === 0) {
console.log('No elements selected');
return;
}
for (const node of nodes) {
if (node.type === 'RECTANGLE') {
await figma.modify({
target: `nodeId:${node.id}`,
fill: '#FF0000'
});
}
}
Font Loading Issues
Some fonts may not be available. Use safe defaults:
try {
await figma.create({
type: 'text',
content: 'Hello',
fontFamily: 'Inter',
fontSize: 16
});
} catch (error) {
console.error('Font not available:', error.message);
await figma.create({
type: 'text',
content: 'Hello',
fontSize: 16
});
}
Performance with Large Documents
When working with large documents, filter data before returning:
const { nodes } = await figma.query({ target: 'document' });
const { nodes } = await figma.query({ target: 'document' });
const buttons = nodes.filter(n =>
n.name.includes('Button') &&
n.type === 'FRAME'
).slice(0, 10);
console.log(buttons.map(b => ({ id: b.id, name: b.name })));
Best Practices
- Always validate query results before processing
- Use batch operations instead of multiple individual calls
- Filter data before returning to reduce context window usage
- Log meaningful messages to help track progress
- Handle errors gracefully with try/catch blocks
- Use design tokens for consistent styling
- Create components for reusable elements
- Check accessibility regularly with WCAG checks
Architecture
┌─────────────┐ stdio ┌─────────────────┐ HTTP ┌──────────────┐
│ MCP Client │ <------------> │ MCP Server │ <-----------> │ Figma Plugin │
│ │ │ (with bridge) │ port 38451 │ │
└─────────────┘ └─────────────────┘ └──────────────┘
The MCP server acts as a bridge between the AI client and the Figma plugin, enabling code execution with full Figma API access.