sapo-mcp
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
sapo-mcp
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.
<details>
<summary><b>pos-online — 51 tools (with SAPO_ALLOW_OPS=*)</b></summary>
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)
</details>
<details> <summary><b>web — 31 tools</b></summary>
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
</details>
<details> <summary><b>pos-counter — 15 tools</b></summary>
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)
</details>
<details> <summary><b>analytics — 10 tools</b></summary>
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
</details>
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
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。