Figma design system integration with API automation, design tokens, and component libraries for scalable design infrastructure. Enhanced MCP integration, error handling, and performance optimization.
Enterprise design system architecture with Figma API, design tokens, and automation
Primary Agent: design-expert, component-designer Stack: Figma API 2025+, MCP integration, design tokens, component libraries, design-to-code automation Keywords: figma, design-system, design-tokens, components, design-to-code, mcp, automation Updated: Enhanced MCP tool patterns, error handling, performance optimization (v4.1.0)
| Tool | Purpose | Use Cases |
|---|---|---|
get_design_context | Retrieve design metadata + generated code | Component generation, design inspection |
get_screenshot | Export design as PNG/SVG | Asset export, visual documentation |
get_variable_defs | Extract design tokens/variables | Token syncing, design system export |
export_components | Batch export multiple components | Library generation, code scaffolding |
Design systems connect design and development with MCP automation:
Use when output of one call feeds into the next:
// Step 1: Get design context (metadata + generated code)
const context = await mcp__figma__get_design_context({
nodeId: "689:1242",
clientLanguages: "typescript",
dirForAssetWrites: "./src/generated/figma-assets" // REQUIRED!
});
// Step 2: Get screenshot based on context
const screenshot = await mcp__figma__get_screenshot({
nodeId: context.nodeId,
format: "png",
scale: 2
});
// Step 3: Extract variables from context
const tokens = context.variables || [];
When to use: Design inspection → Asset export → Token extraction
Use for independent requests to reduce total execution time (20-30% speedup):
// Fetch multiple independent resources in parallel
const [context, variables, screenshot] = await Promise.all([
mcp__figma__get_design_context({
nodeId: "689:1242",
clientLanguages: "typescript",
dirForAssetWrites: "./src/generated/figma-assets"
}),
mcp__figma__get_variable_defs({
fileId: "abc123xyz",
teamId: "team-456"
}),
mcp__figma__get_screenshot({
nodeId: "689:1242",
format: "svg"
})
]);
// All requests complete simultaneously
console.log("Parallel execution time: ~3-4s vs sequential 9-12s");
Speedup calculation:
Skip unnecessary calls based on requirements:
// Only call what you need
const config = {
needsCode: true,
needsAssets: false,
needsTokens: true
};
const requests = [];
// Conditionally add only required calls
if (config.needsCode || config.needsAssets) {
requests.push(
mcp__figma__get_design_context({
nodeId: "689:1242",
clientLanguages: "typescript",
dirForAssetWrites: config.needsAssets ? "./assets" : undefined
})
);
}
if (config.needsTokens) {
requests.push(
mcp__figma__get_variable_defs({
fileId: "abc123xyz"
})
);
}
const results = await Promise.all(requests);
Benefit: Reduce API calls by 30-50% based on actual needs
dirForAssetWrites (CRITICAL - Common Error Source)
// ❌ WRONG: Will cause 400 Bad Request
const context = await mcp__figma__get_design_context({
nodeId: "689:1242",
clientLanguages: "typescript"
// Missing dirForAssetWrites!
});
// ✅ CORRECT: Specify asset output directory
const context = await mcp__figma__get_design_context({
nodeId: "689:1242",
clientLanguages: "typescript",
dirForAssetWrites: "/tmp/figma-assets" // Required even if not using assets
});
Why: MCP tool needs to know where to write exported assets (even if not used)
// NodeId examples (format: "parent-id:component-id")
const validNodeIds = [
"689:1242", // Simple component
"0:1", // Page/root
"689:1242:5678", // Nested instance
"I123:456:789" // Copy instance
];
// Validation pattern
function validateNodeId(nodeId: string): boolean {
// Format: alphanumeric:digit, optionally nested
return /^[a-zA-Z0-9]+:[0-9]+(:[0-9a-zA-Z:]+)?$/.test(nodeId);
}
if (!validateNodeId(nodeId)) {
throw new Error(`Invalid nodeId format: ${nodeId}`);
}
// Auto-detect from project context
function detectFramework(projectPath: string): string {
const packageJson = require(`${projectPath}/package.json`);
if (packageJson.dependencies?.react) {
return packageJson.dependencies.typescript ? "typescript" : "javascript";
}
if (packageJson.dependencies?.vue) {
return "typescript"; // Vue 3 + TS recommended
}
if (packageJson.dependencies?.["@angular/core"]) {
return "typescript"; // Angular always TS
}
return "typescript"; // Default
}
// Usage
const clientLanguages = detectFramework("./");
const context = await mcp__figma__get_design_context({
nodeId: "689:1242",
clientLanguages, // Auto-detected
dirForAssetWrites: "./src/generated"
});
// Error symptoms:
// - 400 Bad Request
// - "dirForAssetWrites is required"
// - Asset export fails silently
// Solution
try {
const context = await mcp__figma__get_design_context({
nodeId: "689:1242",
clientLanguages: "typescript",
dirForAssetWrites: "./src/generated/figma-assets" // Add this!
});
} catch (error) {
if (error.message.includes("dirForAssetWrites")) {
console.error("Missing dirForAssetWrites parameter");
// Provide default asset directory
return await mcp__figma__get_design_context({
nodeId: "689:1242",
clientLanguages: "typescript",
dirForAssetWrites: "/tmp/figma-assets" // Fallback
});
}
throw error;
}
Do NOT call in sequence unless necessary - Use parallel calls instead:
// ❌ INEFFICIENT: Sequential calls
const screenshot1 = await mcp__figma__get_screenshot({nodeId: "id1"});
const vars1 = await mcp__figma__get_variable_defs({fileId: "file1"});
const screenshot2 = await mcp__figma__get_screenshot({nodeId: "id2"});
const vars2 = await mcp__figma__get_variable_defs({fileId: "file1"});
// Total time: 16-20s (sequential)
// ✅ EFFICIENT: Parallel calls grouped by type
const [screenshots, variables] = await Promise.all([
Promise.all([
mcp__figma__get_screenshot({nodeId: "id1"}),
mcp__figma__get_screenshot({nodeId: "id2"})
]),
mcp__figma__get_variable_defs({fileId: "file1"})
]);
// Total time: 3-4s (parallel)
Benefits: 4-5x faster for batch operations
// Exponential backoff for rate-limited requests
async function callWithBackoff(
fn: () => Promise<any>,
maxRetries = 3,
initialDelay = 1000
): Promise<any> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.status === 429) { // Rate limited
const delay = initialDelay * Math.pow(2, attempt);
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
throw new Error(`Max retries exceeded`);
}
// Usage
const screenshot = await callWithBackoff(() =>
mcp__figma__get_screenshot({nodeId: "689:1242"})
);
// Cache metadata with different TTLs based on change frequency
const cacheConfig = {
metadata: { ttl: 72 * 3600 }, // Design rarely changes (72h)
variables: { ttl: 24 * 3600 }, // Tokens updated daily (24h)
screenshots: { ttl: 6 * 3600 }, // Visual assets change frequently (6h)
components: { ttl: 48 * 3600 } // Component structure stable (48h)
};
// Implementation
const cache = new Map();
async function getWithCache(key: string, fetcher: () => Promise<any>, ttl: number) {
const cached = cache.get(key);
const now = Date.now();
if (cached && (now - cached.timestamp) < (ttl * 1000)) {
console.log(`Cache hit for ${key}`);
return cached.value;
}
const value = await fetcher();
cache.set(key, { value, timestamp: now });
return value;
}
// Usage
const variables = await getWithCache(
`variables:abc123`,
() => mcp__figma__get_variable_defs({fileId: "abc123"}),
cacheConfig.variables.ttl
);
// Process components in optimal batch sizes (10-20 per batch)
async function exportComponentsBatch(
nodeIds: string[],
batchSize = 15
): Promise<any[]> {
const results = [];
for (let i = 0; i < nodeIds.length; i += batchSize) {
const batch = nodeIds.slice(i, i + batchSize);
// Parallel requests within batch
const batchResults = await Promise.all(
batch.map(nodeId =>
mcp__figma__get_design_context({
nodeId,
clientLanguages: "typescript",
dirForAssetWrites: "./src/generated"
})
)
);
results.push(...batchResults);
// Small delay between batches to respect rate limits
if (i + batchSize < nodeIds.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return results;
}
// Usage: Export 150 components in 10 parallel requests
const allComponents = await exportComponentsBatch(
Array.from({length: 150}, (_, i) => `component:${i}`)
);
import * as fs from "fs";
import * as path from "path";
interface DesignSystemConfig {
figmaFileId: string;
figmaTeamId?: string;
outputDir: string;
componentNodeIds: string[];
clientLanguages: "typescript" | "javascript";
}
async function syncDesignSystem(config: DesignSystemConfig) {
const {figmaFileId, outputDir, componentNodeIds, clientLanguages} = config;
console.log(`Starting design system sync for ${componentNodeIds.length} components...`);
// Phase 1: Extract design tokens
console.log("Phase 1: Extracting design tokens...");
const variables = await mcp__figma__get_variable_defs({
fileId: figmaFileId,
teamId: config.figmaTeamId
});
const tokensOutput = path.join(outputDir, "tokens.json");
fs.writeFileSync(tokensOutput, JSON.stringify(variables, null, 2));
console.log(`✓ Tokens exported to ${tokensOutput}`);
// Phase 2: Generate component code (parallel batch processing)
console.log("Phase 2: Generating component code...");
const components = await exportComponentsBatch(componentNodeIds, 15);
const componentsDir = path.join(outputDir, "components");
fs.mkdirSync(componentsDir, {recursive: true});
components.forEach((component, index) => {
const componentFile = path.join(
componentsDir,
`${component.componentName || `Component-${index}`}.ts`
);
fs.writeFileSync(componentFile, component.generatedCode || "");
});
console.log(`✓ Generated ${components.length} components`);
// Phase 3: Export visual assets (parallel)
console.log("Phase 3: Exporting visual assets...");
const screenshots = await Promise.all(
componentNodeIds.slice(0, 10).map(nodeId => // Limit to 10 for performance
mcp__figma__get_screenshot({
nodeId,
format: "png",
scale: 2
})
)
);
const assetsDir = path.join(outputDir, "assets");
fs.mkdirSync(assetsDir, {recursive: true});
screenshots.forEach((screenshot, index) => {
const assetFile = path.join(assetsDir, `component-${index}.png`);
fs.writeFileSync(assetFile, screenshot.imageData);
});
console.log(`✓ Exported ${screenshots.length} component previews`);
// Phase 4: Generate documentation
console.log("Phase 4: Generating documentation...");
const docMarkdown = generateComponentDocs(components, variables);
const docsFile = path.join(outputDir, "COMPONENTS.md");
fs.writeFileSync(docsFile, docMarkdown);
console.log(`✓ Documentation generated at ${docsFile}`);
console.log(`\nDesign system sync complete!`);
console.log(`Output directory: ${outputDir}`);
}
// Helper function
function generateComponentDocs(components: any[], tokens: any[]): string {
let md = "# Auto-Generated Component Documentation\n\n";
md += `Generated: ${new Date().toISOString()}\n\n`;
md += "## Design Tokens\n\n";
tokens.forEach(token => {
md += `- \`${token.name}\`: ${token.value}\n`;
});
md += "\n## Components\n\n";
components.forEach((comp, i) => {
md += `### ${comp.componentName || `Component ${i}`}\n`;
md += `Path: \`${comp.nodePath}\`\n`;
md += "```typescript\n";
md += comp.generatedCode?.substring(0, 200) + "...\n";
md += "```\n\n";
});
return md;
}
// Extract tokens from Figma and export to multiple formats
async function exportTokens(figmaFileId: string, outputDir: string) {
const variables = await mcp__figma__get_variable_defs({
fileId: figmaFileId
});
// Format 1: CSS Custom Properties
let cssContent = ":root {\n";
variables.forEach(token => {
cssContent += ` --${token.name}: ${token.value};\n`;
});
cssContent += "}\n";
fs.writeFileSync(path.join(outputDir, "tokens.css"), cssContent);
// Format 2: JSON (for JavaScript)
const jsonTokens = Object.fromEntries(
variables.map(token => [token.name, token.value])
);
fs.writeFileSync(
path.join(outputDir, "tokens.json"),
JSON.stringify(jsonTokens, null, 2)
);
// Format 3: SCSS Variables
let scssContent = "";
variables.forEach(token => {
scssContent += `$${token.name}: ${token.value};\n`;
});
fs.writeFileSync(path.join(outputDir, "tokens.scss"), scssContent);
console.log(`✓ Exported ${variables.length} tokens in 3 formats`);
}
Last Updated: 2025-11-19 Format: Markdown | Language: English Status: Stable (v4.1.0) Version: 4.1.0
v4.1.0 (2025-11-19)
v4.0.0 (2025-11-18)