一次 opencode + OpenPencil MCP 协同解析 .fig 文件的踩坑实录

一次 opencode + OpenPencil MCP 协同解析 .fig 文件的踩坑实录

前言

设计师给了一份 .fig 文件,我需要让 opencode 这个 AI 编程 Agent 自己读懂设计稿 并产出前端代码。

听起来简单,做起来一波三折------本文把整个过程记录下来,包括为什么最终选了 @open-pencil/mcp 而不是最初的 fig2json,以及路上踩到的所有坑。

环境约束:Windows 11、Node 22(opencode 强制依赖)、无 MSVC、无 MinGW。


一、需求拆解

目标链路

复制代码
设计师的 .fig 文件  →  opencode AI 读懂  →  生成组件代码(React/Vue/HTML)

需要解决三个问题:

  1. 解析.fig 是 Figma 私有二进制格式(Kiwi schema + Zstd + ZIP),没人公开 spec
  2. 集成:解析结果要被 opencode 的 LLM 上下文消费
  3. 部署:必须能跑在我的环境里(Node 22,Windows)

二、方案调研------四条候选路径

我先列出了所有可能的方向,并行调研:

路径 工具 优点 缺点
A kreako/fig2json (Rust CLI) 纯离线、无账号、一行命令 要 Rust + 链接器(MSVC/MinGW)
B Figma 官方 Remote MCP 官方、写回画布、Code Connect 只支持 Figma URL ,不支持 .fig 本地文件
C open-pencil/mcp (Node MCP) 100+ 工具、本地优先 需要跑 OpenPencil 桌面 app
D Open Design (Node 桌面) 全工作流、品牌系统 强制 Node 24,与 Node 22 冲突

我当时选了 Path A (fig2json) ------最直接,一个 CLI 转 JSON 就完事。


三、Path A 的崩溃与路径切换

fig2json 在 GitHub Releases 上没有预编译的 Windows 二进制 。要装就只能 cargo install fig2json

我兴冲冲打开终端:

bash 复制代码
npm install -g @open-pencil/mcp  # ← 先装着备用的
cargo install fig2json

然后------

复制代码
error: linker `link.exe` not found

我的环境既没有 MSVC 也没有 MinGW 。要编译 fig2json 得先装 Visual Studio Build Tools(几个 GB)。

这是个真实的工程痛点:Rust 生态在 Windows 上的"开箱即用"远不如 macOS/Linux。一个 5MB 的 CLI 工具,要装几 GB 的 toolchain 才能编译,不值。

