Lexware Office MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Lexware Office MCP ServerList my recent invoices"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Lexware Office MCP Server
An open-source, self-hostable MCP server for the Lexware Office accounting API. Run your own instance, connect it to Claude, and let an agent read your invoices, vouchers, contacts and articles — and (optionally) draft new ones.
Bring your own Lexware API key — the server is single-tenant per deployment and never stores anyone else's credentials. It runs as a remote HTTP server on any container host, built on the official MCP TypeScript SDK.
Two authentication methods — choose one:
OAuth 2.1 — the way to use this as a web MCP server / custom connector in the Claude app, claude.ai web, or ChatGPT, with each user signing in.
Static bearer token — simpler. Works with Claude Code / Claude Desktop (which let you send a request header). On claude.ai it works only through the organization-admin "static headers" connector option, which is in beta for a limited set of organizations.
Setup for both is in the Client support & authentication section below.
Related projects — local (stdio) Lexware MCP servers: lazyants/lexware-mcp-server, JannikWempe/mcp-lexware-office.
⚠️ This brokers real accounting data. Read SECURITY.md, protect your tokens, and note that finalized invoices are legally binding — you are responsible for their tax/legal correctness. No warranty (MIT).
Capabilities
62 tools across three tiers you enable via environment variables, plus one opt-in tool
outside them (upload-file-from-url, see below):
Tier | Default | What it covers |
Read | always on | Profile; contacts & articles (list/get); the voucherlist (plus |
Drafts/writes ( | on | Create draft invoices/quotations/credit-notes/order-confirmations/delivery-notes/dunnings (the Lexware API has no update endpoint for these — set every field, including payment terms, at creation); create & update contacts, articles, and bookkeeping vouchers; upload files and attach receipts to vouchers — inline as base64, or without base64 via a short-lived upload ticket ( |
Finalize ( | off | Issue legally binding finalized documents in one step via the dedicated |
Set LEXWARE_READ_ONLY=true to force read-only (overrides the flags above).
upload-file-from-url — outside the tiers, off by default
One tool sits outside this table: upload-file-from-url fetches a file from a share link
server-side and stores it in Lexware, which is how a receipt already sitting in
OneDrive/SharePoint gets into the books without a round trip through the model. It is
enabled with LEXWARE_ENABLE_URL_UPLOAD=true and needs the drafts tier (it will not turn
that tier on for you).
It has its own switch because it has its own risk: it is the only tool that makes this server originate an outbound request to a destination the model chose — server-side request forgery, in the general case. Three things bound it, applied at every redirect hop:
A host allow-list, matched on a dot boundary so
evilsharepoint.comcannot pass assharepoint.com. Configure withLEXWARE_UPLOAD_ALLOWED_HOSTS; unset means the Microsoft file-sharing defaults, a set value replaces them, an empty value blocks everything.A resolved-address check rejecting loopback, private, link-local (including the
169.254.169.254metadata endpoint), CGNAT, multicast and reserved space, in every IPv4, IPv6 and IPv4-in-IPv6 spelling.Connection pinning. The socket connects to an address from the very lookup that step 2 approved, rather than letting the HTTP client resolve the name again. Without this, steps 1 and 2 describe one lookup and the connection uses another, and a DNS answer that changes in between (rebinding) slips past a check that looked correct. TLS is unaffected: the certificate is still validated against the hostname.
Only https is accepted, the download is capped at 20 MB and 30 s, and redirects are
followed manually — at most three — so no hop skips the checks.
What that looks like in practice
"Summarize my open invoices for this quarter — who still owes what?"
"Draft an invoice to Müller GmbH for 12 consulting hours at 140 €, due in 14 days."
"Here's the Hetzner receipt for June — file it in the bookkeeping." → see Uploading receipts
"Render invoice RE-2026-0042 as a PDF and give me the deeplink to it."
Related MCP server: Lexware Office MCP Server
Uploading receipts (no base64 through the model)
Attaching a file through a chat has an awkward default: upload-file needs the bytes as
base64 inside the model's tool call, so the whole receipt travels through the
conversation — every byte billed as tokens (base64 adds ~33% on top), the file's contents
sitting in the transcript, and a ~8 MB practical ceiling. The model never needed the bytes;
it only needs the file id that comes back.
The ticket flow routes the bytes around the model:
You ask Claude to file a receipt. It calls
create-upload-ticketand hands you a link likehttps://your-server…/upload/<ticket>.You open the link and drag the file onto the page — or run the ready-made
curlone-liner next to a local file (replace only theFILE=path; nothing else needs to be installed or edited). The bytes go client → server → Lexware directly.Claude reads the resulting file id with
get-upload-result(thecurlvariant prints it directly) and carries on with the actual bookkeeping — e.g.create-voucherwithfiles: [fileId], or linking the receipt to an existing voucher.
The ticket is the credential. It is minted only through the authenticated /mcp
endpoint (drafts tier), is 24 random bytes (192-bit) rendered base64url, valid for
15 minutes, single-use, and write-only — it authorizes exactly one file drop and grants no
read access. The /upload/:ticket endpoint sits outside the OAuth gate by design (a
plain browser or curl holds no MCP token); this is the same trust model as a pre-signed
upload URL. Filenames travel as X-Filename-B64 (base64url of the UTF-8 bytes), so
umlauts, dashes, typographic quotes and emoji arrive in the books intact.
The ticket store is in-memory: run a single instance (see Deploy), and note
that a restart voids open tickets — they answer 410, and you simply issue a new one.
Client support & authentication
The server supports two ways to protect /mcp, chosen by environment:
OAuth 2.1 (
OAUTH_ISSUER, …) — the recommended path. Use any OAuth provider (e.g. WorkOS AuthKit, Stytch, Auth0, Clerk; or self-hosted Keycloak/Zitadel) as the authorization server. This makes the server work as a custom connector in the Claude app, on claude.ai web, and in ChatGPT, with a real sign-in. Optionally restrict access withOAUTH_ALLOWED_EMAIL_DOMAINS(enforced server-side via the token's email / the provider's userinfo endpoint).Static bearer token (
MCP_AUTH_TOKEN) — the simpler fallback. Works with Claude Code and Claude Desktop (which let you set a request header). claude.ai can send one too, but only when an organization admin adds the connector with a static header — a beta available to a limited set of organizations. Claude sends the value exactly as entered, so enterBearer <token>. Every user of that connector then shares one credential, so prefer OAuth wherever users sign in individually.
OAuth takes precedence when OAUTH_ISSUER is set; otherwise the static token is used. With
neither set, the server refuses to start unless MCP_ALLOW_UNAUTHENTICATED=true.
Connecting as a custom connector (OAuth)
Use the MCP endpoint URL — https://…/mcp, exactly as users will enter it in Claude — in all
three places below. Claude sends that URL (path included) as the RFC 8707 resource when it
asks for a token, and expects this server's protected-resource metadata to name it exactly.
In your provider, register that URL as a Resource Indicator (the token audience). Tokens then carry it as
aud, which this server checks (OAUTH_VERIFY_AUDIENCE, on by default). With WorkOS AuthKit: add it under the MCP Auth resource indicators in the dashboard — without one, AuthKit ignores the requestedresourceand stamps every token with an environment-wide default audience.Let Claude register itself as a client. Prefer Client ID Metadata Documents (CIMD), Claude's recommended option; in WorkOS it is off by default, under Connect → Configuration. Claude uses CIMD only when the issuer's metadata advertises it. Dynamic Client Registration still works, but the MCP spec deprecated it in 2026-07-28 and it lets anyone register a client on your tenant — enable it only for clients without CIMD. Or pre-register Claude's redirect
https://claude.ai/api/mcp/auth_callbackand give users the client ID.Deploy with
OAUTH_ISSUER,OAUTH_RESOURCE=https://…/mcp, and optionallyOAUTH_ALLOWED_EMAIL_DOMAINS. Upload links are built from the same URL without/mcp.In the Claude app → Connectors → Add custom connector, enter
https://…/mcp. Claude discovers the authorization server via the protected-resource metadata and walks you through sign-in.
Quick start (Docker)
git clone https://github.com/marselsel/Lexware-MCP-Server && cd Lexware-MCP-Server
cp .env.example .env # set LEXWARE_API_KEY and MCP_AUTH_TOKEN
docker compose up --build # serves on http://127.0.0.1:8080/mcpGenerate a strong auth token:
openssl rand -hex 32Without Docker:
npm install
npm run build
LEXWARE_API_KEY=... MCP_AUTH_TOKEN=... npm startConfiguration
Env var | Default | Purpose |
| — (required) | Your Lexware API key (create one) |
| — | OAuth authorization-server issuer URL. Setting it enables OAuth mode¹ |
|
| This server's public URL. Required in OAuth mode, where it is the token audience / Resource Indicator: set it to the MCP endpoint, |
| — | Comma-separated allow-list of email domains (e.g. |
|
| Verify the token |
| — | Comma-separated additional accepted |
| — | Scopes advertised as |
| derived from issuer | Override the JWKS / OIDC userinfo endpoints (defaults use the WorkOS-AuthKit layout) |
| derived from issuer | Override the endpoints advertised in the authorization-server metadata. Defaults use the WorkOS layout ( |
| — (required¹) | Static bearer token clients send to reach |
|
| Opt out of auth (trusted local use only — bind to localhost/private network) |
|
| Register only read tools (hard override) |
|
| Enable create-draft tools |
|
| Enable finalize / legally-binding tools (also enables Drafts) |
|
| Ask the human before |
| random per process | HMAC key (≥ 32 bytes) signing the confirmation's round-trip state. Set it when more than one instance can serve the same client |
|
| Enable |
| Microsoft file-sharing hosts | Hosts |
|
| API base URL |
|
| Web-app base for document deeplinks |
|
| Listen port (your platform may inject this) |
|
| Interface to bind: an IP address or |
|
| Verbose logs (never secrets/bodies) |
¹ The server needs either OAUTH_ISSUER (OAuth) or MCP_AUTH_TOKEN (static). It
refuses to start with neither, unless MCP_ALLOW_UNAUTHENTICATED=true.
Connect to Claude (Code / Desktop)
Add to your MCP config (e.g. ~/.claude.json or the Desktop config):
{
"mcpServers": {
"lexware": {
"type": "http",
"url": "https://<your-host>/mcp",
"headers": { "Authorization": "Bearer <MCP_AUTH_TOKEN>" }
}
}
}Deploy
The server is a standard Docker container (Dockerfile) — run it on any host that can serve HTTPS (a VPS, Fly.io, Render, Railway, Cloud Run, Kubernetes, …):
docker build -t lexware-mcp .
docker run -p 127.0.0.1:8080:8080 --env-file .env -e HOST=0.0.0.0 lexware-mcp-e HOST=0.0.0.0 comes after --env-file, so it wins over a HOST=127.0.0.1 in your local
.env; inside the container the server must bind every interface to be reachable.
Production notes:
Serve over HTTPS (terminate TLS at your platform or a reverse proxy).
Set auth via env (
OAUTH_ISSUER…orMCP_AUTH_TOKEN) — the server fails closed otherwise.Run a single instance (or cap autoscaling to 1), for two reasons: the ~2 req/s rate limiter is per-process, so multiple instances would aggregate beyond Lexware's limit — and upload tickets live in process memory, so behind a load balancer without sticky sessions an upload can land on an instance that never issued its ticket.
Health check:
GET /status(returns200).
Google Cloud Run: a step-by-step recipe (Secret Manager + gcloud run deploy + custom
domain) is in docs/cloud-run.md.
How it works
src/config.ts— env parsing/validation, fail-closed auth, capability tiers.src/auth.ts— constant-time static-bearer middleware on/mcp.src/lexware/— rate-limited (~2 req/s, token bucket), retry-aware client with safe error mapping; never retries non-idempotent POSTs on ambiguous failures (no duplicate documents).src/tools/— tools registered conditionally by tier.src/uploads/— the single-use ticket store and the raw-body/upload/:ticketroutes (mounted only with the drafts tier; the ticket is the credential).src/server.ts— wires it together: an Express app (src/http-app.ts) serving the SDK's per-request MCP handler on/mcp, bound toHOST(default127.0.0.1).
Development
See CONTRIBUTING.md. npm run dev starts the server with reload on
change at http://127.0.0.1:8080/mcp.
License
MIT © marselsel.
This server cannot be deployed
Maintenance
Related MCP Connectors
Document sharing, invoicing, and personal finance platform. 15+ AI tools via OAuth 2.1.
Chile DTE for AI agents - boleta/factura electronica via OpenFactura or LibreDTE. Stateless BYO.
Free invoice drafts, templates and direct PDF generation for AI agents. No account needed.
Create PDF invoices from your AI chat: clients, numbering, VAT, overdue reports. All data is local.
Related MCP Servers
- AlicenseCqualityAmaintenanceEnables interaction with the Estonian e-arveldaja (RIK e-Financials) REST API to manage financial records like invoices and journal entries using natural language. It supports automating purchase invoice entry from PDFs, reconciling bank transactions, and generating financial reports.130966 npm35Apache 2.0
- AlicenseAqualityBmaintenanceEnables MCP-capable assistants to query and manage Lexware Office contacts, sales documents, vouchers, files, payments, webhooks, and reference data via the Lexware Office public API. Adds bank reconciliation tools for matching bank statement CSVs against Lexware vouchers or scanned receipt PDFs.4MIT
- AlicenseNot gradedqualityAmaintenanceConnects to the Lexware Office API to provide read and write access to accounting data such as invoices, contacts, vouchers, and articles. Enables natural language queries and management operations through an MCP client.61 PyPIMIT
- AlicenseBqualityBmaintenanceEnables AI assistants to query and manage self-hosted accounting data—invoices, balances, and books—through natural language, with read-only tools by default and optional scoped write operations.10MIT