Saudi Invoice MCP 🇸🇦 — How can my AI agent report or clear a ZATCA Fatoora e-invoice in Saudi Arabia?
Remote MCP server that lets any AI agent submit Saudi Arabia tax invoices to ZATCA (Fatoora Phase 2) — the Zakat, Tax and Customs Authority national e-invoicing gateway. It forwards an already-signed UBL 2.1 invoice to the reporting (B2C simplified) or clearance (B2B standard) endpoint and returns ZATCA's response. Stateless, bring-your-own credentials, never stores anything.
Live endpoint: https://inv-sa.wishpool.app/mcp · Registry: app.wishpool/saudi-invoice-mcp
Honest scope — what this server does and does NOT do
This wraps the submission calls only. Signing and the hash-chain stay merchant-side.
- ✅ It does: forward
{ invoiceHash, uuid, invoice(base64) } to the ZATCA reporting/clearance/compliance endpoints, add the mandatory headers (Accept-Version: V2, Accept-Language: en, HTTP Basic auth from your CSID, Clearance-Status: 1 on clearance), and translate the response (status, warnings, cleared XML).
- ❌ It does NOT: build your XML, apply your ECDSA signature, compute the invoice hash, or maintain the ICV counter / PIH previous-invoice-hash chain. Those are cryptographic state that must live in your EGS (E-invoicing Generation Solution), next to your private key. You sign each invoice and pass the signed bytes here.
This deliberate split keeps the security-critical material (private key, hash-chain) entirely on your side — a compromise of this stateless forwarder cannot forge, replay, or reorder your invoices.
Quick start
{
"mcpServers": {
"saudi-invoice": {
"type": "http",
"url": "https://inv-sa.wishpool.app/mcp",
"headers": {
"x-zatca-csid-token": "your-csid-binarySecurityToken",
"x-zatca-csid-secret": "your-csid-secret",
"x-zatca-mode": "core"
}
}
}
}
Get your CSID (a binarySecurityToken + secret) from ZATCA EGS onboarding. Omit x-zatca-mode to stay in the developer-portal sandbox (no fiscal effect); simulation = simulation env; core = production. Required headers: x-zatca-csid-token + x-zatca-csid-secret. Optional: x-zatca-mode (default developer) and the owner-policy headers below.
| Tool | What it does |
|---|
report_invoice | Simplified invoice (B2C, فاتورة مبسطة) → ZATCA reporting/single. You issue to the buyer, then report within 24h. In: invoiceHash, uuid, invoice (base64 signed UBL). Out: reporting_status REPORTED (or accepted-with-warnings). |
clear_invoice | Standard invoice (B2B, فاتورة ضريبية) → ZATCA clearance/single (sent with Clearance-Status: 1). ZATCA stamps it before you issue it. Out: clearance_status CLEARED + cleared_invoice (the ZATCA-stamped base64 XML you hand to the buyer). |
check_invoice_compliance | Validate a signed invoice against ZATCA compliance/invoices without reporting/clearing (onboarding / debugging). No fiscal effect in any mode. |
All three take the same envelope: { invoiceHash, uuid, invoice }.
Owner policy guardrails ride optional headers (x-agentpay-max-amount, x-agentpay-approval-above, x-agentpay-allowed-tools) — set by the human owner in client config; the agent cannot relax them. Cap scope: the amount gate reads the invoice grand total from cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount (SAR) inside the signed UBL. If that element cannot be parsed from the base64 payload, the amount gate is skipped for that call (the tool allow-list still applies unconditionally); this is by design because the invoice is opaque signed XML we do not parse in full.
Endpoints wrapped (per env)
https://gw-fatoora.zatca.gov.sa/e-invoicing/{developer-portal | simulation | core}/…
x-zatca-mode | Path segment | Reporting | Clearance | Compliance |
|---|
developer (default) | developer-portal | /invoices/reporting/single | /invoices/clearance/single | /compliance/invoices |
simulation | simulation | same | same | same |
core (production) | core | same | same | same |
ZATCA HTTP responses are passed through: 200 cleared/reported · 202 accepted-with-warnings · 303 standard invoice sent while clearance is off (use report_invoice) · 400 invalid request · 401 bad CSID · 413/429/500/503/504 transient (resend with a new UUID/hash/ICV if rejected).
Develop
node test/serve.js
node test/e2e.js
The e2e suite makes real calls to the ZATCA developer-portal gateway: a fake CSID runs the full HTTP Basic auth against reporting/single, clearance/single and compliance/invoices and surfaces ZATCA's native 401 Unauthorized — the deepest live verification possible without an onboarded CSID (we never sign, so we cannot mint a valid document).
Safety
Pure stateless translation layer straight to the government ZATCA gateway. ECDSA signing and the ICV/PIH hash-chain stay merchant-side; the CSID token and secret travel per-request in headers, nothing is stored. Privacy policy.
Sister servers
Invoices: Poland KSeF inv-pl · Mexico CFDI 4.0 inv-mx · Chile DTE inv-cl · Brazil NF-e inv-br · Peru CPE inv-pe · India GST inv-in · Local payments in 81 countries, one family: mcp.wishpool.app · Taiwan e-invoice 電子發票 included.
MIT licensed.