Sapo
FreeNot checkedModel Context Protocol server for Sapo.vn POS & e-commerce platform, providing 105 tools across 4 modes for managing orders, inventory, customers, and more.
About
Model Context Protocol server for Sapo.vn POS & e-commerce platform, providing 105 tools across 4 modes for managing orders, inventory, customers, and more.
README
npm version License: MIT Node 20+
Model Context Protocol server for Sapo.vn POS & e-commerce platform.
Features
- 4 modes, 105 unique tools:
pos-online(51),web(31),pos-counter(15),analytics(10) — 2 read tools shared between pos-online and web - Two transports: stdio (Claude Desktop, Cursor) and Streamable HTTP (Docker, GoClaw)
- Safe by default: destructive ops gated via
SAPO_ALLOW_OPS(default: none); all destructive calls also requireconfirm: true - Single-tenant: one shop per server instance via Private App credentials
- Pre-1.0: tool names and schemas may shift on minor bumps. See Post-1.0 roadmap.
Installation
Requires Node.js 20 or newer. Verify with node --version.
Option A — npx (no install)
Recommended for MCP clients (Claude Desktop, Cursor). Always pulls the latest published version:
npx -y sapo-mcp@latest --version
Option B — global install
npm install -g sapo-mcp
sapo-mcp --version
sapo-mcp --help
Option C — local project dependency
npm install sapo-mcp
npx sapo-mcp --mode=pos-online
Quick Start
1. Get Credentials
Create a Private App at developers.sapo.vn:
- Store name:
mystorename→mystorename.mysapo.net - API Key + Secret
2. Configure your MCP client
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%/Claude/claude_desktop_config.json (Windows):
{
"mcpServers": {
"sapo": {
"command": "npx",
"args": ["-y", "sapo-mcp", "--mode=pos-online,web,analytics"],
"env": {
"SAPO_STORE": "mystorename",
"SAPO_API_KEY": "xxx",
"SAPO_API_SECRET": "yyy"
}
}
}
}
Restart Claude Desktop. Tools appear under the 🔌 plug icon.
Cursor
Edit ~/.cursor/mcp.json or per-workspace .cursor/mcp.json:
{
"mcpServers": {
"sapo": {
"command": "npx",
"args": ["-y", "sapo-mcp", "--mode=pos-online,web"],
"env": {
"SAPO_STORE": "mystorename",
"SAPO_API_KEY": "xxx",
"SAPO_API_SECRET": "yyy"
}
}
}
}
MCP Inspector (test/debug)
npx @modelcontextprotocol/inspector \
-e SAPO_STORE=mystorename \
-e SAPO_API_KEY=xxx \
-e SAPO_API_SECRET=yyy \
npx -y sapo-mcp --mode=pos-online
Open the printed URL in a browser to inspect/invoke registered tools.
Use --mode=pos-online,web,analytics to register multiple modes (union of tools; shared tools registered once).
3. CLI Flags
| Flag | Default | Description |
|---|---|---|
--mode=<modes> |
pos-online |
Comma-separated list of modes to activate |
--transport=<t> |
stdio |
Transport type (stdio or http) |
--port=<port> |
3333 |
HTTP port (ignored for stdio) |
--help |
— | Print usage and exit |
--version |
— | Print version and exit |
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
SAPO_STORE |
Yes | — | Store subdomain |
SAPO_API_KEY |
Yes | — | Private App API Key |
SAPO_API_SECRET |
Yes* | — | Private App API Secret |
SAPO_API_SECRET_FILE |
Yes* | — | Path to file containing secret (takes precedence) |
SAPO_ALLOW_OPS |
No | "" |
CSV of allowed destructive categories |
SAPO_MAX_AUTO_PAGES |
No | 10 |
Max auto-pagination pages |
SAPO_RETRY_MAX |
No | 3 |
HTTP retry attempts |
SAPO_LOG_LEVEL |
No | info |
Log level (error/warn/info/debug/trace) |
SAPO_HTTP_HOST |
No† | 127.0.0.1 |
HTTP bind host (loopback by default) |
SAPO_HTTP_PORT |
No | 3333 |
HTTP port |
SAPO_HTTP_MAX_SESSIONS |
No | 100 |
Max concurrent MCP sessions |
SAPO_HTTP_SESSION_IDLE_MS |
No | 1800000 |
Idle session GC threshold (30 min) |
SAPO_MCP_AUTH_TOKEN |
No† | — | Bearer token. Required if host is non-loopback |
SAPO_HTTP_CORS_ORIGINS |
No | — | CSV of allowed CORS origins (default: disabled) |
*One of SAPO_API_SECRET or SAPO_API_SECRET_FILE is required.
†HTTP-only. Token is enforced when SAPO_HTTP_HOST is not 127.0.0.1/localhost/::1.
Modes
| Mode | Status | Description |
|---|---|---|
pos-online |
0.5.0 | Online orders, customers, fulfillment (51 tools) |
web |
0.5.0 | Storefront, collections, articles, SEO (31 tools) |
pos-counter |
0.5.0 | POS counter: locations, inventory write, suppliers, shifts, stock transfers (15 tools) |
analytics |
0.5.0 | Composed reports: revenue, top products/customers, LTV, tax, channel breakdown, discount usage, shift report (10 tools) |
Note:
pos-counterexcludes 5 internal-only endpoints (purchase_orders,purchase_returns,stock_adjustments,cash_transactions,cashbook) — these return HTTP 403 for Private App credentials and require an OAuth Partner App (post-1.0 roadmap).Analytics tools auto-paginate up to
SAPO_MAX_AUTO_PAGES; results carrytruncated: truewhen the cap is hit.
Tool Verification Status
Verification levels for the 105 unique tools (last updated 2026-04-30):
| Symbol | Level | What it means |
|---|---|---|
| ✅ | Canary-monitored | Endpoint + schema verified live; daily nightly probe via .github/workflows/canary.yml |
| 🟢 | Live-verified | Endpoint hit during development, schema captured to fixture, not in daily canary |
| 🟡 | Docs-only | Schema from docs/sapo-api-reference.md (Sapo official); mock tests only, no live probe |
| 🔵 | Composed | Not a Sapo endpoint — internal aggregation logic, logic-tested via unit tests |
| 🚨 | Broken | Known non-functional, see notes |
Canary-monitored endpoints (drift detected within 24h)
| Resource | Mode(s) | Endpoint |
|---|---|---|
| store | all | /admin/store.json |
| products (read) | pos-online, pos-counter | /admin/products.json |
| orders | pos-online, pos-counter | /admin/orders.json |
| customers | pos-online, pos-counter | /admin/customers.json |
| inventory_levels | pos-online, pos-counter | /admin/inventory_levels.json |
| locations | pos-counter | /admin/locations.json |
| draft_orders | pos-online | /admin/draft_orders.json |
| price_rules | pos-online | /admin/price_rules.json |
| pages | web | /admin/pages.json |
| suppliers | pos-counter | /admin/suppliers.json |
| stock_transfers | pos-counter | /admin/stock_transfers.json |
| payment_methods | pos-counter | /admin/payment_methods.json |
🚨 Broken — needs investigation
| Tool | Resource | Issue |
|---|---|---|
list_pos_shifts |
pos_shifts | /admin/pos_shifts.json returns Content-Type: text/html (Sapo POS web app shell), not JSON. |
get_pos_shift |
pos_shifts | Same as above. JSON API endpoint not yet located. |
These 2 tools may fail at runtime. POS shift management currently requires admin UI.
Per-tool status
Click each section to expand the full tool list with status icons.
pos-online — 51 tools (with `SAPO_ALLOW_OPS=*`)
Customers (7)
- ✅
list_customers,get_customer,search_customers,count_customers,list_customer_orders - 🟢
create_customer,update_customer
Customer Addresses (4)
- 🟡
list_customer_addresses,add_customer_address,update_customer_address,set_default_customer_address
Products (read) (4)
- ✅
list_products,get_product,search_products,count_products
Variants (read) (2)
- 🟡
list_variants_for_product,get_variant
Inventory (read) (1)
- ✅
get_inventory_levels
Orders (4)
- ✅
list_orders,get_order,count_orders,search_orders
Order Transactions (2)
- 🟢
list_order_transactions,create_order_transaction
Fulfillments (4)
- 🟢
list_fulfillments_for_order,get_fulfillment - 🟡
create_fulfillment,update_fulfillment_tracking
Refunds (3)
- 🟢
list_refunds,get_refund,create_refund(create_refund destructive:SAPO_ALLOW_OPS=refund)
Draft Orders (7)
- ✅
list_draft_orders,get_draft_order - 🟡
create_draft_order,update_draft_order,complete_draft_order,send_draft_order_invoice,delete_draft_order(destructive:delete)
Price Rules (5)
- ✅
list_price_rules,get_price_rule - 🟡
create_price_rule,update_price_rule,delete_price_rule(destructive:delete)
Discount Codes (3)
- 🟡
list_discount_codes,create_discount_code,delete_discount_code(destructive:delete)
Destructive (cancel/delete-strict) (4)
- 🟡
cancel_order,close_order,cancel_fulfillment(destructive:cancel) - 🟡
delete_customer,delete_variant(destructive:delete_strict)
web — 31 tools
Store (1)
- ✅
get_store_info
Articles (5)
- 🟡
list_articles,get_article,create_article,update_article,delete_article(destructive:delete)
Blogs (5)
- 🟡
list_blogs,get_blog,create_blog,update_blog,delete_blog(destructive:delete)
Pages (4)
- ✅
list_pages,get_page - 🟡
update_page_seo,delete_page(destructive:delete)
Collections (10)
- 🟡
list_custom_collections,get_custom_collection,create_custom_collection,update_custom_collection,delete_custom_collection(destructive:delete) - 🟡
list_smart_collections,get_smart_collection - 🟡
list_collects,create_collect,delete_collect(destructive:delete)
Script Tags (3)
- 🟡
list_script_tags,create_script_tag,delete_script_tag(destructive:delete)
Products SEO (1)
- 🟡
update_product_seo
Variants (read, shared) (2)
- 🟡
list_variants_for_product,get_variant
pos-counter — 15 tools
Locations (2)
- ✅
list_locations,get_location
Payment Methods (1)
- ✅
list_payment_methods
Inventory (write) (3)
- 🟡
adjust_inventory_level,connect_inventory_level - 🟡
set_inventory_level(destructive:inventory_set)
Variants (write) (1)
- 🟡
update_variant
POS Orders (2)
- ✅
list_pos_orders,get_pos_order(uses/admin/orders?source_name=pos)
Suppliers (2)
- ✅
list_suppliers,get_supplier
Stock Transfers (2)
- ✅
list_stock_transfers,get_stock_transfer
POS Shifts (2)
- 🚨
list_pos_shifts,get_pos_shift(see Broken section above — endpoint returns HTML)
analytics — 10 tools
All 10 are 🔵 composed (aggregate from multiple Sapo endpoints, no single endpoint to verify):
revenue_summary, top_products, top_customers, customer_ltv, tax_summary, online_vs_counter_breakdown, discount_usage_report, shift_report, inventory_low_stock, inventory_value
Destructive Operations
Destructive tools (cancel, delete, bulk-delete) are blocked by default. To enable specific categories:
# Allow order cancellation and standard deletes only
SAPO_ALLOW_OPS=cancel,delete npx sapo-mcp --mode=pos-online
Supported categories: cancel, delete, delete_strict, inventory_set, refund, shift_close, cashbook_write.
All destructive tool calls also require confirm: true in the tool arguments — this prevents accidental execution when an LLM calls the tool without explicit intent.
Security
ENV credentials are visible to other processes via ps / /proc/<pid>/environ on shared hosts. Use SAPO_API_SECRET_FILE to pass the secret via a file instead of an env var where possible.
Note: full secret-file isolation (reading the secret only at startup, then clearing) is tracked for the post-1.0 roadmap.
Probing
Verify which Sapo API endpoints are available on your store before configuring tools.
# Read-only probe — safe to run against any store (GET only)
SAPO_STORE=mystore SAPO_API_KEY=xxx SAPO_API_SECRET=yyy npm run probe
# Schema drift check — same probe + Zod validation against fixtures
npm run canary
Write-probe (npm run probe:write) refuses to run unless SAPO_STORE contains test, dev, or sandbox — never against production.
HTTP Transport (Remote / Docker)
Run as an HTTP server for remote MCP clients (e.g. GoClaw) or self-hosted Docker:
# Local-only (no auth required)
sapo-mcp --mode=pos-online --transport=http --port=3333
# Public bind (auth token required)
SAPO_HTTP_HOST=0.0.0.0 \
SAPO_MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
sapo-mcp --mode=pos-online --transport=http
Endpoints:
| Method | Path | Description |
|---|---|---|
GET |
/health |
Liveness probe — returns { status, version, modes, sessions } |
POST |
/mcp |
JSON-RPC over Streamable HTTP (creates a session on initialize) |
GET |
/mcp |
SSE long-poll for an existing session (mcp-session-id header) |
DELETE |
/mcp |
Terminate a session |
Session model: Each MCP client gets a UUID session, isolated by an
McpServer instance. Idle sessions (SAPO_HTTP_SESSION_IDLE_MS) are
evicted automatically. Concurrent sessions cap at SAPO_HTTP_MAX_SESSIONS
(503 returned when full).
Security:
- Defaults to
127.0.0.1— loopback only. Token optional. - Setting
SAPO_HTTP_HOST=0.0.0.0(or any non-loopback) requiresSAPO_MCP_AUTH_TOKEN. The server refuses to start otherwise. - CORS is off by default. Enable per-origin via
SAPO_HTTP_CORS_ORIGINS(CSV of origins, or*for all). Only enable when serving browser-based agents.
Docker
See examples/Dockerfile and examples/docker-compose.yml.
docker build -f examples/Dockerfile -t sapo-mcp:local .
docker run --rm -p 3333:3333 \
-e SAPO_STORE=mystore \
-e SAPO_API_KEY=xxx -e SAPO_API_SECRET=yyy \
-e SAPO_HTTP_HOST=0.0.0.0 \
-e SAPO_MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
sapo-mcp:local
Development
npm install
npm run typecheck # Type check
npm run lint # Lint
npm run test # Run tests
npm run build # Build for release
Post-1.0 roadmap
The 1.0.0 stable cut is gated on the following items, deferred from this release:
- MCP Resources —
sapo://shop/info,sapo://orders/today,sapo://orders/pending,sapo://inventory/low-stock. Read-heavy data is more efficient as a Resource than repeated tool calls. - MCP Prompts — templated workflows (
respond_to_complaint,weekly_report,seo_optimize_product,customer_followup). - Webhook receiver — sub-package surfacing Sapo webhooks as MCP events.
- OAuth 2.0 Partner App — multi-tenant SaaS deployments. Unlocks the 5 internal-only endpoints currently excluded from
pos-counterand 4 deferred finance/PO reports (cashflow_summary,pnl_summary,supplier_purchase_summary,daily_pos_report). - Storefront GraphQL module — verify Private App access; scope tools if available.
Changelog
See CHANGELOG.md for the per-version history.
License
MIT — See LICENSE
Installing Sapo
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/nguyennguyenit/Sapo-MCPFAQ
Is Sapo MCP free?
Yes, Sapo MCP is free — one-click install via Unyly at no cost.
Does Sapo need an API key?
No, Sapo runs without API keys or environment variables.
Is Sapo hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Sapo in Claude Desktop, Claude Code or Cursor?
Open Sapo on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare Sapo with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
