Releases: marselsel/Lexware-MCP-Server
Release list
v0.1.13 — robustness fixes from a full audit + review
Robustness fixes surfaced by a full security audit and code-review pass of the server. No behaviour changes for valid input, and no security vulnerabilities were found — these are correctness/hygiene touches.
Fixed
upload-file-from-url: an empty-stringmimeTypeoverride no longer files a receipt with a blank content type (falls through to the response type;||not??).- A present-but-unusable
Content-Dispositionfilename*(empty / control-chars only) degrades to the plainfilename=instead of discarding it. RFC 6266 precedence preserved. get-documentrejects an unknownvoucherTypecleanly viaObject.hasOwninstead of letting a prototype key stringify into the request path.postMultipartclassifies a mid-read failure after a 2xx like the other client methods.- The default upload host allow-list is returned as a copy, not the shared module array.
Full detail in CHANGELOG.md.
v0.1.12 — upload-file-from-url (pinned transport, opt-in)
The server-side URL fetcher held back in 0.1.11 — now with its DNS-rebinding TOCTOU closed by connection-level IP pinning. Based on the contribution by @gutencoder (#39, merged as #41).
Added
upload-file-from-url— fetch a file from a share link server-side and store it in Lexware, so a receipt already in OneDrive/SharePoint reaches the books without its bytes crossing the model context. Off by default (LEXWARE_ENABLE_URL_UPLOAD); its own capability; requires the drafts tier (does not enable it).LEXWARE_UPLOAD_ALLOWED_HOSTS— hosts the tool may fetch from (dot-boundary match; unset = Microsoft file-sharing defaults; a set value replaces them; empty blocks every host).
Security
- The connection is pinned to the address that was checked (
src/uploads/pinned-fetch.ts, onnode:https— no new dependency). The guard's one DNS resolution is both range-checked and fed to the socket, so there is no second lookup to disagree with the first. TLS is untouched: SNI and certificate validation still bind to the hostname; only the dialled address comes from the pin. - Three layers at every redirect hop: host allow-list → resolved-address range check (loopback/private/link-local/metadata/CGNAT/… in every IPv4/IPv6 spelling, fail-closed) → pinning.
- URLs with embedded credentials are refused; filenames are sanitized (the same trust boundary as the ticket flow).
Verification
- 321 tests, including real-certificate TLS tests proving pinning doesn't weaken cert validation, a raw-ClientHello SNI check, and an RFC-6761
.invalid-host connection proof. - Reviewed through multiple adversarial passes,
/code-review high, and/security-review high(no findings).
Default-off, so deploying changes no behavior until an operator sets LEXWARE_ENABLE_URL_UPLOAD=true. Full detail in CHANGELOG.md.
v0.1.11 — Upload without base64 (ticket flow)
Upload a receipt without pushing its bytes through the model context. Based on the contribution by @gutencoder (#34, merged as #38); the server-side URL-fetch tool from that PR was intentionally held back (see the CHANGELOG Security note).
Added
create-upload-ticket/get-upload-result(drafts tier): a short-lived (15 min), single-use ticket with a browser drag-and-drop URL and a ready-to-runcurlcommand. Bytes go client → server → Lexware; the model only sees the file id. Filenames travel asX-Filename-B64(base64url of the UTF-8 bytes), so umlauts, dashes, quotes and emoji survive.SERVER_URL(orOAUTH_RESOURCE) now applies in every auth mode — upload links are built from it; static-token deployments behind a real domain no longer hand out loopback links.
Security & hardening
- The
/upload/:ticketroute mounts only when the drafts capability is enabled; the ticket page is servedno-store+nosniff+ frame-denied. - One buffering body per ticket at a time; invalid/expired/used tickets are rejected before any body is read; gzip framing refused (
inflate: false). - Operator credential failures (Lexware 401/403) answered as generic 502 to the unauthenticated uploader; unknown-outcome transport failures name the duplicate risk.
upload-file-from-urldeferred: its DNS-rebinding TOCTOU is moot for the built-in Microsoft allow-list but live for custom allow-lists; it returns only with connection-level IP pinning, disabled by default.
Notes
- The ticket store is in-process — a single-instance feature (documented in the CHANGELOG).
- 250 tests, including an end-to-end test that executes the emitted
curlcommand against the live routes.
Full details in CHANGELOG.md.
v0.1.7 — Line-item optional/alternative flags
Document line items now expose two lexoffice fields the tools didn't model:
optional— an optional position: shown with its price but not counted in the document total (Optionale Position).alternative— an alternative position (Alternativposition).
Added to the shared lineItemSchema, so it applies to every document create tool (invoice, quotation, credit-note, order-confirmation, delivery-note). Both are string-coercible and forwarded to the API; the model can now set them (previously they were only ever returned on reads).
Verified live: a two-position quotation with the second marked optional: true returns lineItems[1].optional === true and a total that excludes the optional position (net 100, not 150).
v0.1.6 — Security & correctness hardening
First tagged release of the open-source Lexware Office MCP server — a self-hostable Model Context Protocol server exposing the Lexware Office (lexoffice) API to AI agents, with read / drafts / finalize capability tiers and OAuth or static-token auth.
This release folds in a review-driven security & correctness hardening pass (0.1.4 → 0.1.6), verified live against a real Lexware account and running in production. See CHANGELOG.md for the full per-version breakdown.
Highlights
Correctness
create-draft-*rejects a stalefinalize=trueloudly instead of silently returning a draft; issuing lives only in the dedicatedcreate-finalized-*tools.- Error-body read failures are classified as
LexwareApiError(a 404 stays a 404). update-voucherone-offcontactNamehandling;get-documentdispatchesrecurringtemplate; base64 uploads validated (no silent corruption); idempotent deletes; empty-list pagination text.- Removed the read-only, silently-ignored
archivedparam from the contact tools.
Security
- The 12 MB upload body limit is parsed on
/mcpafter auth, so unauthenticated requests can't force a multi-MB parse. - Webhook event-subscription create + delete moved to the finalize tier (off by default);
create-event-subscriptionrequires anhttps://callback. - OAuth-domain denial returns 403 (not a 401 re-auth loop); static 401s carry a
WWW-Authenticatechallenge; OAuth endpoints overridable for non-WorkOS IdPs. - Patched a transitive
honohigh-severity advisory (→ 4.12.27).
Robustness
additionalFieldsescape hatch on every create tool (reserved control keys stripped) so valid unmodeled fields (e.g.xRechnung) aren't dropped; shared helpers; the tools layer no longer imports Skybridge at runtime.
Typecheck clean; 134 tests passing.