Nếu bạn đã quen với Claude Code hoặc Cursor, OpenCode là một lựa chọn đáng chú ý khác: một CLI agent mã nguồn mở (open-source), không phụ thuộc vào một nhà cung cấp LLM duy nhất, và hỗ trợ MCP (Model Context Protocol) gần như đầy đủ tính năng. Với các team không muốn "khóa" vào một vendor, hoặc muốn tự host/tùy biến agent theo pipeline nội bộ, OpenCode + Figma MCP là một combo đáng thử để biến design trên Figma thành code thật, thay vì chỉ nhìn ảnh mockup rồi đoán CSS.
Bài này đi thẳng vào phần thực chiến: cài đặt, cấu hình, chạy thử một ví dụ chuyển frame thành React component, và quan trọng không kém — những giới hạn thực tế bạn sẽ gặp khi dùng combo này trong dự án thật.
Installing and Connecting Figma MCP to OpenCode
OpenCode là một CLI agent, cài qua npm giống hầu hết tool trong hệ sinh thái Node hiện nay:
npm install -g opencode-ai
Sau khi cài xong, kiểm tra version để chắc chắn binary đã nằm trong PATH:
opencode --version
Chạy opencode trong thư mục project sẽ mở giao diện CLI tương tác (interactive), tương tự Claude Code. Nhưng trước khi gõ prompt đầu tiên, bạn cần khai báo MCP server cho Figma.
OpenCode đọc cấu hình MCP từ file opencode.json (hoặc opencode.jsonc nếu bạn muốn có comment trong JSON). File này có thể đặt ở project root (áp dụng riêng cho project đó) hoặc ở global config tại ~/.config/opencode/opencode.json (áp dụng cho mọi project bạn mở bằng OpenCode). Với hầu hết dev, mình khuyên để ở global config trước, sau đó override riêng theo project nếu cần.
Cấu hình remote MCP server cho Figma trông như sau:
{
"mcp": {
"figma": {
"type": "remote",
"url": "https://mcp.figma.com/mcp",
"enabled": true
}
}
}
Ba field quan trọng:
type: "remote"— báo cho OpenCode biết đây là MCP server chạy qua HTTP, không phải process local.url— endpoint MCP chính thức của Figma.enabled— bật/tắt server mà không cần xóa cấu hình, tiện khi bạn muốn tạm disable lúc debug.
Lần đầu tiên OpenCode gọi tool thuộc server figma, nó sẽ tự động trigger luồng OAuth: mở trình duyệt, yêu cầu bạn đăng nhập tài khoản Figma và cấp quyền truy cập. Sau khi authorize thành công, token được lưu lại (thường trong thư mục cấu hình local của OpenCode), nên các lần chạy sau không phải đăng nhập lại — trừ khi token hết hạn hoặc bạn revoke quyền truy cập từ phía Figma.
Ngoài remote MCP, nếu bạn đang chạy Figma desktop app với Dev Mode MCP server bật local (chạy qua stdio trên máy), OpenCode cũng hỗ trợ khai báo kiểu local process. Tuy nhiên trong thực tế, phần lớn team chọn remote endpoint vì không cần mở app Figma desktop chạy nền, không phụ thuộc máy cụ thể nào, và dễ dùng chung config giữa nhiều máy trong team (chỉ cần commit opencode.json vào repo).
Để xác nhận MCP server đã connect thành công, gõ prompt bất kỳ có nhắc tới Figma, ví dụ:
Liệt kê các tool MCP hiện có từ server figma
Nếu OpenCode trả về danh sách tool như get_code, get_metadata, get_screenshot... nghĩa là kết nối đã ổn.
Mẹo: Nếu OAuth flow không tự mở trình duyệt (hay gặp khi chạy OpenCode trên máy remote/SSH không có GUI), hãy copy URL đăng nhập được in ra terminal và mở thủ công trên máy có trình duyệt. Sau khi authorize xong, quay lại terminal — OpenCode sẽ tự nhận diện callback và tiếp tục.
Extracting Design Context and Generating Code from Figma Frames in OpenCode
Sau khi kết nối xong, câu hỏi tiếp theo là: OpenCode dùng Figma MCP như thế nào để biến một frame thành code? Server MCP của Figma expose một bộ tool cố định, và OpenCode agent tự quyết định gọi tool nào dựa trên nội dung prompt của bạn:
| Tool | Chức năng |
|---|---|
get_code |
Sinh code (React/HTML/CSS...) từ một node/frame cụ thể |
get_variable_defs |
Lấy định nghĩa design token (màu, spacing, typography...) đang dùng trong node |
get_screenshot |
Chụp ảnh render của node để agent "nhìn thấy" giao diện |
get_code_connect_map |
Lấy mapping giữa component Figma và component code thật (nếu team đã cấu hình Code Connect) |
get_metadata |
Lấy cây cấu trúc (structure tree) của node — tên layer, loại, node-id con |
create_design_system_rules |
Sinh file rule mô tả design system (token, quy ước đặt tên...) để agent tuân theo khi generate code |
Điểm mấu chốt: để các tool này hoạt động, bạn phải cung cấp link Figma có chứa node-id — tức là link bạn copy bằng cách right-click vào frame/layer trong Figma rồi chọn "Copy link to selection", không phải link file chung chung. Link dạng này có cấu trúc:
https://www.figma.com/design/abcXYZ123/My-Project?node-id=123-456
Phần node-id=123-456 chính là thứ MCP server dùng để xác định chính xác frame nào bạn đang muốn generate.
Một prompt thực tế gửi cho OpenCode có thể là:
Hãy lấy code cho frame này và convert thành React component dùng Tailwind CSS:
https://www.figma.com/design/abcXYZ123/My-Project?node-id=123-456
Khi nhận prompt này, OpenCode agent thường thực hiện một chuỗi tool call ngầm:
- Gọi
get_metadatađể hiểu cấu trúc layer bên trong frame (đôi khi bỏ qua bước này nếu frame nhỏ). - Gọi
get_codevới node-id tương ứng để lấy code khung (thường là JSX/HTML + style gần đúng). - Gọi
get_variable_defsđể lấy đúng giá trị token (màu, font-size, spacing) thay vì để LLM tự đoán màu hex. - Nếu cần xác nhận trực quan, gọi thêm
get_screenshotđể so sánh layout.
Việc get_variable_defs trả về token thật (ví dụ color.primary.500 = #2563EB) thay vì để model "bịa" màu gần đúng là lý do code sinh ra từ MCP thường khớp design hơn nhiều so với việc chỉ dán ảnh screenshot cho agent tự nhìn đoán.
Một câu hỏi hay gặp: OpenCode có tự tạo file component thật trong project không, hay chỉ trả về code trong chat? Câu trả lời: hoàn toàn phụ thuộc cách bạn prompt. Nếu bạn chỉ hỏi "cho tôi xem code", OpenCode sẽ chỉ in ra terminal. Nếu bạn yêu cầu rõ ràng "tạo file src/components/PricingCard.tsx với nội dung này", agent sẽ dùng tool file-system của chính OpenCode (write file) để ghi trực tiếp vào project — đây là điểm mạnh của một coding agent CLI so với việc dùng Figma plugin thuần túy.
Mẹo: Luôn prompt kèm stack cụ thể ("dùng Tailwind", "dùng CSS Modules", "dùng styled-components") thay vì để mặc định. Nếu không chỉ định,
get_codethường trả về inline style hoặc CSS thuần, và bạn sẽ phải tự refactor lại theo convention của project — mất công hơn là nói rõ ngay từ đầu.
Practical Example: Translating a Figma Frame to a React Component in OpenCode
Để cụ thể hóa, hãy đi qua một ví dụ thật: chuyển một frame "Pricing Card" trên Figma thành component React + CSS trong một project Vite/React có sẵn.
Giả sử bạn đã mở project bằng opencode trong terminal, và có link frame:
https://www.figma.com/design/xyzABC789/Landing-Page?node-id=88-210
Prompt gửi cho OpenCode:
Đây là frame "Pricing Card" trong design system của tôi:
https://www.figma.com/design/xyzABC789/Landing-Page?node-id=88-210
Hãy:
1. Lấy variable definitions (màu, spacing, font) từ frame này trước.
2. Generate component React tên PricingCard, dùng CSS Modules (file PricingCard.module.css riêng).
3. Dùng đúng token màu/spacing lấy được ở bước 1, không tự bịa giá trị hex.
4. Ghi 2 file vào src/components/PricingCard/: PricingCard.tsx và PricingCard.module.css.
5. Sau khi ghi xong, in ra đoạn code để tôi review.
OpenCode sẽ gọi get_metadata (hiểu cấu trúc: card có tiêu đề gói, giá, danh sách feature, nút CTA), gọi get_variable_defs (lấy token dạng color.surface.card = #FFFFFF, color.text.primary = #111827, spacing.md = 16px...), rồi gọi get_code cho node 88-210. Kết quả sinh ra thường có dạng như sau:
// src/components/PricingCard/PricingCard.tsx
import styles from "./PricingCard.module.css";
interface PricingCardProps {
planName: string;
price: string;
features: string[];
onSelect: () => void;
highlighted?: boolean;
}
export function PricingCard({
planName,
price,
features,
onSelect,
highlighted = false,
}: PricingCardProps) {
return (
<div className={`${styles.card} ${highlighted ? styles.cardHighlighted : ""}`}>
<h3 className={styles.planName}>{planName}</h3>
<p className={styles.price}>
{price}
<span className={styles.priceUnit}>/tháng</span>
</p>
<ul className={styles.featureList}>
{features.map((feature) => (
<li key={feature} className={styles.featureItem}>
{feature}
</li>
))}
</ul>
<button className={styles.ctaButton} onClick={onSelect}>
Chọn gói này
</button>
</div>
);
}
/* src/components/PricingCard/PricingCard.module.css */
.card {
display: flex;
flex-direction: column;
gap: 16px; /* spacing.md */
padding: 24px; /* spacing.lg */
background-color: #ffffff; /* color.surface.card */
border: 1px solid #e5e7eb; /* color.border.default */
border-radius: 12px;
}
.cardHighlighted {
border-color: #2563eb; /* color.primary.500 */
box-shadow: 0 8px 24px rgba(37, 99, 235, 0.12);
}
.planName {
font-size: 18px;
font-weight: 600;
color: #111827; /* color.text.primary */
}
.price {
font-size: 36px;
font-weight: 700;
color: #111827;
}
.priceUnit {
font-size: 14px;
font-weight: 400;
color: #6b7280; /* color.text.secondary */
}
.featureList {
list-style: none;
padding: 0;
display: flex;
flex-direction: column;
gap: 8px; /* spacing.sm */
}
.featureItem {
font-size: 14px;
color: #374151;
}
.ctaButton {
margin-top: 8px;
padding: 12px 16px;
background-color: #2563eb; /* color.primary.500 */
color: #ffffff;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
}
.ctaButton:hover {
background-color: #1d4ed8; /* color.primary.600 */
}
Điểm đáng chú ý: các comment /* color.primary.500 */ trong CSS không phải OpenCode "tự nhiên" thêm vào cho đẹp — đó là kết quả trực tiếp của việc agent gọi get_variable_defs trước và map giá trị hex về đúng tên token. Nếu design system của bạn đã export token ra file CSS variables (--color-primary-500: #2563EB), bạn có thể prompt thêm "dùng CSS variable từ file tokens.css thay vì hex trực tiếp" — OpenCode sẽ tự thay thế nếu bạn trỏ đúng file token đang có trong project.
Sau khi có code, đừng vội tin 100%: mở Storybook hoặc chạy dev server, so sánh trực tiếp với get_screenshot (bạn có thể yêu cầu OpenCode tự chụp và so sánh) để bắt các lệch nhỏ về padding, line-height — đây là bước review bắt buộc, không phải tùy chọn.
Mẹo: Với component có nhiều state (hover, disabled, active...), hãy prompt yêu cầu OpenCode liệt kê các variant có trong Figma (thường nằm ở các frame/layer riêng như "Card/Default", "Card/Highlighted") và generate prop tương ứng, thay vì chỉ lấy đúng 1 trạng thái đang hiển thị trên frame bạn gửi link.
Known Limitations and Workarounds for Figma MCP in OpenCode
OpenCode là tool mã nguồn mở phát triển rất nhanh, cộng đồng đóng góp nhiều, nhưng chính vì tốc độ release nhanh và không phải "chính chủ" của Figma hay của một vendor lớn kiểm soát toàn bộ vòng đời, nên có độ trễ nhất định trong việc theo kịp các tính năng MCP mới nhất — so với Claude Code (được Anthropic maintain sát với đặc tả MCP) thì một số edge case xử lý chưa mượt bằng. Dưới đây là các vấn đề thực tế hay gặp và cách xử lý.
1. get_code timeout hoặc tràn context window trên frame lớn
Đây là vấn đề phổ biến nhất. Một frame full-page (landing page, dashboard nhiều widget) có cấu trúc layer rất sâu — khi gọi thẳng get_code trên node gốc, MCP server phải serialize toàn bộ cây layer, kết quả trả về có thể rất lớn, khiến request timeout hoặc nội dung trả về vượt quá context window khả dụng của OpenCode, làm agent "quên" phần đầu prompt.
Workaround: đừng gọi get_code trực tiếp lên node cha lớn. Luôn bắt đầu bằng get_metadata để lấy cây cấu trúc trước:
Trước tiên, lấy metadata (cấu trúc cây) của frame này để tôi xem có những component con nào:
https://www.figma.com/design/xyzABC789/Landing-Page?node-id=1-1
Từ kết quả, xác định node-id của từng component con (ví dụ Header, Hero, PricingSection, Footer), rồi gọi get_code lần lượt cho từng node con:
opencode run "Lấy code cho node-id=88-210 (PricingSection) trong file này: https://www.figma.com/design/xyzABC789/Landing-Page"
Cách này vừa tránh timeout, vừa cho kết quả code sạch hơn vì mỗi lần agent chỉ tập trung vào một component, không bị nhiễu bởi context của cả trang.
2. Xử lý ảnh trả về từ get_screenshot chưa mượt
Một số phiên bản OpenCode xử lý ảnh (image content) trả về từ tool result chưa tối ưu bằng — đôi khi ảnh bị nén quá mức trước khi đưa vào context của model, khiến agent "nhìn" không rõ chi tiết nhỏ (icon, border radius tinh tế). Nếu thấy code sinh ra lệch nhiều so với ảnh gốc dù bạn đã gọi get_screenshot, đừng chỉ dựa vào screenshot — hãy kết hợp thêm get_variable_defs và get_metadata để có dữ liệu số chính xác (giá trị px, hex) thay vì để model đoán từ ảnh.
3. MCP request timeout mặc định quá ngắn cho frame phức tạp
Với các tổ chức có design system lớn, request tới get_code/get_variable_defs có thể mất nhiều thời gian hơn timeout mặc định của OpenCode. Nếu gặp lỗi timeout dù đã tách nhỏ node, tăng timeout trong cấu hình MCP:
{
"mcp": {
"figma": {
"type": "remote",
"url": "https://mcp.figma.com/mcp",
"enabled": true,
"timeout": 60000
}
}
}
(Giá trị tính bằng milliseconds; điều chỉnh theo thực tế — bắt đầu từ 60s thay vì default thường quanh 30s.)
4. Code Connect map không đầy đủ
Nếu team chưa cấu hình Code Connect (mapping giữa component Figma và component code thật), get_code_connect_map sẽ trả về rỗng hoặc không có gợi ý nào, khiến OpenCode tự sinh component mới thay vì tái sử dụng component đã có sẵn trong codebase (ví dụ đã có <Button> nhưng agent lại tạo <button> thuần). Workaround tạm thời: prompt rõ trong yêu cầu — liệt kê các component có sẵn cần tái sử dụng ("Dùng component Button đã có tại src/components/Button, đừng tạo mới") thay vì trông chờ MCP tự suy luận.
Nhìn chung, các giới hạn này không phải lỗi nghiêm trọng — chúng là hệ quả tự nhiên của việc kết hợp một CLI agent mã nguồn mở, phát triển nhanh, với một MCP server bên thứ ba có payload đôi khi rất lớn. Chỉ cần quen với thói quen "metadata trước, code sau, chia nhỏ node", trải nghiệm sẽ ổn định hơn nhiều.
Mẹo: Khi gặp lỗi lạ (timeout, tool result rỗng, hoặc agent trả lời không liên quan), việc đầu tiên nên làm là chạy
opencode --versionvà so với changelog trên GitHub repo của OpenCode — vì đây là tool cập nhật rất nhanh, nhiều bug về MCP thường đã được fix ở bản mới hơn chỉ sau vài ngày.