paperlessngx-mcp
Allows interaction with a Paperless-ngx server, providing tools for searching, tagging, and organizing documents with privacy tiers, including metadata-only operations, local extraction of fields, and full document content retrieval.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@paperlessngx-mcp find recent utility bills"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
paperless-mcp
A privacy-first Model Context Protocol (MCP) server for Paperless-ngx. It lets an LLM agent search, organize, tag, and reference your documents without the full text of those documents entering the model's context window — unless you explicitly ask for it.
Built with the official @modelcontextprotocol/sdk as a stdio server, TypeScript/ESM, Node 20+.
Why this exists: the three privacy tiers
The whole point of this server is to enforce a boundary between document metadata and document content. Most document management — "find my 2024 tax return", "tag this as an invoice", "what did the electric company send me" — needs metadata, not the OCR'd text of the document. Yet the naive Paperless API call (GET /api/documents/{id}/) returns the entire OCR content field by default, which would silently dump full document text into the model's context.
Every tool here falls into exactly one tier, and the tier dictates what data reaches the calling model:
Tier 1 — Metadata only (default)
Returns titles, dates, tags, correspondents, document types, custom fields, page counts, file sizes. Never returns OCR content. This covers the large majority of practical use cases. Search runs server-side inside Paperless and comes back as metadata.
Two mechanisms enforce this:
List/search calls pass Paperless's sparse fieldset (
fields=id,title,correspondent,...) so thecontentfield is never even serialized by the server.The single-document metadata tool calls the detail endpoint (which includes content) but explicitly deletes the
contentfield before returning.
Tier 2 — Local extraction (server-side processing)
The MCP server fetches content into its own process, runs a local parser (regex today; a local LLM in the future), and returns only the extracted value(s). The full text never leaves the server. Example: extract every dollar amount, or the "amount due" line, from an invoice — without the invoice body entering the model context.
This is the extensibility point: paperless_extract_field is designed so a future local-model extraction backend drops in behind the same contract (read content locally → parse → return only the field).
Tier 3 — Full content (explicit intent required)
Returns the full OCR text or the document binary to the calling model. These tools are clearly named and their descriptions carry a privacy_warning. Reserve them for when the user explicitly asks to read, summarize, or analyze a document's contents.
The tiers are deliberately not collapsed. A tool that searches returns metadata; a tool that fetches content is a separate, clearly-marked tool.
Related MCP server: KnowledgeMCP
Tools by tier
Tool | Tier | Returns |
| 1 | Matching docs as metadata (id, title, names, tags, dates, page_count) |
| 1 | One doc's full metadata (content stripped), incl. checksum, size, custom fields, notes |
| 1 | All tags: id, name, document_count |
| 1 | All correspondents: id, name, document_count |
| 1 | All document types: id, name, document_count |
| 1 | Totals, inbox count, type & tag breakdowns |
| 1 | Sets a document's tags (by name) |
| 1 | Sets a document's correspondent (by name) |
| 1 | Sets a document's type (by name) |
| 1 | Paperless's own server-side suggestions (no content to the model) |
| 2 | Only the field(s) extracted locally from content |
| 3 | Full OCR text ⚠ |
| 3 | Saves the binary PDF to disk, returns the path ⚠ |
All Tier 1 tools resolve IDs to human-readable names. There is exactly one tool that returns OCR text (paperless_get_document_content) and one that materializes the binary (paperless_download_document).
Configuration
All config is via environment variables.
Variable | Required | Default | Description |
| yes | — | e.g. |
| yes | — | Paperless API token ( |
| no |
| Set |
| no |
| Where Tier 3 downloads are written |
Get an API token in Paperless under Settings → My Profile → API Token.
Running locally (stdio)
npm install
npm run build
PAPERLESS_BASE_URL=http://localhost:8000 \
PAPERLESS_API_TOKEN=xxxxxxxx \
node dist/index.jsThe server speaks JSON-RPC over stdio; it is launched by your MCP client, not run as a standalone HTTP service.
Example MCP client config:
{
"mcpServers": {
"paperless": {
"command": "node",
"args": ["/path/to/paperless-mcp/dist/index.js"],
"env": {
"PAPERLESS_BASE_URL": "http://localhost:8000",
"PAPERLESS_API_TOKEN": "xxxxxxxx",
"PAPERLESS_VERIFY_SSL": "false"
}
}
}
}Deploying via Portainer + using with hermes
Because this is a stdio server (JSON-RPC over stdin/stdout, exits when stdin closes), it is not a long-lived HTTP service. Two patterns:
Pattern A — on-demand container (recommended). Build the image, then have hermes launch a fresh container per session:
docker run -i --rm --env-file .env paperless-mcp:latestThe -i is essential — it keeps stdin open for the JSON-RPC stream.
Pattern B — persistent container you exec into. Deploy the included docker-compose.yml as a Portainer Stack. It builds the image and keeps a container alive (via a sleep loop) so hermes can attach the MCP entrypoint on demand:
docker exec -i paperless-mcp node /app/dist/index.jsPortainer steps
In Portainer: Stacks → Add stack.
Paste the contents of
docker-compose.yml(or point it at this repo).Add the environment variables (
PAPERLESS_BASE_URL,PAPERLESS_API_TOKEN,PAPERLESS_VERIFY_SSL) in the stack's Environment variables section, or upload your.env.Deploy. If Paperless runs in the same Docker network, use its service name as the host (e.g.
http://paperless-ngx:8000).
Then point hermes' MCP configuration at whichever launch command matches your chosen pattern (docker run -i ... or docker exec -i ...).
Privacy guarantees & limits
Tier 1 list/search calls send
fields=so Paperless never serializescontent.paperless_get_document_metadatadeletescontentfrom the detail response in-process before returning.paperless_extract_fieldreads content only inside the server process and returns just the extracted value, with aprivacy_noteconfirming content was not returned.Only
paperless_get_document_contentandpaperless_download_documentsurface full content; both carry an explicitprivacy_warning.
This server controls what it returns. It cannot stop a client/agent from separately calling the Tier 3 tools — that's exactly why those tools are named and described to make the privacy cost obvious to the model and the user.
Extending Tier 2 extraction
src/tools.ts contains an EXTRACTORS registry of named, dependency-free regex extractors (dollar_amounts, dates, emails, phone_numbers, addresses, total_amount). extraction_pattern also accepts a raw regex.
To plug in a local LLM (the design goal): keep the same contract — fetch content into the process, run your local model, return only the requested field(s). Replace the body of paperless_extract_field's handler (or add a new named extractor that calls your local inference endpoint). The privacy boundary is preserved as long as only the extracted value is returned.
Project layout
paperless-mcp/
├── src/
│ ├── index.ts # entrypoint: stdio transport, tool registration, dispatch
│ ├── paperless-client.ts # REST client: auth, pagination, TTL cache, ID↔name resolution
│ └── tools.ts # tool defs + handlers, grouped by privacy tier
├── package.json
├── tsconfig.json
├── Dockerfile
├── docker-compose.yml # Portainer stack
├── .env.example
└── README.mdLicense
MIT
Available Tools
13 toolspaperless_download_documentA
PRIVACY TIER 3: Download the original document binary (PDF) to the server's filesystem and return the saved file path. Use only when the user explicitly needs the actual file rather than metadata or text. privacy_warning: This materializes the full document file on the MCP server host.
Inputs: document_id (required), original.
paperless_extract_fieldA
PRIVACY TIER 2 (local extraction): Extract ONLY a specific field from a document. The OCR text is fetched into the MCP server process, parsed LOCALLY, and only the extracted value(s) are returned — the full content never enters the model context. extraction_pattern may be a named built-in extractor (dollar_amounts, dates, emails, phone_numbers, addresses, total_amount) or a custom JavaScript-style regular expression. This is the extensibility point where a future local LLM extraction backend would plug in.
Inputs: document_id (required), extraction_pattern (required), regex_flags.
paperless_get_document_contentA
PRIVACY TIER 3: Returns FULL document content. The calling model will see the ENTIRE OCR text of this document. Only use this when the user has EXPLICITLY asked to read, summarize, or analyze a document's contents. For finding, organizing, or referencing documents, use the Tier 1 metadata tools instead. privacy_warning: This returns full document content into the model's context.
Inputs: document_id (required).
paperless_get_document_metadataARead-only
PRIVACY TIER 1 (metadata only): Return full metadata for one document — title, correspondent, document type, tags, dates, page count, checksum, file size, mime type, storage path, custom fields, and notes. The underlying Paperless detail endpoint includes OCR content, but this tool STRIPS the content field before returning. Use paperless_get_document_content if you actually need the text.
Inputs: document_id (required).
paperless_get_statisticsARead-only
PRIVACY TIER 1 (metadata only): Library-wide statistics — total documents, total file size, inbox count, and breakdowns by document type and tag. No document content involved.
Inputs: no named parameters.
paperless_list_correspondentsARead-only
PRIVACY TIER 1 (metadata only): List all correspondents with id, name, and document_count. Optional case-insensitive name filter.
Inputs: search.
paperless_list_document_typesARead-only
PRIVACY TIER 1 (metadata only): List all document types with id, name, and document_count. Optional case-insensitive name filter.
Inputs: search.
paperless_list_tagsBRead-only
PRIVACY TIER 1 (metadata only): List all tags with id, name, and document_count. Optional case-insensitive name filter.
Inputs: search.
paperless_search_documentsARead-only
PRIVACY TIER 1 (metadata only): Search documents and return METADATA ONLY — never OCR content. Full-text search runs server-side inside Paperless; this tool returns only titles, dates, tags, correspondent and document-type names. Use this to find and reference documents without pulling their text into context. Returns id, title, correspondent_name, document_type_name, tags, created_date, added_date, page_count.
Inputs: search_query, correspondent_name, tag, document_type, +4 more.
paperless_set_correspondentBRead-only
PRIVACY TIER 1 (management): Set the correspondent of a document by name (resolved to an ID internally). No content is read or returned.
Inputs: document_id (required), name (required).
paperless_set_document_typeARead-only
PRIVACY TIER 1 (management): Set the document type of a document by name (resolved to an ID internally). No content is read or returned.
Inputs: document_id (required), name (required).
paperless_suggest_tagsARead-only
PRIVACY TIER 1 (metadata only): Return Paperless-ngx's OWN built-in suggestions for tags, correspondents, document types, dates, and storage paths for a document. These are computed server-side inside Paperless; no OCR content enters the model context. IDs are resolved to names.
Inputs: document_id (required).
paperless_tag_documentBRead-only
PRIVACY TIER 1 (management): Set the tags on a document. Accepts tag NAMES (resolved to IDs internally). This REPLACES the document's tag set with exactly the tags provided. Returns the applied tags. No document content is read or returned.
Inputs: document_id (required), tag_names (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Each tool has a clearly distinct purpose, from downloading full documents to extracting specific fields or setting metadata. The descriptions explicitly differentiate between returning full content, metadata only, or specific extracted fields, leaving no ambiguity.
All tools follow a consistent `paperless_verb_noun` pattern using snake_case. Verbs like `get`, `list`, `set`, `search`, `extract`, `download` clearly indicate the action, and nouns are precise and consistent.
With 13 tools, the set is well-scoped for a document management server. The number is sufficient to cover essential operations without being overwhelming, though it slightly borders on the higher end of the ideal range.
The tools cover most common operations: listing, searching, reading metadata/content/downloading, extracting fields, and updating metadata. Missing are create/delete document tools, but the core workflow for managing documents is well supported.
Related MCP Connectors
Self-hosted AI workspace: semantic RAG document search, long-term memory, and AI chat via MCP.
Personal knowledge base MCP server with semantic search, auto-categorization, metadata extraction
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Related MCP Servers
- AlicenseBqualityBmaintenanceAn MCP (Model Context Protocol) server for interacting with a Paperless-NGX API server. This server provides tools for managing documents, tags, correspondents, and document types in your Paperless-NGX instance.44722 npm151TypeScriptISC
- AlicenseNot gradedqualityDmaintenanceAn MCP server that enables AI assistants to perform semantic searches over local document collections using multi-context organization and automatic OCR. It supports various file formats including PDF, DOCX, and images, ensuring all data processing remains local and private.8MIT
- AlicenseAqualityBmaintenanceMCP server that gives AI coding agents on-demand access to private project docs via BM25 ranked search. One setup for Claude Code, Cursor, Codex, Gemini CLI, and more. Docs stay private, never in public repos.1516Apache 2.0
- AlicenseNot gradedqualityBmaintenanceAn MCP server that adds AI-powered document intelligence to Paperless-ngx, enabling semantic search, automatic classification, receipt data extraction, bank statement matching, and accounting export — all running locally via Ollama.32MIT