Skip to main content
Glama
Dev10x-Guru
by Dev10x-Guru

PyPI Python Testy Pokrycie 100% Licencja AGPL-3.0

ksef-mcp — faktury z KSeF prosto do Twojego agenta AI

Strona projektu · PyPI · Instalacja · Narzędzia MCP · Dziennik zmian

ksef-mcp

Faktury z KSeF prosto do Twojego agenta AI. Lokalny serwer MCP do Krajowego Systemu e-Faktur, zbudowany przez Dev10x.Guru: pobiera, archiwizuje i przegląda faktury zakupowe na Twojej maszynie — dane faktur nie przechodzą przez żadną usługę pośredniczącą.

Problem

Faktury zakupowe leżą w KSeF, a praca na nich — znaleźć fakturę za miesiąc, przekazać zestawienie księgowej, sprawdzić, co przyszło od ostatniego razu — dzieje się gdzie indziej. Darmowa aplikacja Ministerstwa nie powie, które faktury są nowe, a zdalne serwery MCP przepuszczają dokumenty i uwierzytelnienie przez cudzą usługę.

Related MCP server: mcp-facturacion-electronica-es

Jak to rozwiązujemy

  • Lokalnie. Klient MCP uruchamia serwer przez uvx; archiwum XML zostaje na Twoim dysku.

  • Token w keyringu. Poświadczenia trafiają do systemowego magazynu kluczy, nie do pliku konfiguracyjnego.

  • Domyślnie środowisko testowe. Produkcja wymaga świadomego wyboru.

  • Oszczędnie z limitami. Z narzędzi MCP do KSeF sięga tylko synchronizacja; po odmowie z powodu limitu nic nie ponawia samo.

Instalacja w trzech krokach

Potrzebujesz tylko uv — jedno polecenie instalacyjne dla Windows, macOS i Linuksa; to z niego pochodzi uvx.

  1. uvx ksef-mcp onboarding — NIP, token KSeF, środowisko, katalog roboczy i rejestracja w Claude Code.

  2. uvx ksef-mcp verify — potwierdza połączenie i pokazuje ostatnie faktury.

  3. „Zsynchronizuj faktury z KSeF" — poproś agenta; resztę robią narzędzia MCP.

Wymaga Pythona 3.13 (pobiera go uv). Node jest potrzebny wyłącznie do PDF-ów — bez niego działa wszystko poza nimi. Szczegóły każdego kroku w sekcji Instalacja i uruchomienie.

Stan projektu

Serwer MCP wystawia dziś sześć narzędzi: server_info, synchronise_invoices, list_recent_invoices, export_period_statement, review_new_invoices i render_invoice_pdf — patrz sekcja Narzędzia MCP niżej. Wszystko, co poniżej oznaczono jako 🚧 planowane, jeszcze nie istnieje w kodzie — opisujemy to, żeby kierunek był jawny, nie żeby sugerować gotowość.

Obszar

Stan

Pakiet, uruchamianie przez uvx, testy

✅ działa

Konfiguracja: onboarding, doctor, token

✅ działa

Potwierdzenie połączenia: verify

✅ działa

Synchronizacja i przegląd faktur zakupowych

✅ działa

Wyszukiwanie i pobieranie faktur sprzedażowych

🚧 planowane

Wizualizacja PDF

✅ działa, wymaga Node

Narzędzia MCP

Narzędzie

Co robi

Sięga do KSeF

server_info

zwraca nazwę i wersję działającego serwera

nie

synchronise_invoices

pobiera, odszyfrowuje i archiwizuje paczki faktur zakończone przez KSeF od ostatniego uruchomienia

tak

list_recent_invoices

listuje metadane faktur z ostatnich trzydziestu dni, wg typu podmiotu

nie

export_period_statement

zapisuje faktury zakupowe za wybrany miesiąc jako CSV do przekazania księgowej

nie

review_new_invoices

pokazuje faktury, które przyszły od ostatniego przeglądu — czego nie potrafi darmowa aplikacja Ministerstwa

nie

render_invoice_pdf

zapisuje już zarchiwizowaną fakturę jako PDF, bez sięgania do sieci

nie

synchronise_invoices jest jedynym narzędziem z tej listy, które wydaje godzinowy budżet zapytań do KSeF. Pozostałe pracują na danych już zarchiwizowanych lokalnie.

Co ten projekt robi

MVP jest wąski i celowo: znajdź faktury za wybrany miesiąc i pobierz je.

Mapa drogowa ma trzy etapy:

  1. jeden podmiot, faktury zakupowe, wyłącznie odczyt,

  2. ten sam podmiot, faktury sprzedażowe,

  3. biura rachunkowe z przełączaniem podmiotów.

Poza zakresem: wysyłka faktur, korekty, zarządzanie uprawnieniami. To nie jest zapomniane — to jest świadomie niezbudowane.

Dla kogo

Odbiorcą jest użytkownik techniczny. Instalacja wymaga terminala i menedżera wersji; w kliencie innym niż Claude Code także ręcznej edycji jego pliku konfiguracyjnego. Nie udajemy, że jest to instalacja dla osoby nietechnicznej — dystrybucja dla takiego odbiorcy (instalator albo rozszerzenie do klienta) to osobny, przyszły etap.

Wymagania wstępne

uv — jedyne, co trzeba zainstalować samemu. Z niego pochodzą uvx, którym uruchamia się serwer, i interpreter Pythona niżej.

Python 3.13.14 — przypięty dokładnie, nie zakresem (.python-version oraz requires-python w pyproject.toml). uv pobierze ten interpreter sam, więc nie trzeba instalować go ręcznie.

Zasada obowiązuje w całym projekcie: przypinamy konkretne wersje, nigdy zakresy — także zależności. Rozjazd interpretera pociąga rozjazd rozwiązanych wersji bibliotek, a uvx i tak rozwiązuje wersję za nas.

Node 22.17.0 przez fnm — potrzebny wyłącznie do generowania PDF-ów.

winget install Schniz.fnm   # Windows

⚠️ Sama obecność pliku .node-version nie wystarczy. Automatyczne przełączanie wersji wymaga fnm env w profilu powłoki — bez tego plik jest deklaracją bez egzekucji i można pracować na innej wersji Node, nie wiedząc o tym.

Bez Node narzędzie działa i oddaje XML, CSV oraz listę faktur. Traci wyłącznie PDF. To degradacja, nie awaria.

