Skip to main content
Glama

sapo-mcp

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 require confirm: 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: mystorenamemystorename.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-counter excludes 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 carry truncated: true when 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.

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)

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

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)

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) requires SAPO_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 Resourcessapo://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-counter and 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

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
0dRelease cycle
2Releases (12mo)

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nguyennguyenit/Sapo-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server