Skip to main content
Glama

figma_to_code

Extract Figma node IDs and convert designs into HTML, React with CSS Modules, or React with Tailwind CSS using MCP server f2c-mcp-server to automate design-to-code workflows.

Instructions

Convert Figma designs into code. This tool extracts specified Figma nodes and transforms them into HTML, React with CSS Modules, or React with Tailwind CSS, facilitating automated design-to-code conversion.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
fileKeyYesThe unique identifier for a Figma file. Can be found in the Figma file URL, such as: https://www.figma.com/file/XXXXXXXXXXXX/, where XXXXXXXXXXXX is the fileKey.
formatNoThe output code format: 'html' for pure HTML and CSS code, 'react-cssmodules' for React components with CSS modules, 'react-tailwind' for React components using Tailwind CSS.html
idsYesList of Figma node IDs to convert, separated by commas. These can be obtained in Figma by selecting elements, right-clicking and choosing 'Copy/Paste as' → 'Copy ID'.
personalTokenNoYour Figma personal access token

Implementation Reference

  • Registers the 'figma_to_code' MCP tool with server, providing description, input schema using Zod, and the execution handler.
    export const registerF2cServer = (server: McpServer) => {
      // Register Figma to HTML conversion tool
      server.tool(
        'figma_to_code',
        'Transform Figma designs into production-ready code. This tool converts selected Figma nodes into HTML,enabling seamless design-to-code workflow.',
        {
          fileKey: z
            .string()
            .describe(
              'The Figma file identifier found in the file URL (e.g., https://www.figma.com/file/XXXXXXXXXXXX/). Extract the XXXXXXXXXXXX portion as the fileKey.',
            ),
          ids: z
            .string()
            .describe(
              'Comma-separated list of Figma node IDs for conversion. To obtain node IDs, select elements in Figma, right-click and select "Copy/Paste as" → "Copy ID".',
            ),
          format: z
            .enum(['html', 'react-cssmodules', 'react-tailwind'])
            .default('html')
            .describe(
              'Specify the output format: "html" generates semantic HTML/CSS, "react-cssmodules" creates React components with scoped CSS modules, "react-tailwind" produces React components with utility-first Tailwind classes.',
            ),
          personalToken: z
            .string()
            .optional()
            .describe(
              'Figma personal access token for API authentication.The parameters are not required when the tool is called.',
            ),
          localPath: z
            .string()
            .optional()
            .describe(
              'Absolute path for image asset storage. Directory will be created if non-existent. Path must follow OS-specific format without special character escaping. When set, all static resources will be saved to the images directory under this path.',
            ),
          imgFormat: z
            .enum(['png', 'jpg', 'svg'])
            .default('png')
            .describe(
              'Export format for image assets: "png" for lossless quality, "jpg" for compressed files, or "svg" for vector graphics.',
            ),
          scaleSize: z
            .number()
            .min(1)
            .max(4)
            .default(2)
            .describe(
              'Image export scale factor (1-4). Higher values yield better quality at the cost of larger file sizes.',
            ),
        },
        async (o): Promise<CallToolResult> => {
          downloader.setup({...o, format: 'html'})
          try {
            // const cb: NodeToCodeFile[] = (await api.nodeToCode({...o, format: 'react-tailwind'})) || []
            const cb: NodeToCodeFile[] = (await api.nodeToCode(o)) || []
            await downloader.checkLocalAndDownload(cb)
            if (!cb) {
              return {
                content: [
                  {
                    type: 'text',
                    text: 'Failed to generate any code. Please check if the provided Figma node IDs are correct or if the nodes can be converted to code.',
                  },
                ],
              }
            }
    
            const files = Array.isArray(cb) ? cb : [cb]
    
            // Handle case when returned file array is empty
            if (files.length === 0) {
              return {
                content: [
                  {
                    type: 'text',
                    text: 'Conversion succeeded but no files were generated. Please check if the selected Figma nodes contain convertible content.',
                  },
                ],
              }
            }
    
            // Create file summary
            const summary = files.map((file, index) => `${index + 1}. ${file.path}`).join('\n')
            // If local path is specified, return save location information instead of detailed content
            if (o.localPath) {
              return {
                content: [
                  {
                    type: 'text',
                    text: `# Files Saved Locally
    
    ## Save Location
    ${o.localPath}
    
    ## Generated Files
    ${summary}. Convert the Tailwind to vanilla CSS if not already used in the codebase. Do not install any dependencies.`,
                  },
                ],
              }
            }
    
            // Create detailed file content (only when local path is not specified)
            const fileDetails = files
              .map((file, index) => {
                const fileExtension = file.path.split('.').pop() || ''
                return `## File ${index + 1}: ${file.path}\n\`\`\`${fileExtension}\n${file.content}\n\`\`\``
              })
              .join('\n\n')
    
            return {
              content: [
                {
                  type: 'text',
                  text: `# Generated Files Summary\n${summary}\n\n# File Details\n${fileDetails}. Convert the Tailwind to vanilla CSS if not already used in the codebase. Do not install any dependencies.`,
                },
              ],
            }
          } catch (error: any) {
            logger.error('Tool execution error:', error)
            return {
              content: [{type: 'text', text: `Error: ${error.message}`}],
            }
          }
        },
      )
    }
  • The tool execution handler. Sets up downloader, calls the F2C API via api.nodeToCode, downloads assets if localPath provided, formats response with file contents or paths.
        async (o): Promise<CallToolResult> => {
          downloader.setup({...o, format: 'html'})
          try {
            // const cb: NodeToCodeFile[] = (await api.nodeToCode({...o, format: 'react-tailwind'})) || []
            const cb: NodeToCodeFile[] = (await api.nodeToCode(o)) || []
            await downloader.checkLocalAndDownload(cb)
            if (!cb) {
              return {
                content: [
                  {
                    type: 'text',
                    text: 'Failed to generate any code. Please check if the provided Figma node IDs are correct or if the nodes can be converted to code.',
                  },
                ],
              }
            }
    
            const files = Array.isArray(cb) ? cb : [cb]
    
            // Handle case when returned file array is empty
            if (files.length === 0) {
              return {
                content: [
                  {
                    type: 'text',
                    text: 'Conversion succeeded but no files were generated. Please check if the selected Figma nodes contain convertible content.',
                  },
                ],
              }
            }
    
            // Create file summary
            const summary = files.map((file, index) => `${index + 1}. ${file.path}`).join('\n')
            // If local path is specified, return save location information instead of detailed content
            if (o.localPath) {
              return {
                content: [
                  {
                    type: 'text',
                    text: `# Files Saved Locally
    
    ## Save Location
    ${o.localPath}
    
    ## Generated Files
    ${summary}. Convert the Tailwind to vanilla CSS if not already used in the codebase. Do not install any dependencies.`,
                  },
                ],
              }
            }
    
            // Create detailed file content (only when local path is not specified)
            const fileDetails = files
              .map((file, index) => {
                const fileExtension = file.path.split('.').pop() || ''
                return `## File ${index + 1}: ${file.path}\n\`\`\`${fileExtension}\n${file.content}\n\`\`\``
              })
              .join('\n\n')
    
            return {
              content: [
                {
                  type: 'text',
                  text: `# Generated Files Summary\n${summary}\n\n# File Details\n${fileDetails}. Convert the Tailwind to vanilla CSS if not already used in the codebase. Do not install any dependencies.`,
                },
              ],
            }
          } catch (error: any) {
            logger.error('Tool execution error:', error)
            return {
              content: [{type: 'text', text: `Error: ${error.message}`}],
            }
          }
        },
  • Zod schema for tool inputs defining parameters like fileKey, node ids, output format, token, local save path, image options.
    {
      fileKey: z
        .string()
        .describe(
          'The Figma file identifier found in the file URL (e.g., https://www.figma.com/file/XXXXXXXXXXXX/). Extract the XXXXXXXXXXXX portion as the fileKey.',
        ),
      ids: z
        .string()
        .describe(
          'Comma-separated list of Figma node IDs for conversion. To obtain node IDs, select elements in Figma, right-click and select "Copy/Paste as" → "Copy ID".',
        ),
      format: z
        .enum(['html', 'react-cssmodules', 'react-tailwind'])
        .default('html')
        .describe(
          'Specify the output format: "html" generates semantic HTML/CSS, "react-cssmodules" creates React components with scoped CSS modules, "react-tailwind" produces React components with utility-first Tailwind classes.',
        ),
      personalToken: z
        .string()
        .optional()
        .describe(
          'Figma personal access token for API authentication.The parameters are not required when the tool is called.',
        ),
      localPath: z
        .string()
        .optional()
        .describe(
          'Absolute path for image asset storage. Directory will be created if non-existent. Path must follow OS-specific format without special character escaping. When set, all static resources will be saved to the images directory under this path.',
        ),
      imgFormat: z
        .enum(['png', 'jpg', 'svg'])
        .default('png')
        .describe(
          'Export format for image assets: "png" for lossless quality, "jpg" for compressed files, or "svg" for vector graphics.',
        ),
      scaleSize: z
        .number()
        .min(1)
        .max(4)
        .default(2)
        .describe(
          'Image export scale factor (1-4). Higher values yield better quality at the cost of larger file sizes.',
        ),
    },
  • Core helper function that builds query parameters and calls external F2C API (https://f2c-figma-api.yy.com/api/nodes) to convert Figma nodes to code files.
    async nodeToCode(o: NodeToCodeWithF2COptions): Promise<NodeToCodeFile[]> {
      const op = {
        fileKey: o.fileKey,
        nodeIds: o.ids,
        personal_token: o.personalToken || config.personalToken,
        option: {
          cssFramework: 'inlinecss',
          imgFormat: o.imgFormat || 'png',
          scaleSize: o.scaleSize || 2,
        },
        format: 'files',
        // format: 'allFiles',
      }
      if (o.format === 'react-cssmodules') {
        op.option.cssFramework = 'cssmodules'
      } else if (o.format === 'react-tailwind') {
        op.option.cssFramework = 'tailwindcss'
      }
      const url = this.opToUrl(`${this.f2cHost}/nodes`, op)
      return this.fetch(url, 'json', o.ideInfo || 'other')
    }
  • Downloader helper that replaces remote Figma image URLs in generated code with local files and saves the code files to localPath if provided.
    public async checkLocalAndDownload(files: NodeToCodeFile[]) {
      if (!this.op.localPath) return
    
      await Promise.all(
        files.map(async f => {
          f.content = await this.downLoadImageAndReplaceContent(f.content)
        }),
      )
    
      for (const file of files) {
        try {
          const savedPath = await this.saveContentToFile(file.content, file.path)
          logger.debug(`Successfully saved: ${savedPath}`)
        } catch (error) {
          logger.error(`Failed to save file ${file.path}:`, error)
        }
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions the transformation process but lacks details on behavioral traits such as rate limits, error handling, authentication requirements (though hinted by the 'personalToken' parameter), or output structure. This is inadequate for a tool with mutation-like behavior (conversion).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose in the first sentence, followed by specifics on extraction and transformation. It uses two concise sentences with zero wasted words, efficiently conveying the tool's functionality without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a conversion tool with no annotations and no output schema, the description is insufficient. It lacks details on behavioral aspects (e.g., authentication, rate limits), error cases, and the structure of the generated code. This leaves significant gaps for an AI agent to understand how to invoke and interpret results effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all four parameters. The description adds minimal value by mentioning the output formats ('HTML, React with CSS Modules, or React with Tailwind CSS'), which aligns with the 'format' parameter's enum, but does not provide additional semantics beyond what the schema already covers.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('convert Figma designs into code'), the resource ('specified Figma nodes'), and the transformation ('HTML, React with CSS Modules, or React with Tailwind CSS'). It distinguishes from sibling tools like 'figma_get_file_data' by focusing on code generation rather than data/metadata retrieval.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for automated design-to-code conversion but does not explicitly state when to use this tool versus alternatives like the sibling tools (e.g., 'figma_get_file_data' for raw data). No guidance on prerequisites or exclusions is provided, leaving the context somewhat open-ended.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

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/f2c-ai/f2c-mcp'

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