This page is for people building apps with AI tools (Claude Code, Cursor, Codex, Copilot…) and for the AI tools themselves.
Give your AI tool the context
| Option | Use when | What the AI can do |
|---|---|---|
pos-app MCP server (recommended) |
Your AI tool supports MCP | Look up the docs, see the shop's real ids, check the app, try it in POS, read errors, deploy, report bugs to Pancake |
AGENTS.md |
Every project created with create-pos-app |
Read the mandatory rules on every change |
llms-full.txt |
Tools without MCP, or chat on the web | Read all the docs at once |
They work together: a create-pos-app project already has AGENTS.md; just add the MCP server.
MCP
pos-app mcp starts an MCP server so AI tools can look up the docs, see the shop's real metadata, check the app, read runtime errors and deploy without leaving the chat. It runs on your machine (stdio), in the app's project folder, with the account signed in through pos-app login.
Connect the MCP server
- Sign the CLI in once: in POS go to Settings › Advanced › Custom apps, click Connect CLI › Create token and run the command it shows (see Manage apps › Connect the CLI).
- Add the server to your AI tool, in the app's project folder (replace
123with your shop id):
# Claude Code
claude mcp add pos-app -- npx @pancake-pos/app-cli mcp --shop 123Cursor (.cursor/mcp.json in the project):
{
"mcpServers": {
"pos-app": { "command": "npx", "args": ["@pancake-pos/app-cli", "mcp", "--shop", "123"] }
}
}VS Code with Copilot (.vscode/mcp.json in the project):
{
"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) does not run inside the project folder, so add --cwd:
{
"mcpServers": {
"pos-app": { "command": "npx", "args": ["@pancake-pos/app-cli", "mcp", "--shop", "123", "--cwd", "/path/to/my-app"] }
}
}- Restart the AI tool and ask it to "list the pos-app tools". In Claude Code,
/mcpshows whether the server is connected.
Tools
| Tool | Input | Returns |
|---|---|---|
search_docs |
query |
Matching sections of these docs, as Markdown |
get_target |
target, for example purchase.details.action |
Context data schema, expected return value, manifest fields and a short example |
list_shop_meta |
resource |
Ids and names the app can use in conditions and code: statuses, tags, warehouses, product categories, order sources |
sample_context |
target, optional record id |
A realistic pos.data built from the shop's data, filtered by the app's access and the signed-in user's permissions |
check_app |
none | The pos-app check result: each problem with file, line, rule id and a suggested fix |
dev_start, dev_status |
none | Starts pos-app dev; returns the local URL, the POS link to open in development mode and build errors |
get_errors |
version (dev by default), since |
Runtime errors POS recorded for the app: target, message, stack trace, time |
deploy |
message |
Runs check and build, then uploads a new inactive version; returns the version and the link to the custom apps page |
send_feedback |
kind (bug or feature), title, description, include_errors |
Sends a POS Apps bug report or feature request to Pancake's POS support team; returns an FB-… id |
deploynever activates a version. An admin reviews access and activates it on the Custom apps page, exactly as withpos-app deploy.- If
versioninpos-app.jsonalready exists,deployfails withduplicate_version; bumpversionand deploy again. - Tools act as the signed-in user on the given shop. They never return app tokens or API keys, and they do not write shop data:
list_shop_metaandsample_contextonly read. App code still reads and writes data throughpos.api.
Typical loop
get_targetandsearch_docsto pick the right targets and types.list_shop_metaandsample_contextto writeconditionsand code against real ids and fields.- Write the code, then
check_appuntil it reports no errors. dev_start, try the app in POS, thenget_errorsto fix runtime errors.deployand ask an admin to activate the new version.
Sample prompts
Create a POS app "Debt reminder": a block in the order form showing the customer's total debt,
and a "Send reminder" button that calls https://crm.example.com/api/remind. Read-only access is enough.
Use sample_context for real order data, run check_app until it is clean, then dev_start so I can try it in POS.The "Push to ERP" button in version 1.2.0 is failing. Read get_errors for that version, find the cause, fix it,
bump the version and deploy with a note.Show the "Approve order" action only for orders waiting for confirmation that come from Facebook.
Get the status and order source ids with list_shop_meta instead of hard-coding names.Report bugs and request features
When you hit a platform bug (runtime, components, SDK, CLI, MCP, docs, Open API) or need something that does not exist yet, ask your AI tool to send it to Pancake with the send_feedback tool:
Report to Pancake: pos-table does not show the date column when format is "date".- The AI drafts a title and description (what happened, what you expected, steps to reproduce; or the need and the use case) and shows it to you first.
- Only after you agree does it send. The report goes straight to Pancake's POS support channel; the AI gives you an
FB-…id to quote when following up.
Reports automatically include the CLI and runtime versions, OS, Node, the app handle and version, and the app's 5 most recent runtime errors (skip them with include_errors: false). Code and shop data are never sent; tokens pasted by mistake are masked before sending. Each shop can send up to 20 reports per day.
Without MCP
- All docs as Markdown:
/developers/apps/llms-full.txt. Index:/developers/apps/llms.txt. Every page has a Markdown version at the same path with.md, for example/developers/apps/targets.md. - These LLM files are in English by default. Vietnamese versions live under
/developers/apps/vi/, for example/developers/apps/vi/llms-full.txt. - The
create-pos-apptemplate generates anAGENTS.mdwith the content in the last section. Keep it in the project so AI tools always read the rules.
Sample prompt:
Read https://pos.pages.fm/developers/apps/llms-full.txt.
Create a POS app "Debt reminder": a block in the order form showing the customer's total debt,
and a "Send reminder" button that calls https://crm.example.com/api/remind. Read-only access is enough.Mandatory rules
Runtime environment
- Code in
extensions/runs in a Web Worker with no real DOM.- Do not use
window,document.querySelector,document.getElementById,localStorage,sessionStorage,alert,confirm,canvas,getBoundingClientRect. - Do not import UI libraries (antd, MUI, Tailwind, chart libraries, drag-and-drop libraries) or CSS files.
- Use Preact (recommended) or plain JS to render
pos-*elements intodocument.body. Do not use React inextensions/. - Do not install any library to connect to POS or render UI; the POS runtime is already in the worker.
- Do not use
- Iframe pages (
page/) are regular web apps: any library is allowed, but they must getposfromconnect(), must not accesswindow.top/window.parent, and must not use routes starting with/_pos/(reserved for POS).
UI
- Worker UI extensions use only elements from UI components. Do not use
div,span,p,img,table,button,input. - Text must be inside
pos-text,pos-heading,pos-badge,pos-link,pos-buttonorpos-banner. - Do not use
styleorclassName. Event values come fromevent.detail. - Display amounts with
pos-moneyor a column withformat: 'money'; do not format them yourself.
Data
- Get POS data only through
pos.api, with paths like/shops/${pos.shop.id}/...and endpoints listed in the Open API. - Never hardcode a shop id, API key or token; never send the app token elsewhere, except to the backend of a self-hosted page; never put secrets in code.
- Every method used in code (
POST,PUT,DELETE) must be listed inaccess.methods. Every external domain (includingpos-imagesources) must be listed innetwork.allowed_origins. - Read the current record from
data.record(data.recordsin bulk actions). Call the Open API withrecord.system_idfor orders andrecord.idfor products, purchases, exports and warehouse transfers.
Modules and manifest
- Every module has
export default async function (data).- Blocks,
modal/fullscreenactions,render: "worker"pages: render intodocument.body. headlessactions: return{ message?, refresh? }or throw.should_render: return aboolean, finish within 300 ms, no slow API calls.*.before_submit: return{ ok: true }or{ ok: false, message }, finish withintimeout_ms.
- Blocks,
- Every extension in
pos-app.jsonhas a uniqueid;targetmust exist in Extension points. - Prefer wrapping modules with
defineExtension(target, fn)from@pancake-pos/ui-extensionsso the types ofdataand of the return value match the target.
Quality
- Wrap every API call in
try/catch; show errors withpos-banner(in UI) orpos.toast.show(..., { tone: 'error' }). - Handle
403(the user lacks permission) and429(retry later with an increasing delay). - Do not call the API on every render; use
useEffectwith explicit dependencies, pagination andAbortController. - Write the UI in the user's language (
pos.user.locale). - After every change, run
npx eslint .(or thecheck_apptool) and fix every problem. Do not addeslint-disableto silence a rule;pos-app checkstill blocks the deploy.
Common mistakes
| Wrong | Right |
|---|---|
<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(...) in an extension |
Store it through the Open API (CRM tables) or your own backend |
alert('Done') |
pos.toast.show('Done') |
data.order, data.purchase |
data.record (data.resource tells which resource) |
`/shops/${shopId}/purchases/${record.display_id}` |
`/shops/${shopId}/purchases/${record.id}` |
import { Table } from 'antd' in an extension |
<pos-table columns={…} rows={…} /> |
Calling PUT while access.methods only has GET |
Add "PUT" to access.methods |
new Intl.NumberFormat(...).format(total) + ' VND' |
<pos-money value={total} /> |
Pre-deploy checklist
-
pos-app.jsonis valid:handle, a newversion,api_version, no duplicateid. -
access.methodscovers every write request; no unnecessary permissions. - Every external domain is in
network.allowed_origins. - Modules in
extensions/use no real DOM, UI libraries or CSS. - No tokens, API keys or secrets in code.
- Errors and loading states are handled.
-
npx pos-app checkreports no errors. - Tested with
npx pos-app dev(an account with the Settings permission), then deployed to a test shop and tried with a narrow-permission staff account.
Sample AGENTS.md
# POS App
This project is a custom app that runs inside Pancake POS.
Docs: https://pos.pages.fm/developers/apps/llms-full.txt (read before changing code).
## Structure
- pos-app.json: manifest (permissions, external domains, extensions).
- extensions/: modules that run in a Web Worker and render pos-* elements with Preact.
- page/: iframe page (React + Vite), gets `pos` from `connect()` in `@pancake-pos/ui-extensions/page`; routes starting with /_pos/ are reserved for POS.
## Rules
- extensions/: no real DOM, window, localStorage, React, UI libraries or CSS. Only pos-* elements from the docs.
- Text goes inside pos-text/pos-heading/pos-badge/pos-link/pos-button/pos-banner. Event values are in event.detail.
- POS data only through pos.api with paths like /shops/${pos.shop.id}/... and Open API endpoints.
- Never hardcode shop ids, API keys, tokens or secrets.
- Write methods must be in access.methods; external domains must be in network.allowed_origins.
- Modules: export default async function (data). Headless returns { message }; before_submit returns { ok } or { ok: false, message }.
- Catch errors on every API call; handle 403 and 429.
## Commands
- npx pos-app dev: try in POS (turn on Development mode on the custom apps management page); POS reloads when code changes.
- npx eslint .: check the code after every change; do not add eslint-disable to silence rules.
- npx pos-app check: validate the manifest and code; fix every error before deploying.
- npx pos-app deploy: build, validate the manifest, upload a new version.
- With the pos-app MCP server: get_target/list_shop_meta/sample_context before writing code, check_app after every change, get_errors after testing in POS, deploy when done (uploads an inactive version; an admin activates it). For a platform bug or a missing feature: draft a report, show it to the user, and call send_feedback only after they agree.