Unofficial MCP server and CLI for paperless.tax, the Israeli bookkeeping platform
Strong tool definitions with excellent descriptions and clear schemas. All 6 tools have detailed, LLM-optimized descriptions (100-300 chars) that explain WHAT, WHEN, and HOW. Input schemas use Zod with proper types and descriptions. Error handling returns structured text responses. Main gaps: no output schemas documented, no tool annotations (readOnlyHint), and raw_request lacks input validation guidance. Naming is verb-forward and clear. Composition is sound, each tool has one responsibility and tools chain via IDs.
Fetch one document via `GET documents/get/document/{id}`. Returns exactly the same fields as list_documents, so this is for spot checks — not for looping over a list.
Resolve a document to its PDF via `POST documents/get/downloadurl`. Returns a presigned S3 URL valid for 2 hours. Do not send an Authorization header when fetching it — the signature does not cover one. This PDF is the document itself; the aDocURLs/sDocURLEdit fields in a listing are lossy JPEG page renders capped at 1000px. Records with nothing scanned against them have no PDF and this call fails.
List documents via `POST documents/get/bydate`. Confirmed against the live API. Periods are YYMM strings, NOT ISO dates — pass 2506 for June 2025. Both are optional and default to the app's own unbounded range (1001..2912), which returns the entire account history in one response: there is no pagination, and the records are complete, so do NOT follow up with get_document for each one. Amounts are integer agorot (iAmount 10341 = ₪103.41).
List uploaded-but-unfiled documents via `GET documents/get/pending/{sUserID}`.
No output schemas documented. LLMs cannot plan downstream calls or extract fields without knowing response structure. list_documents, get_document, get_download_url, pending_documents, and raw_request all return JSON but lack field documentation.
raw_request lacks input validation guidance. The 'body' parameter accepts arbitrary JSON strings with no constraints on structure, size, or content. Description mentions 'sUserID' substitution but does not warn about injection risks or malformed payloads.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). All tools are read-only, but this is not declared in the schema. Agents cannot infer safety properties without explicit hints.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 64 | 2026-07-28+ | v2 |
Call an arbitrary endpoint — the escape hatch for mapping unknown surface. `path` is relative to the API base and MUST include the controller prefix (e.g. `documents/get/searchdp`, not `searchdp`); root-level paths return 404. Prefer a named tool where one exists. Pass sUserID as the literal "auto" to have it filled in. A 503 carrying NotImplementedException means the route exists but the feature does not, so retrying will not help.
Fetch account context from `users/get/params`. Confirmed endpoint. Use this first — it is the cheapest check that the configured token is actually accepted.
list_documents applies doc_type filter client-side. Description states 'Applied client-side' but does not explain why the API does not support server-side filtering. This is a performance and clarity issue, agents may not realize they are fetching all documents and filtering locally.
No pagination or result limits documented. list_documents returns 'the entire account history in one response' with no cap. For large accounts, this could exhaust context windows. No guidance on when to use raw_request for pagination.