paper-invoice-mcp
paper-invoice-mcp
MCP (Model Context Protocol) server for Paper.id — Indonesian invoicing & accounting platform.
Exposes 31 tools covering partner CRUD, invoice CRUD, send invoice, QRIS payment, and reporting.
Token is persisted in SQLite and auto-refreshed on 401 — no manual token management needed.
Quick Setup
Option A — JWT token only
Best for quick testing or CI. No password stored — just paste your token.
1. Get your token from Chrome DevTools:
Open app.paper.id → DevTools (
F12) → Network tabClick any request to
api.paper.id→ Headers → Request HeadersCopy the value after
Authorization: Bearer(theeyJ...part)
2. Get your Company ID and User ID:
Same DevTools window → Response tab of any request
Look for
body.user.uuid(User ID) andbody.user.company_id(Company ID)Or: set
PAPERID_TOKENfirst, then call toolpaperid_get_current_user— it returns both
3. Add to your MCP client config:
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json)
{
"mcpServers": {
"paperid": {
"command": "node",
"args": ["/absolute/path/to/paper-invoice-mcp/dist/index.js"],
"env": {
"PAPERID_TOKEN": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.your.token",
"PAPERID_COMPANY_ID": "37e0eae0-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"PAPERID_USER_ID": "3f5a9896-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}Cursor (.cursor/mcp.json or ~/.cursor/mcp.json)
{
"mcpServers": {
"paperid": {
"command": "node",
"args": ["/absolute/path/to/paper-invoice-mcp/dist/index.js"],
"env": {
"PAPERID_TOKEN": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.your.token",
"PAPERID_COMPANY_ID": "37e0eae0-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"PAPERID_USER_ID": "3f5a9896-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}VS Code (.vscode/mcp.json)
{
"servers": {
"paperid": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/paper-invoice-mcp/dist/index.js"],
"env": {
"PAPERID_TOKEN": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.your.token",
"PAPERID_COMPANY_ID": "37e0eae0-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"PAPERID_USER_ID": "3f5a9896-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}JWT expires in ~30 days. When it does, copy a fresh token from DevTools and update the config.
Full step-by-step with troubleshooting:examples/jwt-only.md
Option B — Phone + Password (auto-refresh)
Best for long-running assistants. Token saved to SQLite, silently refreshed on 401.
Claude Desktop / Cursor / VS Code:
{
"mcpServers": {
"paperid": {
"command": "node",
"args": ["/absolute/path/to/paper-invoice-mcp/dist/index.js"],
"env": {
"PAPERID_PHONE": "08xxxxxxxxxx",
"PAPERID_PASSWORD": "your_password_here"
}
}
}
}First run — call paperid_login (or paperid_refresh_token) once to store the JWT in SQLite.
After that, the server handles everything automatically.
Token persisted at
~/.paperid-mcp/tokens.db. Override path withPAPERID_DB_PATH.
Features
Partner management — create, read, list, search, update, delete
Invoice management — create, read, list, send (WA/email/SMS), generate QRIS, delete
Notes & Terms —
notes(Keterangan) andterms(Syarat & Ketentuan) support plain text or HTMLToken persistence — JWT stored in SQLite (
~/.paperid-mcp/tokens.db)Auto token refresh — re-logins on 401 when
PAPERID_PHONE+PAPERID_PASSWORDare setReporting — account receivable/payable insights, profit & loss
Requirements
Node.js ≥ 18 (uses built-in
FormData)Bun (recommended) or npm
Installation
git clone git@github.com:igun997/paper-invoice-mcp.git
cd paper-invoice-mcp
bun install
bun run buildConfiguration
Create a .env file (or set environment variables):
# Required for auto token refresh
PAPERID_PHONE=08xxxxxxxxxx
PAPERID_PASSWORD=your_password_here
# Optional overrides (loaded from SQLite if omitted)
PAPERID_TOKEN= # JWT token (leave blank to use SQLite)
PAPERID_COMPANY_ID= # Company UUID
PAPERID_USER_ID= # User UUID
# Optional: custom SQLite path (default: ~/.paperid-mcp/tokens.db)
PAPERID_DB_PATH=/path/to/tokens.dbSecurity: Never commit
.envto version control. The.gitignoreexcludes it.
Token Lifecycle
First run: PAPERID_PHONE + PAPERID_PASSWORD set
→ paperid_login (or paperid_refresh_token tool)
→ JWT saved to SQLite
Subsequent runs:
→ JWT loaded from SQLite automatically
→ On 401: re-login silently, retry request
Manual refresh:
→ Call paperid_refresh_token tool
→ Or: set PAPERID_TOKEN env var for one-time useMCP Client Configuration
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"paperid": {
"command": "node",
"args": ["/absolute/path/to/paper-invoice-mcp/dist/index.js"],
"env": {
"PAPERID_PHONE": "08xxxxxxxxxx",
"PAPERID_PASSWORD": "your_password_here"
}
}
}
}Cursor / VS Code (.cursor/mcp.json or .vscode/mcp.json)
{
"mcpServers": {
"paperid": {
"command": "node",
"args": ["/absolute/path/to/paper-invoice-mcp/dist/index.js"],
"env": {
"PAPERID_PHONE": "08xxxxxxxxxx",
"PAPERID_PASSWORD": "your_password_here"
}
}
}
}Using Bun directly
{
"mcpServers": {
"paperid": {
"command": "bun",
"args": ["run", "/absolute/path/to/paper-invoice-mcp/src/index.ts"],
"env": {
"PAPERID_PHONE": "08xxxxxxxxxx",
"PAPERID_PASSWORD": "your_password_here"
}
}
}
}Available Tools (31 total)
Auth & Token
Tool | Description |
| Login with phone + password |
| Re-login and update SQLite token |
| Check token expiry (does not expose raw JWT) |
| Get authenticated user info |
Partners
Tool | Description |
| List partners with pagination |
| Search partners by name |
| Get partner by UUID |
| Create new partner |
| Update existing partner |
| Delete partner |
| Get partner contacts |
| Get partner bank accounts |
| Get next auto-generated partner number |
Invoices
Tool | Description |
| List invoices with filters + pagination |
| Create invoice (supports notes + terms) |
| Get invoice by UUID |
| Get invoice PDF data |
| Send via WhatsApp / Email / SMS |
| Generate QRIS payment QR code |
| Delete invoice |
Reporting
Tool | Description |
| AR insight (unpaid, overdue) |
| AP insight |
| Profit & Loss report |
| Company details |
| KYC verification status |
| Subscription package info |
| Onboarding completion |
| Referral code & link |
| User notifications |
| Dashboard to-do list |
| Promotional banners |
Usage Examples
Create Invoice with Notes & Terms
{
"tool": "paperid_create_invoice",
"arguments": {
"partner_id": "uuid-of-partner",
"partner_name": "PT Mitra Sejati",
"number": "INV/2026/0012",
"invoice_date": "2026-05-14",
"due_date": "2026-06-13",
"items": [
{
"item_name": "Jasa Konsultasi",
"item_description": "Konsultasi IT bulan Mei 2026",
"quantity": 1,
"price": 5000000
}
],
"notes": "Transfer ke BCA 1234567890 a/n PT Cipta Dua Saudara",
"terms": "Pembayaran jatuh tempo 30 hari. Keterlambatan dikenakan denda 2%/bulan.",
"status": 4
}
}notes and terms accept plain text (auto-wrapped in <p> tags) or raw HTML.
Send Invoice via WhatsApp
{
"tool": "paperid_send_invoice",
"arguments": {
"invoiceId": "uuid-of-invoice",
"whatsapp": { "number": "6281234567890" }
}
}Refresh Token
{
"tool": "paperid_refresh_token",
"arguments": {}
}Returns expiry info. Raw JWT is never returned by any tool.
MCP Resources
The server exposes 6 resources AI clients can read for accurate tool usage:
URI | Description |
| Overview, tool groups, typical workflow |
| Login flow, SQLite persistence, auto-refresh |
| JWT-only setup, config examples, expiry handling |
| Partner schema, field types, phone format, base paths |
| Invoice schema, line items, notes/terms, status codes |
| All 31 HTTP endpoints with methods and paths |
Project Structure
paper-invoice-mcp/
├── src/
│ ├── index.ts # Entrypoint — connects MCP server to stdio
│ ├── server-factory.ts # createMcpServer() — all tools, handlers, resources
│ ├── client.ts # Paper.id API client (axios)
│ └── token-store.ts # SQLite token persistence (better-sqlite3)
├── examples/
│ └── jwt-only.md # JWT-only setup guide (step-by-step + all client configs)
├── .github/
│ └── workflows/ci.yml # CI: build+resources (all PRs), e2e (merge to master)
├── dist/ # Compiled JS (gitignored)
├── .env.example # Sample env vars (no real credentials)
├── tsconfig.json
└── package.jsonDevelopment
bun run build # Compile TypeScript → dist/
bun run dev # Run with tsx (no compile step)SQLite Schema
CREATE TABLE tokens (
phone TEXT PRIMARY KEY,
token TEXT NOT NULL,
user_id TEXT NOT NULL,
company_id TEXT NOT NULL,
expires_at INTEGER NOT NULL, -- Unix timestamp
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);Token auto-refreshes when expired (with 5-minute buffer) if credentials are in env.
License
MIT