Skip to main content
Glama
alucardeht

Figma MCP

by alucardeht

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:

  1. get_frame_info → see what assets exist and identify composite groups

  2. extract_assets → download all

  3. Check summary for file paths

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_keyYesFigma file key
page_nameYesPage name (partial match)
frame_nameYesFrame name (partial match)
output_dirNoOutput directory (default: ./figma-assets)./figma-assets

Implementation Reference

  • 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) }] };
    }
  • 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;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/alucardeht/figma-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server