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
- Đă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).
- Thêm server vào công cụ AI, trong thư mục dự án app (thay
123bằng mã shop):
# Claude Code
claude mcp add pos-app -- npx @pancake-pos/app-cli mcp --shop 123Cursor (.cursor/mcp.json trong dự án):
{
"mcpServers": {
"pos-app": { "command": "npx", "args": ["@pancake-pos/app-cli", "mcp", "--shop", "123"] }
}
}VS Code với Copilot (.vscode/mcp.json trong dự án):
{
"servers": {
"pos-app": { "type": "stdio", "command": "npx", "args": ["@pancake-pos/app-cli", "mcp", "--shop", "123"] }
}
}# 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:
{
"mcpServers": {
"pos-app": { "command": "npx", "args": ["@pancake-pos/app-cli", "mcp", "--shop", "123", "--cwd", "/đường/dẫn/tới/my-app"] }
}
}- Mở lại công cụ AI và hỏi "Liệt kê tool của pos-app". Trong Claude Code, lệnh
/mcpcho 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-… |
deploykhô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ệtpos-app deploy.- Nếu
versiontrongpos-app.jsonđã tồn tại,deploybáo lỗiduplicate_version; tăngversionrồ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_metavàsample_contextchỉ đọc. Code của app vẫn đọc/ghi dữ liệu quapos.api.
Vòng làm việc
get_targetvàsearch_docsđể chọn đúng target và kiểu dữ liệu.list_shop_metavàsample_contextđể viếtconditionsvà code theo mã, trường thật.- Viết code, chạy
check_appđến khi hết lỗi. dev_start, thử app trong POS, rồiget_errorsđể sửa lỗi khi chạy.deployvà nhờ quản trị viên kích hoạt version mới.
Câu lệnh mẫu
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.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ú.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:
Báo cho Pancake: pos-table không hiện cột ngày khi format là "date".- 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.
- 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-appsinh sẵnAGENTS.mdvớ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:
Đọ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
- 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àodocument.body. Không dùng React trongextensions/. - 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.
- Không dùng
- Trang iframe (
page/) là ứng dụng web bình thường: được dùng thư viện tuỳ ý, nhưng phải lấyposbằngconnect(), không truy cậpwindow.top/window.parentvà không dùng route bắt đầu bằng/_pos/(dành riêng cho POS).
Giao diện
- Extension Worker UI chỉ dùng element trong Component giao diện. Không dùng
div,span,p,img,table,button,input. - Text phải nằm trong
pos-text,pos-heading,pos-badge,pos-link,pos-buttonhoặcpos-banner. - Không dùng
style,className. Giá trị sự kiện lấy từevent.detail. - Số tiền hiển thị bằng
pos-moneyhoặc cộtformat: 'money', không tự định dạng.
Dữ liệu
- Dữ liệu POS chỉ lấy qua
pos.apivới đường dẫn/shops/${pos.shop.id}/...và endpoint có trong Open API. - 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.
- Mọi method dùng trong code (
POST,PUT,DELETE) phải có trongaccess.methods. Mọi domain ngoài (kể cả ảnh củapos-image) phải có trongnetwork.allowed_origins. - Đọc bản ghi hiện tại từ
data.record(data.recordsở action hàng loạt). Gọi Open API bằngrecord.system_idvới đơn hàng,record.idvới sản phẩm, phiếu nhập, phiếu xuất, phiếu chuyển kho.
Module và manifest
- Mỗi module có
export default async function (data).- Block, action
modal/fullscreen, trangrender: "worker": render vàodocument.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ướitimeout_ms.
- Block, action
- Mỗi extension trong
pos-app.jsoncóidduy nhất;targetphải có trong Extension point. - Nên bọc module bằng
defineExtension(target, fn)từ@pancake-pos/ui-extensionsđể type củadatavà giá trị trả về khớp target.
Chất lượng
- Bọc mọi lời gọi API trong
try/catch; hiện lỗi bằngpos-banner(trong giao diện) hoặcpos.toast.show(..., { tone: 'error' }). - 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ờ). - Không gọi API trong mỗi lần render; dùng
useEffectvới dependency rõ ràng, phân trang vàAbortController. - 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. - Sau mỗi lần sửa, chạy
npx eslint .(hoặc toolcheck_app) và sửa hết lỗi. Không thêmeslint-disableđể né lỗi;pos-app checkvẫ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.jsonhợp lệ:handle,versionmới,api_version,idkhô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 checkkhô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
# 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 ý.