Hướng dẫn

Làm app với AI

Kết nối MCP server pos-app với Claude Code, Cursor, Codex…, câu lệnh mẫu, báo lỗi cho Pancake và quy tắc công cụ AI phải theo.

Xem Markdown

Trang này dành cho người viết app bằng công cụ AI (Claude Code, Cursor, Codex, Copilot…) và cho chính công cụ AI đọc.

Cho AI hiểu POS Apps

Cách Dùng khi AI làm được
MCP server pos-app (khuyên dùng) Công cụ AI hỗ trợ MCP Tra tài liệu, xem mã thật của shop, kiểm tra app, chạy thử trong POS, đọc lỗi, deploy, báo lỗi cho Pancake
AGENTS.md Mọi dự án tạo bằng create-pos-app Đọc quy tắc bắt buộc mỗi lần sửa code
llms-full.txt Công cụ không hỗ trợ MCP, hoặc chat trên web Đọc toàn bộ tài liệu một lần

Ba cách dùng chung được: dự án tạo bằng create-pos-app đã có AGENTS.md, chỉ cần thêm MCP server.

MCP

pos-app mcp chạy một MCP server để công cụ AI tự tra tài liệu, xem dữ liệu nền thật của shop, kiểm tra app, đọc lỗi khi chạy và deploy ngay trong cuộc trò chuyện. Server chạy trên máy của bạn (stdio), trong thư mục dự án app, bằng tài khoản đã đăng nhập qua pos-app login.

Kết nối MCP server

  1. Đăng nhập CLI một lần: trong POS vào Cấu hình › Nâng cao › Ứng dụng tuỳ biến, bấm Kết nối CLI › Tạo token rồi chạy lệnh hiện ra (xem Quản lý ứng dụng › Kết nối CLI).
  2. Thêm server vào công cụ AI, trong thư mục dự án app (thay 123 bằng mã shop):
Terminal
# Claude Code
claude mcp add pos-app -- npx @pancake-pos/app-cli mcp --shop 123

Cursor (.cursor/mcp.json trong dự án):

JSON
{
  "mcpServers": {
    "pos-app": { "command": "npx", "args": ["@pancake-pos/app-cli", "mcp", "--shop", "123"] }
  }
}

VS Code với Copilot (.vscode/mcp.json trong dự án):

JSON
{
  "servers": {
    "pos-app": { "type": "stdio", "command": "npx", "args": ["@pancake-pos/app-cli", "mcp", "--shop", "123"] }
  }
}
TOML
# Codex: ~/.codex/config.toml
[mcp_servers.pos-app]
command = "npx"
args = ["@pancake-pos/app-cli", "mcp", "--shop", "123"]

Claude Desktop (claude_desktop_config.json) không chạy trong thư mục dự án, nên thêm --cwd:

JSON
{
  "mcpServers": {
    "pos-app": { "command": "npx", "args": ["@pancake-pos/app-cli", "mcp", "--shop", "123", "--cwd", "/đường/dẫn/tới/my-app"] }
  }
}
  1. Mở lại công cụ AI và hỏi "Liệt kê tool của pos-app". Trong Claude Code, lệnh /mcp cho biết server đã kết nối chưa.

Tool

