retail-ops-mcp-kit
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@retail-ops-mcp-kit show open purchase orders"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
retail-ops-mcp-kit
Four small Python MCP servers — one per enterprise-system class a retail/CPG operations
agent integrates (ERP / supplier network / email / spreadsheet). Each server models a
real named SaaS on the wire (SAP S/4HANA, SPS Commerce, Microsoft Graph, Google Sheets, …),
selectable by env, over one shared synthetic corpus. Every mutation is a staged
write: it validates, records a pending_approval record, returns an approval_id, and
never executes. A separate approve(approval_id, token) needs a human token that lives
only in the server environment and that the agent cannot read — so auto-execution is
impossible by construction. Humans stay in control of what runs versus what waits, designed
into the connector layer.
4 servers, one runtime dependency (
mcp>=1.27.2); everything else is stdlib.FastMCP + hatchling src-layout + uv, Python 3.12, docstring-per-tool.
All data is fully synthetic and methodology-only — see
PROVENANCE.md.
"I know your stack" — tool → real SaaS endpoint
Each server's tool signature is identical across every dialect it models; only the wire envelope changes (the connector seam). A host cannot tell which system answered.
retail-erp-mcp — the three-way match (PO / goods-receipt / invoice)
MCP tool | Kind | SAP S/4HANA (default) | Oracle NetSuite | MS Dynamics 365 F&O |
| read |
|
|
|
| read |
|
|
|
| read |
|
|
|
| read |
|
|
|
| staged-write |
|
|
|
| staged-write |
|
|
|
| webhook seam | Advanced Event Mesh CloudEvent | SuiteScript user-event | Business Events → Azure Event Grid |
Auth handshakes modeled per dialect (documented, never performed): SAP x-csrf-token: Fetch
round-trip + sap-client; NetSuite OAuth1.0a account-ID host; D365 @odata.etag + If-Match.
retail-supplier-mcp — supplier network (ASNs / exceptions / deductions)
MCP tool | Kind | SPS Commerce (default) | Coupa | SAP Ariba |
| read | EDI-856 Universal-JSON: | REST ASN document | cXML |
| read | 214-style status | ASN status document | cXML |
| read | trading-partner | supplier record (derived) | supplier + |
| read | exception-queue rows | — same abstraction — | — same abstraction — |
| staged-write | EDI 810-variant | Coupa |
|
| staged-write | SPS debit doc | Coupa negative invoice |
|
| staged-write | EDI 855 PO-ack ( |
| cXML confirmation |
| webhook seam |
|
| cXML |
Ack/event vocabulary modeled: 850 (inbound PO), 856 (ASN), 810 (invoice), 855 (PO ack),
997 (functional ack); Coupa X-COUPA-API-KEY + scoped OAuth (core.invoice.write); Ariba
dual apikey + OAuth and cXML <Credential domain="NetworkID">.
retail-email-mcp — supplier chase (mailbox threads + staged send)
MCP tool | Kind | Microsoft Graph (default) | Gmail API |
| read |
|
|
| read |
|
|
| staged-write |
|
|
| staged-write |
|
|
| webhook seam | Graph change-notification + | Pub/Sub |
The chase ladder tone (soft → firm → escalation) is recomputed from prior_chases
against policy.chase_ladder — never read from a stored "next step".
retail-sheets-mcp — reconciliation worksheet
MCP tool | Kind | Google Sheets (default) | MS Graph Excel |
| read |
|
|
| read |
|
|
| staged-write |
|
|
| staged-write |
|
|
| webhook seam | Drive | driveItem change-notification (+ 409/423 stale workbook-session) |
Reconciliation is a PO+receipt+invoice+ASN join; Approvals (from approval_actions,
with a recomputed Gated column) and Audit (from audit_entries, with a recomputed
Complete column over policy.audit_required_fields) are also readable.
Related MCP server: conduit-mcp
The safety model — humans control what runs vs. what waits
Every mutation is a staged write. A mutation tool validates its payload against the corpus, calls
staging.stage(...), and returns{result:"pending_approval", approval_id, target_endpoint, payload, validation, status:"pending"}. Nothing executes at stage time.approve(approval_id, token)is the only path to execution, and it requires the human approval token from the server environment (RETAIL_OPS_MCP_APPROVAL_TOKEN). The suppliedtokenis compared withhmac.compare_digest. That token is never a tool return, never in the corpus, never a default — the agent has no channel to read it, so any token it fabricates fails. Absent a configured token, approval fails closed (refused).Auto-execution is impossible by construction. Staging never executes;
approveneeds a secret the agent structurally cannot hold. This is the deliberate inverse of "no creds in argv": here the argv token is the human's proof-of-presence, intentionally withheld from the agent.Execution is fabrication, not persistence. On a valid human approval, the stored
execute(payload)returns a synthetic SaaS response envelope (a SAP journal-entry doc number, a Graph202, a SheetsupdatedCells) recorded on the in-process ledger — it never appends to a JSONL or edits the corpus.A fingerprint invariant makes it testable.
corpus.fingerprint()(sha256 over all fixture bytes) is byte-identical before staging, after staging, and after a full approve+execute.smoke_test.pyasserts exactly this.
Shared on every server: approve, list_pending_approvals, get_approval_status, and an
active_system() tell.
System selection
Each server picks the modeled SaaS dialect from an env var (validated allowlist, safe local default, no credentials):
Server | Env var | Allowlist | Default |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
active_system() reports the wired dialect without leaking any credential.
Data posture
All 11 fixtures are fully synthetic, methodology-only, and vendored verbatim from the
agentic-eval-harness retail_ops set — see PROVENANCE.md. policy.json
numbers are <<TOKEN>>-tagged invented placeholders; exception rows carry only abstract
MARKER_* tokens in benign filler. The suppliers() resource is the one kit-authored
derivation (synthesized from the supplier FK, flagged _derived). No network call is ever
made; there is no runtime path back to the harness.
Develop
just setup # uv sync --extra dev
just check # lint + typecheck + test + smoke (the gate)
just smoke # dependency-light staged-write proof → prints SMOKE OK
just run-erp # run a server on stdio (RETAIL_ERP_SYSTEM overridable)Wire into an MCP host
Claude Desktop (claude_desktop_config.json) or a project .mcp.json:
{
"mcpServers": {
"retail-erp": { "command": "uv", "args": ["run", "retail-erp-mcp"],
"env": { "RETAIL_ERP_SYSTEM": "sap_s4hana",
"RETAIL_OPS_MCP_APPROVAL_TOKEN": "<operator-injected; NOT known to the agent>" } },
"retail-supplier": { "command": "uv", "args": ["run", "retail-supplier-mcp"],
"env": { "RETAIL_SUPPLIER_SYSTEM": "sps_commerce",
"RETAIL_OPS_MCP_APPROVAL_TOKEN": "<operator-injected>" } },
"retail-email": { "command": "uv", "args": ["run", "retail-email-mcp"],
"env": { "RETAIL_EMAIL_SYSTEM": "msgraph",
"RETAIL_OPS_MCP_APPROVAL_TOKEN": "<operator-injected>" } },
"retail-sheets": { "command": "uv", "args": ["run", "retail-sheets-mcp"],
"env": { "RETAIL_SHEETS_SYSTEM": "google_sheets",
"RETAIL_OPS_MCP_APPROVAL_TOKEN": "<operator-injected>" } }
}
}The approval token belongs in the host/operator's env injection (or a secrets manager), and is deliberately withheld from the agent — that withholding is the entire control. Without it configured, all approvals fail closed.
License
Dual-licensed under Apache-2.0 OR MIT (added at T1). All data synthetic — see PROVENANCE.md.
Available Tools
11 toolsactive_systemA
Which ERP dialect is wired (sap_s4hana | netsuite | dynamics365). Lets a host confirm the modeled system without leaking any credential.
Inputs: no named parameters. Returns: object
approveA
Approve a staged write. Requires the human approval token (server env RETAIL_OPS_MCP_APPROVAL_TOKEN) — the agent does not hold it, so an agent-supplied token is refused. On a valid human token the staged action executes (mock) and the record moves pending → executed. Refused if no token is configured (fail closed).
Inputs: token (required), approval_id (required). Returns: object
get_approval_statusA
Return the current record for approval_id (pending | approved | executed | refused).
Inputs: approval_id (required). Returns: object
get_goods_receiptA
Fetch the goods receipt(s) for a PO — the GRN "actual" leg of the match.
- po_id: corpus PO id, e.g. "PO-8801".
Under sap_s4hana returns an API_MATERIAL_DOCUMENT_SRV A_MaterialDocumentHeader
collection with a to_MaterialDocumentItem list (GoodsMovementType 101). `condition`
and `qty_damaged` from the corpus surface as item-level annotations (short-ship /
over-ship / damage / UOM-mismatch traps). Returns an empty collection if nothing was
received.Inputs: po_id (required). Returns: object
get_invoiceA
Fetch one supplier invoice — the "charged" leg of the match.
- invoice_id: corpus invoice id, e.g. "INV-01".
Under sap_s4hana returns an API_SUPPLIERINVOICE_PROCESS_SRV A_SupplierInvoice with a
to_SuplrInvcItemPurOrdRef line referencing the PO. Carries
SupplierInvoiceIDByInvcgParty for duplicate-invoice detection (the PO-8808 trap:
INV-08a and INV-08b bill the same PO). Under netsuite: a vendorBill; under
dynamics365: a VendorInvoiceHeader. Raises if the invoice is unknown.Inputs: invoice_id (required). Returns: object
get_purchase_orderA
Fetch one purchase order as the configured ERP would return it.
- po_id: corpus PO id, e.g. "PO-8801". Join key across the whole kit.
Returns the ERP-native PO envelope. Under sap_s4hana: an
API_PURCHASEORDER_PROCESS_SRV `{"d": {A_PurchaseOrder ... to_PurchaseOrderItem}}`
document with stringified decimals and /Date(ms)/ dates. Under netsuite: a REST
Record `purchaseOrder` with JSON numbers and an `item.items[]` sublist. Under
dynamics365: an OData V4 PurchaseOrderHeadersV2 entity with `@odata.etag` and
`dataAreaId`. Raises if the PO is unknown.Inputs: po_id (required). Returns: object
list_open_posA
List purchase orders with no fully-matching invoice yet — the open three-way-match workqueue.
Takes no args. A PO is "open" when its billed qty (summed across invoices) is less
than the ordered qty. Under sap_s4hana returns an OData collection
`{"d": {"results": [A_PurchaseOrder, ...], "__count": "N"}}`; under netsuite a
`{items, hasMore, offset, totalResults}` page; under dynamics365 an OData V4
`{"value": [...]}`.Inputs: no named parameters. Returns: object
list_pending_approvalsA
List this server's staged, not-yet-approved writes (status=pending). Read-only. Lets a human see what awaits approval; the agent can see them but cannot approve.
Inputs: no named parameters. Returns: result
simulate_po_eventA
Synthesize an inbound ERP change event for the given PO (read-only; no state change).
- po_id: corpus PO id, e.g. "PO-8801".
- event_type: e.g. "Created" | "Changed".
Under sap_s4hana returns a SAP Advanced Event Mesh CloudEvents envelope on topic
sap/s4/beh/purchaseorder/v1/PurchaseOrder/<event_type>/v1 with payload {PurchaseOrder}.
Under netsuite: a SuiteScript user-event callback; under dynamics365: a Business Events
envelope. Use it to exercise event-driven wiring.Inputs: po_id (required), event_type. Returns: object
stage_invoice_paymentA
STAGE (do not execute) a supplier-invoice payment for human approval.
- invoice_id: corpus invoice id, e.g. "INV-01".
- amount: payment amount; echoed into the staged payload for the human to confirm.
- currency: ISO code, default "USD".
Validates the invoice exists and the amount is numeric, then records a pending record
and returns {result:"pending_approval", approval_id, target_endpoint, ...}. Models SAP
API_JOURNALENTRYITEMBASIC_SRV vendor payment. THIS DOES NOT PAY ANYTHING — a human must
approve(approval_id, token) to execute. 'pay_invoice' is in policy.gated_actions, so
validation.policy_gated is true.Inputs: amount (required), invoice_id (required), currency. Returns: object
stage_po_amendmentA
STAGE (do not execute) a PO amendment (qty and/or price) for human approval.
- po_id: corpus PO id, e.g. "PO-8801".
- new_qty: optional new order quantity.
- new_unit_price: optional new net price per unit.
Validates the PO exists and at least one change is present, records a pending record,
returns pending_approval + approval_id. Models a PATCH to API_PURCHASEORDER_PROCESS_SRV
A_PurchaseOrder (If-Match / x-csrf-token write). 'po_amendment' is gated. Executes only
on human approval.Inputs: po_id (required), new_qty, new_unit_price. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 11 tools
Each tool targets a distinct resource or action: fetching PO, invoice, goods receipt, approval status, listing open POs, staging payment/amendment, simulating events, approving, and listing approvals. Only minor potential confusion exists between get_approval_status and list_pending_approvals, but their inputs (approval_id vs listing) clarify the difference.
The set mostly follows a verb_noun pattern (get_*, list_*, stage_*, simulate_*), but 'active_system' and 'approve' deviate from that pattern. These deviations are minor and the intent remains readable.
With 11 tools, the server is well-scoped for its purpose: covering purchase order, invoice, goods receipt, approval workflow, and simulation. No tool is redundant; each adds a distinct capability.
The toolkit covers the core three-way match (PO, GR, invoice), open PO listing, staging of payment and PO amendments, and the approval flow. Minor gaps include no explicit reject/cancel for staged actions and no PO/invoice creation, but these may be outside the server's intended scope.
Maintenance
Related MCP Connectors
MCP server for MarketTime — wholesale orders, customers, catalog and shipment tracking.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Governed retail, FMCG, and CPG operational tools: runs, cases, approvals, audit-ready execution.
Guarded MCP server for agent-readable business truth, provenance, readiness, and discovery.
Related MCP Servers
- FlicenseAqualityDmaintenanceMCP server exposing the StoreLink grocery stocking API as tools for category buyer workflows, including inventory checks and replenishment orders.5-
- AlicenseNot gradedqualityDmaintenanceA governance-layer MCP server that gates AI writes to systems of record, requiring human signatures and providing a tamper-evident audit trail. It offers read, propose, approve, commit, and audit tools, with demo and Xero backends.22 npmMIT
- FlicenseNot gradedqualityDmaintenanceMCP server that provides mock APIs and deterministic seed data for ERP/OMS, WMS, and CRM systems, enabling supply chain data exploration and integration testing.-
- AlicenseNot gradedqualityCmaintenanceSafe-write Shopify operations MCP server with plan-before-execute writes, out-of-band approval, and tamper-evident audit trail.47 npmMIT