Skip to main content
Glama
junter1989k-ai

thailand-invoice-mcp

README.md
# Thailand Invoice MCP 🇹🇭 — How can my AI agent issue a Thai e-Tax Invoice (ใบกำกับภาษี)?

<!-- install-cta -->
## Use it in 60 seconds

Paste this into your MCP client config (Claude Desktop, Cursor, Windsurf, or any MCP-capable agent):

```json
{
  "mcpServers": {
    "thailand-invoice": {
      "type": "http",
      "url": "https://inv-th.wishpool.app/mcp"
    }
  }
}
```

Nothing to install. Credentials, when you need them, travel as HTTP headers on each request and are never stored — see the [threat model](https://mcp.wishpool.app/trust).

### Or run it yourself

Would you rather not send production credentials to a server you do not control? Deploy this identical code to your own account and point your agent at your own URL:

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/junter1989k-ai/thailand-invoice-mcp)

```bash
git clone https://github.com/junter1989k-ai/thailand-invoice-mcp && cd thailand-invoice-mcp && npx vercel --prod
```

MIT-licensed. Self-hosting removes us from the picture entirely, at no cost and with no loss of function.

---

Remote MCP server that lets any AI agent create **Thailand e-Tax Invoices** (**ใบกำกับภาษี / Tax Invoice**) through **Leceipt**, the merchant's own Revenue-Department-authorised e-Tax service provider. It computes **Thai VAT 7%** locally, submits the document to Leceipt, then polls the job and downloads the finished PDF/XML. Stateless, bring-your-own Leceipt API key, never stores anything.

**Live endpoint:** `https://inv-th.wishpool.app/mcp` · Registry: `app.wishpool/thailand-invoice-mcp`

## Honest scope — what this server does and does NOT do

**This wraps Leceipt's submit / job / file calls only. Signing and Revenue-Department filing happen inside your own Leceipt account.**

- ✅ **It does:** compute the THB VAT math (percentVat `7` / `0` / `-1` per line; `includeVat` backs VAT out of gross prices), convert the date to the Buddhist-Era `dateBE`, assemble the Leceipt document, forward `POST /etax/documents/invoices-taxinvoices` with your API key, and translate the response (job → status → file). `query_job` polls; `download_document` fetches the produced file.
- ❌ **It does NOT:** sign your invoice, hold your **digital certificate** (CA — TDID/INET), or act as an **authentication intermediary**. Your company profile and certificate live in **your own Leceipt account**; Leceipt signs each document and files it with the Thai Revenue Department. This server never sees the certificate.

This deliberate split keeps the security-critical material (certificate, RD credentials) entirely inside your Leceipt account — a compromise of this stateless forwarder cannot sign or file invoices.

## Quick start

```json
{
  "mcpServers": {
    "thailand-invoice": {
      "type": "http",
      "url": "https://inv-th.wishpool.app/mcp",
      "headers": {
        "x-leceipt-api-key": "your-leceipt-api-key",
        "x-leceipt-mode": "live"
      }
    }
  }
}
```

