mcp-henrri
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
mcp-henrri
Your Henrri invoicing, answered by asking.
A single Rust binary that puts the whole Henrri v1 API — customers, quotes, invoices, credit notes, payment schedules, deposits, payments, webhooks — in front of any MCP client.
🇫🇷 Serveur MCP non officiel pour Henrri : clients, devis, factures, avoirs, échéances, acomptes et paiements, pilotés en langage naturel depuis Claude, Cursor ou tout client MCP.
This is not an official Henrri product. It is an independent, open-source project, not made, endorsed or supported by Henrri. It covers the published Henrri v1 specification operation by operation, as faithfully as the API allows, and documents where the API behaves differently from its documentation. See Disclaimer.
Ask your assistant "invoice Atelier Martin for three days of development at 600 € excl. tax", "who still owes me money?", "turn the signed quote D-26-11-3 into an invoice" — and get it done in the same Henrri your accountant reads.
> who still owes me money?
list_payment_milestones is_overdue = true · 7 milestones
ATELIER MARTIN I-26-10-12 1 440,00 € 23 days late
LOVELACE I-26-10-17 864,00 € 9 days late
…
overdue total 4 812,40 €
> invoice Atelier Martin for 3 days of development at 600 € excl. tax
create_document draft invoice 9632 for ATELIER MARTIN
add_document_line 3 × Journée de développement · 600,00 € · VAT 20 %
get_document_with_all 1 800,00 € excl. tax · 360,00 € VAT · 2 160,00 € incl. tax
finalize it? It locks the invoice for good and costs 8 credits.Why
Henrri's API is a clean REST surface, but handed raw to a language model it goes wrong in
predictable ways: a PUT erases every field it is not sent, a document is a draft until a
call that can never be undone, finalizing costs credits, the monthly quota and the rate limiter
both answer 429, and a model cannot tell a document's id from its number.
The whole API, nothing hidden. All 87 operations of the v1 specification: 85 routes, one tool each, and the two authentication routes driven by the client itself. A test checks the tool table against
spec/operations.tsv, and CI checks that file against the live production and sandbox specifications every day.Updates that update. Every
update_*tool takes only what changes, reads the resource, merges, and writes back what Henrri accepts — no more customers losing their comment because their website changed.Fail-closed where it cannot be undone.
finalize_document,validate_quotation, the pay tools,cancel_depositand every delete refuse to run withoutconfirm: true, and the refusal states what would happen: the document, its total, its customer, the credits.HENRRI_READ_ONLY=1withholds every write tool.Errors a model can act on. Failures come back as tool results with
isError: trueand Henrri's RFC 9457 problem document: the stableerrorCode(ALREADY_FINALIZED,LINES_MISSING,QUOTA_EXCEEDED…), thedetail, the invalid fields, and thetraceIdfor Henrri's support, plus a hint when the fix is not obvious.The API's manners, handled once. Ten-minute tokens renewed ahead of expiry; the rate limiter,
503and busy finalizations retried; the credit quota never retried; aPOSTnever resent after it may have run; list filters sent as repeated keys; periods such aslast monthturned into Henrri's inclusive dates.Webhooks, verified. Over HTTP the server receives Henrri's deliveries at
/webhooks, checks their HMAC-SHA256 signature in constant time, refuses replays, and lets the model read what happened.Written for a model to read. Structured output with schemas on all 89 tools, annotations on every one, four prompts that stop and ask before anything irreversible, and server instructions that say up front: ids not numbers, euros, drafts versus finalized.
Related MCP server: wefact-mcp
Install
Download the archive for your platform from the
latest release,
check it against its .sha256, and put mcp-henrri on your PATH:
# macOS on Apple silicon; swap the target for x86_64-apple-darwin,
# x86_64-unknown-linux-gnu or aarch64-unknown-linux-gnu
V=v0.1.0 T=aarch64-apple-darwin
curl -LO https://github.com/edouard-claude/henrri-mcp/releases/download/$V/mcp-henrri-$V-$T.tar.gz
tar xzf mcp-henrri-$V-$T.tar.gz
sudo mv mcp-henrri-$V-$T/mcp-henrri /usr/local/bin/Windows: take the x86_64-pc-windows-msvc.zip archive.
git clone https://github.com/edouard-claude/henrri-mcp
cd henrri-mcp
cargo build --release
# the binary is at target/release/mcp-henrricargo install --git https://github.com/edouard-claude/henrri-mcpdocker run --rm -i \
-e HENRRI_USERNAME -e HENRRI_PASSWORD \
ghcr.io/edouard-claude/henrri-mcp:latestThe image speaks stdio by default; add --http 0.0.0.0:8080 (with HENRRI_HTTP_TOKEN) to
serve HTTP.
Get API access
Henrri gives API access with its developer offer. There are two environments, with separate accounts:
Environment | API root | Credits |
Production |
| charged on issuance, monthly quota |
Sandbox |
| unlimited, throwaway data |
Three ways to authenticate, in this order of precedence:
A developer client —
HENRRI_CLIENT_IDandHENRRI_CLIENT_SECRET, exchanged atPOST /v1/users/authenticateand renewed throughPOST /v1/users/refresh-token.An API user —
HENRRI_USERNAMEandHENRRI_PASSWORD, the OAuth2passwordgrant onPOST /v1/users/token(this is what the sandbox hands out, e.g.xxxx@sandbox.henrri.net).A ready-made token —
HENRRI_ACCESS_TOKEN, used as is and never renewed (ten minutes).
Then check everything works:
HENRRI_ENVIRONMENT=sandbox HENRRI_USERNAME=… HENRRI_PASSWORD=… mcp-henrri --checkenvironment sandbox
base url https://api-sandbox.henrri.io
credentials API user (OAuth2 password grant)
toolsets account, customers, documents, catalog, payments, revenues, webhooks
tools 89
token ok
hello world "Hello World"
user me@sandbox.henrri.net (16079), role admin
company My company (947), plan discovery
credits used 100 of unlimited, unlimited left
ready.Configure
Everything is read from the environment, once, at startup. A missing or malformed variable stops the process with a message naming it.
Variable | Default | |
| An API user. | |
| Or a developer client. | |
| Or a bearer token, never renewed. | |
|
|
|
| per environment | Override the API root. |
| all |
|
|
|
|
|
| Per-request timeout. |
|
| Timeout of the PDF routes, which render on demand. |
|
| Retries on the rate limiter, |
| per-user cache | Where PDFs are saved ( |
| The signing secret from Henrri's webhook settings. | |
| Serve HTTP on this address instead of stdio (same as | |
| Bearer token the HTTP transport requires; mandatory off loopback. | |
|
| Log filter. Logs go to stderr, never to stdout. |
Copy .env.example to .env for local work; it is git-ignored.
Connect a client
In claude_desktop_config.json:
{
"mcpServers": {
"henrri": {
"command": "/usr/local/bin/mcp-henrri",
"env": {
"HENRRI_USERNAME": "you@example.com",
"HENRRI_PASSWORD": "…",
"HENRRI_ENVIRONMENT": "production"
}
}
}
}claude mcp add henrri \
-e HENRRI_USERNAME=you@example.com -e HENRRI_PASSWORD=… \
-- /usr/local/bin/mcp-henrri.cursor/mcp.json or .vscode/mcp.json:
{
"servers": {
"henrri": {
"type": "stdio",
"command": "/usr/local/bin/mcp-henrri",
"env": { "HENRRI_USERNAME": "you@example.com", "HENRRI_PASSWORD": "…" }
}
}
}Tip: a bookkeeper who only reads can run with HENRRI_READ_ONLY=1; an assistant that only
chases payments can run with HENRRI_TOOLSETS=customers,payments.
Tools
89 tools in seven toolsets. mcp-henrri --list-tools prints the ones your configuration
exposes. R reads, W writes, D is destructive or irreversible and needs
confirm: true (so do the two pay tools and validate_quotation).
documents — quotes, invoices, credit notes, lines, PDFs
Tool | Route | |
| R |
|
| R |
|
| W |
|
| R |
|
| R |
|
| R |
|
| W |
|
| D |
|
| D |
|
| W |
|
| W |
|
| R |
|
| R |
|
| R |
|
| W |
|
| R |
|
| R |
|
| W |
|
| R |
|
| W |
|
| R |
|
| R |
|
| W |
|
| R |
|
| W |
|
| D |
|
| W |
|
| R |
|
payments — milestones, deposits, payments
Tool | Route | |
| R |
|
| R |
|
| W |
|
| R |
|
| W |
|
| D |
|
| W |
|
| W |
|
| R |
|
| W |
|
| W |
|
| D |
|
| R |
|
| R |
|
| R |
|
| R |
|
customers — customers and contacts
Tool | Route | |
| R |
|
| R |
|
| R |
|
| R |
|
| W |
|
| R |
|
| W |
|
| D |
|
| R |
|
| R |
|
| R |
|
| W |
|
| R |
|
| W |
|
| D |
|
catalog — items, categories, text items, units
Tool | Route | |
| R |
|
| R |
|
| R |
|
| R |
|
| W |
|
| R |
|
| W |
|
| D |
|
| R |
|
| R |
|
| W |
|
| R |
|
| W |
|
| D |
|
| R |
|
| W |
|
| R |
|
account, revenues and webhooks
Tool | Route | |
| R |
|
| R |
|
| R |
|
| R |
|
| R |
|
| R |
|
| R |
|
| R | the |
| R |
|
| R |
|
| R | the specification's 12 webhook events and the test ping |
| R | HMAC-SHA256 of |
| R | deliveries received at |
POST /v1/users/authenticate and POST /v1/users/refresh-token are not tools: the client
calls them itself, as does the OAuth2 token endpoint, POST /v1/users/token.
Resources and prompts
Resource | |
| The user and the company. |
| Document types, line types, payment modes, item categories, units. |
| Credits used and left, rate limit. |
| The webhook event catalogue. |
| The deliveries received. |
| A document with all its elements. |
| A document's PDF, as a blob. |
| A customer with its payment milestones. |
| A year's revenue, total and monthly. |
Prompt | |
| From "who" and "what" to a reviewed draft, finalized only on a yes. |
| What is overdue, per customer, with reminder messages to send yourself. Read-only. |
| A year's revenue, month by month, best customers and items. Read-only. |
| Check the quote, record the signature, transform, review, finalize on a yes. |
Henrri, as it actually behaves
Found against the sandbox while building this server, and handled so the model never meets
them. The tests in tests/api_contract.rs pin each one.
A
PUTerases what it is not sent. Updating a customer's website without its comment deletes the comment. Update tools read, merge and write back.Some fields read back as
""and are refused as"".vatNumber,siret… An unchanged empty string is left out of the write.A customer
PUTcarrying contacts is refused (PUT_WITH_CUSTOMER); a linePUTcarrying its nested item, its type or its computed totals is refused too. They are dropped.A body-less
POSTwithoutContent-Lengthgets an IIS411.transform-to-invoice,deposit-request,pdf/url… carry an explicit empty body.A comma-separated list filter is not an error, it is an empty page (the documentation says
400). Lists are sent as repeated keys.The due date is ignored when a milestone is created, and honoured when it is updated:
create_payment_milestonesets it in a second call.confirmFirstOfMonthis refused in the finalize body although theFIRST_ACCOUNTING_DOCUMENT_MONTHerror asks for it: it is sent as a query parameter.A new unit is answered with
id: 0:create_unitfinds it by its name.The company-wide milestone lists only show finalized documents, and a finalized document points at a frozen copy of its customer (
parentCustomerId): a draft's schedule is read through the document, a customer's through the copy.The city follows the postal code. Henrri rewrites it ("Paris 01"); a city that does not match the code is blanked. Change both together.
refresh_tokenis refused on/v1/users/token; an API user is simply granted again.Two different
429s. The per-minute rate limiter is retried;QUOTA_EXCEEDED(the monthly credits) is not.PDF rendering is slow, and broken on the sandbox at the time of writing (
500or no answer); the PDF routes get their own, longer timeout.
Safety
Irreversible calls are fail-closed: without
confirm: truenothing leaves the process, and the refusal says what would happen. The server instructions and the prompts tell the model to show the person and get a yes first.HENRRI_READ_ONLY=1registers no write tool at all: read-only is decided by each tool'sreadOnlyHint, not by a list that could drift.Secrets never reach a log or a
Debugoutput; logs go to stderr.PDFs are written only inside
HENRRI_DOWNLOAD_DIR, under a sanitized file name.Over HTTP,
/mcprequiresHENRRI_HTTP_TOKEN(compared in constant time) and the server refuses to listen off loopback without one; on loopback,Hostis validated against DNS rebinding.Webhook deliveries are accepted only with a valid signature, a timestamp within five minutes, and once per delivery id.
Run it over HTTP
HENRRI_HTTP_TOKEN=$(openssl rand -hex 32) \
HENRRI_WEBHOOK_SECRET=… \
mcp-henrri --http 127.0.0.1:8080
# MCP at http://127.0.0.1:8080/mcp, webhooks at /webhooks, health at /healthzThe Streamable HTTP transport serves many sessions, which share one Henrri token and one
webhook inbox. Behind a reverse proxy, bind 0.0.0.0 (a token is then mandatory) and point
Henrri's webhook callback at https://<your-host>/webhooks. Henrri sends each delivery once,
with no retry: the receiver answers 204 immediately, duplicates included.
Verify
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test # unit, contract (fake Henrri), stdio and HTTP protocol testsAgainst the real sandbox, with your sandbox credentials in .env (it builds a draft quote and
deletes it; nothing is finalized):
set -a; source .env; set +a
HENRRI_SANDBOX_TESTS=1 cargo test --test sandboxHas the API moved? This prints the live operations, in the format of spec/operations.tsv
(CI runs it daily against production and sandbox):
scripts/spec-operations.sh | diff - spec/operations.tsvReleasing
Bump version in Cargo.toml and server.json, add a CHANGELOG.md section, then push a
vX.Y.Z tag. The release workflow checks the three agree, builds binaries for five targets,
publishes the multi-arch image to GHCR and the release notes from the changelog.
Contributing
Issues and pull requests are welcome. Keep the rule the code follows: one route, one tool, and a test for every behaviour the real API forces on it. See CONTRIBUTING.md.
Disclaimer
Henrri is a trademark of its owner. This project is unofficial: it is not affiliated with, endorsed by or supported by Henrri. It is built from Henrri's public v1 API specification and tested against Henrri's sandbox; behaviour may change when the API does. Finalizing documents in production has fiscal consequences and costs API credits: you remain responsible for what is issued in your name. Provided under the MIT license, without warranty.
License
MIT © 2026 Edouard Claude
This server cannot be deployed
Maintenance
Related MCP Connectors
Invoicing you drive by talking to your AI: log time, raise invoices and track what's owed via MCP.
Create, send and track invoices from a conversation in Claude, ChatGPT or any MCP client that supports OAuth. Draft an invoice from one sentence or from last month's invoice for the same client, finalize it, email it to your client and send payment reminders for overdue ones. See revenue by month and by client, download PDFs and e-invoices (ZUGFeRD, XRechnung), and export DATEV packages for your tax advisor. Invoices follow the tax rules of 25 countries, including reverse charge and the small-business VAT exemption. New invoices are drafts, and nothing is emailed until you ask. Pay per invoice with no subscription: the first invoice is free.
Create EU-compliant invoices, manage clients and download PDFs from your AI assistant.
Invoicing tools for AI assistants: clients, draft invoices with computed tax, and email sending.
Related MCP Servers
AlicenseBqualityBmaintenanceEnables natural language interaction with the Zenskar billing platform, covering customers, contracts, invoices, payments, accounting, products, plans, and more.10040 npm1MIT- AlicenseBqualityBmaintenanceEnables natural language interaction with the WeFact invoicing platform, allowing users to manage debtors, invoices, products, subscriptions, and perform various administrative tasks via MCP-compatible clients.181AGPL 3.0
- AlicenseBqualityDmaintenanceMCP server for Invoice Ninja v5 API. Enables AI assistants to manage clients, invoices, quotes, payments, and time tracking through natural language.3222 npm2MIT
- -licenseNot gradedqualityBmaintenanceEnables AI assistants to prepare and create invoices via Outvoicer's invoicing platform using Streamable HTTP MCP.-