ezPay E-Invoice MCP Server
README.md
# mcp-ezpay-einvoice
**MCP Server for Taiwan's ezPay E-Invoice API**
[](https://pypi.org/project/mcp-ezpay-einvoice/)
[](https://pypi.org/project/mcp-ezpay-einvoice/)
[](https://opensource.org/licenses/MIT)
[](https://modelcontextprotocol.io/)
[](https://github.com/asgard-ai-platform/mcp-ezpay-einvoice/stargazers)
[](https://github.com/asgard-ai-platform/mcp-ezpay-einvoice/issues)
[](https://github.com/asgard-ai-platform/mcp-ezpay-einvoice/commits/main)
[Traditional Chinese (繁體中文)](README.zh-TW.md)
---
## Overview
An MCP (Model Context Protocol) server that exposes 7 AI-callable tools for the full Taiwan e-invoice lifecycle through the [ezPay](https://www.ezpay.com.tw/) platform. Covers invoice issuance (B2B/B2C), triggering, voiding, allowances (credit notes/refunds), and querying -- all accessible to AI assistants like Claude via the standard MCP protocol.
Part of the [Asgard AI Platform](https://github.com/asgard-ai-platform) open-source ecosystem.
## Features
- **issue_invoice** -- Issue B2B (business) or B2C (individual) e-invoices with full item detail, tax types, carriers, and donation codes
- **trigger_invoice** -- Trigger deferred or scheduled invoices for immediate issuance
- **void_invoice** -- Void issued invoices within the allowed reporting period
- **issue_allowance** -- Issue allowances (credit notes / partial refunds) against existing invoices
- **trigger_allowance** -- Confirm or cancel pending allowances
- **void_allowance** -- Void confirmed allowances
- **query_invoice** -- Query invoice details by invoice number or order number
- **AES-256-CBC encryption** -- Implements the ezPay encrypted form POST protocol (URL-encode, PKCS7 pad, AES encrypt, hex encode)
- **CheckCode verification** -- SHA-256 based response validation
- **Dual environment** -- Supports both test (`cinv.ezpay.com.tw`) and production (`inv.ezpay.com.tw`) environments
## Prerequisites
- Python 3.10 or higher
- An ezPay merchant account with API credentials (MerchantID, HashKey, HashIV)
- Test environment: register at https://cinv.ezpay.com.tw/
- Production environment: register at https://inv.ezpay.com.tw/
## Installation
### Option 1: Install from PyPI
```bash
pip install mcp-ezpay-einvoice
```
Or with [uv](https://github.com/astral-sh/uv):
```bash
uv pip install mcp-ezpay-einvoice
```
### Option 2: Run directly with uvx (no install needed)
```bash
EZPAY_MERCHANT_ID=your_id EZPAY_HASH_KEY=your_key EZPAY_HASH_IV=your_iv \
uvx mcp-ezpay-einvoice
```
### Option 3: Install from source
```bash
git clone https://github.com/asgard-ai-platform/mcp-ezpay-einvoice.git
cd mcp-ezpay-einvoice
uv venv && source .venv/bin/activate
uv pip install -e .
```
## Configuration
Set the following environment variables before running the server:
| Environment Variable | Required | Description |
|---------------------|----------|-------------|
| `EZPAY_MERCHANT_ID` | Yes | Your ezPay merchant ID |
| `EZPAY_HASH_KEY` | Yes | AES-256 encryption key (32 chars) |
| `EZPAY_HASH_IV` | Yes | AES-CBC initialization vector (16 chars) |
| `EZPAY_IS_PRODUCTION` | No | Set to `true` for production. Defaults to `false` (test environment) |
You can also copy the `.env.example` file to `.env` and fill in your values:
```bash
cp .env.example .env
```
## Usage
### Claude Desktop
Add the following to your `claude_desktop_config.json`:
**Using uvx (recommended -- no manual install needed):**
```json
{
"mcpServers": {
"ezpay-einvoice": {
"command": "uvx",
"args": ["mcp-ezpay-einvoice"],
"env": {
"EZPAY_MERCHANT_ID": "your_merchant_id",
"EZPAY_HASH_KEY": "your_hash_key",
"EZPAY_HASH_IV": "your_hash_iv",
"EZPAY_IS_PRODUCTION": "false"
}
}
}
}
```
**Using pip install:**
```json
{
"mcpServers": {
"ezpay-einvoice": {
"command": "mcp-ezpay-einvoice",
"env": {
"EZPAY_MERCHANT_ID": "your_merchant_id",
"EZPAY_HASH_KEY": "your_hash_key",
"EZPAY_HASH_IV": "your_hash_iv",
"EZPAY_IS_PRODUCTION": "false"
}
}
}
}
```
### Claude Code
If you cloned the repo, the `.mcp.json` in the project root is auto-discovered by Claude Code. Just set the environment variables:
```bash
export EZPAY_MERCHANT_ID=your_merchant_id
export EZPAY_HASH_KEY=your_hash_key
export EZPAY_HASH_IV=your_hash_iv
```
Or add the server via CLI:
```bash
claude mcp add ezpay-einvoice -- uvx mcp-ezpay-einvoice
```
### Cursor / Windsurf
Add to your MCP settings (`.cursor/mcp.json` or equivalent):
```json
{
"mcpServers": {
"ezpay-einvoice": {
"command": "uvx",
"args": ["mcp-ezpay-einvoice"],
"env": {
"EZPAY_MERCHANT_ID": "your_merchant_id",
"EZPAY_HASH_KEY": "your_hash_key",
"EZPAY_HASH_IV": "your_hash_iv",
"EZPAY_IS_PRODUCTION": "false"
}
}
}
}
```
### Running Directly
```bash
# With environment variables
EZPAY_MERCHANT_ID=your_id EZPAY_HASH_KEY=your_key EZPAY_HASH_IV=your_iv \
python mcp_server.py
# Or with uvx
EZPAY_MERCHANT_ID=your_id EZPAY_HASH_KEY=your_key EZPAY_HASH_IV=your_iv \
uvx mcp-ezpay-einvoice
```
### Testing
```bash
# Validate credentials and connectivity
python scripts/auth/test_connection.py
# Run all 7 tool E2E tests
python tests/test_all_tools.py
```
## Usage Examples
Once the MCP server is running, an AI assistant (e.g. Claude) can call these tools on your behalf. Here are real-world scenarios showing what you say and how the AI responds.
---
### "Help me issue an invoice for a customer who bought a test product"
> **You:** 幫我開一張發票,客人買了一個測試商品 105 元含稅,要印紙本發票
**AI calls:**
```
issue_invoice(
merchant_order_no = "ORDER20260404001",
buyer_name = "Test Buyer",
category = "B2C",
tax_type = "1",
tax_rate = 5,
amt = 100,
tax_amt = 5,
total_amt = 105,
item_name = "Test Product",
item_count = "1",
item_unit = "pc",
item_price = "105",
item_amt = "105",
print_flag = "Y",
status = "1",
)
```
**Result:** `SUCCESS` -- Invoice `TE00000000` issued, amount NT$105, random code `9393`. The response includes BarCode, QRcodeL, and QRcodeR for printing.
---
### "Issue a B2B invoice with the buyer's tax ID"
> **You:** 幫我開一張 B2B 發票給統編 54352706 的公司,品名是顧問服務費 10000 元加稅 500 元
**AI calls:**
```
issue_invoice(
merchant_order_no = "B2B20260404001",
buyer_name = "範例科技股份有限公司",
buyer_ubn = "54352706",
category = "B2B",
tax_type = "1",
tax_rate = 5,
amt = 10000,
tax_amt = 500,
total_amt = 10500,
item_name = "顧問服務費",
item_count = "1",
item_unit = "式",
item_price = "10000",
item_amt = "10000",
print_flag = "Y",
status = "1",
)
```
**Result:** `SUCCESS` -- B2B invoice issued with buyer UBN `54352706`. The printed invoice includes BarCode and QR codes for tax reporting.
---
### "Issue a B2C invoice with phone barcode carrier"
> **You:** 客人要存手機條碼載具 /ABC1234,不印紙本,品項是拿鐵咖啡 2 杯 65 元、巧克力蛋糕 1 個 85 元
**AI calls:**
```
issue_invoice(
merchant_order_no = "POS20260404001",
buyer_name = "顧客",
category = "B2C",
carrier_type = "0",
carrier_num = "/ABC1234",
tax_type = "1",
tax_rate = 5,
amt = 205,
tax_amt = 10,
total_amt = 215,
item_name = "拿鐵咖啡|巧克力蛋糕",
item_count = "2|1",
item_unit = "杯|個",
item_price = "65|85",
item_amt = "130|85",
print_flag = "N",
status = "1",
)
```
**Result:** `SUCCESS` -- Invoice stored to phone barcode carrier `/ABC1234`, no paper copy printed.
---
### "Query the invoice I just issued"
> **You:** 幫我查一下訂單 ORDER20260404001 的發票資料
**AI calls:**
```
query_invoice(
search_type = "1",
merchant_order_no = "ORDER20260404001",
total_amt = 105,
)
```
**Result:** `SUCCESS` -- Returns full invoice details including invoice number `TE00000000`, buyer info, tax breakdown (Amt: 100, TaxAmt: 5, TotalAmt: 105), item details, invoice status (1=issued), upload status, BarCode, and QR codes.
---
### "I need to issue a refund for a returned item"
> **You:** 客人退了一杯拿鐵 65 元,幫我對發票 TE00000000 開折讓
**AI calls:**
```
issue_allowance(
invoice_no = "TE00000000",
merchant_order_no = "ORDER20260404001",
item_name = "拿鐵咖啡",
item_count = "1",
item_unit = "杯",
item_price = "65",
item_amt = "65",
item_tax_amt = "3",
total_amt = 68,
status = "1",
)
```
**Result:** `SUCCESS` -- Allowance issued against invoice `TE00000000` for NT$68 (65 + 3 tax). The remaining invoice amount is updated accordingly.
---
### "Void that invoice -- it was issued by mistake"
> **You:** 發票 TE00000000 開錯了,幫我作廢,原因是誤開發票
**AI calls:**
```
void_invoice(
invoice_number = "TE00000000",
invalid_reason = "誤開發票",
)
```
**Result:** `SUCCESS` -- Invoice `TE00000000` voided. The platform will notify the buyer and upload the void record to the tax authority.
> **Note:** Invoices can only be voided before the 14th of the next odd month (e.g., invoices issued in March-April can be voided before May 14th).
---
### "Create a scheduled invoice for next week"
> **You:** 幫我建一張預約發票,預計 2026-04-10 開立,金額 1000 元含稅
**AI calls:**
```
issue_invoice(
merchant_order_no = "SCH20260404001",
buyer_name = "預約客戶",
category = "B2C",
tax_type = "1",
tax_rate = 5,
amt = 952,
tax_amt = 48,
total_amt = 1000,
item_name = "訂閱服務",
item_count = "1",
item_unit = "式",
item_price = "1000",
item_amt = "1000",
print_flag = "Y",
status = "3",
create_status_time = "2026-04-10",
)
```
**Result:** `SUCCESS` -- Invoice data saved, scheduled to issue on 2026-04-10. If you need to issue it earlier, use `trigger_invoice` to trigger it immediately.
---
### "Trigger that scheduled invoice now"
> **You:** 預約發票 SCH20260404001 要提前開立,幫我觸發
**AI calls:**
```
trigger_invoice(
invoice_trans_no = "26040423034604894",
merchant_order_no = "SCH20260404001",
total_amt = 1000,
)
```
**Result:** `SUCCESS` -- Scheduled invoice triggered and issued immediately. Invoice number and random code are returned.
## Tools Reference
| Tool | Description | Key Parameters |
|------|-------------|----------------|
| `issue_invoice` | Issue B2B/B2C e-invoice | `category`, `buyer_name`, `tax_type`, `amt`, `total_amt`, `item_name`, `item_count`, `item_price`, `print_flag` |
| `trigger_invoice` | Trigger deferred/scheduled invoice | `invoice_trans_no`, `merchant_order_no`, `total_amt` |
| `void_invoice` | Void an issued invoice | `invoice_number`, `invalid_reason` |
| `issue_allowance` | Issue allowance (credit note) | `invoice_no`, `item_name`, `item_count`, `item_price`, `total_amt` |
| `trigger_allowance` | Confirm or cancel pending allowance | `allowance_no`, `allowance_status` (`C`=confirm, `D`=cancel) |
| `void_allowance` | Void confirmed allowance | `allowance_no`, `invalid_reason` |
| `query_invoice` | Query invoice details | `search_type` (`0`=by invoice number, `1`=by order number) |
## Error Codes Reference
Common ezPay API response status codes:
| Status | Meaning |
|--------|---------|
| `SUCCESS` | Operation completed successfully |
| `LIB10001` | Merchant ID does not exist |
| `LIB10002` | Encryption verification failed |
| `LIB10003` | Timestamp has expired |
| `INV10001` | Duplicate merchant order number |
| `INV10003` | Invoice number does not exist |
| `INV20001` | Invoice has already been voided |
| `INV20002` | Invoice void period has passed |
| `ALW10001` | Allowance amount exceeds invoice amount |
| `ALW10002` | Allowance number does not exist |
For the complete error code list, refer to the [ezPay API documentation](https://inv.ezpay.com.tw/).
## Architecture
```
stdio (JSON-RPC 2.0)
-> mcp_server.py Entry point, side-effect import triggers tool registration
-> app.py FastMCP("mcp-ezpay-einvoice") singleton
-> tools/invoice_tools.py 7 @mcp.tool() decorated functions
-> connectors/ezpay_client.py Encrypted form POST connector
-> auth/ezpay_crypto.py AES-256-CBC encrypt + CheckCode verify
-> config/settings.py Endpoints, URL builder, env var config
```
```
mcp-ezpay-einvoice/
|-- app.py # FastMCP server singleton
|-- mcp_server.py # Entry point (stdio transport)
|-- config/settings.py # API endpoints, URL builder, credentials
|-- connectors/ezpay_client.py # Encrypted form POST connector
|-- auth/ezpay_crypto.py # AES-256-CBC encryption, CheckCode
|-- tools/invoice_tools.py # 7 MCP tools (invoice + allowance lifecycle)
|-- tests/test_all_tools.py # E2E test runner
|-- scripts/auth/test_connection.py # Connection and credential validator
|-- .mcp.json # Claude Code auto-discovery config
`-- .env.example # Environment variable template
```
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions and guidelines.
1. Fork the repository
2. Create a feature branch: `git checkout -b feat/your-feature`
3. Make changes and run tests
4. Submit a pull request
## License
MIT License -- see [LICENSE](LICENSE) for details.
## Part of the Asgard Ecosystem
This server is part of the Asgard AI Platform, connecting AI assistants to real-world services across e-commerce, finance, government data, and more. See the full [Asgard AI Platform](https://github.com/asgard-ai-platform).
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessUnresponsive