模块 1:Figma MCP Server 介绍

Figma MCP Server 是连接 Figma 设计工具与 AI 模型的桥梁。它基于 Model Context Protocol (MCP) 协议,让 AI 助手能够读取、理解和操作 Figma 设计文件,实现设计与开发的无缝协作。

什么是 MCP(Model Context Protocol)?

MCP 是一种标准化的通信协议,允许 AI 模型与外部工具和数据源进行交互。它定义了工具注册、调用和响应的格式,使 AI 助手能够安全地访问设计文件、生成代码、同步设计变更。

MCP 核心概念:

  • Server — 提供 MCP 服务的进程,注册工具并处理请求
  • Client — 连接 MCP Server 的 AI 助手(如 Claude、Cursor)
  • Tool — Server 暴露的可调用功能(如读取文件、导出图片)
  • Resource — Server 提供的数据源(如 Figma 文件节点)
  • Transport — 通信方式(stdio、HTTP/SSE)

Figma MCP Server 的核心能力

设计文件读取

读取 Figma 文件的完整节点树,包括图层结构、样式属性、文本内容、布局信息等。

组件与样式提取

自动提取 Figma 组件库中的设计 Token(颜色、字体、间距),生成代码变量和设计系统文档。

设计转代码

将 Figma 设计稿自动转换为前端代码(React、Vue、HTML/CSS),保持设计还原度。

图片与资源导出

导出 Figma 中的图片资源(PNG、SVG、PDF),支持多种分辨率和格式配置。

架构概览

# Figma MCP Server 架构示意
#
#  ┌─────────────┐     MCP Protocol      ┌──────────────────┐
#  │  AI Client   │ ◄──────────────────► │  Figma MCP Server │
#  │  (Claude/    │    stdio / SSE        │                  │
#  │   Cursor)    │                       │  ┌─────────────┐ │
#  └─────────────┘                       │  │  Tool Registry│ │
#                                        │  └─────────────┘ │
#                                        │  ┌─────────────┐ │
#                                        │  │  Figma API   │ │
#                                        │  │  Client      │ │
#                                        │  └──────┬──────┘ │
#                                        └─────────┼────────┘
#                                                  │
#                                        ┌─────────▼────────┐
#                                        │   Figma REST API  │
#                                        │   api.figma.com   │
#                                        └──────────────────┘

Figma MCP Server 采用客户端-服务器架构:AI Client 通过 MCP 协议与 Server 通信,Server 内部通过 Figma REST API 与 Figma 平台交互。Server 注册了一系列工具(Tool),AI Client 可以根据用户请求调用相应的工具来操作 Figma 文件。

快速安装与配置

# 方式 1:使用 npm 安装
npm install -g @anthropic/figma-mcp-server

# 方式 2:使用 npx 直接运行(推荐)
npx @anthropic/figma-mcp-server

# 获取 Figma Personal Access Token
# 1. 登录 Figma → Settings → Account → Personal access tokens
# 2. 点击 "Generate new token"
# 3. 复制 token(只会显示一次)

# 配置 MCP Server
export FIGMA_ACCESS_TOKEN="figd_your_token_here"

# 在 Claude Desktop 配置中添加 MCP Server
# 编辑 ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "figma": {
      "command": "npx",
      "args": ["-y", "@anthropic/figma-mcp-server"],
      "env": {
        "FIGMA_ACCESS_TOKEN": "figd_your_token_here"
      }
    }
  }
}

# 在 Cursor 编辑器中配置
# Settings → Features → MCP → Add Server
# Name: Figma MCP
# Command: npx -y @anthropic/figma-mcp-server
# Env: FIGMA_ACCESS_TOKEN=figd_xxx

安装步骤:1) 通过 npm 全局安装或使用 npx 直接运行;2) 在 Figma 设置中生成 Personal Access Token;3) 将 token 配置为环境变量;4) 在 AI 客户端(Claude Desktop 或 Cursor)的配置文件中添加 MCP Server 定义。配置完成后,AI 助手就能访问你的 Figma 文件了。

验证安装

# 测试 MCP Server 是否正常运行
# 在 Claude Desktop 或 Cursor 中发送以下请求

# 1. 列出可用工具
# AI 会自动发现 MCP Server 注册的所有工具

# 2. 读取 Figma 文件
# 粘贴 Figma 文件链接:
# https://www.figma.com/design/XXXXXX/My-Design-File

# 3. 典型对话示例
# 用户:读取这个 Figma 文件的结构
# AI 会调用 figma_get_file 工具获取文件信息

# 使用 curl 直接测试 Figma API 连通性
curl -H "X-Figma-Token: figd_your_token" \
  "https://api.figma.com/v1/me"
# 应返回你的 Figma 用户信息

# 测试读取文件
curl -H "X-Figma-Token: figd_your_token" \
  "https://api.figma.com/v1/files/FILE_KEY" \
  | jq '.name, .lastModified'

