The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Paperless Ngx listing page.
A Model Context Protocol server for Paperless-NGX. Exposes the full Paperless-NGX REST API to AI assistants — documents, tags, correspondents, document types, custom fields, storage paths, saved views, share links and bundles, workflows, mail accounts and rules, document versions, notes, trash, and tasks.

/api/schema/ against the tools here and fails when upstream adds one that is neither wrapped nor deliberately skipped.delete_* tool, empty_trash, merge_documents_as_versions and bulk delete require confirm: true; bulk edits across "all matching documents" refuse filters Paperless would silently ignore; and the triage_inbox prompt proposes changes and waits for your go-ahead before writing anything.list_*, get_*, delete_*, …) group into one permission wildcard each.npx, a Docker image, a one-click Claude Desktop extension, or the official MCP Registry.Targets Paperless-ngx 3.2 (tested against 3.2.1). Older Paperless versions are not supported — use paperless-ngx-mcp@3.1.1 for Paperless 2.x. The package major version tracks the Paperless-ngx major it targets; there are no 1.x or 2.x releases.
The server is published to npm as paperless-ngx-mcp. You can run it with npx — no clone or build required.
Drop --scope user to install for the current project only. See claude mcp add --help for more options.
This writes the entry to ~/.codex/config.toml.
Download paperless-ngx-mcp.mcpb from the latest release and double-click it, or install it from Settings → Extensions. Claude Desktop asks for your Paperless URL and API token.
The buttons install placeholder values; replace PAPERLESS_URL and PAPERLESS_API_KEY afterwards. Or add this to your client's MCP config file (e.g. claude_desktop_config.json, ~/.cursor/mcp.json, ~/.config/cline/mcp.json):
A multi-arch image (amd64, arm64) is published to ghcr.io/cubinet-code/paperless-ngx-mcp. As a stdio server in any MCP client config:
Or as a long-running Streamable HTTP server. It has no authentication, so keep it off untrusted networks:
| Variable | Required | Purpose |
|---|---|---|
PAPERLESS_URL | yes | Base URL the MCP server uses to talk to Paperless-NGX. |
PAPERLESS_API_KEY | yes | API token (see above). |
PAPERLESS_PUBLIC_URL | no | Public URL the assistant uses when constructing browser links to documents. Falls back to PAPERLESS_URL. |
CLI flags (--baseUrl, --token, --publicUrl, --http, --port, plus the HTTP session limits) take precedence over environment variables.
Things you can ask Claude (or any MCP-aware assistant):
The server registers tools across twelve domains.
list_documents, get_document, get_document_content, search_documents, download_document, download_documents_bulk, get_document_thumbnail, get_document_preview, get_document_history, get_document_metadata, update_document, post_document, email_document, edit_documents_bulk, delete_document, search_autocomplete, get_document_suggestions, get_document_ai_suggestions, get_next_asn, upload_document_version, update_document_version, delete_document_version, merge_documents_as_versions
list_tags, get_tag, create_tag, update_tag, delete_tag, edit_tags_bulk
list_correspondents, get_correspondent, create_correspondent, update_correspondent, delete_correspondent, edit_correspondents_bulk
list_document_types, get_document_type, create_document_type, update_document_type, delete_document_type, edit_document_types_bulk
list_custom_fields, get_custom_field, create_custom_field, update_custom_field, delete_custom_field, edit_custom_fields_bulk
list_storage_paths, get_storage_path, create_storage_path, update_storage_path, delete_storage_path, test_storage_path
list_saved_views, get_saved_view, create_saved_view, update_saved_view, delete_saved_view
list_share_links, list_document_share_links, get_share_link, create_share_link, delete_share_link, list_share_link_bundles, get_share_link_bundle, create_share_link_bundle, rebuild_share_link_bundle, delete_share_link_bundle
list_workflows, get_workflow, create_workflow, update_workflow, delete_workflow, list_workflow_actions, get_workflow_action, create_workflow_action, update_workflow_action, delete_workflow_action, list_workflow_triggers, get_workflow_trigger, create_workflow_trigger, update_workflow_trigger, delete_workflow_trigger
list_mail_accounts, get_mail_account, create_mail_account, update_mail_account, delete_mail_account, test_mail_account, process_mail_account, list_mail_rules, get_mail_rule, create_mail_rule, update_mail_rule, delete_mail_rule
get_statistics, get_system_status, list_document_notes, create_document_note, delete_document_note, list_trash, restore_from_trash, empty_trash, list_tasks, list_active_tasks, get_task_status_counts, get_task_summary, acknowledge_tasks
Tool names are verb-first, so wildcard-based permission rules group cleanly by operation:
| Wildcard | Covers |
|---|---|
mcp__paperless__list_* | All list/index reads |
mcp__paperless__get_* | All single-item reads |
mcp__paperless__search_* | Full-text search and autocomplete |
mcp__paperless__download_* | download_document and download_documents_bulk |
mcp__paperless__create_* | All create endpoints |
mcp__paperless__update_* | Per-item PATCH updates |
mcp__paperless__edit_*_bulk | All bulk-edit operations across entity types |
mcp__paperless__delete_* | ⚠️ Destructive — system-wide deletes |
mcp__paperless__test_* | Dry-run checks: test_storage_path, test_mail_account |
mcp__paperless__upload_* | upload_document_version |
mcp__paperless__rebuild_* | rebuild_share_link_bundle |
mcp__paperless__merge_* | ⚠️ merge_documents_as_versions — merged documents stop existing on their own |
mcp__paperless__process_* | process_mail_account — fetches mail now and runs its rules (which may delete or move mail on the server) |
A read-only allowlist is therefore: list_*, get_*, search_*, download_*, test_*. Write access without destructive operations: add create_*, update_*, edit_*_bulk, upload_*, rebuild_*, post_document, email_document, restore_from_trash, acknowledge_tasks. delete_*, merge_*, process_* and empty_trash should require explicit user approval.
The server also registers MCP prompts — reusable, parameterized instructions that surface as slash commands in clients like Claude Code (e.g. /mcp__paperless__triage_inbox).
triage_inboxWalks the assistant through inbox triage: gather existing tags / correspondents / document types, propose metadata for each inbox document preferring existing items, present a confirmation table, and only apply changes after the user replies apply. New correspondents / types / tags are flagged (NEW) so you can veto creations before they happen.
Argument:
limit (optional, default 25): maximum number of inbox documents to triage in one pass.edit_documents_bulkPerform bulk operations on multiple documents.
Parameters:
documents (array of IDs), or all: true + filters with optional excluded_documents. filters takes Paperless document filter names such as correspondent__id, tags__id__all, document_type__id, title_content or query — not list_documents' tool arguments. Keys Paperless doesn't know are refused (it would otherwise ignore them and select every document), a preview query catches invalid values, and the result reports matched_documents. all: true isn't supported for merge, split, delete_pages, edit_pdf or remove_password.method: one of set_correspondent, set_document_type, set_storage_path, add_tag, remove_tag, modify_tags, modify_custom_fields, delete, reprocess, set_permissions, merge, split, rotate, delete_pages, edit_pdf, remove_passwordcorrespondent, document_type, storage_path, tag, add_tags, remove_tags, add_custom_fields, remove_custom_fields, set_permissions, owner, merge, metadata_document_id, delete_originals, pages, degrees, operations, update_document, include_metadata, password, delete_original, remote_ocrPaperless answers remove_password with OK even when it skips a document (its latest version isn't encrypted) or the password is wrong, so check the document's versions afterwards.
A workflow — its triggers plus the actions they run — is what Paperless executes; create_workflow builds one in a single call. Standalone triggers and actions (create_workflow_trigger / create_workflow_action) do nothing on their own, and Paperless deletes unattached ones whenever any workflow is updated.
update_workflow changes only what you pass — but a triggers or actions list replaces the whole list: entries with an id are updated, entries without one are created, and omitted ones are deleted. get_workflow output can be edited and sent straight back.**********). Sending the masked list back keeps the stored passwords; a new action needs the real ones.upload_document_version adds a new file to an existing document (a signed copy, a corrected scan), and merge_documents_as_versions folds duplicate documents into one. Content, search, downloads and get_document_metadata follow the latest version, but page_count and the file names on get_document describe the original (root) version — check get_document_content to see whether the current version is readable.
post_documentUpload a new document.
Parameters: file, filename, plus optional title, created, correspondent, document_type, storage_path, tags, archive_serial_number, custom_fields, poll, poll_timeout_seconds.
file accepts base64-encoded contents (the universal method — works for any deployment, since the bytes travel over the wire) or an absolute file path that the server reads from its own filesystem. The path option only works when the MCP server runs on the same machine as the file (local/stdio deployments); for a remote server, use base64.
Upload is asynchronous. By default the tool returns a task UUID (track it with list_tasks). Set poll: true to wait for the consumer to finish and get the result in one call — the new document_id on success, or the consumer error on failure. poll_timeout_seconds (default 30, max 300) caps the wait; raise it for large scans where OCR is slow.
create_tag, create_correspondent, create_document_type, and create_storage_path accept a matching_algorithm (0–6). Workflow triggers accept 0–5 (no Automatic):
| Value | Meaning |
|---|---|
| 0 | None |
| 1 | Any word |
| 2 | All words |
| 3 | Exact match |
| 4 | Regular expression |
| 5 | Fuzzy word |
| 6 | Automatic |
Since Paperless-ngx 3.2, Automatic matching only assigns when the classifier is confident enough (PAPERLESS_CLASSIFIER_MATCH_THRESHOLD, default 0.6), and regular-expression matching gives up after PAPERLESS_MATCH_REGEX_TIMEOUT_SECONDS (default 0.1 s) — raise it if regex rules miss on long documents.
The default mode. The server communicates over stdio — that's what every MCP client config in the Quick Start uses. You usually never run this manually; the MCP client launches it for you.
If you do want to run it directly (e.g. for debugging):
Use the --http flag to expose the server over HTTP. --port defaults to 3000.
POST /mcp on the chosen port, backed by StreamableHTTPServerTransport in stateful mode.initialize call) creates a session and returns an Mcp-Session-Id header; subsequent requests must send that header back to reuse the same session. Transports are kept in an in-memory Map, so this only works for single-instance deployments.GET /mcp streams server-initiated messages for a session; DELETE /mcp terminates it and evicts it from the map. Both require a valid Mcp-Session-Id header.GET /sse + POST /messages SSE transport is also exposed for clients that don't yet support the streamable transport.Every session holds its own MCP server instance (~3.5 MB), and the HTTP port has no authentication — so sessions are bounded:
| Flag | Environment variable | Default | Purpose |
|---|---|---|---|
--maxSessions | PAPERLESS_MAX_SESSIONS | 50 | Concurrent sessions allowed. Past this, initialize is refused with HTTP 503. |
--sessionIdleMinutes | PAPERLESS_SESSION_IDLE_MINUTES | 30 | Evict a session after this long with no activity. |
A client that is actively connected — including one holding a GET /mcp stream open — is never evicted, no matter how long it stays idle. Only genuinely abandoned sessions are reclaimed.
Because sessions live in memory, --http only works for single-instance deployments. Do not expose the port to an untrusted network: there is no auth, and it binds all interfaces.
Tool calls return clear errors when:
PAPERLESS_URL or PAPERLESS_API_KEY is missing or wrong{"non_field_errors":["password not specified"]} (HTTP 400)), while HTML error pages are reduced to the status lineYou only need this section if you're modifying the server itself. End users should follow the Quick Start instead — there's no need to clone or build.
npm run start accepts the same flags / env vars as the built binary.
E2E tests spin up a real Paperless-NGX container via Docker Compose:
Runs against ghcr.io/paperless-ngx/paperless-ngx:3.2.1.
Built with:
This MCP server wraps endpoints from the Paperless-NGX REST API. See the official API documentation for details on the underlying behaviour and field semantics.
ISC. See LICENSE.