feat: add update_document tool for editing existing document metadata - #22
feat: add update_document tool for editing existing document metadata#22hannahswain wants to merge 7 commits into
Conversation
Adds a PATCH-based updateDocument method to PaperlessAPI and a corresponding update_document MCP tool. Exposes title, created date, correspondent, document_type, storage_path, tags, and archive_serial_number — the fields bulk_edit_documents cannot reach. The created date field in particular is needed because Paperless defaults to the upload/scan date rather than the document's actual date. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 40 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughA new document update feature is introduced, comprising an Changes
Sequence DiagramsequenceDiagram
actor Client
participant Tool as update_document Tool
participant API as PaperlessAPI
participant Backend as Paperless Backend
Client->>Tool: Request update with id + metadata
Tool->>Tool: Validate inputs (Zod schema)
Tool->>Tool: Check api configured
Tool->>API: updateDocument(id, data)
API->>Backend: PATCH /documents/{id}/
Backend-->>API: Response
API-->>Tool: Resolved response
Tool-->>Client: Update result
Estimated Code Review Effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/tools/documents.ts`:
- Line 109: The schema for archive_serial_number is defined as
z.number().nullable().optional() but must be a string to match post_document and
accept non-numeric serials; update the schema entry for archive_serial_number to
use z.string().nullable().optional() (keeping the same .describe text) in the
documents schema and verify any validation or consumers of archive_serial_number
expect a string (e.g., post_document-related code paths).
- Around line 111-115: The update handler currently allows a PATCH with only an
id (no fields) because it destructures args into { id, ...data } and blindly
calls api.updateDocument(id, data); add a guard after destructuring to check
that data has at least one own property (e.g., Object.keys(data).length > 0) and
throw a clear error (e.g., "No fields to update") if it's empty, so
api.updateDocument is only called when there is at least one field to change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: cb565d7f-cada-4742-a631-637d5b9a5119
📒 Files selected for processing (2)
src/api/PaperlessAPI.tssrc/tools/documents.ts
- Add guard to reject PATCH calls with no fields to update - Add comment clarifying archive_serial_number type difference vs post_document (z.number() is correct for JSON PATCH; post_document uses z.string() because multipart form data stringifies everything) - Add update_document section to README with parameter docs and examples Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…llision Paperless-NGX document objects include a `content` field (OCR text, a string). MCP SDK v1.11+ treats any returned object with a `content` key as a pre-formed response and validates it as ContentBlock[] — causing an InvalidInput error when the value is a plain string. Strip the OCR content field and return JSON.stringify() instead so the SDK wraps it correctly as a text content block. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tool handlers must return { content: ContentBlock[] } — not plain objects
or strings. The MCP client validates responses against CallToolResultSchema
and closes the connection on mismatch.
- update_document: strip OCR "content" field, wrap result in content block
- list_correspondents: wrap API response in content block
The root cause: Paperless document JSON has a "content" key (OCR text string)
which the MCP client tries to interpret as ContentBlock[], causing immediate
connection close. Other tools returning raw objects without a "content" key
fail silently ("no output") rather than crashing the connection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply the same fix as list_correspondents/update_document to all remaining tools that were returning raw API objects: - search_documents - get_document (also strips OCR content field to avoid key collision) - list_tags - list_document_types Root cause: MCP SDK 1.11.x validates tool responses against CallToolResultSchema which requires content: ContentBlock[]. Raw API objects without a content key show "no output"; objects with content as a string (Paperless OCR field) cause connection close. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
create_correspondent and create_document_type accept matching_algorithm as a
human-readable string enum ("any", "all", "exact", "regular expression",
"fuzzy") but the Paperless-NGX API expects integers (1-5). The mismatch caused
all create calls with a matching_algorithm to return HTTP 400.
Also wrap responses in proper MCP ContentBlock format (same fix as other tools).
Note: create_tag already used z.number() and was unaffected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes type inconsistency flagged by CodeRabbit: post_document uses z.string() for archive_serial_number (multipart form), so update_document should too. Archive serial numbers can be non-numeric (e.g. '2024-001'), and the Paperless API accepts string values for this field. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
updateDocument(id, data)toPaperlessAPIusingPATCH /api/documents/{id}/update_documentMCP tool exposing:title,created,correspondent,document_type,storage_path,tags,archive_serial_numberMotivation
The existing tools cover uploading new documents (
post_document) and bulk metadata operations (bulk_edit_documents), but there's no way to update fields on an existing document — including thecreateddate.This matters because Paperless-NGX defaults the document date to the upload/scan date rather than the actual date printed on the document. Without this tool, users have to fix dates manually in the UI even when Claude can read the correct date directly from the OCR content.
What's not changed
bulk_edit_documentsis unchanged — it remains the right tool for setting correspondent/type/tags across many documents at once.update_documentis for single-document edits and fields bulk operations don't reach (title, date).Test
Tested against a live Paperless-NGX instance.
PATCH /api/documents/{id}/is a standard endpoint in the Paperless-NGX API.🤖 Generated with Claude Code
Summary by CodeRabbit