extract_assets
Extract all assets from a Figma frame, including icons, images, and composite groups, with automatic categorization and progress tracking.
Instructions
Extract all assets from a frame with progress tracking.
HOW IT WORKS:
Detects "composite groups" (image + decorative shapes) and exports them as single PNG
For composite groups, the ENTIRE group is exported as one image, preserving layout
Automatically categorizes into icons/, images/, and images/composites/
Uses smart naming based on component hierarchy
Shows progress: "Processing batch 1/5 - found 8 icons, 3 images, 2 composites"
Final summary with all file paths
Look for "isCompositeAsset: true" in the frame tree to identify composite groups
TYPICAL WORKFLOW:
get_frame_info → see what assets exist and identify composite groups
extract_assets → download all
Check summary for file paths
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_key | Yes | Figma file key | |
| page_name | Yes | Page name (partial match) | |
| frame_name | Yes | Frame name (partial match) | |
| output_dir | No | Output directory (default: ./figma-assets) | ./figma-assets |
Implementation Reference
- src/tools/handlers/assets.js:6-121 (handler)Core implementation: Extracts icons (SVG), images (PNG), and composite groups (PNG) from Figma frame. Batches API calls, smart categorization, saves to outputDir/icons|images|composites/, returns paths and summary.export async function extractAssets(ctx, fileKey, pageName, frameName, outputDir) { const { chunker, figmaClient } = ctx; const file = await figmaClient.getFile(fileKey, 2); const page = figmaClient.findPageByName(file, pageName); if (!page) throw new Error(`Page "${pageName}" not found`); const frameRef = figmaClient.findFrameByName(page, frameName); if (!frameRef) throw new Error(`Frame "${frameName}" not found`); const frame = await figmaClient.getNode(fileKey, frameRef.id); const assets = findAssets(frame, { collectBounds: true }); const iconsDir = join(outputDir, "icons"); const imagesDir = join(outputDir, "images"); const compositeDir = join(imagesDir, "composites"); await mkdir(iconsDir, { recursive: true }); await mkdir(imagesDir, { recursive: true }); await mkdir(compositeDir, { recursive: true }); const results = { icons: [], images: [], composites: [], failed: [] }; const assetMap = {}; const batchSize = 10; for (let i = 0; i < assets.length; i += batchSize) { const batch = assets.slice(i, i + batchSize); const ids = batch.map((a) => a.id).join(","); try { const svgData = await figmaClient.getImage(fileKey, ids, "svg"); const pngData = await figmaClient.getImage(fileKey, ids, "png", 2); for (const asset of batch) { try { if (asset.isComposite) { if (pngData.images[asset.id]) { const pngResponse = await axios.get(pngData.images[asset.id], { responseType: "arraybuffer" }); const filePath = join(compositeDir, `${asset.name}.png`); await writeFile(filePath, Buffer.from(pngResponse.data)); results.composites.push({ path: filePath, uniqueName: asset.name, originalName: asset.originalName, section: getSectionFromPath(asset.path), bounds: asset.bounds, isComposite: true, }); assetMap[asset.name] = filePath; } } else if (asset.category === "icon" && svgData.images[asset.id]) { const svgResponse = await axios.get(svgData.images[asset.id]); const filePath = join(iconsDir, `${asset.name}.svg`); await writeFile(filePath, svgResponse.data); results.icons.push({ path: filePath, uniqueName: asset.name, originalName: asset.originalName, section: getSectionFromPath(asset.path), bounds: asset.bounds, }); assetMap[asset.name] = filePath; } else if (pngData.images[asset.id]) { const pngResponse = await axios.get(pngData.images[asset.id], { responseType: "arraybuffer" }); const filePath = join(imagesDir, `${asset.name}.png`); await writeFile(filePath, Buffer.from(pngResponse.data)); results.images.push({ path: filePath, uniqueName: asset.name, originalName: asset.originalName, section: getSectionFromPath(asset.path), bounds: asset.bounds, }); assetMap[asset.name] = filePath; } } catch (err) { results.failed.push({ name: asset.name, originalName: asset.originalName, error: err.message, }); } } } catch (err) { batch.forEach((a) => results.failed.push({ name: a.name, originalName: a.originalName, error: err.message, })); } } const response = chunker.wrapResponse( { frame: frame.name, outputDir, summary: { icons: results.icons.length, images: results.images.length, composites: results.composites.length, failed: results.failed.length, }, icons: results.icons, images: results.images, composites: results.composites, assetMap, failed: results.failed, }, { step: "Asset extraction complete", progress: `${results.icons.length} icons, ${results.images.length} images, ${results.composites.length} composite groups`, nextStep: "Assets saved to disk. Use extract_styles for design tokens.", } ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] }; }
- src/tools/schemas.js:146-172 (schema)Tool schema defining name, description, and input parameters (file_key, page_name, frame_name, output_dir). Part of toolSchemas array exported for MCP server.name: "extract_assets", description: `Extract all assets from a frame with progress tracking. HOW IT WORKS: - Detects "composite groups" (image + decorative shapes) and exports them as single PNG - For composite groups, the ENTIRE group is exported as one image, preserving layout - Automatically categorizes into icons/, images/, and images/composites/ - Uses smart naming based on component hierarchy - Shows progress: "Processing batch 1/5 - found 8 icons, 3 images, 2 composites" - Final summary with all file paths - Look for "isCompositeAsset: true" in the frame tree to identify composite groups TYPICAL WORKFLOW: 1. get_frame_info → see what assets exist and identify composite groups 2. extract_assets → download all 3. Check summary for file paths`, inputSchema: { type: "object", properties: { file_key: { type: "string", description: "Figma file key" }, page_name: { type: "string", description: "Page name (partial match)" }, frame_name: { type: "string", description: "Frame name (partial match)" }, output_dir: { type: "string", description: "Output directory (default: ./figma-assets)", default: "./figma-assets" }, }, required: ["file_key", "page_name", "frame_name"], }, },
- src/index.js:60-62 (registration)Registration in main server switch statement: maps 'extract_assets' tool call to handlers.extractAssets with parsed arguments. handlers imported from './tools/handlers/index.js'.case "extract_assets": result = await handlers.extractAssets(this.ctx, args.file_key, args.page_name, args.frame_name, args.output_dir || "./figma-assets"); break;