验证安装的方法:1) 用 curl 测试 Figma API 连通性,确保 token 有效;2) 在 AI 客户端中粘贴 Figma 文件链接,观察 AI 是否能读取文件内容;3) 检查 AI 是否识别到 figma_* 系列工具。如果 curl 返回用户信息,说明 token 配置正确。

MCP 协议的核心组件是什么?

模块 2:核心概念 — Figma API 与 MCP 工具

本模块将深入探讨 Figma API 的核心概念、MCP 工具的注册机制和 Figma 文件的数据结构。

Figma 文件数据结构

// Figma 文件的 JSON 结构(简化版)
{
  "name": "Mobile App Design",
  "lastModified": "2024-01-15T10:30:00Z",
  "thumbnailUrl": "https://...",
  "document": {
    "id": "0:0",
    "name": "Document",
    "type": "DOCUMENT",
    "children": [
      {
        "id": "0:1",
        "name": "Page 1",
        "type": "CANVAS",
        "children": [
          {
            "id": "1:2",
            "name": "Login Screen",
            "type": "FRAME",
            "absoluteBoundingBox": {
              "x": 0, "y": 0,
              "width": 375, "height": 812
            },
            "children": [
              {
                "id": "1:3",
                "name": "Header",
                "type": "FRAME",
                "layoutMode": "HORIZONTAL",
                "fills": [{"type": "SOLID", "color": {"r": 1, "g": 1, "b": 1}}],
                "children": [
                  {
                    "id": "1:4",
                    "name": "Logo",
                    "type": "IMAGE",
                    "absoluteBoundingBox": {"width": 120, "height": 40}
                  },
                  {
                    "id": "1:5",
                    "name": "Title",
                    "type": "TEXT",
                    "characters": "Welcome Back",
                    "style": {
                      "fontFamily": "Inter",
                      "fontSize": 24,
                      "fontWeight": 600
                    }
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}

Figma 文件是一个嵌套的节点树。顶层是 DOCUMENT,包含多个 CANVAS(页面),每个页面包含 FRAME(画框)和其他节点。关键属性:type 节点类型(FRAME/TEXT/IMAGE/RECTANGLE 等);absoluteBoundingBox 绝对位置和尺寸;fills/strokes 填充和描边样式;layoutMode 布局模式(AUTO LAYOUT)。

MCP 工具注册与调用

// Figma MCP Server 注册的工具列表(典型)
const tools = [
  {
    name: "figma_get_file",
    description: "获取完整的 Figma 文件结构",
    inputSchema: {
      type: "object",
      properties: {
        fileKey: { type: "string", description: "Figma 文件 URL 中的 key" },
        depth: { type: "number", description: "返回的节点树深度" }
      },
      required: ["fileKey"]
    }
  },
  {
    name: "figma_get_node",
    description: "获取特定节点的详细信息",
    inputSchema: {
      type: "object",
      properties: {
        fileKey: { type: "string" },
        nodeId: { type: "string", description: "节点 ID,如 '1:2'" },
        geometry: { type: "string", enum: ["paths", "none"] }
      },
      required: ["fileKey", "nodeId"]
    }
  },
  {
    name: "figma_get_styles",
    description: "获取文件中定义的所有样式(颜色、字体、效果)",
    inputSchema: {
      type: "object",
      properties: {
        fileKey: { type: "string" }
      },
      required: ["fileKey"]
    }
  },
  {
    name: "figma_get_components",
    description: "获取文件中的所有组件和组件变体",
    inputSchema: {
      type: "object",
      properties: {
        fileKey: { type: "string" }
      },
      required: ["fileKey"]
    }
  },
  {
    name: "figma_export_image",
    description: "导出节点为图片(PNG/SVG/PDF)",
    inputSchema: {
      type: "object",
      properties: {
        fileKey: { type: "string" },
        nodeId: { type: "string" },
        format: { type: "string", enum: ["png", "jpg", "svg", "pdf"] },
        scale: { type: "number", description: "导出倍率(1, 2, 3, 4)" }
      },
      required: ["fileKey", "nodeId"]
    }
  },
  {
    name: "figma_search_nodes",
    description: "在文件中搜索特定名称或类型的节点",
    inputSchema: {
      type: "object",
      properties: {
        fileKey: { type: "string" },
        query: { type: "string", description: "搜索关键词" },
        nodeType: { type: "string", description: "节点类型过滤" }
      },
      required: ["fileKey", "query"]
    }
  }
];

MCP 工具通过 inputSchema 定义参数格式,AI 客户端根据 schema 自动构建调用。核心工具包括:文件读取(get_file)、节点查询(get_node)、样式提取(get_styles)、组件管理(get_components)、图片导出(export_image)和节点搜索(search_nodes)。每个工具都有清晰的参数说明,AI 可以自主选择合适的工具。

Figma API 认证与权限

认证方式:Figma API 使用 Personal Access Token(PAT)或 OAuth 2.0 进行认证。MCP Server 通常使用 PAT,因为它更简单且适合个人使用。Token 权限继承自生成它的用户账户。

# Figma API 请求格式
# 所有 API 请求需要携带 X-Figma-Token 头

# 获取文件信息
GET https://api.figma.com/v1/files/{file_key}
Headers: X-Figma-Token: figd_xxxxx

# 获取特定节点
GET https://api.figma.com/v1/files/{file_key}/nodes?ids=1:2,1:3
Headers: X-Figma-Token: figd_xxxxx

# 导出图片
GET https://api.figma.com/v1/images/{file_key}?ids=1:2&format=png&scale=2
Headers: X-Figma-Token: figd_xxxxx

# 获取样式
GET https://api.figma.com/v1/files/{file_key}/styles
Headers: X-Figma-Token: figd_xxxxx

# 获取组件
GET https://api.figma.com/v1/files/{file_key}/components
Headers: X-Figma-Token: figd_xxxxx

# API 速率限制
# Personal Access Token: 120 requests/minute
# 超过限制返回 429 Too Many Requests
# 响应头 X-RateLimit-Remaining 显示剩余请求数

Figma REST API 的基本使用:所有请求需要携带 X-Figma-Token 请求头。文件 key 从 Figma URL 中提取(/design/ 后面的字符串)。API 有速率限制(120次/分钟),MCP Server 会自动处理限流和重试。

Auto Layout 解析

// Figma Auto Layout 属性映射到 CSS
const layoutMapping = {
  // 布局方向
  "layoutMode": {
    "HORIZONTAL": "flex-direction: row;",
    "VERTICAL": "flex-direction: column;",
  },
  // 主轴对齐
  "primaryAxisAlignItems": {
    "MIN": "justify-content: flex-start;",
    "CENTER": "justify-content: center;",
    "MAX": "justify-content: flex-end;",
    "SPACE_BETWEEN": "justify-content: space-between;",
  },
  // 交叉轴对齐
  "counterAxisAlignItems": {
    "MIN": "align-items: flex-start;",
    "CENTER": "align-items: center;",
    "MAX": "align-items: flex-end;",
  },
  // 间距
  "itemSpacing": (val) => `gap: ${val}px;`,
  // 内边距
  "paddingLeft": (val) => `padding-left: ${val}px;`,
  "paddingRight": (val) => `padding-right: ${val}px;`,
  "paddingTop": (val) => `padding-top: ${val}px;`,
  "paddingBottom": (val) => `padding-bottom: ${val}px;`,
  // 尺寸
  "layoutSizingHorizontal": {
    "FIXED": "",  // 使用 absoluteBoundingBox.width
    "HUG": "width: fit-content;",
    "FILL": "width: 100%;",
  },
  "layoutSizingVertical": {
    "FIXED": "",
    "HUG": "height: fit-content;",
    "FILL": "height: 100%;",
  }
};

// 使用示例:将 Figma 节点转换为 CSS
function figmaNodeToCSS(node) {
  let css = `.${node.name.toLowerCase().replace(/\s+/g, '-')} {\n`;

  if (node.layoutMode) {
    css += `  display: flex;\n`;
    css += `  ${layoutMapping.layoutMode[node.layoutMode]}\n`;

    if (node.primaryAxisAlignItems) {
      css += `  ${layoutMapping.primaryAxisAlignItems[node.primaryAxisAlignItems]}\n`;
    }
    if (node.counterAxisAlignItems) {
      css += `  ${layoutMapping.counterAxisAlignItems[node.counterAxisAlignItems]}\n`;
    }
    if (node.itemSpacing) {
      css += `  gap: ${node.itemSpacing}px;\n`;
    }
  }

  if (node.absoluteBoundingBox) {
    const bb = node.absoluteBoundingBox;
    css += `  width: ${bb.width}px;\n`;
    css += `  height: ${bb.height}px;\n`;
  }

  css += `}\n`;
  return css;
}

Figma Auto Layout 与 CSS Flexbox 一一对应,这是设计转代码的核心映射规则:layoutMode 对应 flex-directionprimaryAxisAlignItems 对应 justify-contentcounterAxisAlignItems 对应 align-itemsitemSpacing 对应 gap。理解这个映射关系,就能准确还原 Figma 设计。

Figma 文件的节点树结构是什么?

模块 3:实践应用 — 设计转代码

本模块将演示如何使用 Figma MCP Server 在实际项目中实现设计转代码、设计系统提取和自动化工作流。

实战 1:Figma 设计稿转 React 组件

// AI 助手通过 MCP 读取 Figma 后生成的 React 组件
// 基于自动提取的设计参数

import React from 'react';
import styled from 'styled-components';

// 设计 Token(从 Figma 样式自动提取)
const tokens = {
  colors: {
    primary: '#6366F1',      // figma fill color
    primaryHover: '#4F46E5',
    background: '#FFFFFF',
    textPrimary: '#1A1A2E',
    textSecondary: '#6B7280',
    border: '#E5E7EB',
  },
  typography: {
    heading: {
      fontFamily: 'Inter',       // figma font
      fontSize: '24px',          // figma fontSize
      fontWeight: 600,
      lineHeight: '32px',
    },
    body: {
      fontFamily: 'Inter',
      fontSize: '14px',
      fontWeight: 400,
      lineHeight: '20px',
    },
  },
  spacing: {
    xs: '4px',     // figma padding/gap
    sm: '8px',
    md: '16px',
    lg: '24px',
    xl: '32px',
  },
  borderRadius: {
    sm: '6px',     // figma cornerRadius
    md: '8px',
    lg: '12px',
    full: '9999px',
  },
};

// LoginScreen 组件(直接从 Figma FRAME 转换)
const LoginScreen = () => {
  return (
    <Container>
      <Header>
        <Logo src="/logo.svg" alt="Logo" />
        <Title>Welcome Back</Title>
        <Subtitle>Sign in to continue</Subtitle>
      </Header>

      <Form>
        <InputField label="Email" type="email" placeholder="your@email.com" />
        <InputField label="Password" type="password" placeholder="Enter password" />
        <RememberRow>
          <Checkbox label="Remember me" />
          <ForgotLink>Forgot password?</ForgotLink>
        </RememberRow>
        <Button>Sign In</Button>
      </Form>

      <Footer>
        <span>Don't have an account?</span>
        <SignUpLink>Sign Up</SignUpLink>
      </Footer>
    </Container>
  );
};

// 样式组件(从 Figma Auto Layout 映射)
const Container = styled.div`
  display: flex;                    /* layoutMode: VERTICAL */
  flex-direction: column;
  align-items: center;              /* counterAxisAlignItems: CENTER */
  justify-content: space-between;   /* primaryAxisAlignItems: SPACE_BETWEEN */
  padding: 48px 24px;               /* paddingTop/Bottom/Left/Right */
  gap: 32px;                        /* itemSpacing */
  width: 375px;                     /* absoluteBoundingBox.width */
  height: 812px;                    /* absoluteBoundingBox.height */
  background: ${tokens.colors.background};
`;

设计转代码的关键步骤:1) 从 Figma 提取设计 Token(颜色、字体、间距)作为 CSS 变量;2) 识别 Figma 的 FRAME 层次结构,映射为 React 组件树;3) 将 Auto Layout 属性转换为 Flexbox CSS;4) 保持命名与 Figma 图层名一致。这样生成的代码高度还原设计稿,且易于维护。

实战 2:设计系统自动提取

"""从 Figma 自动提取设计系统"""
import requests
import json

class FigmaDesignSystemExtractor:
    def __init__(self, token: str, file_key: str):
        self.token = token
        self.file_key = file_key
        self.headers = {"X-Figma-Token": token}
        self.base_url = "https://api.figma.com/v1"

    def extract_all(self) -> dict:
        """提取完整设计系统"""
        return {
            "colors": self.extract_colors(),
            "typography": self.extract_typography(),
            "spacing": self.extract_spacing(),
            "effects": self.extract_effects(),
            "components": self.extract_components(),
        }

    def extract_colors(self) -> list:
        """提取颜色 Token"""
        url = f"{self.base_url}/files/{self.file_key}/styles"
        resp = requests.get(url, headers=self.headers)
        styles = resp.json().get("meta", {}).get("styles", [])

        colors = []
        for style in styles:
            if style.get("style_type") == "FILL":
                # 获取样式的具体节点信息
                node_url = f"{self.base_url}/files/{self.file_key}/nodes"
                node_url += f"?ids={style['node_id']}"
                node_resp = requests.get(node_url, headers=self.headers)
                node_data = node_resp.json()["nodes"][style["node_id"]]

                fills = node_data["document"].get("fills", [])
                for fill in fills:
                    if fill["type"] == "SOLID":
                        c = fill["color"]
                        hex_color = "#{:02X}{:02X}{:02X}".format(
                            int(c["r"] * 255),
                            int(c["g"] * 255),
                            int(c["b"] * 255),
                        )
                        colors.append({
                            "name": style["name"],
                            "hex": hex_color,
                            "rgba": f"rgba({int(c['r']*255)}, {int(c['g']*255)}, {int(c['b']*255)}, {fill.get('opacity', 1)})",
                        })
        return colors

    def extract_typography(self) -> list:
        """提取排版 Token"""
        url = f"{self.base_url}/files/{self.file_key}/styles"
        resp = requests.get(url, headers=self.headers)
        styles = resp.json().get("meta", {}).get("styles", [])

        typography = []
        for style in styles:
            if style.get("style_type") == "TEXT":
                node_url = f"{self.base_url}/files/{self.file_key}/nodes"
                node_url += f"?ids={style['node_id']}"
                node_resp = requests.get(node_url, headers=self.headers)
                node_data = node_resp.json()["nodes"][style["node_id"]]

                text_style = node_data["document"].get("style", {})
                typography.append({
                    "name": style["name"],
                    "fontFamily": text_style.get("fontFamily", "Inter"),
                    "fontSize": text_style.get("fontSize", 14),
                    "fontWeight": text_style.get("fontWeight", 400),
                    "lineHeight": f"{text_style.get('lineHeightPx', 20)}px",
                    "letterSpacing": f"{text_style.get('letterSpacing', 0)}px",
                })
        return typography

    def generate_css_variables(self, design_system: dict) -> str:
        """生成 CSS 变量文件"""
        css = ":root {\n"

        for color in design_system.get("colors", []):
            name = color["name"].lower().replace(" ", "-")
            css += f"  --color-{name}: {color['hex']};\n"

        for typo in design_system.get("typography", []):
            name = typo["name"].lower().replace(" ", "-")
            css += f"  --font-{name}-size: {typo['fontSize']}px;\n"
            css += f"  --font-{name}-weight: {typo['fontWeight']};\n"
            css += f"  --font-{name}-family: '{typo['fontFamily']}';\n"

        css += "}\n"
        return css

# 使用示例
extractor = FigmaDesignSystemExtractor(
    token="figd_your_token",
    file_key="XXXXXX"
)
design_system = extractor.extract_all()
css = extractor.generate_css_variables(design_system)
print(css)

设计系统提取流程:1) 通过 Figma API 获取文件中的所有样式定义;2) 按类型分类(FILL 颜色、TEXT 排版、EFFECT 效果);3) 从节点详情中提取具体属性值;4) 将 Figma 的 RGB(0-1)范围转换为十六进制颜色;5) 生成标准的 CSS 变量文件。这样可以将设计变更自动同步到代码中。

