Paperless MCP
Allows interaction with a Paperless-NGX API server, providing tools for managing documents, tags, correspondents, document types, and custom fields in a Paperless-NGX instance.
Click on "Install 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., "@Paperless MCPfind all invoices from ACME from last quarter"
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
Paperless-NGX document management over MCP: search, tag, upload, and read documents; manage tags, correspondents, document types, and custom fields.
Documentation | PyPI | Docker
Features
Document search & retrieval — full-text and filtered list queries against Paperless-NGX, plus access to extracted OCR text, metadata, thumbnails, and original-file downloads via short-lived signed URLs.
Tag, correspondent, document-type, custom-field management — full CRUD and bulk-edit for every classification dimension Paperless exposes.
Document lifecycle — upload new documents, update fields, attach notes, and inspect audit history and AI-suggested tags/correspondents/types.
Operational introspection — saved views, storage paths, share links, background tasks (with
wait_for_task), statistics, and remote Paperless-NGX version.MCP tools — 50 LLM-visible tools with Lucide icons and read-only gating; see
src/paperless_mcp/tools/.MCP resources — 20 URIs exposing documents and domain collections; see
src/paperless_mcp/resources/.Read-only mode — flip
PAPERLESS_MCP_READ_ONLY=trueto disable every mutating tool at startup.
Related MCP server: Paperless-NGX MCP Server
What you can do with it
With this server mounted in an MCP client (Claude, etc.), you can:
"Find last quarter's invoices from ACME." Composes
search_documentswith a correspondent filter, then streams matches viapaperless://documents/{id}/content."Tag these three documents as 'reviewed' and move them to the Accounting correspondent." Uses
bulk_edit_documentsin a single call."Upload this PDF and wait until OCR finishes." Composes
upload_document+wait_for_taskso the assistant only reports back once the document is indexed."What changed on document 4213 in the last week?" Reads
paperless://documents/4213/historyand summarises the audit trail."Give me a time-limited link to the original file for document 982." Calls
create_download_link— the URL is valid forPAPERLESS_MCP_DOWNLOAD_LINK_TTL_SECONDSand does not expose the API token.
Installation
From PyPI
pip install pvliesdonk-paperless-mcpIf you add optional extras via the PROJECT-EXTRAS-START / PROJECT-EXTRAS-END sentinels in pyproject.toml, document them below:
pip install pvliesdonk-paperless-mcp[docs]— pulls inmkdocs-materialandmkdocstrings[python]for building the documentation site locally (uv run mkdocs serve).
From source
git clone https://github.com/pvliesdonk/paperless-mcp.git
cd paperless-mcp
uv sync --all-extras --devDocker
docker pull ghcr.io/pvliesdonk/paperless-mcp:latestA compose.yml ships at the repo root as a starting point — copy .env.example to .env, edit, and docker compose up -d.
Linux packages (.deb / .rpm)
Download .deb or .rpm packages from the GitHub Releases page. Both install a hardened systemd unit; env configuration is sourced from /etc/paperless-mcp/env (copy from the shipped /etc/paperless-mcp/env.example).
Claude Desktop (.mcpb bundle)
Download the .mcpb bundle from the GitHub Releases page and double-click to install, or run:
mcpb install paperless-mcp-<version>.mcpbClaude Desktop prompts for required env vars via a GUI wizard — no manual JSON editing needed.
Quick start
paperless-mcp serve # stdio transport
paperless-mcp serve --transport http --port 8000 # streamable HTTPFor library usage (embedding the domain logic without the MCP transport), import from the paperless_mcp package directly — see src/paperless_mcp/domain.py for the entry point scaffold.
Configuration
All settings come from environment variables with the PAPERLESS_MCP_ prefix.
Required
Variable | Description |
| Base URL of the Paperless-NGX REST API (no trailing slash). |
| Paperless service-account token. |
Optional (with defaults)
Variable | Default | Description |
| (same as | Public-facing Paperless UI URL used to construct user-visible links (e.g. |
|
| Per-request HTTP timeout (seconds). |
|
| Retries (not counting the initial attempt) on 5xx/network errors. |
|
| TTL of URLs issued by |
|
| Default |
|
| When |
| (built-in) | Operator-supplied description appended to MCP instructions. |
See the Transport & Auth section below for the inherited transport, auth, and logging variables.
Transport & Auth
The following variables are inherited unchanged from fastmcp-server-template:
Variable | Description |
| Server transport: |
| Bind host for HTTP/SSE transport (default |
| Bind port for HTTP/SSE transport (default |
| URL path prefix for HTTP transport (default |
| Public base URL for artifact download links. |
| OIDC provider settings when OIDC auth is enabled. |
| Static bearer token for simple token auth. |
| Log level: |
| Log format: |
Tools
Documents
Tool | Description |
| List documents with optional filtering; OCR |
| Full-text search across documents; OCR |
| Retrieve a document by ID; OCR |
| Get the extracted text content of a document |
| Get the thumbnail image of a document |
| Get metadata (original filename, checksums, etc.) |
| List notes attached to a document |
| Get the audit history of a document |
| Get AI-generated tag/correspondent/type suggestions |
| Update document fields (title, tags, correspondent, etc.); response OCR |
| Delete a document |
| Upload a new document for processing |
| Apply a bulk operation to multiple documents |
| Add a note to a document |
| Delete a note from a document |
get_document, list_documents, search_documents, and update_document include a web_url field pointing to the document in the Paperless UI (e.g. https://paperless.example.com/documents/42/). Set PAPERLESS_MCP_PAPERLESS_PUBLIC_URL if the public URL differs from the API URL; otherwise the API URL is used.
Paginated tools return next/previous as bare page=N markers (never full URLs) — callers pass page=N explicitly when walking pages. None means no further page.
Tags
Tool | Description |
| List all tags |
| Get a tag by ID |
| Create a new tag |
| Update a tag |
| Delete a tag |
| Bulk edit tags |
Correspondents
Tool | Description |
| List all correspondents |
| Get a correspondent by ID |
| Create a new correspondent |
| Update a correspondent |
| Delete a correspondent |
| Bulk edit correspondents |
Document Types
Tool | Description |
| List all document types |
| Get a document type by ID |
| Create a new document type |
| Update a document type |
| Delete a document type |
| Bulk edit document types |
Custom Fields
Tool | Description |
| List all custom fields |
| Get a custom field by ID |
| Create a new custom field |
| Update a custom field |
| Delete a custom field |
Observability
Tool | Description |
| List storage paths |
| Get a storage path by ID |
| List saved views |
| Get a saved view by ID |
| List share links (includes |
| Get a share link by ID (includes |
| List background tasks. Paginates ( |
| Get a task by ID |
| Wait until a task completes |
| Get server statistics |
| Get the Paperless-NGX version |
Downloads
Tool | Description |
| Create a time-limited download URL for a document |
Resources
URI | Description |
| Server configuration snapshot |
| Document statistics |
| Paperless-NGX version |
| All tags |
| All correspondents |
| All document types |
| All custom fields |
| All storage paths |
| All saved views |
| All background tasks |
| Document by ID |
| Extracted text content |
| File metadata |
| Document notes |
| Audit history |
| Thumbnail image |
| PDF preview |
| Original file download |
Shared framework variables
Inherited from fastmcp-pvl-core across all services built on the template:
Variable | Default | Description |
|
| Log level for FastMCP internals and app loggers ( |
|
| Set to |
|
| Event store backend for HTTP session persistence — |
GitHub secrets
CI workflows reference three repository secrets. Configure them via Settings → Secrets and variables → Actions or with gh secret set:
Secret | Used by | How to generate |
|
| Fine-grained PAT at https://github.com/settings/personal-access-tokens/new with |
|
| https://codecov.io — sign in with GitHub, add the repo, copy the upload token from the repo settings page. |
|
| Run |
gh secret set RELEASE_TOKEN
gh secret set CODECOV_TOKEN
gh secret set CLAUDE_CODE_OAUTH_TOKENGITHUB_TOKEN is auto-provided — no action needed.
Local development
The PR gate (matches CI):
uv run pytest -x -q # tests
uv run ruff check --fix . && uv run ruff format . # lint + format
uv run mypy src/ tests/ # type-checkPre-commit runs a subset of the gate on each commit; see .pre-commit-config.yaml for details, or CLAUDE.md for the full Hard PR Acceptance Gates.
Troubleshooting
Moving a scaffolded project
uv sync creates .venv/bin/* scripts with absolute shebangs pointing at the venv Python. If you move the repo after scaffolding (mv /old/path /new/path), uv run pytest fails with ModuleNotFoundError: No module named 'fastmcp' because the stale shebang resolves to a different interpreter than the venv's site-packages.
Fix:
rm -rf .venv
uv sync --all-extras --devuv run python -m pytest also works as a one-shot workaround (bypasses the stale entry-script shim).
uv.lock refresh after copier update
When copier update introduces new dependencies (e.g. a new extra added to pyproject.toml.jinja), CI runs uv sync --frozen which fails against a stale lockfile. Run uv lock locally and commit the refreshed uv.lock alongside accepting the copier-update PR.
Links
Domain configuration
The full domain env-var surface is documented under Configuration above (Required / Optional tables). Those PAPERLESS_MCP_* fields are composed inside src/paperless_mcp/config.py between the CONFIG-FIELDS-START / CONFIG-FIELDS-END sentinels; env reads go through fastmcp_pvl_core.env(_ENV_PREFIX, "SUFFIX", default) so naming stays consistent.
Key design decisions
Read-only gating at startup, not per-call.
PAPERLESS_MCP_READ_ONLY=trueskips registration of every mutating tool so they simply aren't part of the advertised tool surface — clients can't invoke a write that will be refused.Download links are signed and time-limited.
create_download_linkmints a short-lived URL (default 300 s, clamped to[30, 3600]) that proxies through the MCP server, so the Paperless API token never leaves the host.HTTP layer retries idempotent reads only.
PAPERLESS_MCP_HTTP_RETRIESapplies to GETs on 5xx/network errors; writes never retry automatically, to avoid double-applying bulk edits or uploads.Tool icons come from Lucide. Every tool carries a Lucide icon hint so MCP clients that render icons (Claude Desktop) get a coherent visual surface — see
src/paperless_mcp/tools/_icons.py.Models accept unknown upstream fields. Pydantic models use lenient validation for list-endpoint responses so newer Paperless-NGX versions don't break the client (the
Document.some_future_paperless_fieldtest pins this behaviour).No prompts ship in v1.
prompts.pyis intentionally empty; prompts land as concrete user-workflow patterns emerge in practice.
Maintenance
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/pvliesdonk/paperless-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server