一次 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 → 代码 自动化经验。

相关推荐
用户931456355668 小时前
别再写胶水代码了!用MCP多服务器架构让AI同时操控地图、浏览器、文件系统
mcp
嘟嘟071713 小时前
从浏览器到 hello world:Docker + nginx 反向代理 + Node 最小服务器一次讲清
docker·容器·node.js
FungLeo13 小时前
Node 后端实战 · 老系统数据迁移怎么不出乱子?V1→V2 重构实战与 3 个生产坑
node.js·serverless·数据库迁移·d1 数据库
来日方长。。。。long14 小时前
生产级MCP落地指南:FastMCP与官方MCP SDK的选型、架构与实战
架构·mcp
今日无bug14 小时前
Tool Use 工具调用 —— 让LLM缸中大脑突破物理限制的3个阶段
node.js·llm·agent
To_OC1 天前
装完 ESLint 它一声不吭?我还以为代码写得多好
后端·node.js·eslint
FungLeo1 天前
Node 后端实战 · 列表查询到底怎么写?一个通用 DSL 封装,过滤分页排序一次搞定
数据库·node.js·serverless·dsl 封装·通用列表查询
_codemonster1 天前
npm run dev 是在开发模式运行,怎么在生产环境运行
前端·npm·node.js
leoZ2311 天前
Vue3 还原一个企业级后台-08-API注册管理
目标检测·机器学习·分类·状态模式·迁移学习·集成学习·figma
csdn_aspnet1 天前
开源人工智能编码代理列表
人工智能·continue·cline·goose·aider·opencode·openhands