实战 3:与 AI 工作流集成

实际使用场景:在 Cursor 或 Claude Desktop 中,直接粘贴 Figma 文件链接,AI 会自动调用 MCP Server 读取设计,然后根据你的要求生成代码、提取样式或分析设计规范。

# 典型 AI 对话工作流

# === 场景 1:生成组件代码 ===
# 用户输入:
# "读取这个 Figma 文件 https://figma.com/design/XXX/Button
#  并生成对应的 React 组件,使用 Tailwind CSS"

# AI 执行步骤:
# 1. 调用 figma_get_file(fileKey="XXX") 获取文件结构
# 2. 调用 figma_get_node(fileKey="XXX", nodeId="1:5") 获取按钮节点
# 3. 解析 Auto Layout、填充、圆角等属性
# 4. 生成 React + Tailwind 代码

# === 场景 2:设计审查 ===
# 用户输入:
# "检查这个页面的无障碍性,对比度是否达标"

# AI 执行步骤:
# 1. 读取页面所有 TEXT 节点
# 2. 提取文字颜色和背景颜色
# 3. 计算 WCAG 对比度比率
# 4. 报告不达标的文字组合

# === 场景 3:响应式设计生成 ===
# 用户输入:
# "将这个移动端设计转为响应式布局,支持平板和桌面"