Self-register at [my.leceipt.com/signup](https://my.leceipt.com/signup), then generate your **API key** in _Settings → Connection to API → Generating API Key_ (shown only once). **Required** header: `x-leceipt-api-key` (for `live` and for `query_job` / `download_document`). **Optional:** `x-leceipt-mode` and the owner-policy headers below.

**Modes.** `x-leceipt-mode: test` (default) is a **LOCAL dry-run** — `create_invoice` assembles the document and computes VAT totals but does **not** submit (no fiscal effect). `x-leceipt-mode: live` submits to Leceipt → Thai Revenue Department (real fiscal effect). Leceipt has **no separate sandbox host**; for non-fiscal end-to-end testing use a **test certificate** inside your own Leceipt account.

## Tools

| Tool | What it does |
|---|---|
| `create_invoice` | Assemble a Thai e-Tax Invoice from `{ number, customer, items, includeVat?, discount?, date?/dateBE? }`, compute Thai VAT 7% locally, and (in `live` mode) submit to Leceipt. Out: `job_id` (live) or `computed_totals` + `payload` (test dry-run). |
| `query_job` | Poll `GET /etax/jobs/{id}`. Out: `status` **processing** (still creating/signing) or **complete** (with `file_id`). |
| `download_document` | `GET /etax/files/{fileId}`. Out: `{ content_type, size_bytes, content_base64 }` — base64-decode for the signed PDF/XML. |

## Thai VAT & the document fields

- **`items[].vatRate`** (Leceipt `percentVat`): `7` standard VAT 7% · `0` zero-rated (e.g. exports) · `-1` VAT-exempt / non-VAT. Default `7`.
- **`includeVat`**: `false` (default) = unit prices are net, VAT added on top; `true` = prices already include VAT (the server backs it out).
- **`customer`**: `name` + `taxId` required. `taxNumberType` = `TXID` (juristic, 13-digit taxpayer id, default) · `NIDN` (individual, 13-digit national id) · `CCPT` (passport) · `OTHR`. For `TXID`/`NIDN` the `taxId` must be exactly 13 digits. `branchNumber` defaults to head office `00000`.
- **`date`**: pass Gregorian ISO `YYYY-MM-DD` (converted to the Buddhist-Era `dateBE` `dd/MM/yyyy`, year + 543) or `dateBE` directly.

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 **computed grand total** (THB, VAT-inclusive). When a document-level `discount` is set, Leceipt's total is authoritative.

## Endpoints wrapped

Auth: single header `API-Key: <key>` (Azure API Management subscription key — no token exchange).

| Call | Endpoint |
|---|---|
| Submit | `POST https://api.leceipt.com/etax/documents/invoices-taxinvoices` → job `{ id }` |
| Job status | `GET https://api.leceipt.com/etax/jobs/{id}` → `processing` / `complete` |
| Download | `GET https://api.leceipt.com/etax/files/{fileId}` → PDF/XML |

## Develop

```bash
node test/serve.js   # local server on :3251
node test/e2e.js     # protocol + VAT-7% math + CORS + policy + validation asserts + LIVE Leceipt probe
```

The e2e suite makes a **real** call to the Leceipt e-Tax API: a fake API key runs `POST /etax/documents/invoices-taxinvoices` and surfaces Leceipt's native **HTTP 401 `Access denied due to invalid subscription key`** (Azure APIM) — the deepest live verification possible without an onboarded merchant key (Leceipt signs inside the account; we never sign).

## Safety

Pure stateless translation layer. Signing and the Revenue-Department filing stay inside your own Leceipt account; the API key travels per-request in a header, nothing is stored. [Privacy policy](https://inv-th.wishpool.app/privacy).

## One family

Invoices, local payments in 81 countries, and cross-border logistics — one family, same stateless BYO pattern:

- 💳 Payments hub (Taiwan e-invoice 電子發票 included): [mcp.wishpool.app](https://mcp.wishpool.app/)
- 🧾 Invoice family hub: [inv.wishpool.app](https://inv.wishpool.app/)
- 📦 Logistics family hub: [logi.wishpool.app](https://logi.wishpool.app/)

Sister invoice servers: Saudi ZATCA [inv-sa](https://inv-sa.wishpool.app) · Mexico CFDI 4.0 [inv-mx](https://inv-mx.wishpool.app) · Poland KSeF [inv-pl](https://inv-pl.wishpool.app) · Chile DTE [inv-cl](https://inv-cl.wishpool.app) · Brazil NF-e [inv-br](https://inv-br.wishpool.app) · Peru CPE [inv-pe](https://inv-pe.wishpool.app) · India GST [inv-in](https://inv-in.wishpool.app) · Costa Rica [inv-cr](https://inv-cr.wishpool.app) · Malaysia [inv-my](https://inv-my.wishpool.app).

MIT licensed.