sapo-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| SAPO_STORE | Yes | Store subdomain | |
| SAPO_API_KEY | Yes | Private App API Key | |
| SAPO_ALLOW_OPS | No | CSV of allowed destructive categories | |
| SAPO_HTTP_HOST | No | HTTP bind host (loopback by default) | 127.0.0.1 |
| SAPO_HTTP_PORT | No | HTTP port | 3333 |
| SAPO_LOG_LEVEL | No | Log level (error/warn/info/debug/trace) | info |
| SAPO_RETRY_MAX | No | HTTP retry attempts | 3 |
| SAPO_API_SECRET | No | Private App API Secret (or use SAPO_API_SECRET_FILE) | |
| SAPO_MAX_AUTO_PAGES | No | Max auto-pagination pages | 10 |
| SAPO_MCP_AUTH_TOKEN | No | Bearer token. Required if host is non-loopback | |
| SAPO_API_SECRET_FILE | No | Path to file containing secret (takes precedence) | |
| SAPO_HTTP_CORS_ORIGINS | No | CSV of allowed CORS origins (default: disabled) | |
| SAPO_HTTP_MAX_SESSIONS | No | Max concurrent MCP sessions | 100 |
| SAPO_HTTP_SESSION_IDLE_MS | No | Idle session GC threshold (30 min) | 1800000 |
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| list_customersA | List customers with optional filters. Returns paginated results via since_id cursor. If has_more=true, call again with next_since_id. |
| get_customerA | Get a single customer by ID, including their address list and order count. |
| search_customersA | Search customers by query string (matches email, phone, name). Returns paginated list. |
| count_customersA | Count total customers in the store, optionally filtered by creation date. |
| list_customer_ordersA | List all orders placed by a specific customer. Returns paginated results via since_id cursor. |
| create_customerA | Create a new customer. Must provide either email OR phone (or both). Optionally include addresses array for inline address creation. Returns the created customer including auto-generated id. |
| update_customerA | Update an existing customer. Only provided fields are modified. Use add_customer_address / update_customer_address tools for address management. |
| list_customer_addressesA | List all saved addresses for a customer, including default address flag and Vietnamese province/district/ward fields. |
| add_customer_addressA | Add a new address to a customer. Required: address1, city, country. Province handling: pass either |
| update_customer_addressA | Update an existing customer address. Only provided fields are modified. To set this address as default, use set_default_customer_address (Sapo dedicated endpoint). IMPORTANT: When changing subdivision fields, pass BOTH the text name AND the code (e.g. province + province_code together) — codes alone are silently dropped. Sapo write API only accepts the pre-2025 3-tier schema; new 2-tier addresses cannot be written via this endpoint (returns 422 "Ward is not supported"). |
| set_default_customer_addressA | Set a customer's default address via Sapo's dedicated endpoint (PUT /customers/{id}/addresses/{addr_id}/default.json). Returns the address now marked default. |
| list_provincesA | List Vietnamese provinces. Default (omit level) = level=3 = pre-2025 63 tỉnh, 3-tier. Use level=2 for post-2025 34 tỉnh, 2-tier (after merger). Province codes differ between schemas: level=3 codes are "1"–"63"; level=2 codes start at "2001". WORKFLOW: For new schema → list_provinces({level:2}) then list_wards({level:2, province_code}). For old schema → list_provinces() → list_districts({province_code}) → list_wards({district_code}). NOTE: Sapo write endpoints (add/update_customer_address) currently accept ONLY level=3 codes; level=2 is read-only as of 2026-05. |
| list_districtsA | List districts (Quận/Huyện) within a province. Only meaningful for level=3 (pre-2025 3-tier schema, default). With level=2 the response is empty because the district level was abolished on 2025-07-01. |
| list_wardsA | List wards (Phường/Xã). For level=3 (default, 3-tier) pass district_code obtained from list_districts. For level=2 (2-tier post-reform) pass province_code obtained from list_provinces({level:2}). Post-reform wards attach directly to province — their district_code is the sentinel "-1". |
| list_productsA | List products with optional filters (status, vendor, product_type). Returns paginated results via since_id cursor. If has_more=true, call again with next_since_id. |
| get_productB | Get a single product by ID, including its variants, options, and images. |
| search_productsA | Search products by title substring. Uses the title filter on the list endpoint. Returns paginated results. |
| count_productsA | Count total products in the store, optionally filtered by status, vendor, or product_type. |
| list_variants_for_productA | List all variants for a product (SKUs, prices in VND, inventory quantities). Returns paginated results via since_id cursor. |
| get_variantA | Get a single product variant by ID. Returns price (VND, float with 4 decimals), SKU, barcode, inventory quantity, and option values. |
| get_inventory_levelsA | Get inventory levels (available + committed quantities) for specific variants at specific locations. Filter by location_id, inventory_item_id, or both. |
| list_ordersA | List orders with optional filters. Returns paginated results via since_id cursor. Filter by status, financial_status, fulfillment_status, or source_name (facebook, web, pos). If has_more=true, call again with next_since_id. |
| get_orderA | Get a single order by ID. Returns full order including line_items, fulfillments, customer, billing/shipping address, and financial details. |
| count_ordersA | Count total orders, optionally filtered by status, financial_status, or date range. Returns { count: N }. |
| search_ordersA | Search orders by name (order #), customer email, or customer phone. Uses filter params on the orders list endpoint. Returns paginated results. |
| update_orderA | Update an existing order via PUT /admin/orders/{id}.json. Supports: tags, note, note_attributes, email, buyer_accepts_marketing. Verified live 2026-05-01. NOTE: The |
| list_order_transactionsA | List all transactions for a specific order. Returns payment attempts, captures, refunds, and voids linked to the order. |
| create_order_transactionA | Create a transaction for an order (e.g. capture, sale, refund). Side effect: creates a Transaction record and may change order financial_status. kind: authorization | capture | sale | void | refund. |
| list_fulfillments_for_orderA | List all fulfillments for a specific order. Returns fulfillment records with tracking info, line_items, and delivery status. |
| get_fulfillmentA | Get a single fulfillment by ID. Returns full fulfillment with tracking info, line_items, and origin address. |
| create_fulfillmentA | Create a fulfillment for an order, optionally specifying line items and tracking info. Side effect: creates a Fulfillment record and marks specified line_items as fulfilled. |
| update_fulfillment_trackingA | Update tracking information for an existing fulfillment. Side effect: modifies the Fulfillment tracking_company, tracking_number, or tracking_url. |
| list_draft_ordersA | List draft orders with optional filters. Returns paginated results via since_id cursor. Filter by status. If has_more=true, call again with next_since_id. |
| get_draft_orderA | Get a single draft order by ID. Returns full draft order including line_items, customer, address, and applied discounts. |
| create_draft_orderA | Create a new draft order. Side effect: creates a DraftOrder in "open" status. Requires at least one line_item with variant_id or title+price for custom items. |
| update_draft_orderA | Update a draft order in "open" status. Side effect: modifies draft order fields. Cannot update completed or cancelled drafts. |
| complete_draft_orderA | Complete a draft order and convert it into a real order. Side effect: creates an Order from the draft; draft status becomes "completed". Use payment_pending=true if payment not yet collected. |
| send_draft_order_invoiceA | Send an invoice email for a draft order. Side effect: sends email to customer; draft status becomes "invoice_sent". Requires customer email on the draft or provide via to param. |
| list_price_rulesA | List price rules (discount rules) with optional filters. Returns paginated results via since_id cursor. Filter by status (active, archived, scheduled). |
| get_price_ruleA | Get a single price rule by ID. Returns full price rule including value_type, value (string), entitled/prerequisite IDs, and usage stats. |
| create_price_ruleA | Create a new price rule (discount rule). Side effect: creates a PriceRule record. value must be a STRING (e.g. "-10.0" for 10% off). title, value_type, value, target_type, allocation_method, customer_selection, target_selection are required. |
| update_price_ruleA | Update an existing price rule. Side effect: modifies the price rule. Only provided fields are updated. value must remain a string if updating. |
| list_discount_codesA | List discount codes for a specific price rule. Returns paginated results via since_id cursor. Must provide price_rule_id. |
| create_discount_codeA | Create a discount code for a price rule. Side effect: creates a DiscountCode that customers can apply at checkout. Code must be unique across the store. |
| list_refundsA | List refunds for a specific order. Returns full refund records with associated transactions, refund line items, and order adjustments. |
| get_refundA | Get a single refund by ID. Returns full refund record. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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