# AI 执行步骤:
# 1. 读取移动端 FRAME(375x812)
# 2. 识别布局结构(导航、内容区、底部栏)
# 3. 生成 Tailwind 响应式断点代码
# 4. sm/md/lg/xl 各断点的布局适配

# === 场景 4:设计规范文档 ===
# 用户输入:
# "为这个设计文件生成设计规范文档"

# AI 执行步骤:
# 1. 调用 figma_get_styles 获取所有样式
# 2. 调用 figma_get_components 获取所有组件
# 3. 整理为 Markdown 格式的规范文档
# 4. 包含颜色调色板、字体规范、间距系统、组件列表

AI 工作流的核心优势是自然语言交互:你只需描述需求(中文或英文),AI 会自动选择合适的 MCP 工具、解析 Figma 数据、生成代码或文档。四种高频场景:组件代码生成、设计审查(无障碍/对比度)、响应式适配、设计规范文档。MCP 让这些操作无需手动导出或复制。

批量导出与资源管理

"""批量导出 Figma 资源"""
import requests
import os
import base64

class FigmaExporter:
    def __init__(self, token: str):
        self.token = token
        self.headers = {"X-Figma-Token": token}

    def export_all_icons(self, file_key: str, output_dir: str = "icons"):
        """导出文件中所有图标为 SVG"""
        os.makedirs(output_dir, exist_ok=True)

        # 获取文件中所有组件
        url = f"https://api.figma.com/v1/files/{file_key}/components"
        resp = requests.get(url, headers=self.headers)
        components = resp.json().get("meta", {}).get("components", [])

        icon_ids = []
        icon_names = {}
        for comp in components:
            if "icon" in comp["name"].lower() or "Icon" in comp["name"]:
                icon_ids.append(comp["node_id"])
                icon_names[comp["node_id"]] = comp["name"]

        # 批量导出为 SVG
        if icon_ids:
            ids_param = ",".join(icon_ids)
            export_url = f"https://api.figma.com/v1/images/{file_key}"
            export_url += f"?ids={ids_param}&format=svg"

            export_resp = requests.get(export_url, headers=self.headers)
            images = export_resp.json().get("images", {})

            for node_id, image_url in images.items():
                if image_url:
                    svg_resp = requests.get(image_url)
                    name = icon_names.get(node_id, node_id.replace(":", "-"))
                    safe_name = name.replace("/", "-").replace(" ", "-")
                    filepath = os.path.join(output_dir, f"{safe_name}.svg")
                    with open(filepath, "w") as f:
                        f.write(svg_resp.text)
                    print(f"Exported: {filepath}")

    def export_design_tokens(self, file_key: str, output_path: str = "tokens.json"):
        """导出设计 Token 为 JSON"""
        url = f"https://api.figma.com/v1/files/{file_key}/styles"
        resp = requests.get(url, headers=self.headers)
        styles = resp.json().get("meta", {}).get("styles", [])

        tokens = {"colors": {}, "typography": {}, "effects": {}}
        for style in styles:
            category = {
                "FILL": "colors",
                "TEXT": "typography",
                "EFFECT": "effects",
            }.get(style.get("style_type"), "other")

            tokens[category][style["name"]] = {
                "id": style["node_id"],
                "description": style.get("description", ""),
            }

        with open(output_path, "w") as f:
            json.dump(tokens, f, indent=2)
        print(f"Tokens exported to {output_path}")

