Skip to main content
Glama
igun997

paper-invoice-mcp

by igun997

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 tab

  • Click any request to api.paper.id → Headers → Request Headers

  • Copy the value after Authorization: Bearer (the eyJ... part)

2. Get your Company ID and User ID:

  • Same DevTools window → Response tab of any request

  • Look for body.user.uuid (User ID) and body.user.company_id (Company ID)

  • Or: set PAPERID_TOKEN first, then call tool paperid_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 with PAPERID_DB_PATH.


Features

  • Partner management — create, read, list, search, update, delete

  • Invoice management — create, read, list, send (WA/email/SMS), generate QRIS, delete

  • Notes & Termsnotes (Keterangan) and terms (Syarat & Ketentuan) support plain text or HTML

  • Token persistence — JWT stored in SQLite (~/.paperid-mcp/tokens.db)

  • Auto token refresh — re-logins on 401 when PAPERID_PHONE + PAPERID_PASSWORD are set

  • Reporting — 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 build

Configuration

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.db

Security: Never commit .env to version control. The .gitignore excludes 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 use

MCP 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

paperid_login

Login with phone + password

paperid_refresh_token

Re-login and update SQLite token

paperid_get_token_info

Check token expiry (does not expose raw JWT)

paperid_get_current_user

Get authenticated user info

Partners

Tool

Description

paperid_get_partners

List partners with pagination

paperid_search_partners

Search partners by name

paperid_get_partner

Get partner by UUID

paperid_create_partner

Create new partner

paperid_update_partner

Update existing partner

paperid_delete_partner

Delete partner

paperid_get_partner_contacts

Get partner contacts

paperid_get_partner_bank_accounts

Get partner bank accounts

paperid_get_next_partner_number

Get next auto-generated partner number

Invoices

Tool

Description

paperid_get_sales_invoices

List invoices with filters + pagination

paperid_create_invoice

Create invoice (supports notes + terms)

paperid_get_invoice

Get invoice by UUID

paperid_get_invoice_pdf

Get invoice PDF data

paperid_send_invoice

Send via WhatsApp / Email / SMS

paperid_generate_invoice_qris

Generate QRIS payment QR code

paperid_delete_invoice

Delete invoice

Reporting

Tool

Description

paperid_get_account_receivable

AR insight (unpaid, overdue)

paperid_get_account_payable

AP insight

paperid_get_profit_loss

Profit & Loss report

paperid_get_company

Company details

paperid_get_kyc_status

KYC verification status

paperid_get_user_package

Subscription package info

paperid_get_onboarding_status

Onboarding completion

paperid_get_referral_link

Referral code & link

paperid_get_notifications

User notifications

paperid_get_dashboard_todos

Dashboard to-do list

paperid_get_banners

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

paperid://docs/overview

Overview, tool groups, typical workflow

paperid://docs/auth

Login flow, SQLite persistence, auto-refresh

paperid://docs/jwt-only

JWT-only setup, config examples, expiry handling

paperid://docs/partners

Partner schema, field types, phone format, base paths

paperid://docs/invoices

Invoice schema, line items, notes/terms, status codes

paperid://docs/api-reference

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.json

Development

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