mcpField
P
paperless-ngx

paperless-ngx

github.com/kjanatDocuments658 installsUpdated 14w ago
Repository

About paperless-ngx

paperless-ngx is a skill for managing Paperless-ngx documents and related metadata. It covers search, uploads, organization, mail ingestion, tasks, and trash through an MCP toolset spanning nine domains.

Key features

  1. 01Search and retrieve documents
  2. 02Upload and download files
  3. 03Edit tags and document metadata
  4. 04Manage mail import rules
  5. 05Track tasks and restore trash

Use cases

  1. 01Find documents by text or metadata
  2. 02Upload files with assigned metadata
  3. 03Organize documents and taxonomy
  4. 04Review mail imports and restore deleted documents

MCP support

It orchestrates Paperless-ngx through 42 MCP tools across nine domains. Document binaries are also available to MCP clients as resources.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/kjanat/paperless-mcp --skill paperless-ngx

After installing, type/paperless-ngxin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
query-syntax.md
tools.md
workflows.md
scripts/
encode-file.sh
test-connection.sh

Details

From the project README

Paperless-ngx Document Management

Orchestrate Paperless-ngx through 42 MCP tools across 9 domains.

Tool Catalog

Documents (7 tools)
ToolOperationKey Params
search_documentsFull-text searchquery, page, page_size
get_documentFull detailsid
update_documentPatch single docid, title, archive_serial_number, custom_fields, note
delete_document_noteRemove a noteid, note_id
post_documentUpload filefile_path (local) or file (base64), metadata
download_documentGet file base64id, original (bool)
bulk_edit_documentsBatch operationsdocuments (IDs), method, params
Tags (6 tools)
ToolOperation
list_tagsAll tags + colors + matching
get_tagSingle tag by ID
create_tagNew tag, optional auto-match
update_tagModify name/color/matching
delete_tagDeprecated — use bulk_edit_tags operation=delete
bulk_edit_tagsBatch permissions/deletion
Correspondents (5 tools)
ToolOperation
list_correspondentsAll correspondents
get_correspondentSingle correspondent by ID
create_correspondentNew, optional auto-match
update_correspondentModify name/matching rules
bulk_edit_correspondentsBatch permissions/delete
Document Types (5 tools)
ToolOperation
list_document_typesAll document types
get_document_typeSingle type by ID
create_document_typeNew, optional auto-match
update_document_typeModify name/matching rules
bulk_edit_document_typesBatch permissions/delete
Storage Paths (4 tools)
ToolOperation
list_storage_pathsAll storage paths
create_storage_pathNew path template + auto-match
update_storage_pathModify name/template/matching
bulk_edit_storage_pathsBatch permissions/delete
Custom Fields (4 tools)
ToolOperation
list_custom_fieldsAll field definitions + IDs
create_custom_fieldNew field (name + data type)
update_custom_fieldModify name/data type/options
delete_custom_fieldDelete one field + its values everywhere
Tasks (2 tools)
ToolOperation
get_taskStatus + resulting doc IDs for a post_document UUID
list_tasksRecent tasks newest-first; filter status/name, max 100
Trash (3 tools)
ToolOperation
list_trashSoft-deleted documents awaiting purge
restore_from_trashBring documents back with metadata intact
empty_trashPERMANENTLY purge (all, or specific IDs)
Mail (6 tools)
ToolOperation
list_mail_accountsPolled accounts (credentials stripped)
process_mail_accountTrigger an immediate mail poll
list_mail_rulesFilters that decide what gets imported
create_mail_ruleNew import rule (filters + assignments)
update_mail_ruleModify filters/assignments/enabled
delete_mail_ruleRemove a rule (future ingestion only)

Decision Trees

Find a Document
What do you know?
├─ Keywords/content     → search_documents(query="term1 term2")
├─ Document ID          → get_document(id=N)
├─ By tag               → search_documents(query="tag:tagname")
├─ By type              → search_documents(query="type:typename")
├─ By correspondent     → search_documents(query="correspondent:name")
├─ By date              → search_documents(query="created:[2024 to 2025]")
└─ Combined             → search_documents(query="tag:X correspondent:Y created:[2024 to 2025]")
Organize Documents
What operation?
├─ Rename title    → update_document(id=N, title="...")
├─ Set/clear ASN   → update_document(id=N, archive_serial_number=N|null)
├─ Custom fields   → update_document(id=N, custom_fields=[{field, value}])  (IDs via list_custom_fields)
├─ Add note        → update_document(id=N, note="...")
├─ Remove note     → delete_document_note(id=N, note_id=NID)
├─ Add tag         → bulk_edit_documents(method="add_tag", tag=ID)
├─ Remove tag      → bulk_edit_documents(method="remove_tag", tag=ID)
├─ Multi-tag       → bulk_edit_documents(method="modify_tags", add_tags=[...], remove_tags=[...])
├─ Set type        → bulk_edit_documents(method="set_document_type", document_type=ID)
├─ Set sender      → bulk_edit_documents(method="set_correspondent", correspondent=ID)
├─ Merge PDFs      → bulk_edit_documents(method="merge", metadata_document_id=ID)
├─ Rotate pages    → bulk_edit_documents(method="rotate", degrees=90|180|270)
├─ Delete pages    → bulk_edit_documents(method="delete_pages", pages=[1, 3, 5])
├─ Reprocess OCR   → bulk_edit_documents(method="reprocess")
├─ Delete          → bulk_edit_documents(method="delete")  → goes to TRASH
├─ Undo a delete   → list_trash → restore_from_trash(documents=[...])
└─ Purge for good  → empty_trash(documents=[...])  !! PERMANENT !!
Upload a Document
1. Resolve metadata IDs first:
   ├─ list_tags            → find or create_tag
   ├─ list_correspondents  → find or create_correspondent
   └─ list_document_types  → find or create_document_type