# 使用
exporter = FigmaExporter("figd_your_token")
exporter.export_all_icons("FILE_KEY", "src/assets/icons")
exporter.export_design_tokens("FILE_KEY", "design-tokens.json")

批量导出流程:1) 通过 Components API 获取所有组件列表;2) 过滤出图标类组件(按名称匹配);3) 使用 Images API 批量导出为 SVG;4) 保存到项目的资源目录。设计 Token 导出则是将 Figma 样式转换为 JSON 格式,方便在代码中引用。这种自动化导出避免了手动逐一导出的低效操作。

Figma MCP Server 在设计转代码中的核心价值是什么?

模块 4:进阶主题 — 自定义 MCP Server

本模块将涵盖如何构建自定义 Figma MCP Server、实现高级工具、处理缓存和优化性能。

构建自定义 MCP Server

// custom-figma-mcp-server.ts
// 基于 @modelcontextprotocol/sdk 构建自定义 MCP Server

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

// Figma API 客户端
class FigmaClient {
  private token: string;
  private cache = new Map<string, { data: any; expiry: number }>();

  constructor(token: string) {
    this.token = token;
  }

  private async request(endpoint: string): Promise<any> {
    const cacheKey = endpoint;
    const cached = this.cache.get(cacheKey);
    if (cached && cached.expiry > Date.now()) {
      return cached.data;
    }

    const response = await fetch(`https://api.figma.com/v1${endpoint}`, {
      headers: { "X-Figma-Token": this.token },
    });

    if (!response.ok) {
      throw new Error(`Figma API error: ${response.status}`);
    }

    const data = await response.json();
    this.cache.set(cacheKey, { data, expiry: Date.now() + 300000 }); // 5min cache
    return data;
  }