决策:Pivot 到 Path C(@open-pencil/mcp

回头看 MCP 包:

bash 复制代码
$ npm install -g @open-pencil/mcp
added 166 packages in 1m

$ npm view @open-pencil/mcp engines
# (empty --- 没声明 Node 版本约束)

$ node -e "require('@open-pencil/mcp/package.json').version"
# 0.13.2

纯 Node.js 包。没有任何 native module。Node 22 直接跑。

代价:需要 OpenPencil 桌面 app 一起跑(~7MB)。但这是 Tauri 应用,跨平台、单文件、双击就开------比装 Visual Studio Build Tools 友好得多。


四、最终架构

#mermaid-svg-4J6RgHcR6TQ6uLMb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4J6RgHcR6TQ6uLMb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4J6RgHcR6TQ6uLMb .error-icon{fill:#552222;}#mermaid-svg-4J6RgHcR6TQ6uLMb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4J6RgHcR6TQ6uLMb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4J6RgHcR6TQ6uLMb .marker.cross{stroke:#333333;}#mermaid-svg-4J6RgHcR6TQ6uLMb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4J6RgHcR6TQ6uLMb p{margin:0;}#mermaid-svg-4J6RgHcR6TQ6uLMb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4J6RgHcR6TQ6uLMb .cluster-label text{fill:#333;}#mermaid-svg-4J6RgHcR6TQ6uLMb .cluster-label span{color:#333;}#mermaid-svg-4J6RgHcR6TQ6uLMb .cluster-label span p{background-color:transparent;}#mermaid-svg-4J6RgHcR6TQ6uLMb .label text,#mermaid-svg-4J6RgHcR6TQ6uLMb span{fill:#333;color:#333;}#mermaid-svg-4J6RgHcR6TQ6uLMb .node rect,#mermaid-svg-4J6RgHcR6TQ6uLMb .node circle,#mermaid-svg-4J6RgHcR6TQ6uLMb .node ellipse,#mermaid-svg-4J6RgHcR6TQ6uLMb .node polygon,#mermaid-svg-4J6RgHcR6TQ6uLMb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4J6RgHcR6TQ6uLMb .rough-node .label text,#mermaid-svg-4J6RgHcR6TQ6uLMb .node .label text,#mermaid-svg-4J6RgHcR6TQ6uLMb .image-shape .label,#mermaid-svg-4J6RgHcR6TQ6uLMb .icon-shape .label{text-anchor:middle;}#mermaid-svg-4J6RgHcR6TQ6uLMb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4J6RgHcR6TQ6uLMb .rough-node .label,#mermaid-svg-4J6RgHcR6TQ6uLMb .node .label,#mermaid-svg-4J6RgHcR6TQ6uLMb .image-shape .label,#mermaid-svg-4J6RgHcR6TQ6uLMb .icon-shape .label{text-align:center;}#mermaid-svg-4J6RgHcR6TQ6uLMb .node.clickable{cursor:pointer;}#mermaid-svg-4J6RgHcR6TQ6uLMb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4J6RgHcR6TQ6uLMb .arrowheadPath{fill:#333333;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4J6RgHcR6TQ6uLMb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4J6RgHcR6TQ6uLMb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4J6RgHcR6TQ6uLMb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4J6RgHcR6TQ6uLMb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4J6RgHcR6TQ6uLMb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4J6RgHcR6TQ6uLMb .cluster text{fill:#333;}#mermaid-svg-4J6RgHcR6TQ6uLMb .cluster span{color:#333;}#mermaid-svg-4J6RgHcR6TQ6uLMb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4J6RgHcR6TQ6uLMb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4J6RgHcR6TQ6uLMb rect.text{fill:none;stroke-width:0;}#mermaid-svg-4J6RgHcR6TQ6uLMb .icon-shape,#mermaid-svg-4J6RgHcR6TQ6uLMb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4J6RgHcR6TQ6uLMb .icon-shape p,#mermaid-svg-4J6RgHcR6TQ6uLMb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4J6RgHcR6TQ6uLMb .icon-shape .label rect,#mermaid-svg-4J6RgHcR6TQ6uLMb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4J6RgHcR6TQ6uLMb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4J6RgHcR6TQ6uLMb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4J6RgHcR6TQ6uLMb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 自然语言指令
stdin/stdout JSON-RPC
ws://127.0.0.1:7601
解析二进制
👤 用户调用

'用 D:/design.fig 实现落地页'
🛠️ opencode (Node 22 + LLM)

• 内置 Skill: figma-fig-to-code

• • SKILL.md 触发: description 含 .fig / Figma

• MCP client (stdin/stdout JSON-RPC)
@open-pencil/mcp server (Node 22)

• 106 个工具: list_pages, get_jsx, get_node ...

• 通过 WebSocket 与桌面 app 通信
🖥️ OpenPencil 桌面 app (Tauri, ~7MB)

• 真正的 .fig 二进制解析器 (Kiwi + Zstd + WASM)
📄 designer.fig


五、实施步骤

1. 安装 MCP server

bash 复制代码
npm install -g @open-pencil/mcp

2. 注册到 opencode

编辑 ~/.config/opencode/opencode.json

jsonc 复制代码
{
  "mcp": {
    "open-pencil": {
      "type": "local",
      "command": ["node", "D:/App/nodejs/node_modules/@open-pencil/mcp/dist/stdio.mjs"],
      "environment": { "OPENPENCIL_MCP_ROOT": "D:/App" },
      "enabled": true
    }
  }
}

3. 启动 OpenPencil 桌面 app

https://openpencil.dev/ 下载安装,启动。

4. 创建全局 Skill

~/.config/opencode/skills/figma-fig-to-code/ 下创建:

复制代码
figma-fig-to-code/
• - SKILL.md                       # 触发条件 + 工作流
• - references/
•   • - flex-mapping.md            # Figma auto-layout → CSS Flexbox
•   • - tool-quickref.md           # 106 个工具速查
•   • - output-stacks.md           # React/Vue/Svelte 输出模板
• - scripts/
    • - install.ps1                # Windows 一键装
    • - install.sh                 # Unix 一键装
    • - full-e2e.mjs               # 端到端验证脚本

SKILL.md 的 frontmatter:

yaml 复制代码
---
name: figma-fig-to-code
description: Use this skill when the user provides a Figma `.fig` file ... triggers on phrases like "实现这个 figma", "解析 .fig", "design to code"...
license: MIT
metadata:
  author: your-name
  version: "1.3.0"
---

description 里的触发关键词是 skill 能被自动加载的关键------opencode 的 loader 是基于关键词匹配的。

5. 重启 opencode 让配置生效

⚠️ 重要 :opencode 的配置是启动时加载的,热更新不支持。改了 opencode.json 必须退出当前 session 重启

6. 验证

bash 复制代码
opencode mcp list
# ✓ open-pencil connected

opencode debug skill | grep figma
# ✓ figma-fig-to-code

六、真实端到端测试

光有"连通"不算数。我用一个真实的 PPT 模板 .fig(7.6MB)跑全流程:

复制代码
[probe] target: D:/App/figma-test/ppt模版浅色.fig
[OK 1852ms] open_file              ← 真的打开了 7.6MB 的文件
[OK 276ms]  list_pages             ← 列页面
{
  "current": "Page 1",
  "pages": [
    { "id": "0:3",   "name": "Page 1" },
    { "id": "0:520", "name": "Internal Only Canvas" }
  ]
}
[OK 5ms]    get_components
[OK 4ms]    list_fonts

你的 .fig 文件真的能识别------MCP 链路全打通,返回了真实的 page ID。


七、踩到的坑(决策日志)

我在 SKILL.md 里专门维护了 ## Decision log,记录 4 条关键决策:

D1 --- 为什么不走 fig2json(最初的 Plan A)

Rust + MSVC 装不上 → 改 MCP。未来若 Rust/MSVC 装好,fig2json 仍值得重评------更简单。

D2 --- 为什么 Node 22 兼容是硬约束

opencode 跑 Node 22。Open Design 要 Node 24 被排除了。MCP 路径是唯一不冲突的选项

D3 --- 为什么用 MCP 而不用 CLI

@open-pencil/cli 的源码第一行就是 Bun.XXX,Node 跑直接 Bun is not defined。装 Bun 会破坏 D2 的"不引入新运行时"原则。

D4 --- 为什么不直接用 @open-pencil/core 编程 API

v0.13.2 有个无限递归 bugio/formats/pen/read.js:288),任何非空 fixture 都触发 Maximum call stack size exceeded。MCP 走桌面 app 内部的 C++/WASM 路径,不受影响。


