Skip to main content
Glama
alucardeht

Figma MCP

by alucardeht

list_frames

Retrieve frames and screens from a Figma page by name, returning a compact list with names, sizes, and IDs for navigation and asset extraction workflows.

Instructions

List frames/screens in a specific page.

HOW IT WORKS:

  • Search by page name (partial match supported)

  • Large pages (>50 frames) are automatically chunked

  • Returns compact list with frame names, sizes, and IDs

  • Session remembers what was sent

TYPICAL WORKFLOW:

  1. list_pages → find page name

  2. list_frames(page_name) → see frames

  3. get_frame_info(frame_name) → detail one frame

  4. extract_assets(frame_name) → get assets

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_keyYesFigma file key
page_nameYesPage name (partial match, case-insensitive)
continueNoContinue from last response if more frames available

Implementation Reference

  • Core handler function that lists frames in a specified Figma page. Handles continuation for large lists, fetches file data, filters frames/components, computes dimensions, and wraps responses with chunker for MCP compatibility.
    export async function listFrames(ctx, fileKey, pageName, continueFlag = false) {
      const { session, chunker, figmaClient } = ctx;
      const operationId = `list_frames:${fileKey}:${pageName}`;
    
      if (continueFlag && session.hasPendingChunks(operationId)) {
        const chunk = session.getNextChunk(operationId);
        const response = chunker.wrapResponse(
          { frames: chunk.items },
          {
            step: `Showing frames ${(chunk.chunkIndex - 1) * 20 + 1}-${Math.min(chunk.chunkIndex * 20, chunk.totalItems)}`,
            progress: `${chunk.chunkIndex}/${chunk.totalChunks}`,
            nextStep: chunk.chunkIndex < chunk.totalChunks ? "Call with continue=true for more" : "Use get_frame_info to detail a frame",
            operationId,
          }
        );
        return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
      }
    
      session.setCurrentFile(fileKey);
      const file = await figmaClient.getFile(fileKey, 2);
      const page = figmaClient.findPageByName(file, pageName);
    
      if (!page) {
        const available = file.document.children.map((p) => p.name).join(", ");
        throw new Error(`Page "${pageName}" not found. Available: ${available}`);
      }
    
      const frames = (page.children || [])
        .filter((c) => c.type === "FRAME" || c.type === "COMPONENT" || c.type === "COMPONENT_SET")
        .map((f) => {
          session.markFrameExplored(f.id);
          return {
            name: f.name,
            id: f.id,
            type: f.type,
            width: Math.round(f.absoluteBoundingBox?.width || 0),
            height: Math.round(f.absoluteBoundingBox?.height || 0),
            childCount: f.children?.length || 0,
          };
        });
    
      const chunked = chunker.chunkArray(frames, operationId, 20);
    
      if (chunked) {
        const response = chunker.wrapResponse(
          { page: page.name, frameCount: frames.length, frames: chunked.items },
          {
            step: `Showing frames 1-${chunked.items.length} of ${chunked.totalItems}`,
            progress: `1/${chunked.totalChunks}`,
            nextStep: "Call with continue=true for more, or get_frame_info for details",
            alert: `Page has ${frames.length} frames - showing first ${chunked.items.length}`,
            strategy: "Review visible frames, continue if needed, then detail specific ones",
            operationId,
          }
        );
        return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
      }
    
      const response = chunker.wrapResponse(
        { page: page.name, frameCount: frames.length, frames },
        {
          step: "Listed all frames",
          progress: `${frames.length} frames`,
          nextStep: "Use get_frame_info(frame_name) for structure, or extract_assets for icons/images",
        }
      );
    
      return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
    }
  • Input schema and description for the list_frames tool, defining required parameters (file_key, page_name) and optional continue flag.
      {
        name: "list_frames",
        description: `List frames/screens in a specific page.
    
    HOW IT WORKS:
    - Search by page name (partial match supported)
    - Large pages (>50 frames) are automatically chunked
    - Returns compact list with frame names, sizes, and IDs
    - Session remembers what was sent
    
    TYPICAL WORKFLOW:
    1. list_pages → find page name
    2. list_frames(page_name) → see frames
    3. get_frame_info(frame_name) → detail one frame
    4. extract_assets(frame_name) → get assets`,
        inputSchema: {
          type: "object",
          properties: {
            file_key: { type: "string", description: "Figma file key" },
            page_name: { type: "string", description: "Page name (partial match, case-insensitive)" },
            continue: { type: "boolean", description: "Continue from last response if more frames available" },
          },
          required: ["file_key", "page_name"],
        },
      },
  • src/index.js:48-50 (registration)
    Dispatch in the main MCP server request handler (CallToolRequestSchema) that routes 'list_frames' calls to the listFrames handler function.
    case "list_frames":
      result = await handlers.listFrames(this.ctx, args.file_key, args.page_name, args.continue);
      break;
  • Re-export of the listFrames handler from navigation.js, allowing it to be imported as part of the handlers module.
    export { listPages, listFrames, getFrameInfo } from "./navigation.js";

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