  async getFile(fileKey: string, depth?: number) {
    const d = depth ? `&depth=${depth}` : "";
    return this.request(`/files/${fileKey}?${d}`);
  }

  async getNode(fileKey: string, nodeId: string) {
    return this.request(`/files/${fileKey}/nodes?ids=${nodeId}`);
  }

  async exportImage(fileKey: string, nodeId: string, format: string = "png", scale: number = 2) {
    return this.request(`/images/${fileKey}?ids=${nodeId}&format=${format}&scale=${scale}`);
  }
}

// 创建 MCP Server
const figmaToken = process.env.FIGMA_ACCESS_TOKEN!;
const figmaClient = new FigmaClient(figmaToken);

const server = new Server(
  { name: "custom-figma-mcp", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// 注册工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "figma_read_file",
      description: "读取 Figma 文件结构",
      inputSchema: {
        type: "object",
        properties: {
          fileKey: { type: "string", description: "Figma 文件 key" },
          depth: { type: "number", description: "节点树深度" },
        },
        required: ["fileKey"],
      },
    },
    {
      name: "figma_to_react",
      description: "将 Figma 节点转换为 React 组件代码",
      inputSchema: {
        type: "object",
        properties: {
          fileKey: { type: "string" },
          nodeId: { type: "string", description: "要转换的 FRAME 节点 ID" },
          framework: {
            type: "string",
            enum: ["react", "vue", "html"],
            description: "目标框架",
          },
        },
        required: ["fileKey", "nodeId"],
      },
    },
    {
      name: "figma_extract_tokens",
      description: "提取设计 Token(颜色、字体、间距)",
      inputSchema: {
        type: "object",
        properties: {
          fileKey: { type: "string" },
          format: { type: "string", enum: ["css", "scss", "json", "tailwind"] },
        },
        required: ["fileKey"],
      },
    },
  ],
}));