Instalacja i uruchomienie

Jedyny krok do świadomego wykonania to jedna komenda:

uvx ksef-mcp onboarding

Przeprowadzi po kolei przez wszystko, co trzeba ustalić przed pierwszym uruchomieniem, i można ją uruchamiać wielokrotnie — poprawia to, co już zapisano, zamiast dokładać drugi wpis:

  1. sprawdza warunki wstępne: Python, Node (tylko do PDF-ów) i magazyn keyringu, w którym zamieszka token;

  2. pyta o NIP podmiotu, magazyn tokenu i środowisko KSeF — domyślnie testowe, nigdy produkcyjne bez wyraźnego wyboru;

  3. prosi o wklejenie tokena KSeF (bez echa) i zapisuje go w keyringu;

  4. pyta o katalog roboczy na zestawienia i PDF-y i mówi, gdzie naprawdę ląduje archiwum XML — ten katalog obejmuje się kopią zapasową;

  5. rejestruje serwer w Claude Code (claude mcp add) i proponuje instalację skilla, który uczy agenta korzystać z narzędzi;

  6. na koniec proponuje verify — domyślnie nie, bo to jedyny krok, który sięga do KSeF i wydaje godzinowy budżet.

Serwer komunikuje się przez stdio i jest uruchamiany przez klienta MCP, nie ręcznie. Pliku konfiguracyjnego klienta nie trzeba edytować — onboarding robi to za Ciebie, gdy na maszynie jest komenda claude.

Ścieżka awaryjna: klient bez komendy claude

Gdy klient MCP to nie Claude Code (albo claude nie ma na PATH), onboarding pomija rejestrację i pokazuje, co wpisać ręcznie. Wpis w pliku klienta (.mcp.json, claude_desktop_config.json) wygląda tak:

{
  "mcpServers": {
    "ksef": {
      "command": "uvx",
      "args": ["ksef-mcp"]
    }
  }
}

Reszta konfiguracji — NIP, token, środowisko, katalog roboczy — i tak pochodzi z onboardingu; sam wpis w kliencie tylko uruchamia serwer.

Dla współtwórcy: wersja z katalogu roboczego

Instalując z PyPI, tego wariantu nie potrzebujesz. Służy do uruchamiania kodu z lokalnego klonu zamiast opublikowanej wersji:

{
  "mcpServers": {
    "ksef": {
      "command": "uvx",
      "args": ["--from", "/ścieżka/do/ksef-mcp", "ksef-mcp"]
    }
  }
}

Komendy

Komenda

Co robi

Sięga do KSeF

ksef-mcp

uruchamia serwer MCP na stdio

nie

ksef-mcp onboarding

konfiguracja przed pierwszym uruchomieniem

nie

ksef-mcp doctor

same warunki wstępne

nie

ksef-mcp token set|delete|status

token w keyringu

nie

ksef-mcp skill install --scope user|project

uczy agenta, jak używać serwera

nie

ksef-mcp purge

kasuje faktury z archiwum, zachowując indeks deduplikacji

nie

ksef-mcp verify

potwierdza połączenie i pokazuje ostatnie faktury

tak

purge jest bezpiecznikiem bezterminowej retencji: archiwum nie wygasa samo, więc czyszczenie odbywa się jawną komendą. Ciąć można po podmiocie (--nip, domyślnie ten z konfiguracji), po dacie wpływu do KSeF (--od, --do) albo po obu naraz. Zanim cokolwiek zniknie, komenda wypisuje numery KSeF do skasowania i pyta o zgodę — domyślnie odmawia. Indeks deduplikacji zostaje nietknięty, więc ponowna synchronizacja nie ściąga skasowanych faktur powtórnie; nietknięte zostają też punkty kontynuacji i zapis tego, co już przejrzano. Każde skasowanie zostawia wpis w dzienniku audytu.

skill install zapisuje skill dla Claude Code: przy zakresie user do ~/.claude/skills/ksef-mcp/, przy project do ./.claude/skills/ksef-mcp/ w katalogu wywołania. Zakresu nie przyjmuję domyślnie — uvx bywa uruchamiany z przypadkowego miejsca, więc cicho wybrany katalog byłby ostatnim, w którym ktokolwiek szukałby pliku. Komenda instaluje i aktualizuje: gdy skill już jest i różni się od nowego, pokazuje różnicę i pyta, zanim cokolwiek nadpisze — cudze zmiany nie znikają bez pokazania ich.

Na maszynie bez magazynu keyringu (headless, WSL, kontener) tokenu nie da się zapisać. Ścieżką awaryjną jest zmienna KSEF_TOKEN — gdy jest ustawiona, ma pierwszeństwo przed keyringiem, a ksef-mcp token status powie, z którego źródła token pochodzi. Pierwszeństwo jest celowe: kto ją eksportuje, robi to świadomie, a ciche preferowanie keyringu wyglądałoby na zignorowanie eksportu.

Osobnym przypadkiem jest magazyn obecny, ale zablokowany — po uśpieniu maszyny albo po upływie własnego czasu magazynu. Każda komenda dotykająca tokenu sprawdza wtedy stan blokady i przerywa z instrukcją zamiast otwierać okno z prośbą o hasło. Takie okno otwiera się w środku czynności wyglądającej na zwykły odczyt i zawiesza rozmowę z agentem, bo serwer MCP na stdio nie ma gdzie go pokazać. Stan blokady pokazuje też ksef-mcp doctor.

verify jest osobną komendą, a nie ostatnim krokiem onboardingu, celowo. Onboarding uruchamia się wielokrotnie przy poprawianiu konfiguracji, a każde zapytanie do KSeF zjada godzinowy budżet, którego przekroczenia Ministerstwo Finansów rejestruje. Budżet wydajemy wtedy, gdy prosisz o to świadomie.

Gdy KSeF odmówi z powodu limitu, verify wypisze czas oczekiwania i nie ponowi zapytania samoczynnie. Wbudowane ponawianie w ksef2 jest z tego samego powodu ograniczone do jednej próby: jego okno wynosi cztery sekundy, a rzeczywisty Retry-After bywa liczony w minutach, więc pętla nie doczeka końca limitu — doda tylko prób do wzorca wyglądającego na jego obchodzenie.

Wizualizacja PDF

