Skip to main content
Glama
figma.ts5.27 kB
import axios, { AxiosError } from "axios"; import fs from "fs"; import { parseFigmaResponse, SimplifiedDesign } from "./simplify-node-response"; import type { GetImagesResponse, GetFileResponse, GetFileNodesResponse, GetImageFillsResponse, } from "@figma/rest-api-spec"; import { downloadFigmaImage } from "~/utils/common"; import { partition } from "remeda"; import { Logger } from "~/server"; export interface FigmaError { status: number; err: string; } type FetchImageParams = { /** * The Node in Figma that will either be rendered or have its background image downloaded */ nodeId: string; /** * The local file name to save the image */ fileName: string; /** * The file mimetype for the image */ fileType: "png" | "svg"; }; type FetchImageFillParams = Omit<FetchImageParams, "fileType"> & { /** * Required to grab the background image when an image is used as a fill */ imageRef: string; }; export class FigmaService { private readonly apiKey: string; private readonly baseUrl = "https://api.figma.com/v1"; constructor(apiKey: string) { this.apiKey = apiKey; } private async request<T>(endpoint: string): Promise<T> { try { Logger.log(`Calling ${this.baseUrl}${endpoint}`); const response = await axios.get(`${this.baseUrl}${endpoint}`, { headers: { "X-Figma-Token": this.apiKey, }, }); return response.data; } catch (error) { if (error instanceof AxiosError && error.response) { throw { status: error.response.status, err: (error.response.data as { err?: string }).err || "Unknown error", } as FigmaError; } throw new Error("Failed to make request to Figma API"); } } async getImageFills( fileKey: string, nodes: FetchImageFillParams[], localPath: string, ): Promise<string[]> { if (nodes.length === 0) return []; let promises: Promise<string>[] = []; const endpoint = `/files/${fileKey}/images`; const file = await this.request<GetImageFillsResponse>(endpoint); const { images = {} } = file.meta; promises = nodes.map(async ({ imageRef, fileName }) => { const imageUrl = images[imageRef]; if (!imageUrl) { return ""; } return downloadFigmaImage(fileName, localPath, imageUrl); }); return Promise.all(promises); } async getImages( fileKey: string, nodes: FetchImageParams[], localPath: string, ): Promise<string[]> { const pngIds = nodes.filter(({ fileType }) => fileType === "png").map(({ nodeId }) => nodeId); const pngFiles = pngIds.length > 0 ? this.request<GetImagesResponse>( `/images/${fileKey}?ids=${pngIds.join(",")}&scale=2&format=png`, ).then(({ images = {} }) => images) : ({} as GetImagesResponse["images"]); const svgIds = nodes.filter(({ fileType }) => fileType === "svg").map(({ nodeId }) => nodeId); const svgFiles = svgIds.length > 0 ? this.request<GetImagesResponse>( `/images/${fileKey}?ids=${svgIds.join(",")}&scale=2&format=svg`, ).then(({ images = {} }) => images) : ({} as GetImagesResponse["images"]); const files = await Promise.all([pngFiles, svgFiles]).then(([f, l]) => ({ ...f, ...l })); const downloads = nodes .map(({ nodeId, fileName }) => { const imageUrl = files[nodeId]; if (imageUrl) { return downloadFigmaImage(fileName, localPath, imageUrl); } return false; }) .filter((url) => !!url); return Promise.all(downloads); } async getFile(fileKey: string, depth?: number): Promise<SimplifiedDesign> { try { const endpoint = `/files/${fileKey}${depth ? `?depth=${depth}` : ""}`; Logger.log(`Retrieving Figma file: ${fileKey} (depth: ${depth ?? "default"})`); const response = await this.request<GetFileResponse>(endpoint); Logger.log("Got response"); const simplifiedResponse = parseFigmaResponse(response); writeLogs("figma-raw.json", response); writeLogs("figma-simplified.json", simplifiedResponse); return simplifiedResponse; } catch (e) { console.error("Failed to get file:", e); throw e; } } async getNode(fileKey: string, nodeId: string, depth?: number): Promise<SimplifiedDesign> { const endpoint = `/files/${fileKey}/nodes?ids=${nodeId}${depth ? `&depth=${depth}` : ""}`; const response = await this.request<GetFileNodesResponse>(endpoint); writeLogs("figma-raw.json", response); const simplifiedResponse = parseFigmaResponse(response); writeLogs("figma-simplified.json", simplifiedResponse); return simplifiedResponse; } } function writeLogs(name: string, value: any) { try { if (process.env.NODE_ENV !== "development") return; const logsDir = "logs"; try { fs.accessSync(process.cwd(), fs.constants.W_OK); } catch (error) { Logger.log("Failed to write logs:", error); return; } if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir); } fs.writeFileSync(`${logsDir}/${name}`, JSON.stringify(value, null, 2)); } catch (error) { console.debug("Failed to write logs:", error); } }

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/rlagudals95/mcp_figma'

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