// 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "figma_read_file": {
      const data = await figmaClient.getFile(args!.fileKey as string, args!.depth as number);
      return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
    }

    case "figma_to_react": {
      const nodeData = await figmaClient.getNode(
        args!.fileKey as string, args!.nodeId as string
      );
      // 在这里实现设计转代码逻辑
      const code = generateReactComponent(nodeData, args!.framework as string);
      return { content: [{ type: "text", text: code }] };
    }

    case "figma_extract_tokens": {
      const fileData = await figmaClient.getFile(args!.fileKey as string);
      const tokens = extractDesignTokens(fileData, args!.format as string);
      return { content: [{ type: "text", text: tokens }] };
    }

    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

// 启动 Server
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

自定义 MCP Server 的核心步骤:1) 使用 @modelcontextprotocol/sdk 创建 Server 实例;2) 通过 ListToolsRequestSchema 注册自定义工具;3) 通过 CallToolRequestSchema 处理工具调用;4) 实现 Figma API 客户端(含缓存);5) 使用 StdioTransport 与 AI 客户端通信。这个架构可以灵活扩展任意数量的自定义工具。

性能优化策略

请求缓存

对 Figma API 响应进行本地缓存(TTL 5分钟),避免重复请求。文件结构变化不频繁,缓存命中率通常很高。

节点树裁剪

使用 depth 参数控制返回的节点深度。大型设计文件可能有数千个节点,按需加载减少传输和解析开销。

批量操作

使用逗号分隔的 IDs 批量获取节点和导出图片,减少 API 调用次数。Figma 支持单次请求最多 100 个节点。

增量更新

记录上次读取的 lastModified 时间戳,只在文件有更新时重新获取完整数据,节省带宽和时间。

错误处理与最佳实践

// MCP Server 错误处理最佳实践

