sapo-storybook-mcp
v1.0.1
Published
MCP server đọc Storybook (bất kỳ) để xem thiết kế UI và hỗ trợ viết Playwright automation test
Maintainers
Readme
sapo-storybook-mcp
MCP server đọc Storybook (bất kỳ static build nào) để một AI agent (Codex / Kiro / Claude / Gemini) có thể:
- Xem danh sách màn hình / component đã thiết kế trong Storybook.
- Xem giao diện thực tế của từng story (ARIA snapshot + screenshot).
- Trích selector và scaffold Playwright automation test trực tiếp từ DOM story đã render.
Trỏ tới Storybook nào là do bạn cấu hình qua biến môi trường STORYBOOK_BASE_URL (bắt buộc).
Yêu cầu
- Node.js >= 18 (đã test trên Node 22).
- Kết nối mạng tới host Storybook của bạn (giá trị
STORYBOOK_BASE_URL). - Playwright Chromium (tự cài qua
postinstall).
Cấu hình (biến môi trường)
| Biến | Bắt buộc | Ý nghĩa |
|------|:---:|-----------|
| STORYBOOK_BASE_URL | ✓ | Thư mục chứa index.html, iframe.html, index.json của Storybook (không cần dấu / cuối). Thiếu biến này server sẽ báo lỗi và không chạy. |
| STORYBOOK_SCREENSHOT_DIR | ✗ | Nơi lưu file PNG chụp story (mặc định ./screenshots). |
Các tool
| Tool | Cần browser? | Chức năng |
|------|:---:|-----------|
| storybook_list_stories | ✗ | Liệt kê story từ index.json, lọc theo title/name, có phân trang. |
| storybook_search_stories | ✗ | Tìm story fuzzy theo title/name/id. |
| storybook_get_story_tree | ✗ | Dựng lại cây sidebar (Category → Page → các variant/màn) từ title. Xem một folder có những page nào, mỗi page có những màn nào (Đang tải, Mặc định, Biến thể, Báo lỗi, Toast...). |
| storybook_get_story_url | ✗ | Trả về URL iframe (dùng cho page.goto) và URL manager (UI đầy đủ). |
| storybook_get_story_snapshot | ✓ | Render story → ARIA snapshot (YAML). Hiểu bố cục + viết getByRole. |
| storybook_get_story_selectors | ✓ | Render story → danh mục buttons/links/inputs/checkboxes/comboboxes/cột bảng kèm Playwright locator gợi ý. |
| storybook_detect_components | ✓ | Render story → phát hiện component thiết kế màn hình đang dùng (TextField, Checkbox, IndexTable/DataGrid, SegmentedControl, AppFrame...) qua dấu vết DOM, kèm đối chiếu catalog Components/Pattern. |
| storybook_get_story_screenshot | ✓ | Render story → ảnh PNG trả inline để model phân tích. Mặc định không ghi file; chỉ lưu khi truyền save: true hoặc save_path. |
| storybook_generate_playwright_test | ✓ | Render + phân tích DOM → sinh sẵn file spec @playwright/test. |
| storybook_open_session | ✓ (headed) | Mở cửa sổ browser hiện hình tại 1 story, giữ phiên để người dùng thao tác tay. |
| storybook_read_current_state | ✓ (headed) | Đọc trạng thái live hiện tại sau khi người dùng thao tác (snapshot / selectors / screenshot). Gọi lặp lại nhiều lần. |
| storybook_close_session | ✓ (headed) | Đóng cửa sổ session. |
Luồng human-in-the-loop (cho nghiệp vụ mà layout đổi theo tương tác, vd chọn "Lý do thanh toán" → hiện field khác): open_session → bạn tự thao tác trong cửa sổ → read_current_state (lặp lại sau mỗi bước) → close_session. Các tool này dùng browser headed (hiện hình), khác với các tool còn lại chạy headless ngầm.
Tất cả tool đều readOnly (không sửa gì ngoài việc lưu file screenshot).
Quy trình dùng điển hình
storybook_search_stories(hoặclist) để tìmstory_id.storybook_get_story_screenshot/storybook_get_story_snapshotđể xem thiết kế.storybook_get_story_selectorsđể lấy locator ổn định.storybook_generate_playwright_testđể sinh khung test, rồi hoàn thiện cácTODO.
Chi tiết từng tool
Ký hiệu: 🟢 không cần browser (đọc index.json) · 🔵 render headless ngầm · 🟠 headed (hiện cửa sổ).
🟢 storybook_list_stories
Liệt kê story từ index.json.
- Input:
filter?(chuỗi con của title/name, vd"Phiếu thu"),limit?(1–500, mặc định 100),offset?(phân trang),response_format?(markdown|json). - Output: danh sách story gom theo title, mỗi story có
name+id. JSON kèmtotal/has_more/next_offset. - Khi nào dùng: cần liệt kê nhanh/lọc phẳng theo từ khoá. Muốn thấy cây phân cấp thì dùng
get_story_tree.
🟢 storybook_search_stories
Tìm story kiểu fuzzy, xếp hạng theo mức khớp trên title + name + id.
- Input:
query(nhiều từ được tách và cộng điểm),limit?(1–100, mặc định 20),response_format?. - Output: danh sách story xếp theo độ liên quan, kèm
id. - Khi nào dùng: chỉ nhớ mang máng tên màn (vd
"đang tải loading","permission").
🟢 storybook_get_story_tree
Dựng lại cây sidebar (Category → … → Page → các variant/màn) từ trường title.
- Input:
path?(chuỗi con của title để giới hạn nhánh, vd"Tiền mặt/Phiếu thu","Tạo mới"),include_variants?(mặc địnhtrue;false= chỉ xem folder),response_format?. - Output: cây thụt lề — folder (📁) kèm số story; page (📄) liệt kê từng variant +
id. - Khi nào dùng: muốn biết một folder có những page nào, mỗi page có bao nhiêu màn/trạng thái (Đang tải, Mặc định, Biến thể, Báo lỗi, Toast...). Đây là tool khám phá cấu trúc chính.
🟢 storybook_get_story_url
Đổi story_id → URL.
- Input:
story_id,response_format?. - Output:
iframe_url(dùng chopage.gototrong Playwright — render trần, không có chrome Storybook) vàmanager_url(mở UI Storybook đầy đủ cho người xem). - Khi nào dùng: cần URL để nhúng vào test hoặc mở tay trên trình duyệt.
🔵 storybook_get_story_snapshot
Render story ngầm → trả ARIA snapshot (YAML).
- Input:
story_id,viewport?(mobile|tablet|desktop|wide). - Output: cây accessibility (roles, tên, heading, bảng, nút, input) đúng như render; kèm cảnh báo nếu story lỗi render.
- Khi nào dùng: hiểu bố cục màn hình và viết locator theo
getByRole/getByText. Rẻ token, là nguồn phân tích chính (thường không cần chụp ảnh).
🔵 storybook_get_story_selectors
Render story ngầm → danh mục element tương tác kèm Playwright locator gợi ý.
- Input:
story_id,viewport?,response_format?. - Output: buttons/links/inputs/checkboxes/comboboxes + cột bảng (header +
id) + số dòng + breakpoint phát hiện được. Ưu tiên locator theo role/placeholder, fallback theoidcột ổn định (vdtotal_amount,posted_date). - Khi nào dùng: cần locator sẵn để dán vào test.
🔵 storybook_detect_components
Render story ngầm → phát hiện component thiết kế mà màn hình đang dùng.
- Input:
story_id,viewport?,response_format?. - Output: danh sách component (TextField, Checkbox, Select, RadioButton, IndexTable/DataGrid, SegmentedControl, AppFrame, Link, Portal...) kèm bằng chứng (id
UI*/AppFrame*,data-*) và số lần xuất hiện; kèm catalog Components/Pattern từ Storybook và raw signals. - Khi nào dùng: hiểu màn hình được ghép từ component nào để tái sử dụng khi build màn tương tự, hoặc audit.
- Lưu ý: heuristic dựa trên DOM đã render, không phải import graph từ source.
🔵 storybook_get_story_screenshot
Render story ngầm → ảnh PNG.
- Input:
story_id,viewport?,full_page?(mặc địnhtrue),save?(mặc địnhfalse),save_path?(truyền vào ⇒ tự bật save). - Output: ảnh inline để model xem; mặc định không ghi đĩa — chỉ lưu khi
save:true/save_path. - Khi nào dùng: chỉ khi cần nhìn giao diện (bố cục, màu, style). Phân tích cấu trúc/viết test thì dùng snapshot/selectors sẽ rẻ hơn.
🔵 storybook_generate_playwright_test
Render + phân tích DOM → sinh sẵn file spec @playwright/test.
- Input:
story_id,viewport?,test_title?. - Output: code TypeScript:
page.goto(iframe_url)+ assertion theo heading/button/input/cột bảng thực tế + stub tương tác (click, search) kèmTODO. - Khi nào dùng: khởi tạo nhanh test cho một màn; sau đó tự hoàn thiện các
TODO.
🟠 storybook_open_session (headed)
Mở cửa sổ browser hiện hình tại một story và giữ phiên.
- Input:
story_id,viewport?. - Output: xác nhận đã mở. Cửa sổ hiện trên máy chạy MCP; giữ nguyên tới khi
close_session. - Khi nào dùng: bắt đầu luồng human-in-the-loop. Chỉ 1 phiên tại một thời điểm; gọi lại sẽ điều hướng lại cùng cửa sổ. Cần máy có màn hình.
🟠 storybook_read_current_state (headed)
Đọc trạng thái live hiện tại của cửa sổ session (sau khi người dùng vừa thao tác).
- Input:
mode?(snapshotmặc định |selectors|screenshot),full_page?(cho screenshot),response_format?(cho selectors). - Output: ARIA snapshot / danh mục locator / ảnh PNG của màn hình hiện tại. Gọi lặp lại nhiều lần sau mỗi bước.
- Khi nào dùng: sau khi tự chọn "Lý do thanh toán", mở modal, điền form... để AI đọc layout mới. Yêu cầu đã
open_session.
🟠 storybook_close_session (headed)
Đóng cửa sổ session.
- Input: không có.
- Output: xác nhận. Gọi được cả khi chưa mở phiên.
Thêm MCP vào AI client
Server chạy qua stdio, phân phối trên npm với tên sapo-storybook-mcp. Cách chạy chuẩn:
npx -y sapo-storybook-mcp@latestvới biến môi trường STORYBOOK_BASE_URL trỏ tới Storybook của bạn. Lần chạy đầu npx tải package + Chromium (~260MB) nên hơi lâu; các lần sau dùng cache.
Chạy bản local (khi phát triển):
git clonerepo →npm install && npm run build→ dùngcommand: "node",args: ["/duong-dan-tuyet-doi/dist/index.js"]thay chonpx.
Trong tất cả ví dụ dưới, thay https://your-storybook-host/path/to/storybook bằng URL Storybook thật.
Codex CLI
Sửa ~/.codex/config.toml, thêm:
[mcp_servers.storybook]
command = "npx"
args = ["-y", "sapo-storybook-mcp@latest"]
env = { STORYBOOK_BASE_URL = "https://your-storybook-host/path/to/storybook" }Kiểm tra: codex mcp list. Restart phiên Codex sau khi thêm.
Kiro CLI
Bằng lệnh:
kiro-cli mcp add --name storybook --command npx \
--args -y --args sapo-storybook-mcp@latest \
--env STORYBOOK_BASE_URL=https://your-storybook-host/path/to/storybook \
--scope global --forceCờ: --scope global (~/.kiro/settings/mcp.json) hoặc workspace (.kiro/settings/mcp.json); --agent <name> để gắn cho 1 agent; --force ghi đè nếu trùng.
Hoặc sửa tay ~/.kiro/settings/mcp.json:
{
"mcpServers": {
"storybook": {
"command": "npx",
"args": ["-y", "sapo-storybook-mcp@latest"],
"env": {
"STORYBOOK_BASE_URL": "https://your-storybook-host/path/to/storybook"
},
"timeout": 120000
}
}
}Kiểm tra: kiro-cli mcp list, hoặc trong phiên chat gõ /mcp (trạng thái) / /tools. Thêm xong cần restart phiên Kiro.
Claude Code CLI
Dùng -- để ngăn cách flag của claude với lệnh chạy server.
claude mcp add storybook --scope user \
--env STORYBOOK_BASE_URL=https://your-storybook-host/path/to/storybook \
-- npx -y sapo-storybook-mcp@latestCờ: --scope = local (mặc định, chỉ project hiện tại) · user (mọi project) · project (ghi .mcp.json chia sẻ trong repo); --env/-e KEY=VALUE lặp lại cho nhiều biến; --transport mặc định stdio (đúng cho server này).
Kiểm tra: claude mcp list, claude mcp get storybook; gỡ: claude mcp remove storybook.
Claude Desktop
Sửa ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) rồi khởi động lại app:
{
"mcpServers": {
"storybook": {
"command": "npx",
"args": ["-y", "sapo-storybook-mcp@latest"],
"env": {
"STORYBOOK_BASE_URL": "https://your-storybook-host/path/to/storybook"
}
}
}
}Gemini CLI
Sửa ~/.gemini/settings.json, thêm:
{
"mcpServers": {
"storybook": {
"command": "npx",
"args": ["-y", "sapo-storybook-mcp@latest"],
"env": {
"STORYBOOK_BASE_URL": "https://your-storybook-host/path/to/storybook"
}
}
}
}Kiểm tra: trong phiên Gemini gõ /mcp để xem trạng thái + tool. Restart phiên sau khi thêm.
Smoke test
npm run build
STORYBOOK_BASE_URL=https://your-storybook-host/path/to/storybook node scripts/smoke.mjsScript sẽ spawn server qua stdio, list tool và gọi thử list_stories, get_story_url,
get_story_selectors, generate_playwright_test. In ra SMOKE TEST OK nếu thành công.
Cấu trúc dự án
src/
├── index.ts # entry: đăng ký server + tool + stdio transport
├── constants.ts # base URL, viewport presets, limits
├── types.ts # kiểu dữ liệu
├── schemas/index.ts # Zod input schemas
├── services/
│ ├── storybookClient.ts # fetch index.json + build URL
│ ├── browser.ts # quản lý Playwright Chromium (render story)
│ ├── domAnalyzer.ts # ARIA snapshot + trích selector
│ └── format.ts # helper định dạng response
└── tools/ # 1 file / toolGhi chú kỹ thuật
- Story id có ký tự tiếng Việt (unicode) — server tự
encodeURIComponentkhi dựng URL. - Story render trong iframe cô lập (
iframe.html?id=...&viewMode=story) nên phù hợp đểpage.gototrong Playwright. - Nếu story không có
data-testid, locator ưu tiên theo role/placeholder, fallback theoidổn định. - Server chỉ log ra stderr (stdout dành cho giao thức MCP).