2. post_document(file_path="~/Downloads/name.pdf", tags=[...], correspondent=ID, ...)
   → returns a task UUID, not a document ID
   (file=<base64> + filename also works, but only for small files)
3. get_task(task_id=<uuid>) until status="SUCCESS"
   → related_document holds the new document ID
Lost the UUID? → list_tasks(task_name="consume_file") shows recent uploads;
                 list_tasks(status="FAILURE") shows rejected ones (e.g. duplicates)
Manage Taxonomy (Tags/Correspondents/Types)
Need to change metadata objects?
├─ View all          → list_tags / list_correspondents / list_document_types /
│                      list_storage_paths / list_custom_fields
│                      (all take an optional name= substring filter)
├─ Resolve one ID    → get_tag / get_correspondent / get_document_type
├─ Create new        → create_tag / create_correspondent / create_document_type /
│                      create_storage_path / create_custom_field
├─ Edit tag          → update_tag(id, name, color, match, matching_algorithm)
├─ Edit sender       → update_correspondent(id, name, match, matching_algorithm)
├─ Edit type         → update_document_type(id, name, match, matching_algorithm)
├─ Edit storage path → update_storage_path(id, name, path, match, matching_algorithm)
├─ Edit custom field → update_custom_field(id, name, data_type, extra_data)
├─ Batch delete/perm → bulk_edit_tags / bulk_edit_correspondents /
│                      bulk_edit_document_types / bulk_edit_storage_paths
└─ Del custom field  → delete_custom_field(id)  !! drops values from all docs !!

Critical Notes

  • search_documents strips content to save tokens. Use get_document for full OCR text.
  • post_document prefers file_path: the MCP server reads the file from its own filesystem, so size doesn't matter. Inline base64 (file + filename) passes through the model: small files only. file_path works on the stdio transport only; the HTTP transport rejects it.
  • matching_algorithm is integer 0-6 across all endpoints (tags, correspondents, document types): 0=none, 1=any, 2=all, 3=exact, 4=regex, 5=fuzzy, 6=auto. See tools.md.
  • Mail rules shape FUTURE ingestion. create/update/delete_mail_rule change what the next mail polls import; already-consumed documents are untouched. Pause with enabled=false instead of deleting. Mail account credentials never appear in responses and cannot be set via MCP (web UI only).
  • Document binaries are MCP resources: read paperless://documents/{id}/archive|original|thumbnail via resources/read instead of pulling base64 through download_document; pass as_resource_link=true to download_document to get the link. Thumbnail = cheap preview without the full file.
  • Document delete is a soft-delete: bulk_edit_documents(method="delete") moves documents to the trash, restorable via restore_from_trash until the retention period expires or empty_trash purges them. Taxonomy deletes (tags, correspondents, document types, storage paths, custom fields) are immediate and permanent.
  • download_document returns base64 blob + filename from content-disposition.
  • list_tags, list_correspondents, and list_document_types return complete paginated result sets; use IDs from results, not the bare all ID list alone.
  • bulk_edit_documents accepts top-level MCP fields, but Paperless receives a nested parameters object internally. The MCP tool forwards only the fields relevant to the selected method.
  • update_document is single-document only (title/ASN/custom fields/note). The backend bulk endpoint has no set_title method — use update_document to rename; keep tags/correspondent/type in bulk_edit_documents. note appends a note (notes live on a separate Paperless endpoint internally); remove one with delete_document_note.
  • Notes are an append-only log. A document can hold multiple notes, each with its own timestamp and author. There is no edit operation — not in the API, not in the web UI; this is by design (log semantics). To correct an earlier note, append a new one (e.g. "correction: ..."). Reserve delete_document_note for entries that truly must go — deleting rewrites history.
  • post_document returns a task UUID, not a document ID. Poll get_task(task_id) until status="SUCCESS"; related_document then holds the resulting document ID.
  • custom field values need IDs from list_custom_fields. Resolve the field name → numeric ID there before calling update_document.custom_fields or bulk_edit_documents.modify_custom_fields.
  • delete_tag is deprecated (removal in v3.0.0) — use bulk_edit_tags with operation="delete", consistent with correspondents, document types, and storage paths.
  • delete_custom_field is single-delete only — the backend has no bulk endpoint for custom fields. Deletion is permanent and drops the field's values from every document that uses it.

References

TaskFile
Tool parameters & typestools.md
Search query syntaxquery-syntax.md
Multi-step workflowsworkflows.md

Comments

Markdown supported