Tool Đầu vào Trả về
search_docs query Các đoạn tài liệu khớp, dạng Markdown
get_target target, ví dụ purchase.details.action Schema dữ liệu ngữ cảnh, giá trị cần trả về, trường manifest và ví dụ ngắn
list_shop_meta resource Mã và tên dùng được trong conditions và code: trạng thái, thẻ, kho, danh mục sản phẩm, nguồn đơn
sample_context target, mã bản ghi (không bắt buộc) pos.data mẫu dựng từ dữ liệu thật của shop, đã lọc theo access của app và quyền của người đăng nhập
check_app không có Kết quả pos-app check: từng lỗi kèm file, dòng, mã luật và gợi ý sửa
dev_start, dev_status không có Chạy pos-app dev; trả URL local, link POS cần mở ở chế độ phát triển và lỗi build
get_errors version (mặc định dev), since Lỗi khi chạy mà POS ghi lại cho app: target, nội dung, stack trace, thời điểm
deploy message Chạy check và build, rồi upload version mới chưa kích hoạt; trả về version và link tới trang quản lý ứng dụng
send_feedback kind (bug hoặc feature), title, description, include_errors Gửi báo lỗi hoặc yêu cầu tính năng về POS Apps cho đội hỗ trợ POS của Pancake; trả mã FB-…
  • deploy không bao giờ kích hoạt version. Quản trị viên duyệt quyền và kích hoạt trên trang Ứng dụng tuỳ biến, giống hệt pos-app deploy.
  • Nếu version trong pos-app.json đã tồn tại, deploy báo lỗi duplicate_version; tăng version rồi deploy lại.
  • Tool chạy dưới danh nghĩa người đã đăng nhập, trên shop đã chọn. Tool không trả app token hay API key và không ghi dữ liệu shop: list_shop_meta và sample_context chỉ đọc. Code của app vẫn đọc/ghi dữ liệu qua pos.api.

Vòng làm việc

  1. get_target và search_docs để chọn đúng target và kiểu dữ liệu.
  2. list_shop_meta và sample_context để viết conditions và code theo mã, trường thật.
  3. Viết code, chạy check_app đến khi hết lỗi.
  4. dev_start, thử app trong POS, rồi get_errors để sửa lỗi khi chạy.
  5. deploy và nhờ quản trị viên kích hoạt version mới.

Câu lệnh mẫu

Text
Tạo app POS "Nhắc công nợ": block trong form đơn hiện tổng công nợ của khách,
nút "Gửi nhắc nợ" gọi https://crm.example.com/api/remind. Chỉ cần quyền đọc.
Dùng sample_context để lấy dữ liệu đơn thật, check_app đến khi hết lỗi, rồi dev_start để tôi thử trong POS.
Text
Nút "Đẩy sang ERP" của version 1.2.0 đang báo lỗi. Đọc get_errors của version đó, tìm nguyên nhân, sửa,
tăng version rồi deploy kèm ghi chú.
Text
Chỉ hiện action "Duyệt đơn" với đơn ở trạng thái Chờ xác nhận và nguồn Facebook.
Lấy mã trạng thái và nguồn đơn bằng list_shop_meta, không viết cứng theo tên.

Báo lỗi và đề xuất tính năng

Gặp lỗi của nền tảng (runtime, component, SDK, CLI, MCP, tài liệu, Open API) hoặc cần tính năng chưa có, nhờ công cụ AI gửi cho Pancake bằng tool send_feedback:

Text
Báo cho Pancake: pos-table không hiện cột ngày khi format là "date".
  1. AI soạn tiêu đề và mô tả (lỗi gì, mong đợi gì, các bước tái hiện; hoặc nhu cầu và tình huống dùng), cho bạn xem trước.
  2. Bạn đồng ý thì AI mới gửi. Báo cáo vào thẳng kênh hỗ trợ POS của Pancake; AI trả lại mã FB-… để bạn dẫn khi trao đổi tiếp.

Báo cáo tự kèm phiên bản CLI và runtime, hệ điều hành, Node, handle và version của app, 5 lỗi gần nhất của app (bỏ bằng include_errors: false). Không gửi code hay dữ liệu shop; token lỡ dán vào nội dung bị che trước khi gửi. Mỗi shop gửi tối đa 20 báo cáo mỗi ngày.

Không dùng MCP

  • Toàn bộ tài liệu dạng Markdown: /developers/apps/llms-full.txt. Mục lục: /developers/apps/llms.txt. Mỗi trang có bản Markdown tại cùng đường dẫn thêm .md, ví dụ /developers/apps/targets.md.
  • Các file cho LLM mặc định là tiếng Anh. Bản tiếng Việt nằm ở /developers/apps/vi/, ví dụ /developers/apps/vi/llms-full.txt.
  • Template create-pos-app sinh sẵn AGENTS.md với nội dung ở mục cuối. Giữ file này trong dự án để AI luôn đọc quy tắc.

