Paperless-NGX MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| PAPERLESS_URL | Yes | Base URL of your Paperless-NGX instance | |
| PAPERLESS_API_KEY | Yes | API token from your Paperless-NGX profile | |
| PAPERLESS_PUBLIC_URL | No | Public-facing URL for document links | PAPERLESS_URL |
| PAPERLESS_API_VERSION | No | Paperless-ngx REST API version. `9` works on Paperless-ngx v2.x (recent) and v3.x. Paperless-ngx v3.0.0 dropped support for versions below `9`, so older defaults now return HTTP 406. If you see HTTP 406 errors, set this to a version your server supports. | 9 |
| PAPERLESS_MCP_UPLOAD_PATHS | No | Colon-separated list of allowed directories for `file_path` uploads. **Recommended for security.** Example: `/var/uploads:/tmp/scans` | |
| PAPERLESS_MCP_MAX_INLINE_CHARS | No | Hard cap on characters a single tool response may inline. Requests above the budget fail with the document size and a hint toward bounded reads instead of silently truncating or flooding context. | 50000 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
Paperless-NGX MCP Server Instructions
⚠️ CRITICAL: Always differentiate between operations on specific documents vs operations on the entire system:
REMOVE operations (e.g., remove_tag in bulk_edit_documents): Affect only the specified documents, items remain in the system
DELETE operations (e.g., delete_tag, delete_correspondent): Permanently delete items from the entire system, affecting ALL documents that use them
When a user asks to "remove" something, prefer operations that affect specific documents. Only use DELETE operations when explicitly asked to delete from the system.
To view documents in your Paperless-NGX web interface, construct URLs using this pattern: https://paperless.example.com/documents/{document_id}/
Example: If your base URL is "http://localhost:8000", the web interface URL would be "http://localhost:8000/documents/123/" for document ID 123.
The document tools return JSON data with document IDs that you can use to construct these URLs.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| resources | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| bulk_edit_documentsB | Perform bulk operations on multiple documents. Note: 'remove_tag' removes a tag from specific documents (tag remains in system), while 'delete_tag' permanently deletes a tag from the entire system. ⚠️ WARNING: 'delete' method permanently deletes documents and requires confirmation. |
| post_documentA | Upload a new document to Paperless-NGX with optional metadata like title, correspondent, document type, tags, and custom fields. Provide either 'file' (base64-encoded content) or 'file_path' (absolute path to a file on the server's filesystem). Using file_path avoids base64 encoding overhead for large files. SECURITY: When using file_path, set PAPERLESS_MCP_UPLOAD_PATHS environment variable to restrict uploads to specific directories (colon-separated paths). |
| list_documentsA | List and filter documents with pagination and common Paperless filters such as title search, correspondent, document type, tag, storage path, creation date, archive serial number, and simple custom field filters. Use 'query_documents' for full-text query, structured custom field conditions, or advanced documented /api/documents/ query parameters. IMPORTANT: For queries like 'the last 3 contributions' or when searching by tag, correspondent, document type, or storage path, first use the relevant lookup tool to find the correct ID. Note: Document content is excluded from results by default. Use 'get_document_content' when you need the document text. |
| search_passagesA | Search documents full-text and return bounded text passages around each hit instead of whole documents. The window is anchored on the server's own highlighted phrase (falling back to the first verbatim term, then the document head), so the returned passage contains the actual hit even when stemming matched a word variant. Queries that the server rejects with an HTTP 400 are retried once with a defensively rewritten query (apostrophes and stray quotes removed); such responses carry query_sanitized: true. Returns document id, title, score, offset, content_length, and the highlighted snippet. Use this to locate and skim evidence across many documents without flooding context; page further with get_document_content offsets. |
| query_documentsA | Query documents using the full-text query engine plus structured Paperless filters. Use this for complex filtering, custom field conditions, or any documented /api/documents/ query parameters that are not exposed as first-class arguments. Prefer the dedicated top-level arguments where available. custom_field_query supports [field_name_or_id, operator, value] leaves or ['AND'|'OR', [clause1, clause2]] groups. Note: Document content is excluded from results by default. Use 'get_document_content' when you need the document text. |
| get_documentA | Get a specific document by ID with full details including correspondent, document type, tags, and custom fields. Note: Document content is excluded from results by default. Use 'get_document_content' to retrieve content when needed. |
| get_document_contentA | Get the text content of a specific document by ID, in bounded windows. Returns at most max_chars characters (default 8000) starting at offset, plus content_length and pagination metadata so callers can page through long documents. Use offset + max_chars to read successive windows of a long document; use the download_document resource for bulk access to the raw file. |
| search_documentsA | Deprecated compatibility wrapper for full-text document search. Use 'query_documents' with the 'query' argument for new integrations. Note: Document content is excluded from results by default. Use 'get_document_content' to retrieve content when needed. |
| download_documentB | Download a document file by ID. Returns a paperless:// resource URI; read the resource to fetch the file content. |
| get_document_thumbnailB | Get a document thumbnail (image preview) by ID. Returns a paperless:// resource URI; read the resource to fetch the image content. |
| update_documentA | Update a specific document with new values (title, correspondent, document type, storage path, tags, custom fields, and more). Top-level fields you omit are left unchanged. IMPORTANT: custom_fields is the exception — see its parameter description; it replaces the document's entire custom-field set. |
| list_document_notesA | List all notes attached to a document. Notes are free-text comments on a document and are the natural place for an audit trail (e.g. "invoice paid on X from account Y") or progress notes on an action item. |
| create_document_noteA | Add a note to a document. Use this to record an audit trail or progress note directly on the document. Returns the document's full list of notes after the note is added. |
| delete_document_noteA | ⚠️ DESTRUCTIVE: Permanently delete a single note from a document by its note ID. This operation is irreversible. Returns the document's remaining notes. |
| list_tagsB | List all tags. IMPORTANT: When a user query may refer to a tag or document type, you should fetch all tags and all document types up front (with a large enough page_size), cache them for the session, and search locally for matches by name or slug before making further API calls. This reduces redundant requests and handles ambiguity between tags and document types efficiently. |
| create_tagC | Create a new tag with optional color, matching pattern, and matching algorithm for automatic document tagging. |
| update_tagC | Update an existing tag's name, color, matching pattern, or matching algorithm. |
| delete_tagA | ⚠️ DESTRUCTIVE: Permanently delete a tag from the entire system. This will remove the tag from ALL documents that use it. Use with extreme caution. |
| bulk_edit_tagsC | Bulk edit tags. ⚠️ WARNING: 'delete' operation permanently removes tags from the entire system. Use with caution. |
| list_correspondentsC | List all correspondents with optional filtering and pagination. Correspondents represent entities that send or receive documents. |
| get_correspondentA | Get a specific correspondent by ID with full details including matching rules. |
| create_correspondentC | Create a new correspondent with optional matching pattern and algorithm for automatic document assignment. |
| update_correspondentC | Update an existing correspondent's name, matching pattern, or matching algorithm. |
| delete_correspondentA | ⚠️ DESTRUCTIVE: Permanently delete a correspondent from the entire system. This will affect ALL documents that use this correspondent. |
| bulk_edit_correspondentsC | Bulk edit correspondents. ⚠️ WARNING: 'delete' operation permanently removes correspondents from the entire system. |
| list_document_typesA | List all document types. IMPORTANT: When a user query may refer to a document type or tag, you should fetch all document types and all tags up front (with a large enough page_size), cache them for the session, and search locally for matches by name or slug before making further API calls. This reduces redundant requests and handles ambiguity between tags and document types efficiently. |
| get_document_typeB | Get a specific document type by ID with full details including matching rules. |
| create_document_typeC | Create a new document type with optional matching pattern and algorithm for automatic document classification. |
| update_document_typeC | Update an existing document type's name, matching pattern, or matching algorithm. |
| delete_document_typeB | ⚠️ DESTRUCTIVE: Permanently delete a document type from the entire system. This will affect ALL documents that use this type. |
| bulk_edit_document_typesB | Bulk edit document types. ⚠️ WARNING: 'delete' operation permanently removes document types from the entire system. |
| list_custom_fieldsA | List all custom fields. IMPORTANT: When a user query may refer to a custom field, you should fetch all custom fields up front (with a large enough page_size), cache them for the session, and search locally for matches by name before making further API calls. This reduces redundant requests and handles ambiguity efficiently. |
| get_custom_fieldB | Get a specific custom field by ID with full details including data type and extra configuration. |
| create_custom_fieldC | Create a new custom field with a specified data type (string, url, date, boolean, integer, float, monetary, documentlink, or select). For monetary fields, values must use currency code prefix format (e.g., USD10.00, GBP123.45) — NOT trailing symbol format (e.g., 10.00$). |
| update_custom_fieldC | Update an existing custom field's name, data type, or extra configuration data. |
| delete_custom_fieldA | ⚠️ DESTRUCTIVE: Permanently delete a custom field from the entire system. This will remove the field from ALL documents that use it. |
| bulk_edit_custom_fieldsB | Bulk edit custom fields. ⚠️ WARNING: 'delete' operation permanently removes custom fields from the entire system. |
| list_mail_accountsB | List Paperless mail accounts for selecting the account ID needed by mail rules. Does not expose account passwords. |
| get_mail_accountA | Get one Paperless mail account by ID. Password/token fields are redacted if the server returns them. |
| process_mail_accountB | Manually run Paperless mail processing for one account. This can consume matching mails according to enabled Paperless mail rules. |
| list_mail_rulesC | List Paperless mail rules with optional pagination. |
| get_mail_ruleB | Get one Paperless mail rule by ID. |
| create_mail_ruleB | Create a Paperless mail rule. Use list_mail_accounts first to choose account. Prefer attachment-only rules for invoices unless the full mail must be archived. |
| update_mail_ruleB | Patch an existing Paperless mail rule. Only supplied fields are changed. |
| delete_mail_ruleB | Delete one Paperless mail rule. This changes future mail ingestion behavior but does not delete documents. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 45 tools
Most tools map to a distinct resource+action, and the descriptions do a good job steering callers (e.g. list_documents vs query_documents vs search_passages). The main fuzziness is the document-search cluster: list_documents, query_documents, search_documents (deprecated compatibility wrapper), and search_passages all retrieve documents, so an agent must read descriptions carefully to pick the right one. Deprecating search_documents mitigates this, but the overlap keeps it below full clarity.
Nearly everything follows a predictable verb_noun pattern (list_tags, create_tag, update_tag, delete_tag, get_correspondent, etc.) across all resource families. Minor deviations exist: post_document instead of create_document, and bulk_edit_* as a separate prefix rather than part of the standard verb set. These are readable and consistent enough not to confuse.
45 tools is heavy, but the domain genuinely spans seven resource types (documents, notes, tags, correspondents, document types, custom fields, mail accounts/rules) each with CRUD, so much of the surface earns its place. Still, the count is inflated by a deprecated wrapper and a near-identical bulk_edit_* variant for every entity, pushing it into borderline territory.
Coverage is broad: documents have create/read/update/delete (delete via bulk_edit_documents), content paging, download/thumbnail, and search; tags, correspondents, document types, and custom fields all have full CRUD plus bulk. Gaps are minor: no single delete_document (only bulk method), no get_tag (list only), and mail accounts are read/process-only with no create/update/delete, though those may be intentional.