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:
list_pages → find page name
list_frames(page_name) → see frames
get_frame_info(frame_name) → detail one frame
extract_assets(frame_name) → get assets
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_key | Yes | Figma file key | |
| page_name | Yes | Page name (partial match, case-insensitive) | |
| continue | No | Continue from last response if more frames available |
Implementation Reference
- src/tools/handlers/navigation.js:61-129 (handler)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) }] }; }
- src/tools/schemas.js:30-54 (schema)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;
- src/tools/handlers/index.js:1-1 (registration)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";