Câu lệnh mẫu:

Text
Đọc https://pos.pages.fm/developers/apps/llms-full.txt.
Tạo app POS "Nhắc công nợ": block trong form đơn hiện tổng công nợ của khách,
nút "Gửi nhắc nợ" gọi https://crm.example.com/api/remind. Chỉ cần quyền đọc.

Quy tắc bắt buộc

Môi trường chạy

  1. Code trong extensions/ chạy trong Web Worker, không có DOM thật.
    • Không dùng window, document.querySelector, document.getElementById, localStorage, sessionStorage, alert, confirm, canvas, getBoundingClientRect.
    • Không import thư viện UI (antd, MUI, Tailwind, thư viện chart, thư viện kéo thả) và file CSS.
    • Dùng Preact (khuyến nghị) hoặc JS thuần để render element pos-* vào document.body. Không dùng React trong extensions/.
    • Không cài thư viện nào để kết nối hay vẽ giao diện với POS; runtime POS đã có sẵn trong worker.
  2. Trang iframe (page/) là ứng dụng web bình thường: được dùng thư viện tuỳ ý, nhưng phải lấy pos bằng connect(), không truy cập window.top/window.parent và không dùng route bắt đầu bằng /_pos/ (dành riêng cho POS).

Giao diện

  1. Extension Worker UI chỉ dùng element trong Component giao diện. Không dùng div, span, p, img, table, button, input.
  2. Text phải nằm trong pos-text, pos-heading, pos-badge, pos-link, pos-button hoặc pos-banner.
  3. Không dùng style, className. Giá trị sự kiện lấy từ event.detail.
  4. Số tiền hiển thị bằng pos-money hoặc cột format: 'money', không tự định dạng.

Dữ liệu

  1. Dữ liệu POS chỉ lấy qua pos.api với đường dẫn /shops/${pos.shop.id}/... và endpoint có trong Open API.
  2. Không viết cứng shop id, API key, token; không gửi app token ra ngoài, trừ backend của trang tự host; không đặt secret trong code.
  3. Mọi method dùng trong code (POST, PUT, DELETE) phải có trong access.methods. Mọi domain ngoài (kể cả ảnh của pos-image) phải có trong network.allowed_origins.
  4. Đọc bản ghi hiện tại từ data.record (data.records ở action hàng loạt). Gọi Open API bằng record.system_id với đơn hàng, record.id với sản phẩm, phiếu nhập, phiếu xuất, phiếu chuyển kho.

Module và manifest

  1. Mỗi module có export default async function (data).
    • Block, action modal/fullscreen, trang render: "worker": render vào document.body.
    • Action headless: trả { message?, refresh? } hoặc ném lỗi.
    • should_render: trả boolean, chạy dưới 300 ms, không gọi API chậm.
    • *.before_submit: trả { ok: true } hoặc { ok: false, message }, chạy dưới timeout_ms.
  2. Mỗi extension trong pos-app.json có id duy nhất; target phải có trong Extension point.
  3. Nên bọc module bằng defineExtension(target, fn) từ @pancake-pos/ui-extensions để type của data và giá trị trả về khớp target.

Chất lượng

  1. Bọc mọi lời gọi API trong try/catch; hiện lỗi bằng pos-banner (trong giao diện) hoặc pos.toast.show(..., { tone: 'error' }).
  2. Xử lý 403 (người dùng không đủ quyền) và 429 (thử lại sau, tăng dần thời gian chờ).
  3. Không gọi API trong mỗi lần render; dùng useEffect với dependency rõ ràng, phân trang và AbortController.
  4. Viết giao diện bằng ngôn ngữ của người dùng (pos.user.locale); mặc định tiếng Việt.
  5. Sau mỗi lần sửa, chạy npx eslint . (hoặc tool check_app) và sửa hết lỗi. Không thêm eslint-disable để né lỗi; pos-app check vẫn chặn deploy.

