MCP server exposing Paperless-ngx document management as tools
Server has 16 well-named tools with clear descriptions and proper input schemas. Naming follows verb_noun convention (get_, list_, create_, delete_, rename_). Descriptions are substantive (100-250 chars typical), explaining WHAT the tool does and WHEN to use it. All parameters have type definitions and descriptions. However, output schemas are not explicitly documented in the code, only inferred from Pydantic models. Error handling is minimal: no recovery guidance, no categorization of retryable vs fatal errors, no actionable error messages. Destructive operations (delete_*) have confirmation guards, which is good. Missing: per-parameter constraints (enums, ranges), dependency hints, and structured error responses.
Create a new correspondent.
Create a new document type.
Create a new tag.
Permanently delete a correspondent. Documents that had it keep their other fields; this field becomes empty on them. Requires confirm=True as a safety guard.
Permanently delete a document type. Documents that had it keep their other fields; this field becomes empty on them. Requires confirm=True as a safety guard.
Permanently delete a tag, removing it from every document that carries it. Requires confirm=True as a safety guard — calling with confirm=False (or omitted) is a no-op that explains what would happen.
Output schemas not explicitly documented. Pydantic models (NamedItem, DocumentSummary, DocumentDetail) are inferred from code but not exposed in tool registration or docstrings. LLMs cannot reliably predict response structure without explicit schema documentation.
Error handling lacks recovery guidance and categorization. Tools return raw exceptions or minimal strings (e.g., 'Not deleted. Call again with confirm=True...') but do not classify errors as retryable, user-fixable, or fatal. No guidance on what to do when a tag/correspondent/document_type is not found.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 61 | 2026-07-28+ | v2 |
Fetch full details of one document, including OCR content and file checksum.
Return the connected Paperless-ngx instance's base URL (from PAPERLESS_URL), e.g. to build links into its web UI (f"{url}/documents/{id}/details").
List all correspondents defined in Paperless.
List all document types defined in Paperless.
List documents, optionally filtered by tag/correspondent/document_type (name or id), a full-text search query, and/or how many tags a document has. Use min_tags=0, max_tags=0 to find untagged documents, or e.g. min_tags=1, max_tags=2 for sparsely tagged ones. Returns compact summaries without OCR content — use get_document for full text. Always paginates internally, so limit is honored even beyond a single page.
List all tags defined in Paperless.
Rename an existing correspondent, identified by its current name or id. Note: Paperless matches names case-insensitively, so renaming purely to change casing will silently resolve back to the same correspondent instead of changing anything.
Rename an existing document type, identified by its current name or id. Note: Paperless matches names case-insensitively, so renaming purely to change casing will silently resolve back to the same document type instead of changing anything.
Rename an existing tag, identified by its current name or id. Note: Paperless matches names case-insensitively, so renaming purely to change casing (e.g. "egk" -> "eGK") will silently resolve back to the same tag instead of changing anything.
Update a document's metadata. correspondent/document_type/tags accept either existing names/ids or brand-new names (auto-created unless create_missing=False). Passing tags replaces the full tag set (Paperless semantics) — use add_tags/remove_tags to modify it incrementally instead. Pass content to overwrite the extracted OCR text, e.g. after manually transcribing a document where OCR failed (handwriting, poor scan).
list_documents lacks pagination documentation and result limits. Tool accepts 'limit' parameter but does not document max value, default behavior beyond 'limit is honored even beyond a single page', or whether results are truncated. No 'total_count' or 'next_cursor' in response to guide pagination.
Parameter constraints missing. 'ordering' in list_documents accepts free-form strings with no enum or validation. 'limit' has no documented min/max bounds. 'min_tags' and 'max_tags' lack range documentation. LLMs may pass invalid values.
Ambiguous parameter semantics in update_document. 'tags' parameter description says 'replaces full tag set' but does not explain what happens if a tag name does not exist and create_missing=False. 'correspondent' and 'document_type' accept 'existing names/ids or brand-new names', unclear how the tool disambiguates a string that could be either.