// 1. 速率限制处理
async function figmaRequestWithRetry(url: string, maxRetries = 3): Promise<any> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });

    if (response.status === 429) {
      // 读取 Retry-After 头
      const retryAfter = response.headers.get("Retry-After");
      const delay = retryAfter ? parseInt(retryAfter) * 1000 : 60000;
      console.warn(`Rate limited. Retrying after ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
      continue;
    }

    if (!response.ok) {
      throw new Error(`Figma API error: ${response.status} ${response.statusText}`);
    }

    return response.json();
  }
  throw new Error("Max retries exceeded");
}

// 2. 工具调用错误返回
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  try {
    const result = await handleToolCall(request.params);
    return {
      content: [{ type: "text", text: JSON.stringify(result) }],
    };
  } catch (error) {
    // MCP 错误格式
    return {
      content: [{
        type: "text",
        text: `Error: ${error.message}`,
      }],
      isError: true,
    };
  }
});

// 3. 输入验证
function validateFileKey(fileKey: string): boolean {
  return /^[a-zA-Z0-9]+$/.test(fileKey) && fileKey.length > 10;
}

function validateNodeId(nodeId: string): boolean {
  return /^\d+:\d+$/.test(nodeId);
}

// 4. 安全注意事项
// - FIGMA_ACCESS_TOKEN 不要硬编码,使用环境变量
// - 限制可访问的文件范围(如有需要)
// - 日志中不要记录完整 token
// - 对大文件响应进行截断,避免超出 MCP 消息大小限制

生产级 MCP Server 需要健壮的错误处理:速率限制时按 Retry-After 头等待重试;工具调用失败时返回 isError: true 标记;输入参数需要严格验证(文件 key 格式、节点 ID 格式);安全方面,token 必须通过环境变量注入,不记录敏感信息。

自定义 MCP Server 的核心组件是什么?

模块 5:总结与综合练习

本模块将总结 Figma MCP Server 的关键知识并提供综合练习巩固所学。

课程知识回顾

核心知识图谱:

  • 模块 1:MCP 协议概念、Figma MCP Server 能力、安装配置
  • 模块 2:Figma API 数据结构、MCP 工具机制、Auto Layout 映射
  • 模块 3:设计转代码、设计系统提取、AI 工作流集成
  • 模块 4:自定义 MCP Server 开发、性能优化、错误处理

综合练习:构建完整的设计到代码流水线

"""综合练习:完整的 Figma → 代码流水线"""
import json
import os

class DesignToCodePipeline:
    """端到端设计转代码流水线"""

    def __init__(self, figma_token: str):
        self.token = figma_token
        self.design_tokens = {}
        self.components = []

    def step1_read_design(self, file_key: str, frame_id: str) -> dict:
        """步骤 1:读取 Figma 设计数据"""
        print(f"[Step 1] Reading Figma design: {file_key}/{frame_id}")
        # 在实际实现中,这里调用 Figma MCP Server
        return {
            "name": "DashboardPage",
            "type": "FRAME",
            "layout": {
                "mode": "VERTICAL",
                "padding": {"top": 24, "right": 24, "bottom": 24, "left": 24},
                "gap": 16,
                "width": 1440,
                "height": 900,
            },
            "children": [
                {
                    "name": "Header",
                    "type": "FRAME",
                    "layout": {"mode": "HORIZONTAL", "gap": 12},
                    "children": [
                        {"name": "Logo", "type": "IMAGE", "width": 32, "height": 32},
                        {"name": "NavLinks", "type": "TEXT", "characters": "Home About Contact"},
                    ]
                },
                {
                    "name": "Content",
                    "type": "FRAME",
                    "layout": {"mode": "HORIZONTAL", "gap": 24},
                    "children": [
                        {"name": "Sidebar", "type": "FRAME", "width": 240, "height": 800},
                        {"name": "MainArea", "type": "FRAME", "width": 1152, "height": 800},
                    ]
                }
            ]
        }

    def step2_extract_tokens(self, design_data: dict) -> dict:
        """步骤 2:提取设计 Token"""
        print("[Step 2] Extracting design tokens...")
        tokens = {
            "colors": {
                "primary": "#6366F1",
                "background": "#F9FAFB",
                "surface": "#FFFFFF",
                "text": "#111827",
                "textSecondary": "#6B7280",
                "border": "#E5E7EB",
            },
            "spacing": {
                "xs": "4px", "sm": "8px", "md": "16px",
                "lg": "24px", "xl": "32px", "2xl": "48px",
            },
            "borderRadius": {"sm": "6px", "md": "8px", "lg": "12px"},
            "fontSize": {
                "xs": "12px", "sm": "14px", "base": "16px",
                "lg": "18px", "xl": "20px", "2xl": "24px",
            },
        }
        self.design_tokens = tokens
        return tokens

    def step3_generate_component(self, node: dict, framework: str = "react") -> str:
        """步骤 3:生成组件代码"""
        print(f"[Step 3] Generating {framework} component for: {node['name']}")

        if framework == "react":
            return self._generate_react(node)
        elif framework == "vue":
            return self._generate_vue(node)
        elif framework == "html":
            return self._generate_html(node)
        return ""

    def _generate_react(self, node: dict, indent: int = 2) -> str:
        """递归生成 React 组件"""
        space = " " * indent
        name = node["name"].replace(" ", "")

        if node["type"] == "TEXT":
            return f'{space}<span>{node.get("characters", "")}</span>'

        layout = node.get("layout", {})
        css_props = []
        css_props.append("display: flex")
        if layout.get("mode") == "VERTICAL":
            css_props.append("flex-direction: column")
        if layout.get("gap"):
            css_props.append(f'gap: {layout["gap"]}px')

        children_code = ""
        for child in node.get("children", []):
            children_code += "\n" + self._generate_react(child, indent + 2)

        return f'{space}<div style={{{ {"; ".join(css_props)} }}}>{children_code}\n{space}</div>'

    def step4_generate_styles(self) -> str:
        """步骤 4:生成样式文件"""
        print("[Step 4] Generating CSS variables...")
        css = ":root {\n"
        for category, values in self.design_tokens.items():
            css += f"  /* {category} */\n"
            for name, value in values.items():
                css += f"  --{category}-{name}: {value};\n"
            css += "\n"
        css += "}\n"
        return css

    def run(self, file_key: str, frame_id: str):
        """执行完整流水线"""
        # Step 1: 读取设计
        design = self.step1_read_design(file_key, frame_id)

        # Step 2: 提取 Token
        tokens = self.step2_extract_tokens(design)

        # Step 3: 生成组件
        component_code = self.step3_generate_component(design, "react")

        # Step 4: 生成样式
        styles = self.step4_generate_styles()

        # 输出结果
        output = {
            "component.tsx": component_code,
            "tokens.css": styles,
            "tokens.json": json.dumps(tokens, indent=2),
        }

        print("\n=== Generated Files ===")
        for filename, content in output.items():
            print(f"\n--- {filename} ---")
            print(content[:500])

        return output

# 运行流水线
pipeline = DesignToCodePipeline("figd_your_token")
pipeline.run("FILE_KEY", "1:2")

这个综合练习展示了完整的设计到代码流水线:Step 1 通过 MCP 读取 Figma 设计数据;Step 2 提取设计 Token(颜色、间距、字号);Step 3 递归遍历节点树生成 React 组件代码;Step 4 生成 CSS 变量文件。这是 Figma MCP Server 最核心的应用场景,理解这个流水线就掌握了设计转代码的精髓。

进阶学习资源

MCP 协议规范

modelcontextprotocol.io — 学习 MCP 协议的完整规范、SDK 使用和最佳实践。

Figma Plugin API

了解 Figma 插件开发,可以扩展 MCP Server 的能力,如实时监听设计变更。

Design Tokens 规范

W3C Design Tokens Community Group 制定的设计 Token 标准格式,实现跨工具互操作。

CI/CD 集成

将 Figma MCP Server 集成到 CI/CD 管道中,实现设计变更自动同步代码。

Figma MCP Server 的学习要点有哪些?