Germany just made electronic invoicing mandatory. Starting January 2027, businesses with more than 800,000 EUR in annual revenue must send invoices in structured XML formats -- XRechnung or ZUGFeRD. By January 2028, every single business in Germany has to comply.
That's millions of businesses that need to produce machine-readable invoices instead of PDFs. And most of them have no idea how.
I decided to build a tool that solves this -- not as a SaaS, but as an open-source MCP server that any AI agent can use.
What is MCP?
Model Context Protocol (MCP) is an open standard by Anthropic that lets AI agents (like Claude, GPT, or local models) use external tools. Think of it as "function calling, but standardized." An MCP server exposes tools with JSON schemas, and any MCP-compatible client can discover and call them.
This matters because accounting is exactly the kind of task AI agents can help with: structured data, well-defined rules, repetitive operations.
The E-Invoice Landscape in Germany
Two formats dominate:
XRechnung -- Pure XML based on UBL 2.1 (Universal Business Language). Required for invoices to German government agencies. Increasingly used in B2B.
ZUGFeRD -- A CII (Cross Industry Invoice) XML embedded inside a PDF/A-3 file. The visual PDF looks normal to humans, but the embedded XML makes it machine-readable. Compatible with the French standard Factur-X.
Both follow the European standard EN 16931. Germany adds its own business rules on top (prefixed BR-DE-*).
Architecture Decisions
I had to make several key decisions early on:
Standalone package, not a plugin
I'm also building BuchPilot -- n8n nodes and an MCP server for Lexoffice (a popular German accounting tool). Initially, I considered adding e-invoice support there. But the dependencies are different (PDF libraries add ~50MB), and the audience is broader. Anyone who sends invoices in Germany needs e-invoicing -- not just Lexoffice users.
So einvoice-mcp is its own npm package. But it exports a registerEInvoiceTools() function, so BuchPilot can add its 6 tools in two lines of code.
No native dependencies
XML validation libraries like libxmljs2 require native compilation (node-gyp). That's a nightmare for cross-platform distribution. Instead, I went with fast-xml-parser for XML handling and implemented the 20 most important German business rules as TypeScript functions.
Is it 100% spec-compliant? No. Does it catch 95% of real-world errors? Yes. And it runs everywhere without a C compiler.
UBL for XRechnung, CII for ZUGFeRD
The German coordination body (KoSIT) recommends UBL 2.1 for XRechnung. Government portals expect UBL. So XRechnung output is UBL.
ZUGFeRD is based on CII (Cross Industry Invoice), a completely different XML structure. Same data, different tree. The internal data model is format-agnostic, and mapping functions handle the conversion.
The 6 Tools
validate_invoice
Takes XML or PDF, runs three levels of checks:
- Syntax: Is the XML well-formed?
- Schema: Are all required fields present with correct types?
- Business Rules: 20 BR-DE rules -- IBAN format, VAT ID format, mandatory buyer reference, etc.
create_xrechnung
Takes structured data (seller, buyer, line items, payment info) and generates valid UBL 2.1 XML. Conforms to XRechnung 3.0.2.
The hardest part was getting the tax calculation right. German invoices have different tax categories (Standard 19%, Reduced 7%, Zero-rated, Reverse Charge, Intra-community), and each has different rules for what fields are required.
create_zugferd
Same input data, but outputs a PDF/A-3 with embedded CII XML. Uses pdf-lib (pure JavaScript, no Puppeteer) for PDF generation.
convert_format
Converts between UBL, CII, and JSON. Useful when you receive a ZUGFeRD invoice (CII) but need to forward it as XRechnung (UBL).
extract_data
Reads XML or ZUGFeRD PDF and returns structured JSON. Handy for importing invoices into accounting systems.
get_format_info
A reference tool that helps AI agents understand the format requirements -- required fields, tax categories, unit codes (UN/ECE Rec 20), and the legal deadlines.
Implementation Highlights
German Business Rules as Code
The BR-DE rules are implemented as simple TypeScript functions:
function checkBRDE07(invoice: InvoiceData): ValidationIssue | null {
const seller = invoice.seller;
if (!seller.vatId && !seller.taxNumber) {
return {
rule: "BR-DE-07",
level: "error",
location: "/Invoice/AccountingSupplierParty",
message: "Seller must have either VAT ID (USt-IdNr.) or tax number (Steuernummer)"
};
}
return null;
}
20 of these cover the most common validation failures. They're modular -- when XRechnung 3.1 comes out (it always does, yearly), I can update individual rules without touching the rest.
The Plugin Pattern
// Standalone:
npx einvoice-mcp
// As plugin in another MCP server:
import { registerEInvoiceTools } from "einvoice-mcp";
registerEInvoiceTools(yourMcpServer);
This works because MCP tools are just name + schema + handler. Registering them on another server is trivial.
What's Next
- Full XSD validation (probably via WASM-based XML validator)
- More ZUGFeRD profiles (currently EN16931, adding Basic and Extended)
- Automated testing against KoSIT's official XRechnung test suite
- Integration with BuchPilot MCP (Lexoffice creates invoice -> einvoice-mcp creates XRechnung)
Try It
npx einvoice-mcp
Or install it permanently:
npm install -g einvoice-mcp
Source: github.com/makririch/einvoice-mcp
MIT licensed. Contributions welcome.
If you're building tools for the German market and need e-invoice support -- feel free to use the package or the plugin pattern. The 2027 deadline is coming faster than you think.
Top comments (0)