Back to the catalog

Attestwire

EN 16931: validate invoice data or a UBL/CII file, emit UBL or CII XML. XRechnung, Peppol. Not PDF.

Open source Open in the app JSON README (API)

About

EN 16931: validate invoice data or a UBL/CII file, emit UBL or CII XML. XRechnung, Peppol. Not PDF.

Details

Kind
MCP servers
Topic
Finance & crypto
Publisher
com.attestwire
Origin
official
Category
ferramentas
Transport
http
Version
1.1.2
Added
2026-08-29 03:01:00
Updated
2026-08-29 03:01:00
Origin id
com.attestwire/en16931

README

# @attestwire/mcp

European e-invoicing compliance as MCP tools — EN 16931, XRechnung, Peppol BIS
3.0 and Factur-X. Ask what a rule means, validate a real invoice, emit invoice
XML, check whether VIES is up, look up a French company against the 2026-2027
mandate.

**Five of the nine tools need no API key at all.** Connect with no credential
and they work immediately — start there, add a key later.

```sh
npx -y @attestwire/mcp
```

## At a glance

| | |
|---|---|
| **Tools** | **9** — 5 keyless, 3 keyed and metered, 1 keyed and free |
| **Hosted at** | `https://api.attestwire.com/mcp` — streamable HTTP, stateless, no session id |
| **This package** | `npx -y @attestwire/mcp` — a stdio bridge to that endpoint, for clients that cannot speak remote HTTP |
| **Deterministic** | The compliance tools are a rule engine, not a model. The same document against the same ruleset returns the same verdicts, in the same order, every time — no sampling, no temperature, nothing generated. The ruleset is pinned per engine release; `GET https://api.attestwire.com/v1/versions` names the one that is live. |
| **Rules** | EN 16931 plus the national CIUS: XRechnung (UBL and CII), Peppol BIS 3.0. 270 rule ids explained in plain English — most, not all, of what the validator checks. |
| **Reads / writes** | UBL 2.1 `Invoice`, UBL 2.1 `CreditNote`, UN/CEFACT CII `CrossIndustryInvoice`, in both directions |
| **Auth** | Optional `Authorization: Bearer aw_live_...` — a bearer API key, **not** OAuth |
| **Free tier** | 100 documents/month, no card. `issue_api_key` mints a key from inside the session — but minting does not authenticate the connection it was minted on: save it, configure it, reconnect ([§3](#3-add-a-key--two-stages-and-the-second-one-is-not-optional)). |
| **Runtime** | Zero dependencies, Node ≥ 20, MIT |

**The disclaimer, up front, because it is the thing people get wrong:** this is a
**pre-flight, not a clearing house**. It handles **XML and never PDF** — a
Factur-X or ZUGFeRD file is CII XML inside a PDF/A-3 container, and this server
neither opens that container nor builds one. And a document that passes here can
still be rejected by KoSIT or by a receiving platform: rules that constrain the
XML itself do not run on a file this server read. Nothing here is legal or tax
advice. The four limits are spelled out in full
[below](#four-honest-limits-stated-up-front).

## Quickstart

### 1. Connect

If your client speaks streamable HTTP — Claude Code, Claude Desktop custom
connectors, Cursor — point it at the hosted endpoint and skip this package
entirely:

```sh
claude mcp add --transport http attestwire https://api.attestwire.com/mcp
```

If your client can only spawn a local process, this package is a stdio bridge to
that same endpoint. Paste into `claude_desktop_config.json`, `.cursor/mcp.json`,
or whatever your client calls its MCP config:

```json
{
  "mcpServers": {
    "attestwire": {
      "command": "npx",
      "args": ["-y", "@attestwire/mcp"]
    }
  }
}
```

No `env` block, no key, nothing to sign up for. See
[most people do not need this package](#most-people-do-not-need-this-package)
for which of the two to pick.

### 2. Make a call, with no credential

Ask the agent, in these words or your own:

> Use the attestwire MCP server: explain rule BR-DE-15.

`explain_rule` is keyless, so this answers on a fresh connection — the rule's
requirement, the business term it constrains, a fix and an example. Four other
tools work the same way with no key: `check_vies_status`,
`check_french_readiness`, `list_approved_platforms` and `issue_api_key`.

That is the whole first run. Everything below is either the key (§3), the full
tool list, or the limits.

### 3. Add a key — two stages, and the second one is not optional

Authentication is supplied **when the client connects**: this bridge reads
`ATTESTWIRE_API_KEY` once, at process start, and sends it as an
`Authorization: Bearer` header; a client on the remote HTTP transport sends the
header from its own config. **Minting a key does not authenticate the connection
it was minted on** — nothing a tool returns changes a live session's credential,
and there is no automatic reauthentication to wait for.

So it is two stages, not one call:

**Stage one — get a key.**

1. **Mint it.** Ask the agent to call `issue_api_key` with your email address,
   or fill in the browser form at
   [attestwire.com/#keyform](https://attestwire.com/#keyform) — same endpoint,
   same instant key. It is free, there is no signup flow and no card.
2. **Save it.** **The key is shown once and cannot be recovered** — only its
   SHA-256 hash is stored. Put it in a password manager or a secret store, not
   in the chat you are typing into. The browser form is the better route when
   you would rather a one-time secret never entered a transcript at all.

**Stage two — make the connection carry it.**

3. **Configure it.** `ATTESTWIRE_API_KEY` in the `env` block for this stdio
   bridge:

   ```json
   {
     "mcpServers": {
       "attestwire": {
         "command": "npx",
         "args": ["-y", "@attestwire/mcp"],
         "env": { "ATTESTWIRE_API_KEY": "aw_live_..." }
       }
     }
   }
   ```

   Or an `Authorization: Bearer aw_live_...` header if your client speaks remote
   HTTP. `add` over an existing name is the step people skip, so remove first:

   ```sh
   claude mcp remove attestwire
   claude mcp add --transport http attestwire https://api.attestwire.com/mcp \
     --header "Authorization: Bearer aw_live_..."
   ```

4. **Restart or reconnect** the MCP server. Editing the config of a running
   client changes nothing until it reconnects.
5. **Verify** with one metered call — `validate_invoice` on any invoice.

**Four of the nine tools need a key**, not three: the three document tools —
`validate_invoice`, `validate_invoice_xml` and `generate_invoice`, which are the
metered ones — plus `get_upgrade_link`, which is keyed but free, because it has
to know *which* key it is quoting an upgrade for. Called without one, the three
document tools return an error naming `issue_api_key` as the remedy and spelling
out these steps.

Do not mint twice: the limit is one free key per email address, so a second call
answers `409` rather than issuing a replacement. If a metered call still fails
after the reconnect, the problem is the config, not the key.

| Variable | |
|---|---|
| `ATTESTWIRE_API_KEY` | Optional. Needed by `validate_invoice`, `validate_invoice_xml`, `generate_invoice` and `get_upgrade_link`; the other five tools ignore it. |
| `ATTESTWIRE_MCP_URL` | Optional endpoint override. Defaults to `https://api.attestwire.com/mcp`. |

## Tools

You can hand these tools a **JSON invoice model** or an **XML invoice file**.
Both of the syntaxes EN 16931 is written in are read and written: UBL 2.1
`Invoice`, UBL 2.1 `CreditNote` and UN/CEFACT CII `CrossIndustryInvoice`, which
carries invoices and credit notes alike. What is *not* read or written is a PDF.
See [what this does not do](#four-honest-limits-stated-up-front).

| Tool | Key? | |
|---|---|---|
| `explain_rule` | free | One rule id (`BR-DE-15`, `BR-CO-15`, `PEPPOL-EN16931-R010`…) in plain English: what it requires, the business term it constrains, a fix and an example. 270 rules explained at time of writing — most, not all, of what the validator checks; the live number is in the tool's own description. |
| `check_vies_status` | free | Is VIES (EU VAT number validation) up? Overall or per member state, with latency and 24h/7d uptime. Tells a bad VAT number apart from a down endpoint. |
| `check_french_readiness` | free | Look up a French company by SIREN, SIRET or name in INSEE SIRENE open data. |
| `list_approved_platforms` | free | The DGFiP-approved French platforms (Plateformes Agréées, ex-PDP). |
| `issue_api_key` | free | Mint a free key — 100 documents/month, no signup flow, no card. Minting alone does not authenticate the connection: save, configure, reconnect (§3). |
| `validate_invoice` | key · 1 doc | Check a JSON invoice object against EN 16931 plus the national CIUS. Every failure comes back as a *teaching error*: rule id, business term, what the rule requires, a concrete fix, and the XPath the rule would have landed on. |
| `validate_invoice_xml` | key · 1 doc | Check an invoice **file** the user already has — a UBL 2.1 `Invoice`, a UBL 2.1 `CreditNote`, or a UN/CEFACT CII `CrossIndustryInvoice` (invoice or credit note alike). Same teaching errors, plus which syntax it read, the document's BT-24/BT-23, and everything in the file that did not reach the invoice model. Send the file as-is: the reader and the document type are both chosen from the root element. A credit note is not refused. Not a PDF reader. |
| `generate_invoice` | key · 1 doc | Emit compliant XML from a JSON invoice object. The profile chooses the syntax: `xrechnung-cii` and `facturx-en16931` come back as CII, everything else as UBL 2.1. `invoiceTypeCode` `381` emits a credit note in either syntax. Refuses an invalid invoice rather than emitting XML that passes nothing. |
| `get_upgrade_link` | key · free | A checkout link (Paddle-hosted) for a paid plan. Returns a link; a human still has to open it. |

### Four honest limits, stated up front

* **XML, never PDF — in either direction.** Factur-X and ZUGFeRD are CII XML
  inside a PDF/A-3 container. These tools handle the XML. They do **not** parse
  a Factur-X or ZUGFeRD PDF, and they do **not** build one: no container, no
  `factur-x.xml` attachment, no `/AFRelationship`. If you have a PDF, extract
  the XML from it first. If you need a PDF, pass our XML to a Factur-X
  packaging library. A `facturx-en16931` result is the payload, not a Factur-X
  document.
* **The two CII profiles are not equally evidenced, and neither is checked per
  call.** The generator's `xrechnung-cii` *fixture* documents are run through
  the official KoSIT validator on release and accepted. The `facturx-en16931`
  ones are not: that profile's BT-24 is the core EN 16931 one, which matches no
  XRechnung scenario, so KoSIT answers "no scenario matched" rather than passing
  or failing them — and an absence of a verdict is not a pass. Neither case says
  anything about the document *you* generate: nothing is sent to KoSIT at call
  time.
* **Credit notes yes; debit notes no, and one business term does not survive
  one.** A credit note is a first-class document in both syntaxes now:
  `invoiceTypeCode: "381"` emits a UBL `CreditNote` or a CII document with
  `ram:TypeCode` 381, the reader picks the document type off the root element,
  and the whole rule set runs on it unchanged. What is still refused is
  `ubl:DebitNote`, for which EN 16931 has no binding at all. And BT-11, the
  project reference, has no element in `UBL-CreditNote-2.1.xsd`, so no
  conformant UBL credit note can carry one: it is dropped and reported rather
  than emitted somewhere it does not belong. Reading a file is also a
  pre-flight, not a verdict from the receiver: `validate_invoice_xml` checks the
  model it read, not the XML a receiving platform judges, so a file that passes
  can still be rejected.
* `check_french_readiness` **cannot** confirm that a company has registered with
  an approved platform — that lives only in the CAPTCHA-protected DGFiP
  annuaire, which has no open API. The tool says so in every response.

One smaller gap, for completeness: the 25 arithmetic and decimal-precision rules
the validator fires (`BR-45`/`46`/`48`, `BR-CO-18`, `BR-DEC-19`/`20`/`23`, and
the per-category `-01` and `-09` families) have no plain-English write-up yet.
`BR-12`–`BR-15` were on that list until `@attestwire/en16931` 0.6.0 and are not
any more: a document read from XML can omit a total it is required to state, so
the four presence rules became reachable and now have harvested write-ups like
any other rule. None of them can be tripped by anything a caller
sends: they constrain the library's own computed arithmetic, which is what they
are for. `explain_rule` says so for those ids and links
[the rule reference](https://attestwire.com/rules/), which does cover them.

## Most people do not need this package

Attestwire is a **remote** MCP server at `https://api.attestwire.com/mcp`. Any
client that speaks streamable HTTP should point at it directly — that is the
`claude mcp add --transport http` line in the quickstart.

This package exists for clients that can only spawn a local stdio process. It
moves JSON-RPC messages between stdio and HTTPS and does nothing else: no schema
validation, no caching, no interpretation. New tools appear without updating it.

## Notes

* The key is never a tool argument on a metered tool — it is read from the
  connection. `get_upgrade_link` is the one exception: it accepts an optional
  `key`, so a key minted a minute ago can be upgraded before it has been
  configured anywhere. Prefer the header even there, because an argument is
  written into the agent's transcript.
* Zero runtime dependencies. Node ≥ 20.
* Diagnostics go to **stderr**; stdout carries protocol messages only.
* `--help`, `--version`.
* Free tier is 100 documents/month. `validate_invoice`, `validate_invoice_xml`
  and `generate_invoice` cost one document each; nothing else costs anything.
  A file that cannot be read at all — a PDF, a `ubl:DebitNote`, malformed bytes
  — never reaches the rules and costs nothing.

MIT · [attestwire.com](https://attestwire.com) ·
[docs](https://api.attestwire.com/docs#mcp) ·
[changelog](./CHANGELOG.md) · hello@attestwire.com

More