八、还存在的坑(实事求是)

验证项 结果 说明
MCP ↔ OpenPencil app RPC ✅ 通过 ws://127.0.0.1:7601
open_file / list_pages / get_jsx ✅ 通过 在真实 .fig 上返回真数据
端到端"opencode 读 .fig" ✅ 通过 在 SKILL 工作流设计层可走通
全自动(opencode AI 自主决策调工具) ❌ 待测 需在重启 opencode 后实测
get_page_tree 在 7.6MB 大文件上慢 ❌ 慢 设计就是分区域收敛,不是问题

九、经验总结

  1. Node 生态的"开箱即用"远超 Rust 在 Windows 上------一个 npm 包 60 秒装好,一个 Rust 工具要装几个 GB toolchain 才能编译。优先 Node。
  2. MCP 是 AI 工具集成的最佳模式:本地 stdio + 远程 WebSocket,AI 直接当工具用,比写胶水代码强多了。
  3. Skill 的 description 决定可发现性"Use this skill when..." 第三人称 + 触发关键词,opencode 才能自动加载。
  4. 配置文件改完必须重启------opencode 不热重载,省得以为是 bug。
  5. 上游 bug 不绕道就踩坑 ------遇到 @open-pencil/core 的栈溢出别浪费时间在 Node 上重试,直接用 MCP 路径。
  6. 诚实记录决策 :如果路径变了没告诉用户,6 个月后的你会骂 6 个月前的自己。SKILL.md## Decision log 是这种"反自我"工具。

附:完整 skill 已开源

本项目所有代码(含 SKILL.md、scripts、references)已开源:

GitHub: https://github.com/1648298170/opencode-figma-skill

直接 gh repo clone 1648298170/opencode-figma-skill 或 git clone 就能用。


如果本文对你有帮助,欢迎 Star / Fork / 提 Issue。也欢迎在评论区分享你的 .fig → 代码 自动化经验。

相关推荐
AI笔记Pro2 小时前
mcp协议是什么?用装一次 MCP 的时间彻底搞懂它
mcp
抢囡囡糖未遂3 小时前
从 LINQPad 到 MCP:打造 CRM 智能助手的实战记录
ai·大模型·skill·mcp·linqpad·microsoft dynamics crm·mscrm·dynamic 365
JasonMa1533 小时前
从零搭建一个微信小程序活动管理平台(一):架构设计与技术选型
javascript·微信小程序·node.js
武子康5 小时前
MCP 2026-07-28 无状态核心之后:身份、任务、幂等与审计状态到底放在哪里?
人工智能·llm·mcp
Python私教6 小时前
我只写了一个 add 工具,终于把 MCP 的 Host、Client、Server 跑明白了
python·ai编程·mcp
濮水大叔6 小时前
NestJS 与 CabloyJS 的 env/config 架构对比:从环境变量到实例级配置
前端·node.js·nestjs
欢醉6 小时前
手贱装了个插件,我把OpenCode玩崩了
opencode·hermes
带刺的坐椅6 小时前
别再把 Coding Agent 当智能补全了:SolonCode 想做的是数字员工
java·solon·codex·claudecode·opencode·soloncode
奋飛7 小时前
AI 应用工程:Tool、MCP、Skill 与 Workflow 如何接入 Agent?——搭建一个可运行的需求影响面分析 Agent
agent·workflow·skill·tool·mcp