mercury-invoicing-mcp
# π¦ mercury-invoicing-mcp
> Mercury Banking MCP server with full **Invoicing API** support β first MCP to expose Mercury's accounts receivable endpoints.
[](https://github.com/klodr/mercury-invoicing-mcp/actions/workflows/ci.yml)
[](https://github.com/klodr/mercury-invoicing-mcp/actions/workflows/codeql.yml)
[](https://vitest.dev)
[](https://codecov.io/gh/klodr/mercury-invoicing-mcp)
[](https://scorecard.dev/viewer/?uri=github.com/klodr/mercury-invoicing-mcp)
[](https://www.bestpractices.dev/projects/12575)
[](https://socket.dev/npm/package/mercury-invoicing-mcp)
[](https://coderabbit.ai)
[](https://www.npmjs.com/package/mercury-invoicing-mcp)
[](https://www.npmjs.com/package/mercury-invoicing-mcp)
[](https://nodejs.org)
[](https://modelcontextprotocol.io)
[](https://modelcontextprotocol.io)
[](https://github.com/klodr/mercury-invoicing-mcp/pulls)
[](https://github.com/sponsors/klodr)
[](https://www.patreon.com/klodr)
[](https://ko-fi.com/klodr)
A Model Context Protocol (MCP) server giving AI assistants (Claude, Cursor, Continue, etc.) full programmatic access to your **Mercury** business banking account, including the **Invoicing API** (one-shot + recurring) which is missing from every other Mercury MCP.
## β¨ Why this MCP?
| Capability | Official Mercury MCP | dragonkhoi/mercury-mcp | **mercury-invoicing-mcp** |
|---|:---:|:---:|:---:|
| Banking read (accounts, transactions, statements) | β
| β
| β
|
| Banking write (send_money, recipients) | β | β
| β
|
| **Internal transfers between your own Mercury accounts** | β | β | β
|
| **Invoicing API (create, update, cancel, attachments)** | β | β | β
|
| **Customers AR + recurring invoices** | β | β | β
|
| Webhooks full CRUD (incl. `update_webhook`) | β | β | β
|
| Built-in safeguards (rate limit, dry-run, redacted audit log) | β | β | β
|
| Stable token (no frequent re-auth, IP-allowlistable) | β | β
| β
|
| Open source (MIT) | β | β
| β
|
| Node.js floor | N/A (hosted) | β from Node 14 EOL (2023) | β
LTS (`>=24.18.1`), tested on 24 and 26 |
| Total tools exposed | ~10 | ~11 | **39** |
For pure read-only consultation, prefer the [official Mercury MCP](https://docs.mercury.com/docs/what-is-mercury-mcp). Use this one when you need to **automate invoicing, write to Mercury, or expose Mercury to LLM agents safely**.
## π¦ Installation
```bash
npm install -g mercury-invoicing-mcp
```
Or use directly with `npx`:
```bash
npx mercury-invoicing-mcp
```
## βοΈ Configuration
The server reads `MERCURY_API_KEY` from the environment. Get your API key at [Mercury Settings β API Tokens](https://app.mercury.com/settings/tokens).
<a id="right-sizing-the-token"></a>
### π Right-sizing the token
Mercury exposes **fine-grained per-resource scopes** at token creation β not a single read/write toggle. Pick exactly what your use case needs and Mercury enforces the rest server-side: a tool called without the right scope returns `403`, which the MCP surfaces as a clean `isError: true` response (with a Mercury Plus hint when relevant).
When you create the token, you choose:
- **Which accounts** the token can see (one, several, or all).
- **Read or Write per resource family**: Accounts, Transactions, Recipients, Send Money, Cards, Statements, Treasury, Invoicing (AR), Webhooks.
Common scope recipes for this MCP:
| Use case | Scopes to grant |
|---|---|
| Read-only consultation (dashboards, chat-channel bots) | Read on `accounts`, `transactions`, `statements`, `cards`, `treasury` β nothing else |
| Bookkeeping (categorise transactions) | Read everywhere + Write on `transactions` (for `update_transaction`) |
| Invoicing automation | Read on `accounts` + Write on `invoices`, `customers` (Mercury Plus required) |
| Recipients management | + Write on `recipients` |
| Internal transfers between your own accounts | + Write on `send_money` (used for `create_internal_transfer`) |
| Outbound send-money requests | + Write on `send_money` (creates the request β see safety note below) |
| Webhooks-only ops | Write on `webhooks` only |
### β οΈ Important: outbound payments depend on YOUR Mercury approval policy
Whether an outbound payment created via this MCP executes immediately or waits for human approval is **not controlled by the MCP** β it is enforced by your Mercury workspace's approval policy (Settings β Approvals on app.mercury.com). The MCP can only ever *create* the API call; what Mercury does with it is up to your workspace configuration.
The three money tools behave differently:
- **`mercury_request_send_money`** β always creates a **pending approval request** in Mercury, regardless of workspace policy. Designed for the "submit, then wait for an approver" workflow.
- **`mercury_send_money`** β submits a payment. It executes immediately *or* gets queued for approval, **depending on your workspace's approval rules** (amount thresholds, account-specific rules, required approvers). On a workspace configured with a $0 approval threshold, every outbound payment waits for human sign-off in the Mercury web/mobile app. On a more permissive workspace, smaller payments may settle without re-prompting.
- **`mercury_create_internal_transfer`** β moves money between two accounts **you already own** inside the same Mercury organisation. No external recipient, no approval workflow.
β **Set a strict approval policy in Mercury** (e.g. require approval for any outbound payment, regardless of amount) if you intend to expose write tools to an agent. The MCP's per-call rate limits and dry-run mode are useful belt-and-braces, but the authoritative gate is Mercury's approval policy. If a prompt-injected agent calls `send_money`, the safety of that call depends entirely on what Mercury would have done if the same payload arrived from any other API caller.
### π§ͺ Sandbox mode
To test against Mercury's [sandbox environment](https://docs.mercury.com/docs/using-mercury-sandbox) (no real money, pre-populated dummy data), just use a sandbox token:
```bash
MERCURY_API_KEY=secret-token:mercury_sandbox_xxxxxxxxxxxxxxxx
```
The MCP **auto-detects sandbox tokens** (those starting with `mercury_sandbox_`) and points to `https://api-sandbox.mercury.com/api/v1` automatically.
To override the base URL explicitly (e.g. for a self-hosted proxy):
```bash
MERCURY_API_BASE_URL=https://your-proxy.example.com/api/v1
```
### π‘οΈ Behind a local proxy that answers DNS itself
Some proxies answer DNS with their own addresses (Surge in enhanced mode hands out `198.18.0.0/15`), and the SSRF guard refuses them, as it does any address that is not public. If you trust that proxy, list its range:
```bash
MCP_TRUSTED_CIDRS=198.18.0.0/15
```
Comma-separated IPs or CIDR ranges; prefer a single IP whenever the proxy allows it. Refused at startup: a malformed entry, more than 131,072 addresses in all (one range: IPv4 `/15` at most, IPv6 `/111`), and anything that touches loopback, link-local, `0.0.0.0/8` or an IPv6 range carrying an IPv4 (an IPv4-mapped address such as `::ffff:10.47.83.1` counts as its IPv4). Addresses in these ranges are no longer checked by the guard: the trust moves to the proxy. Unset: no exception.
### π€ Claude Desktop / Claude Code
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (or `~/.claude.json` for Claude Code):
```json
{
"mcpServers": {
"mercury-invoicing": {
"command": "npx",
"args": ["-y", "mercury-invoicing-mcp"],
"env": {
"MERCURY_API_KEY": "secret-token:mercury_production_xxxxxxxxxxxxxxxx"
}
}
}
}
```
### π±οΈ Cursor
Add to `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"mercury-invoicing": {
"command": "npx",
"args": ["-y", "mercury-invoicing-mcp"],
"env": {
"MERCURY_API_KEY": "secret-token:..."
}
}
}
}
```
### π¦ OpenClaw
[OpenClaw](https://docs.openclaw.ai) is an open-source self-hosted agent platform that supports MCP via `@modelcontextprotocol/sdk`. Add to `~/.openclaw/openclaw.json`:
```json
{
"mcpServers": {
"mercury-invoicing": {
"command": "npx",
"args": ["-y", "mercury-invoicing-mcp"],
"env": {
"MERCURY_API_KEY": "secret-token:..."
}
}
}
}
```
Restart the gateway (`docker restart openclaw-openclaw-gateway-1` or your equivalent). All tools become available to all your OpenClaw agents.
> **Tip**: For agents exposed to untrusted channels (WhatsApp, Telegram, Slack, incoming emailβ¦), grant the token only the scopes the channel actually needs. Outbound payments still require explicit human approval in the Mercury app β but minimising scopes avoids noise (spurious pending requests) and reduces what an attacker could exfiltrate via reads. See [Right-sizing the token](#right-sizing-the-token) for recipe per use case.
## π οΈ Tools (39 total)
### π¦ Banking β Accounts
- `mercury_list_accounts`, `mercury_get_account`
- `mercury_list_cards`
- `mercury_get_organization`
- `mercury_list_categories`
### π³ Banking β IO Credit
- `mercury_list_credit_accounts` β wraps `GET /credit`, documented under [Credit βΊ List all credit accounts](https://docs.mercury.com/reference/credit) in the Mercury API reference. Returns IO Credit card accounts, which `mercury_list_accounts` filters out server-side.
- `mercury_list_credit_transactions` β wraps `GET /account/{id}/transactions` **(SINGULAR path)**, the path used for IO Credit transactions including pending card authorisations. Note: distinct from the plural `/accounts/{id}/transactions` used for deposit accounts.
- `mercury_list_credit_statements` β wraps `GET /api/v2/account/{id}/statements`. IO Credit statements are served by the **v2** endpoint only: the v1 statements path answers 404 for credit accounts.
### πΈ Banking β Transactions
- `mercury_list_transactions`, `mercury_get_transaction`
- `mercury_get_transaction_attachment` β the only tool that returns a receipt's download URL
- `mercury_update_transaction` (note, category)
- `mercury_send_money`, `mercury_request_send_money`
- `mercury_create_internal_transfer` (between your own Mercury accounts)
Transaction lists return a **compact projection** by default: identifying
fields plus `hasAttachment`, so you can spot which transactions still lack
a receipt. Measured on a real account, this cuts the payload to ~20% of
Mercury's raw response. Pass `detail: "full"` for every field except the
attachment URLs, which no mode returns.
Receipt URLs are **never** included in a transaction payload, in either
mode. Mercury's are pre-signed S3 links β ~2.4 kB each, and a bearer
credential granting unauthenticated access to the document for ~12 hours.
Returning one on every read would both dominate the payload and scatter
short-lived credentials through logs and model context. Ask for one
deliberately, per transaction, via `mercury_get_transaction_attachment`.
### π₯ Banking β Recipients
- `mercury_list_recipients`, `mercury_add_recipient`, `mercury_update_recipient`
### π Banking β Statements
- `mercury_list_statements`
### ποΈ Treasury
- `mercury_get_treasury`
- `mercury_list_treasury_transactions`
- `mercury_list_treasury_statements`
### π§Ύ Invoicing (Accounts Receivable)
> β οΈ **Mercury Plus plan required.** The Invoicing & Customers (AR) APIs are only available on Mercury's [Plus plan](https://mercury.com/pricing) (or higher). Calls to these tools return `403 Forbidden` on Free or Standard plans. The other tools (banking, treasury, webhooks) work on every plan.
- `mercury_list_invoices`, `mercury_get_invoice`
- `mercury_create_invoice`, `mercury_update_invoice`
- `mercury_cancel_invoice`
- `mercury_list_invoice_attachments`
- `mercury_get_invoice_pdf` β returns the download URL of the invoice's PDF via Mercury's documented [`getinvoicepdf`](https://docs.mercury.com/reference/getinvoicepdf) endpoint (`{baseUrl}/ar/invoices/{id}/pdf`, authenticated, sandbox/proxy-safe). Distinct from `mercury_list_invoice_attachments`, which only lists manually-uploaded files.
### π€ Customers (AR) β also requires Mercury Plus
- `mercury_list_customers`, `mercury_get_customer`
- `mercury_create_customer`, `mercury_update_customer`, `mercury_delete_customer`
### π Webhooks
- `mercury_list_webhooks`, `mercury_get_webhook`
- `mercury_create_webhook`, `mercury_update_webhook`, `mercury_delete_webhook`
> **Endpoints not yet wrapped** β Mercury exposes ~22 additional endpoints
> that this MCP does not yet cover. They will land in upcoming releases.
> Tracked: statement PDF download (`getstatementpdf` β the invoice PDF is
> already covered by `mercury_get_invoice_pdf`), attachments
> (`uploadtransactionattachment`, `uploadrecipientattachment`,
> `getattachment`, `listrecipientsattachments`), webhook signature
> verification (`verifywebhook`), webhook events (`getevent`, `getevents`),
> send-money approvals (`listsendmoneyapprovalrequests`,
> `getsendmoneyapprovalrequest`), users (`getuser`, `getusers`),
> Mercury Raise SAFE (`getsaferequest(s)`, `getsaferequestdocument`),
> and OAuth flow (`obtainaccesstoken`, `startoauth2flow`).
>
> Mercury does **not** expose `list_send_money_requests`, COA Templates
> or Journal Entries via the public API at all β those features are
> dashboard-only.
>
> There is **no `send_invoice` endpoint** anywhere (API or dashboard).
> An invoice email is only sent when the invoice is created with
> `sendEmailOption: "SendNow"`. To send a copy later, get the invoice
> PDF URL via `mercury_get_invoice_pdf`, download it, and email it
> manually.
>
> Tools available depend on your Mercury API token scope. The server
> registers all 39 tools but Mercury will reject unauthorized operations
> at the API level.
## πΊοΈ Roadmap
See [ROADMAP.md](docs/ROADMAP.md).
## π Security
- **Never share your API key.** Use environment variables, never CLI args.
- Use **read-only or scoped tokens** when you don't need write access.
- Be aware of **prompt injection** risks when exposing write tools to LLMs that read untrusted content. See [Anthropic's MCP security guidance](https://docs.anthropic.com/en/docs/agents-and-tools/mcp).
### π‘οΈ Built-in safeguards
This MCP includes three middleware layers that activate automatically on write tools (read tools are unaffected):
#### 1οΈβ£ Rate limiting (dual-window)
Each write tool is mapped to a **bucket**. Every bucket enforces **two rolling windows simultaneously** β a daily cap (24 h) and a monthly cap (30-day rolling). A call is rejected as soon as either window is at its cap, so a runaway agent cannot drain accounts even if it stays under the daily limit by pacing itself over weeks.
| Bucket | Tools | Daily | Monthly (30d) |
|---|---|---|---|
| `payments` | send_money, request_send_money | 7 | 150 |
| `internal_transfer` | create_internal_transfer | 2 | 40 |
| `invoices_write` | create_invoice, update_invoice | 10 | 200 |
| `invoices_cancel` | cancel_invoice | 3 | 30 |
| `customers_write` | create_customer, update_customer, delete_customer | 3 | 60 |
| `recipients_add` | add_recipient | 3 | 45 |
| `recipients_update` | update_recipient | 2 | 15 |
| `transactions_update` | update_transaction (bookkeeping, tagging, receipts) | 50 | 500 |
| `webhooks_create` | create_webhook | 2 | 15 |
| `webhooks_update` | update_webhook | 2 | 15 |
| `webhooks_delete` | delete_webhook | 2 | 15 |
Override per bucket (both windows must be supplied):
```bash
MERCURY_MCP_RATE_LIMIT_payments=15/day,300/month # larger supplier batch
MERCURY_MCP_RATE_LIMIT_invoices_write=20/day,400/month # large monthly billing run
MERCURY_MCP_RATE_LIMIT_DISABLE=true # disable all rate limiting (not recommended)
```
When exceeded, the tool returns an `isError: true` response with a structured JSON payload. The `source` and `error_type` prefix make it unambiguous that this is a **local MCP safeguard** β the call was never sent to Mercury. A genuine Mercury 429 surfaces separately as `"Mercury API error 429: ..."`.
```json
{
"source": "mcp_safeguard",
"error_type": "mcp_rate_limit_daily_exceeded",
"message": "MCP Rate Limit Exceeded β Daily (local safeguard, not a Mercury API error)",
"hint": "Daily Limit Exceeded: mercury_send_money (bucket: payments) capped at 7 per 24h. Retry in ~180 min. Override with MERCURY_MCP_RATE_LIMIT_payments=D/day,M/month if this is a legitimate batch.",
"retry_after": "2026-04-22T00:00:00.000Z"
}
```
`error_type` is either `mcp_rate_limit_daily_exceeded` or `mcp_rate_limit_monthly_exceeded` β the agent learns to back off at the right granularity without confusing the MCP's local cap with a server-side Mercury limit.
The rate-limit window **survives process restarts**. State is persisted to `~/.mercury-mcp/ratelimit.json` (mode `0o600`); override the location with `MERCURY_MCP_STATE_DIR=/abs/path` if you need to share state between hosts or pin it to a specific volume. Without persistence, an MCP host that respawns the server per session would silently bypass the limit.
#### 2οΈβ£ Dry-run mode
Inspect what an agent *would* do without actually calling Mercury. Useful for debugging suspected behaviour or staging:
```bash
MERCURY_MCP_DRY_RUN=true
```
Write tools then return a structured payload describing the intended action without hitting the Mercury API.
#### 3οΈβ£ Audit log (opt-in)
Enable structured JSON logging of every write call:
```bash
MERCURY_MCP_AUDIT_LOG=/var/log/mercury-mcp-audit.log
```
Each line is `{ts, tool, result, args}` (one JSON object per line). Result is `ok`, `dry-run`, or `error`. The path must be **absolute**; sensitive fields in `args` (`accountNumber`, `routingNumber`, `apiKey`, `authorization`, `password`, `token`, `secret`, `ssn`) are automatically redacted. The file is created with mode `0600` (owner read/write only).
## π§ Development
```bash
git clone https://github.com/klodr/mercury-invoicing-mcp.git
cd mercury-invoicing-mcp
npm install
npm run build
npm test
```
## π‘ Inspiration
- [@stripe/mcp](https://github.com/stripe/ai/tree/main/tools/modelcontextprotocol) β architecture patterns
- [dragonkhoi/mercury-mcp](https://github.com/dragonkhoi/mercury-mcp) β initial banking tool implementations
- [Official Mercury MCP](https://docs.mercury.com/docs/what-is-mercury-mcp) β read-only reference
## π Ecosystem
Other MCP servers in the klodr family:
- π§ [klodr/gmail-mcp](https://github.com/klodr/gmail-mcp) β Gmail
- π [klodr/faxdrop-mcp](https://github.com/klodr/faxdrop-mcp) β Send real faxes via FaxDrop
- π¦ [klodr/mercury-invoicing-mcp](https://github.com/klodr/mercury-invoicing-mcp) β Mercury banking + invoicing (you are here)
## π License
MIT β see [LICENSE](./LICENSE).
## π€ Contributing
Issues and PRs welcome. See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the test/build/coverage checklist and release process.
TDQS
Scored across 39 tools
The tool set is generally well separated by resource surface: deposit accounts, IO Credit accounts, Treasury, recipients, customers, invoices, statements, and webhooks each have dedicated tools. The many list/get pairs and three statement variants could be confused, but the descriptions consistently clarify boundaries with DO NOT USE notes. A few similar tools (e.g. mercury_list_transactions vs mercury_list_credit_transactions vs mercury_list_treasury_transactions) require careful reading but are distinguishable.
Almost all tools use a consistent mercury_ prefix and snake_case verb_noun pattern. Minor deviations exist, such as mercury_add_recipient vs mercury_create_customer and mercury_request_send_money, but these are still readable and predictable. No chaotic mixing of camelCase or vague verbs.
Thirty-nine tools is heavy for the apparent scope, even for a broad Mercury banking/invoicing surface. The set covers many distinct resources, but the sheer number of list/get/create/update/cancel/delete variants makes discovery and selection costly. A more consolidated or grouped surface would likely be easier to use.
Core workflows are well covered: accounts, transactions, statements, customers, invoices, recipients, webhooks, cards, categories, Treasury, and payments. There are some notable gaps, such as no recipient delete, no invoice attachment upload, and limited card lifecycle operations, but most are explicitly documented as API or MCP limitations. Agents can work around the remaining gaps for typical invoicing and banking tasks.