3.0¶
Compared with v2.1.0, this release keeps large OCR and binary files out of ordinary MCP responses, and document lists and searches no longer download OCR text from Paperless. It prefers Paperless payload v10 with a v9 fallback, and task resources now emit valid timestamp JSON. Three tool results are corrected: update_document matches get_document, list_correspondents reports last_correspondence, and renaming a select custom field works. HTTP and SSE deployments can move full files and OCR Markdown through expiring transfer links. The shared runtime moves to fastmcp-server-template v9 and fastmcp-pvl-core v9, which requires logging-variable changes and a review of explicit authentication settings.
Transfer links for files and Markdown¶
HTTP and SSE deployments can move document files and full OCR text through
expiring capability links instead of putting them into an MCP response. Set
PAPERLESS_MCP_BASE_URL to the public server root and route /transfer/ at
the reverse proxy. The server then registers the generic
create_download_link(ref, ttl_s) and create_upload_link(ref, ttl_s) tools.
Download references select an original, archive, preview, or OCR Markdown
variant. Upload references carry a filename and optional Paperless metadata;
the response contains a Paperless task ID for get_task.
The links came from the need to handle scanned PDFs and long OCR text without
placing whole files in model context (#111, #112). A persistent PAPERLESS_MCP_KV_STORE_URL keeps upload receipts across restarts. The transfer design documents replay handling, access checks at redemption, the upload limit, and the requirement to route /transfer/ (document transfer design).
The final tool names and schemas come from pvl-core's registrar. The earlier domain-specific names were replaced before this release by the core Path 1 registrar (#155, #159); those intermediate names were never in a stable release.
Bounded document content¶
Inline OCR content has one 20,000-character limit across the MCP document
surface. get_document_content and
paperless://documents/{id}/content accept an offset so callers can page
through longer text. The response identifies the returned range, total
length, and next offset. Structured document tools no longer include OCR
content, and the JSON document resource returns content: null.
Original and preview binary resources are no longer sent inline. Use the
transfer links above for full files when the server runs over HTTP or SSE.
Stdio deployments, and HTTP deployments without PAPERLESS_MCP_BASE_URL,
keep the bounded inline path and offset paging (#149, #165).
Document lists and searches also leave OCR text out of the request to
Paperless. Paperless can only project a response to a named list of fields, so
the client names every document field except content. A page of 25 documents
fell from 2,215,697 to 101,385 bytes, and a search page of 12 hits from
10,486,197 to 32,919 bytes, with every other field unchanged (measured on a live
Paperless 3.1.3 instance at payload v10, #164, #178).
Notes and custom-field values sit inside fields that a projection cannot reach,
so they are still fetched and dropped after parsing.
Paperless payload v10 and task resources¶
The client now requests Paperless payload v10 first. It retries with v9 only
when Paperless returns the explicit invalid-version 406 response, then keeps
that choice for the session. V10 task responses use an envelope and structured
fields; the client translates v9's task filters and legacy field names when a
2.x instance requires the fallback. Saved-view visibility fields can now be
null when v10 omits them (#139, #161).
tasks://paperless now serializes timestamps through Pydantic's JSON mode, so
populated pages produce valid ISO 8601 strings instead of failing on a
datetime value. Its scope remains the first page of unacknowledged tasks;
list_tasks remains the paginated interface (#160, #162). The legacy trigger mapping is pinned to Paperless 3.1.3's serializer, including email_consume → auto_task (#163).
Tool results that now agree¶
Three tools returned results that disagreed with their neighbours or failed on a valid request. Each traces to how Paperless behaves, so the fixes live in the client.
update_documentmatchesget_document. Paperless returns a document's full-permission form on every PATCH, so the update result carried apermissionsobject whereget_documentcarriesuser_can_changeandis_shared_by_requester. The server now reads the document again after the update and returns that. It costs one extra request per update, and no tool or resource returnspermissionsany more (#173, #175).list_correspondentsandcorrespondents://paperlessreportlast_correspondence. Paperless computes that date on a list only when the request asks for it, so list rows showednull(the resource omitted the key) whereget_correspondentreturned a date, and ordering by it returned an HTTP 500. The date is now requested on every list. It is the newest document's date, ornullfor a correspondent without documents (#172, #176).update_custom_fieldcan rename a select field. Paperless refuses any update of a select field that lacksextra_data.select_options, so a patch with onlynamereturned a 400. When the patch leavesextra_dataout, the server reads the field and sends its current options, ids included, so every option and the document values that use it survive. That costs one extra read, and a change to the options made by someone else in between is overwritten (#171, #177).
Shared runtime and deployment baseline¶
This server now follows fastmcp-server-template v9.0.0 and fastmcp-pvl-core v9.0.0 (#168). pvl-core owns the process logger and HTTP runner. Logs are JSON when stderr is not a terminal; set PAPERLESS_MCP_LOG_FORMAT=rich when a terminal-style stream is wanted. PAPERLESS_MCP_LOG_LEVEL is the prefixed level variable. FASTMCP_LOG_LEVEL remains a one-major fallback, while FASTMCP_ENABLE_RICH_LOGGING is removed.
Malformed operator configuration now raises one ConfigurationError line and
stops the server. An explicitly selected auth mode with no provider also
refuses to start. The new PAPERLESS_MCP_SHUTDOWN_GRACE_S setting controls
the HTTP drain window and defaults to three seconds. pvl-core v9 also keeps
operator URL credentials out of app origins and error messages (v9.0.0 release notes).
The repository now carries a security policy. Bootstrap can enable private vulnerability reporting and Dependabot alerts; it can also request secret-scanning push protection where the repository plan supports it. The project and its published package metadata now use the MIT license (LICENSE).
Upgrading¶
- Replace
FASTMCP_LOG_LEVELwithPAPERLESS_MCP_LOG_LEVEL. The old name is accepted for one major release and logs a deprecation event. - Remove
FASTMCP_ENABLE_RICH_LOGGING. UsePAPERLESS_MCP_LOG_FORMAT=richorjson; when unset, the renderer chooses Rich for a terminal and JSON elsewhere. - If
PAPERLESS_MCP_AUTH_MODEis set explicitly, provide the complete provider configuration. v9 refuses to start when that mode resolves to no provider. - Python consumers must accept
NoneforSavedView.show_on_dashboardandSavedView.show_in_sidebar. - Automation that read
permissionsfrom anupdate_documentresult will no longer find it; the result now has the same keys asget_document. - HTTP/SSE operators who want full files or OCR Markdown must set
PAPERLESS_MCP_BASE_URLand route/transfer/. Other deployments retain bounded inline OCR and offset paging.