Lỗi thường gặp

Sai Đúng
<div className="box">…</div> <pos-section heading="…">…</pos-section>
<input onChange={(e) => set(e.target.value)} /> <pos-text-field onChange={(e) => set(e.detail)} />
fetch('https://pos.pages.fm/api/v1/shops/123/orders?api_key=...') pos.api.get(`/shops/${pos.shop.id}/orders`)
localStorage.setItem(...) trong extension Lưu qua Open API (bảng CRM) hoặc backend riêng
alert('Xong') pos.toast.show('Xong')
data.order, data.purchase data.record (data.resource cho biết loại bản ghi)
`/shops/${shopId}/purchases/${record.display_id}` `/shops/${shopId}/purchases/${record.id}`
import { Table } from 'antd' trong extension <pos-table columns={…} rows={…} />
Gọi PUT khi access.methods chỉ có GET Thêm "PUT" vào access.methods
new Intl.NumberFormat(...).format(total) + 'đ' <pos-money value={total} />

Checklist trước khi deploy

  • pos-app.json hợp lệ: handle, version mới, api_version, id không trùng.
  • access.methods đủ cho mọi request ghi; không xin quyền thừa.
  • Mọi domain ngoài nằm trong network.allowed_origins.
  • Module trong extensions/ không dùng DOM thật, thư viện UI hay CSS.
  • Không có token, API key, secret trong code.
  • Có xử lý lỗi và trạng thái đang tải.
  • npx pos-app check không còn lỗi.
  • Chạy thử bằng npx pos-app dev (tài khoản có quyền Cấu hình), rồi deploy lên shop test và thử với tài khoản nhân viên quyền hẹp.

AGENTS.md mẫu

Markdown
# POS App

Dự án này là app tuỳ biến chạy trong Pancake POS.
Tài liệu: https://pos.pages.fm/developers/apps/llms-full.txt (đọc trước khi sửa code).

## Cấu trúc
- pos-app.json: manifest (quyền, domain ngoài, extension).
- extensions/: module chạy trong Web Worker, render element pos-* bằng Preact.
- page/: trang iframe (React + Vite), lấy `pos` bằng `connect()` từ `@pancake-pos/ui-extensions/page`; route bắt đầu bằng /_pos/ dành riêng cho POS.

## Quy tắc
- extensions/: không dùng DOM thật, window, localStorage, React, thư viện UI, CSS. Chỉ dùng element pos-* có trong tài liệu.
- Text nằm trong pos-text/pos-heading/pos-badge/pos-link/pos-button/pos-banner. Giá trị sự kiện ở event.detail.
- Dữ liệu POS chỉ qua pos.api với đường dẫn /shops/${pos.shop.id}/... và endpoint Open API.
- Không viết cứng shop id, API key, token, secret.
- Method ghi phải có trong access.methods; domain ngoài phải có trong network.allowed_origins.
- Module: export default async function (data). Headless trả { message }, before_submit trả { ok } hoặc { ok: false, message }.
- Bắt lỗi mọi lời gọi API; xử lý 403 và 429.

## Lệnh
- npx pos-app dev: chạy thử với POS (bật Chế độ phát triển trong trang quản lý ứng dụng tuỳ biến); POS tự nạp lại khi sửa code.
- npx eslint .: kiểm tra code sau mỗi lần sửa; không thêm eslint-disable để né lỗi.
- npx pos-app check: kiểm tra manifest và code; sửa hết lỗi trước khi deploy.
- npx pos-app deploy: build, kiểm tra manifest, upload version mới.
- Có MCP server pos-app: gọi get_target/list_shop_meta/sample_context trước khi viết code, check_app sau mỗi lần sửa, get_errors sau khi thử trong POS, deploy khi xong (upload version chưa kích hoạt; admin kích hoạt). Gặp lỗi của nền tảng hoặc thiếu tính năng: soạn báo cáo, cho người dùng xem, chỉ gọi send_feedback khi họ đồng ý.