Narzędzie MCP render_invoice_pdf bierze numer KSeF faktury już leżącej w archiwum i zapisuje ją jako PDF w katalogu roboczym. Nic nie pobiera: nie zużywa godzinowego budżetu zapytań i działa bez sieci. Faktura, której jeszcze nie zsynchronizowano, jest odmawiana, a nie dociągana.

PDF-y generuje oficjalny generator Ministerstwa Finansów (@akmf/ksef-fe-invoice-converter, licencja MIT), zwendorowany w src/ksef_mcp/rendering/vendor/. Wynik jest tożsamy z tym, co daje portal MF — zweryfikowane uruchomieniem, nie tylko lekturą kodu. Dokument niesie kod QR, link weryfikacyjny i numer KSeF.

Bundel nie pochodzi z rejestru npm — paczki o tej nazwie tam nie ma. Serwuje go portal weryfikacyjny MF pod /client-app/pdf-lib/; szczegóły i suma kontrolna w src/ksef_mcp/rendering/vendor/LICENCJA-MF.md.

Link weryfikacyjny trafia wyłącznie na dokumenty produkcyjne. Środowiska TEST i DEMO nie mają powierzchni weryfikacyjnej, więc PDF stamtąd nie niesie odsyłacza prowadzącego donikąd.

Generator obsługuje FA(1), FA(2), FA(3), UPO i PEF, ale przetestowaliśmy wyłącznie FA(3). Pozostałe schematy traktujemy jako niepotwierdzone.

Bezpieczeństwo danych

Token KSeF nie powinien trafiać do pliku konfiguracyjnego klienta MCP — te pliki są zwykłym tekstem na dysku. Docelowo serwer będzie czytał poświadczenia z keyringu systemowego.

Domyślnym środowiskiem jest TEST. Produkcja wymaga świadomego włączenia.

Limity zapytań

API KSeF ogranicza liczbę zapytań o metadane. Krążące wartości to 8/s, 16/min i 20/h, ale nie potwierdziliśmy ich — nie opierajcie na nich planowania, dopóki nie zostaną zweryfikowane wobec dokumentacji MF.

Rozwój

make help wypisuje wszystkie dostępne komendy.

make install              # uv sync --group dev
make hooks                # instalacja hooków pre-commit i commit-msg
make test                 # uv run pytest z pokryciem
make test-live            # testy ksef_live na środowisku testowym KSeF
make lint                 # pre-commit na całym drzewie
make coverage-report      # testy + otwarcie raportu HTML
make upgrade-requirements # uv lock --upgrade
make build-requirements   # eksport do requirements/*.txt

Linting i formatowanie idą wyłącznie przez pre-commit — to ta sama ścieżka, która blokuje commit, więc lokalny przebieg nie rozjeżdża się z hookiem.

Pokrycie testami jest egzekwowane na poziomie 100% (fail_under w pyproject.toml), więc lokalny przebieg i CI stosują identyczny próg.

make test-live sprawdza kontrakt z prawdziwym rejestrem testowym KSeF, a nie z atrapą. Poświadczenia testowe trafiają do nieśledzonego ksef.secrets.env (cp ksef.secrets.env.example ksef.secrets.env); własna konfiguracja i token produkcyjny zostają nietknięte, a środowisko jest wpisane na sztywno jako test. Ten sam skrypt, bin/ksef_live.py, uruchamia ręczny workflow ksef-live.yml z sekretami środowiska GitHub ksef-test. Każdy przebieg wydaje godzinowy budżet podmiotu testowego.

Licencje

Projekt jest na licencji AGPL-3.0-only — pełny tekst w pliku LICENSE.

Zwendorowany generator PDF Ministerstwa Finansów jest osobnym artefaktem na licencji MIT. Jego nota licencyjna leży obok niego — src/ksef_mcp/rendering/vendor/LICENCJA-MF.md — i dotyczy wyłącznie tego pliku, nie reszty projektu.

To nie jest ksef-mcp.pl

Istnieje niepowiązany z nami projekt o tej samej nazwie, wystawiony jako zdalny serwer MCP pod https://ksef-mcp.pl/mcp (HTTP + OAuth). Różnica jest zasadnicza, nie kosmetyczna: tam faktury i uwierzytelnienie przechodzą przez cudzą usługę, tutaj nie opuszczają Twojej maszyny. Jeśli Twój klient MCP wystawił Ci adres autoryzacyjny w przeglądarce — to nie był ten serwer. Nasz uruchamia się lokalnie przez uvx ksef-mcp i o nic nie pyta w przeglądarce.

Obie dystrybucje instalują skrypt konsolowy o nazwie ksef-mcp, więc przy obu zainstalowanych wygrywa ta wcześniejsza w PATH. Sprawdzisz, co masz, przez ksef-mcp doctor [#75].

Available Tools

6 tools
export_period_statementA

Write one month of purchase invoices as a CSV an accountant can forward.

period is a calendar month spelled YYYY-MM. The month is the unit a period is closed in, and both ends being fixed is what lets the same request be answered from disk instead of spending one of twenty metadata queries an hour a second time.

working_directory overrides the directory declared during onboarding for this one call. Whichever is used is created 0700 if it is new, is refused outright if it lies inside the cache or data root — those are internal storage and deleting statements must never reach the archive — and is reported in warnings when its path looks like a cloud sync folder, unless the taxpayer acknowledged that directory during onboarding.

The file holds ten columns, in this order: KSeF number, the seller's own invoice number, issue date, seller NIP, seller name, gross, net, VAT, currency, and a KOD I verification code. The code is composed from the seller NIP, the issue date and the SHA-256 of the archived invoice body; rows whose body is not in the archive yet say so instead of carrying a blank code.

Addresses, bank accounts, invoice lines and local paths are absent on purpose: this file is written to be attached to an e-mail. The paths are in this answer instead.

Amounts are written exactly as KSeF stated them — no rounding, no float anywhere on the path — with a comma as the decimal separator and a semicolon between fields, which is what a Polish spreadsheet expects. Sums are in this answer, per currency, and never one figure across several of them.

ParametersJSON Schema
NameRequiredDescriptionDefault
periodYes
working_directoryNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
nipYes
pathYes
periodYes
messageYes
completeYes
warningsNo
row_countYes
from_cacheYes
queried_atYes
environmentYes
gross_totalsYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and does so thoroughly: it discloses file creation with 0700 permissions, refusal inside cache/data roots, cloud-sync warnings, CSV column order, decimal/comma separators, absence of certain fields, exact KSeF amounts, and where paths and sums are reported. This is exceptionally transparent about side effects and output format.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Although the description is long, every paragraph earns its place by covering a distinct concern: purpose, period semantics, directory behavior, CSV contents, and formatting rules. The primary purpose is front-loaded, and there is no redundant or filler text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with two parameters, no annotations, and an output schema, the description is remarkably complete. It explains return-relevant details (paths and sums in the answer), file structure, edge cases for missing archive bodies, and constraints on the working directory. There is no meaningful gap an agent would need filled to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must fully explain both parameters. It does: `period` is defined as a YYYY-MM calendar month with a caching rationale, and `working_directory` is explained as a one-call override with creation, refusal, and warning behaviors. This far exceeds what the bare schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb, resource, and deliverable: 'Write one month of purchase invoices as a CSV an accountant can forward.' This clearly distinguishes the tool from siblings like render_invoice_pdf or list_recent_invoices, which address different outputs and workflows.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context on when this tool is appropriate: it exports a closed calendar month and can reuse disk-stored results instead of consuming metadata queries. It does not explicitly name sibling tools as alternatives or state when not to use it, but the usage context is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_recent_invoicesA

List the invoice metadata of the last thirty days, per subject type.

Takes no arguments, for the same reason synchronise_invoices takes none: a window driven by the caller spends a twenty-per-hour metadata allowance in minutes, and the Ministry reads that pattern as working around a limit. The window ends on the hour, so asking again within the same hour is answered from disk and costs nothing.

Reads only. There is no confirmation to click: a gate on a read teaches the person to approve without looking, and spends the gate that a write needs.

At most fifty invoices are listed individually. Above that the rows are dropped and the answer carries the count and the gross total per currency instead — and message says so in as many words. An empty window is its own answer, restating the NIP, the environment, the subject type and both ends of the period, so "nothing arrived" can be told from "wrong question".

Metadata only. An FA(2)/FA(3) body holds a counterparty's personal data and is third-party input; it never enters this answer.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
nipYes
messageYes
warningsNo
period_toYes
thresholdYes
environmentYes
period_fromYes
subject_rolesYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so thoroughly: it discloses read-only semantics, absence of confirmation flow, a 50-invoice cap with aggregated fallback, the empty-window self-describing response, and the exclusion of personal data. These are non-obvious, agent-relevant behaviors that go far beyond a bare action statement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The opening sentence is a model of front-loaded clarity, but the remaining paragraphs are dense and stylized, with narrative explanations that could be tightened. Every sentence carries meaningful behavioral information, so nothing is wasted, but the overall length is greater than necessary for the simplicity of the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given zero parameters, a rich output schema, and no annotations, the description still covers all critical agent-facing edge cases: the 50-row limit, aggregated totals, empty results, read-only guarantee, and privacy constraints. It leaves no meaningful ambiguity about how the tool behaves in unusual circumstances.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the schema covers this fully. The description goes further by explaining why there are no arguments—the caller-driven window would consume the metadata allowance—which gives an agent semantic understanding of the parameter absence and prevents attempts to pass unsupported arguments.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb ('List'), a concrete resource ('invoice metadata'), a clear time window ('the last thirty days'), and a grouping ('per subject type'). This immediately distinguishes it from siblings like render_invoice_pdf or export_period_statement, and the later reference to synchronise_invoices further separates its read-only, metadata-focused role.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains why the tool takes no arguments, mentions the metadata allowance, and notes it is read-only, which implies safe usage. However, it never explicitly states when to choose this tool over alternatives such as synchronise_invoices or review_new_invoices, nor does it name exclusions or conditions that would route an agent to a different sibling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

render_invoice_pdfA

Write one archived invoice as the PDF the Ministry's own application shows.

ksef_number names an invoice already in the archive. Nothing is fetched: this call spends none of the twenty metadata queries an hour and works with no network at all. An invoice that has not been synchronised yet is refused rather than downloaded, so the answer never depends on a query budget.

The document is produced by the Ministry's own generator, run locally from a build vendored with this package — the same code the verification portal loads into a browser. Fidelity is therefore official rather than approximate, and generator_version names the build, the same string the footer of the document carries.

working_directory overrides the directory declared during onboarding for this one call, under the same rules as the statement: created 0700 if new, refused inside the cache or data root, and reported in warnings when its path looks like a cloud sync folder or its permissions are wider than 0700. A synced directory the taxpayer acknowledged during onboarding is not reported again. The PDF names the counterparty exactly as the statement does.

On production the document carries the QR code and verification link, and verification_url repeats it here. Test and demo invoices have no verification surface, so both are absent rather than pointing at a page that would not resolve.

Requires Node — uvx cannot install it and a Python package cannot depend on it. Without Node this one call fails with a message saying what to install; synchronisation, the CSV statement and the listing are unaffected.

The generator accepts FA(1), FA(2), FA(3), UPO and PEF, but only FA(3) has been exercised end to end. Treat a refusal on an older schema as untested rather than impossible, and report it.

ParametersJSON Schema
NameRequiredDescriptionDefault
ksef_numberYes
working_directoryNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
nipYes
pathYes
messageYes
warningsNo
byte_countYes
environmentYes
ksef_numberYes
verification_urlYes
generator_versionYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

There are no annotations, so the description carries the entire burden and meets it: no-network/no-quota behavior, vendored official generator, Node requirement with a precise failure mode, working-directory permission and warning rules, production-only verification URL, and untested FA schema variants are all disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a one-sentence purpose and then moves through quota, fidelity, directory behavior, output surface, runtime, and schema compatibility. Each paragraph adds a distinct piece of operational knowledge, and there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no annotations and a bare input schema, the description covers prerequisites, failure modes, supported schema variants, directory semantics, and output-surface differences. An agent has everything needed to decide whether and how to call it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description fully compensates: ksef_number is explained as the archived-invoice identifier and 'refused' when unsynchronised, and working_directory's override semantics, 0700 creation, refusal locations, and warning conditions are all spelled out.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence names a specific action and target: 'Write one archived invoice as the PDF the Ministry's own application shows.' It also distinguishes the tool from the period-statement, listing, and synchronisation siblings by limiting scope to a single archived invoice and stressing that nothing is fetched.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly situates the tool: use it for one already-synchronised invoice when an official local PDF is needed without spending metadata quota or requiring network. It stops short of explicitly naming sibling tools as alternatives or listing when-not-to-use cases, so a fully explicit routing statement is missing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

review_new_invoicesA

Report the invoices that have arrived since a person was last shown any.

This is the question the free Ministry application cannot answer: it lists what is there, never what is new since somebody last looked. The comparison is against a ledger of KSeF numbers already reported by this tool, kept on disk per subject and per environment, so it survives restarts.

The window is the last ninety days, dated by acceptance in KSeF rather than by the seller's issue date — an invoice issued in March and accepted yesterday is new to the reader, and a window dated by issue date would not contain it. Both ends are fixed, so asking again within the same hour is answered from disk and spends nothing from twenty metadata queries an hour.

received_on per invoice is the day KSeF assigned the number, read off the number itself. It is independent of when this tool fetched anything, and it is not the seller's issue date, which is reported separately.

KSeF has no notion of a closed period and will not stop an invoice from landing in a month already filed, so earlier_month_count counts the new invoices whose number was assigned before the current month. That is a signal to look, never a statement that an invoice belongs to any period — the tax treatment is the reader's decision and this tool does not make it.

Invoices listed individually are recorded as shown, and the next call does not repeat them. Above the threshold the rows are dropped, and then nothing is recorded, because nobody saw them — marked_as_reviewed says which of the two happened. Metadata only: an FA(2)/FA(3) body holds a counterparty's personal data and never enters this answer.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
nipYes
messageYes
warningsNo
period_toYes
thresholdYes
environmentYes
ledger_fileYes
period_fromYes
subject_rolesYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden, and it does so thoroughly. It discloses persistence across restarts, the distinction between KSeF acceptance date and seller's issue date, the 20-requests-per-hour budget, the recording of shown invoices, the threshold-drop behavior, and the meaning of marked_as_reviewed. It also notes that personal data from FA(2)/FA(3) bodies never enters the answer.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded in the first sentence, and the following paragraphs are organized thematically. It is longer than strictly necessary, but each paragraph conveys meaningful nuance about timing, persistence, and tax-period interpretation, so the length earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having no annotations and no parameters, the description is complete enough for an agent to invoke the tool correctly and interpret its result. It explains what is recorded, what happens on repeat calls, what marked_as_reviewed indicates, and what data is excluded, while the output schema can carry structured return details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the input schema has no properties, so the baseline is 4. There are no parameters to clarify; the description does add useful context about the implicit per-subject and per-environment comparison, even though no arguments are needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb and resource: 'Report the invoices that have arrived since a person was last shown any.' This clearly distinguishes the tool from a plain list of invoices and from sibling tools like list_recent_invoices by making the 'new since last viewed' criterion explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly explains when this tool is appropriate: when the user needs what is new since the last look, which the free Ministry application cannot answer. It also gives operational context such as the 90-day window, the on-disk ledger, and the metadata query budget. It stops short of explicitly naming sibling alternatives and stating 'use X instead,' so it misses a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

server_infoA

Report the name and version of the running KSeF connector.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
nameYes
versionYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are supplied, so the description carries the full disclosure burden; it does indicate the tool is a read-only reporting operation that returns name and version. It does not state whether any authentication or connector state is required, nor does it warn about failure modes. Adequate but thin for an unannotated tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler or repetition. Every word contributes to identifying the tool's output.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no parameters, no annotations, and an output schema that already documents the return shape, the description only needs to identify the operation. It does so clearly; extra detail such as auth or connector-state prerequisites would make it fully complete but is not strictly required here.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there are no parameter semantics to document; the baseline for a parameterless tool applies. The input schema is a bare empty object, consistent with the description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb ('Report') and a precisely scoped resource ('the name and version of the running KSeF connector'). There are no sibling tools to disambiguate against, and the statement leaves no ambiguity about what the call returns.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit when-to-use or when-not-to-use guidance is given, and no alternatives are named (none exist among the siblings). For a zero-argument informational tool the intended use is strongly implied, but nothing is stated outright.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

synchronise_invoicesA

Fetch, decrypt and archive every invoice package KSeF finished since the last run.

Takes no arguments on purpose. The date window, the package size and how many packages to ask for are decided by KSeF and by the hourly allowance, never by the caller: an agent driving them spends a twenty-per-hour budget in two minutes and the Ministry reads the pattern as working around a limit.

Safe to call again. A package still being built, or one fetched but not yet stored, stays recorded on disk with its key, so a second call continues it instead of asking for it twice. An invoice already in the archive is reported under already_held rather than downloaded again.

Reports where the invoices landed and which KSeF numbers arrived. It never returns invoice content: an FA(2)/FA(3) document holds a counterparty's personal data, and reading one means opening the file this tool names.

session_ceilings says how much one session may carry and, in assumed, whether KSeF granted those figures or the conservative fallback is in force because the registry answered about limits in a shape that could not be read. An assumed ceiling is a different event from a granted one, and the message says so in as many words.

correlation names this call in the technical journal on stderr. Quote it when reporting a failure: it is what lets the whole pass be reconstructed without running the synchronisation again out of twenty exports an hour.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
nipYes
messageYes
warningsNo
state_fileYes
correlationYes
environmentYes
subject_rolesYes
pending_exportsYes
session_ceilingsYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden, and it delivers: idempotent retry behavior, continuation of incomplete packages, already_held handling, refusal to return invoice content for data-protection reasons, session_ceilings semantics, and stderr correlation for failure tracing. This is far beyond the minimum expected.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Although the description is long for a zero-parameter tool, every paragraph earns its place: purpose, argument rationale, idempotency, privacy, output fields, and failure correlation. It is front-loaded with the core action and uses backticked field names to structure operational details clearly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete for a complex, sensitive synchronisation tool. It explains output fields such as session_ceilings, assumed, already_held, and correlation, while the output schema covers return structure. There is no missing behavioral or safety information that an agent would need to invoke it correctly and safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has zero properties, so the baseline is 4, but the description adds real meaning by explaining why no arguments are accepted and what would happen if an agent tried to drive the process manually. It turns an empty schema into an intentional design constraint.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb phrase: 'Fetch, decrypt and archive every invoice package KSeF finished since the last run.' This names the resource, the action, and the boundary of the operation. It also clarifies the deliberate lack of arguments, which sharply distinguishes it from sibling tools like list_recent_invoices and export_period_statement.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear contextual guidance: this tool runs a KSeF-driven synchronisation since the last run, takes no caller-controlled parameters, and is safe to call again. It does not explicitly name sibling alternatives or state 'use X instead', but the usage context is strong enough for an agent to select it appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.3.4
    • Changedexport_period_statement1 field changed
      • changedOutput schema / required
        Previous value: -[
        -  "nip",
        -  "environment",
        -  "period",
        -  "path",
        -  "row_count",
        -  "gross_totals",
        -  "complete",
        -  "from_cache",
        -  "queried_at",
        -  "message",
        -  "warnings"
        -]New value: +[
        +  "nip",
        +  "environment",
        +  "message",
        +  "period",
        +  "path",
        +  "row_count",
        +  "gross_totals",
        +  "complete",
        +  "from_cache",
        +  "queried_at"
        +]
    • Changedlist_recent_invoices7 fields changed
      • removedOutput schema / $defs / DirectionListingResult
        Removed value: -{
        -  "properties": {
        -    "complete": {
        -      "title": "Complete",
        -      "type": "boolean"
        -    },
        -    "from_cache": {
        -      "title": "From Cache",
        -      "type": "boolean"
        -    },
        -    "gross_totals": {
        -      "items": {
        -        "$ref": "#/$defs/GrossTotal"
        -      },
        -      "title": "Gross Totals",
        -      "type": "array"
        -    },
        -    "invoice_count": {
        -      "title": "Invoice Count",
        -      "type": "integer"
        -    },
        -    "invoices": {
        -      "items": {
        -        "$ref": "#/$defs/InvoiceRow"
        -      },
        -      "title": "Invoices",
        -      "type": "array"
        -    },
        -    "message": {
        -      "title": "Message",
        -      "type": "string"
        -    },
        -    "outcome": {
        -      "title": "Outcome",
        -      "type": "string"
        -    },
        -    "queried_at": {
        -      "anyOf": [
        -        {
        -          "type": "string"
        -        },
        -        {
        -          "type": "null"
        -        }
        -      ],
        -      "title": "Queried At"
        -    },
        -    "subject_type": {
        -      "title": "Subject Type",
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "subject_type",
        -    "outcome",
        -    "message",
        -    "invoices",
        -    "invoice_count",
        -    "gross_totals",
        -    "complete",
        -    "queried_at",
        -    "from_cache"
        -  ],
        -  "title": "DirectionListingResult",
        -  "type": "object"
        -}
      • addedOutput schema / $defs / SubjectRoleListingResult
        Added value: +{
        +  "properties": {
        +    "complete": {
        +      "title": "Complete",
        +      "type": "boolean"
        +    },
        +    "from_cache": {
        +      "title": "From Cache",
        +      "type": "boolean"
        +    },
        +    "gross_totals": {
        +      "items": {
        +        "$ref": "#/$defs/GrossTotal"
        +      },
        +      "title": "Gross Totals",
        +      "type": "array"
        +    },
        +    "invoice_count": {
        +      "title": "Invoice Count",
        +      "type": "integer"
        +    },
        +    "invoices": {
        +      "items": {
        +        "$ref": "#/$defs/InvoiceRow"
        +      },
        +      "title": "Invoices",
        +      "type": "array"
        +    },
        +    "message": {
        +      "title": "Message",
        +      "type": "string"
        +    },
        +    "outcome": {
        +      "title": "Outcome",
        +      "type": "string"
        +    },
        +    "queried_at": {
        +      "anyOf": [
        +        {
        +          "type": "string"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Queried At"
        +    },
        +    "subject_role": {
        +      "title": "Subject Role",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "subject_role",
        +    "outcome",
        +    "message",
        +    "invoices",
        +    "invoice_count",
        +    "gross_totals",
        +    "complete",
        +    "queried_at",
        +    "from_cache"
        +  ],
        +  "title": "SubjectRoleListingResult",
        +  "type": "object"
        +}
      • addedOutput schema / properties / message
        Added value: +{
        +  "title": "Message",
        +  "type": "string"
        +}
      • addedOutput schema / properties / subject_roles
        Added value: +{
        +  "items": {
        +    "$ref": "#/$defs/SubjectRoleListingResult"
        +  },
        +  "title": "Subject Roles",
        +  "type": "array"
        +}
      • removedOutput schema / properties / subject_types
        Removed value: -{
        -  "items": {
        -    "$ref": "#/$defs/DirectionListingResult"
        -  },
        -  "title": "Subject Types",
        -  "type": "array"
        -}
      • addedOutput schema / properties / warnings
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "title": "Warnings",
        +  "type": "array"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "nip",
        -  "environment",
        -  "threshold",
        -  "period_from",
        -  "period_to",
        -  "subject_types"
        -]New value: +[
        +  "nip",
        +  "environment",
        +  "message",
        +  "threshold",
        +  "period_from",
        +  "period_to",
        +  "subject_roles"
        +]
    • Changedrender_invoice_pdf4 fields changed
      • changedOutput schema / description
        Previous value: -"Where the document is and what it was made with. Never its content."New value: +"Where the document is and what it was made with. Never its content.\n\nA PDF carries the same counterparty personal data the CSV statement does, so\nthe working-directory caveats inherited in `warnings` travel with it too\n(#172)."
      • addedOutput schema / properties / message
        Added value: +{
        +  "title": "Message",
        +  "type": "string"
        +}
      • addedOutput schema / properties / warnings
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "title": "Warnings",
        +  "type": "array"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "nip",
        -  "environment",
        -  "ksef_number",
        -  "path",
        -  "byte_count",
        -  "generator_version",
        -  "verification_url"
        -]New value: +[
        +  "nip",
        +  "environment",
        +  "message",
        +  "ksef_number",
        +  "path",
        +  "byte_count",
        +  "generator_version",
        +  "verification_url"
        +]
    • Changedreview_new_invoices7 fields changed
      • removedOutput schema / $defs / DirectionReviewResult
        Removed value: -{
        -  "properties": {
        -    "complete": {
        -      "title": "Complete",
        -      "type": "boolean"
        -    },
        -    "earlier_month_count": {
        -      "title": "Earlier Month Count",
        -      "type": "integer"
        -    },
        -    "from_cache": {
        -      "title": "From Cache",
        -      "type": "boolean"
        -    },
        -    "gross_totals": {
        -      "items": {
        -        "$ref": "#/$defs/GrossTotal"
        -      },
        -      "title": "Gross Totals",
        -      "type": "array"
        -    },
        -    "marked_as_reviewed": {
        -      "title": "Marked As Reviewed",
        -      "type": "boolean"
        -    },
        -    "message": {
        -      "title": "Message",
        -      "type": "string"
        -    },
        -    "new_count": {
        -      "title": "New Count",
        -      "type": "integer"
        -    },
        -    "new_invoices": {
        -      "items": {
        -        "$ref": "#/$defs/ReviewedInvoiceRow"
        -      },
        -      "title": "New Invoices",
        -      "type": "array"
        -    },
        -    "outcome": {
        -      "title": "Outcome",
        -      "type": "string"
        -    },
        -    "queried_at": {
        -      "anyOf": [
        -        {
        -          "type": "string"
        -        },
        -        {
        -          "type": "null"
        -        }
        -      ],
        -      "title": "Queried At"
        -    },
        -    "subject_type": {
        -      "title": "Subject Type",
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "subject_type",
        -    "outcome",
        -    "message",
        -    "new_invoices",
        -    "new_count",
        -    "earlier_month_count",
        -    "gross_totals",
        -    "complete",
        -    "marked_as_reviewed",
        -    "queried_at",
        -    "from_cache"
        -  ],
        -  "title": "DirectionReviewResult",
        -  "type": "object"
        -}
      • addedOutput schema / $defs / SubjectRoleReviewResult
        Added value: +{
        +  "properties": {
        +    "complete": {
        +      "title": "Complete",
        +      "type": "boolean"
        +    },
        +    "earlier_month_count": {
        +      "title": "Earlier Month Count",
        +      "type": "integer"
        +    },
        +    "from_cache": {
        +      "title": "From Cache",
        +      "type": "boolean"
        +    },
        +    "gross_totals": {
        +      "items": {
        +        "$ref": "#/$defs/GrossTotal"
        +      },
        +      "title": "Gross Totals",
        +      "type": "array"
        +    },
        +    "marked_as_reviewed": {
        +      "title": "Marked As Reviewed",
        +      "type": "boolean"
        +    },
        +    "message": {
        +      "title": "Message",
        +      "type": "string"
        +    },
        +    "new_count": {
        +      "title": "New Count",
        +      "type": "integer"
        +    },
        +    "new_invoices": {
        +      "items": {
        +        "$ref": "#/$defs/ReviewedInvoiceRow"
        +      },
        +      "title": "New Invoices",
        +      "type": "array"
        +    },
        +    "outcome": {
        +      "title": "Outcome",
        +      "type": "string"
        +    },
        +    "queried_at": {
        +      "anyOf": [
        +        {
        +          "type": "string"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Queried At"
        +    },
        +    "subject_role": {
        +      "title": "Subject Role",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "subject_role",
        +    "outcome",
        +    "message",
        +    "new_invoices",
        +    "new_count",
        +    "earlier_month_count",
        +    "gross_totals",
        +    "complete",
        +    "marked_as_reviewed",
        +    "queried_at",
        +    "from_cache"
        +  ],
        +  "title": "SubjectRoleReviewResult",
        +  "type": "object"
        +}
      • addedOutput schema / properties / message
        Added value: +{
        +  "title": "Message",
        +  "type": "string"
        +}
      • addedOutput schema / properties / subject_roles
        Added value: +{
        +  "items": {
        +    "$ref": "#/$defs/SubjectRoleReviewResult"
        +  },
        +  "title": "Subject Roles",
        +  "type": "array"
        +}
      • removedOutput schema / properties / subject_types
        Removed value: -{
        -  "items": {
        -    "$ref": "#/$defs/DirectionReviewResult"
        -  },
        -  "title": "Subject Types",
        -  "type": "array"
        -}
      • addedOutput schema / properties / warnings
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "title": "Warnings",
        +  "type": "array"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "nip",
        -  "environment",
        -  "threshold",
        -  "period_from",
        -  "period_to",
        -  "ledger_file",
        -  "subject_types"
        -]New value: +[
        +  "nip",
        +  "environment",
        +  "message",
        +  "threshold",
        +  "period_from",
        +  "period_to",
        +  "ledger_file",
        +  "subject_roles"
        +]
    • Changedsynchronise_invoices11 fields changed
      • addedOutput schema / $defs / SessionCeilingsResult
        Added value: +{
        +  "description": "How much one session may carry, and whether KSeF granted it (GH-118).",
        +  "properties": {
        +    "assumed": {
        +      "title": "Assumed",
        +      "type": "boolean"
        +    },
        +    "max_invoice_megabytes": {
        +      "title": "Max Invoice Megabytes",
        +      "type": "integer"
        +    },
        +    "max_invoice_with_attachment_megabytes": {
        +      "title": "Max Invoice With Attachment Megabytes",
        +      "type": "integer"
        +    },
        +    "max_invoices_per_session": {
        +      "title": "Max Invoices Per Session",
        +      "type": "integer"
        +    },
        +    "message": {
        +      "title": "Message",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "assumed",
        +    "message",
        +    "max_invoice_megabytes",
        +    "max_invoice_with_attachment_megabytes",
        +    "max_invoices_per_session"
        +  ],
        +  "title": "SessionCeilingsResult",
        +  "type": "object"
        +}
      • addedOutput schema / $defs / SubjectRoleResult
        Added value: +{
        +  "description": "Paths and KSeF numbers for one subject type. Never an invoice body (D-011).",
        +  "properties": {
        +    "already_held": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "title": "Already Held",
        +      "type": "array"
        +    },
        +    "archive_directory": {
        +      "anyOf": [
        +        {
        +          "type": "string"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Archive Directory"
        +    },
        +    "archived": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "title": "Archived",
        +      "type": "array"
        +    },
        +    "invoice_count": {
        +      "title": "Invoice Count",
        +      "type": "integer"
        +    },
        +    "message": {
        +      "title": "Message",
        +      "type": "string"
        +    },
        +    "outcome": {
        +      "title": "Outcome",
        +      "type": "string"
        +    },
        +    "part_count": {
        +      "title": "Part Count",
        +      "type": "integer"
        +    },
        +    "subject_role": {
        +      "title": "Subject Role",
        +      "type": "string"
        +    },
        +    "synchronised_up_to": {
        +      "anyOf": [
        +        {
        +          "type": "string"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Synchronised Up To"
        +    }
        +  },
        +  "required": [
        +    "subject_role",
        +    "outcome",
        +    "message",
        +    "invoice_count",
        +    "part_count",
        +    "synchronised_up_to",
        +    "archived",
        +    "already_held",
        +    "archive_directory"
        +  ],
        +  "title": "SubjectRoleResult",
        +  "type": "object"
        +}
      • removedOutput schema / $defs / SubjectTypeResult
        Removed value: -{
        -  "description": "Paths and KSeF numbers for one subject type. Never an invoice body (D-011).",
        -  "properties": {
        -    "already_held": {
        -      "items": {
        -        "type": "string"
        -      },
        -      "title": "Already Held",
        -      "type": "array"
        -    },
        -    "archive_directory": {
        -      "anyOf": [
        -        {
        -          "type": "string"
        -        },
        -        {
        -          "type": "null"
        -        }
        -      ],
        -      "title": "Archive Directory"
        -    },
        -    "archived": {
        -      "items": {
        -        "type": "string"
        -      },
        -      "title": "Archived",
        -      "type": "array"
        -    },
        -    "detail": {
        -      "title": "Detail",
        -      "type": "string"
        -    },
        -    "invoice_count": {
        -      "title": "Invoice Count",
        -      "type": "integer"
        -    },
        -    "outcome": {
        -      "title": "Outcome",
        -      "type": "string"
        -    },
        -    "part_count": {
        -      "title": "Part Count",
        -      "type": "integer"
        -    },
        -    "subject_type": {
        -      "title": "Subject Type",
        -      "type": "string"
        -    },
        -    "synchronised_up_to": {
        -      "anyOf": [
        -        {
        -          "type": "string"
        -        },
        -        {
        -          "type": "null"
        -        }
        -      ],
        -      "title": "Synchronised Up To"
        -    }
        -  },
        -  "required": [
        -    "subject_type",
        -    "outcome",
        -    "detail",
        -    "invoice_count",
        -    "part_count",
        -    "synchronised_up_to",
        -    "archived",
        -    "already_held",
        -    "archive_directory"
        -  ],
        -  "title": "SubjectTypeResult",
        -  "type": "object"
        -}
      • addedOutput schema / properties / correlation
        Added value: +{
        +  "title": "Correlation",
        +  "type": "string"
        +}
      • addedOutput schema / properties / message
        Added value: +{
        +  "title": "Message",
        +  "type": "string"
        +}
      • addedOutput schema / properties / nip
        Added value: +{
        +  "title": "Nip",
        +  "type": "string"
        +}
      • addedOutput schema / properties / session_ceilings
        Added value: +{
        +  "$ref": "#/$defs/SessionCeilingsResult"
        +}
      • addedOutput schema / properties / subject_roles
        Added value: +{
        +  "items": {
        +    "$ref": "#/$defs/SubjectRoleResult"
        +  },
        +  "title": "Subject Roles",
        +  "type": "array"
        +}
      • removedOutput schema / properties / subject_types
        Removed value: -{
        -  "items": {
        -    "$ref": "#/$defs/SubjectTypeResult"
        -  },
        -  "title": "Subject Types",
        -  "type": "array"
        -}
      • addedOutput schema / properties / warnings
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "title": "Warnings",
        +  "type": "array"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "environment",
        -  "subject_types",
        -  "pending_exports",
        -  "state_file"
        -]New value: +[
        +  "nip",
        +  "environment",
        +  "message",
        +  "subject_roles",
        +  "pending_exports",
        +  "state_file",
        +  "session_ceilings",
        +  "correlation"
        +]
  2. 1 tool updatev0.3.1
    • Changedlist_recent_invoices2 fields changed
      • removedOutput schema / properties / period_to / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / period_to / type
        Added value: +"string"
  3. 5 tool updatesv0.3.0
    • Addedexport_period_statement
    • Addedlist_recent_invoices
    • Addedrender_invoice_pdf
    • Addedreview_new_invoices
    • Addedsynchronise_invoices
  4. 1 tool updatev0.1.0
    • First observedserver_info

TDQS

A4.4/5.0

Scored across 6 tools

Disambiguation4/5

Each tool targets a distinct operation—sync, list, review, export CSV, render PDF, and server info—and the descriptions are unusually explicit about boundaries. The only potential confusion is between the two metadata-listing tools (list_recent_invoices and review_new_invoices), whose similar output shapes are nevertheless sharply distinguished as a fixed 30-day window versus a since-last-seen ledger.

Naming Consistency4/5

Five of the six tools follow a consistent snake_case verb_object pattern: export_period_statement, render_invoice_pdf, list_recent_invoices, review_new_invoices, synchronise_invoices. server_info breaks the pattern as a bare noun phrase rather than a verb-led command, which is a minor but real deviation.

Tool Count5/5

Six tools is well-scoped for a domain-specific connector: one ingestion tool, two exposure/list tools, two output tools, and one informational tool. Each earns its place with no redundancy or filler.

Completeness4/5

The core workflow is fully traversable: synchronise brings invoices into the archive, list/review surface what arrived, and export/render produce the accountant-facing deliverables. The main gaps are the absence of a metadata lookup by a specific KSeF number and no way to list or search invoices older than the 30-day window, which agents would need to work around.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Model Context Protocol (MCP) server for Polish Electronic Invoicing (KSeF / FA(2)). Provides tools to validate, generate, and explore API specifications for KSeF interoperability.
    10
    3
    Apache 2.0
  • A
    license
    A
    quality
    A
    maintenance
    Model Context Protocol (MCP) server for Spanish Electronic Invoicing. Provides tools to generate, validate, and submit invoices across VERI\*FACTU, Facturae/FACe, SII, TicketBAI, and Crea y Crece B2B.
    20
    115 PyPI
    2
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    MCP server that provides AI agents with Polish business data tools: identifier validation (NIP, PESEL, REGON, KRS, IBAN), VAT whitelist checks, EU VIES lookups, and NBP exchange rates.
    5
    6 npm
    MIT