Skip to content

feat: add update_document tool for editing existing document metadata - #22

Open
hannahswain wants to merge 7 commits into
nloui:mainfrom
hannahswain:feat/update-document
Open

feat: add update_document tool for editing existing document metadata#22
hannahswain wants to merge 7 commits into
nloui:mainfrom
hannahswain:feat/update-document

Conversation

@hannahswain

@hannahswain hannahswain commented Apr 15, 2026

Copy link
Copy Markdown

Summary

  • Adds updateDocument(id, data) to PaperlessAPI using PATCH /api/documents/{id}/
  • Adds a new update_document MCP tool exposing: title, created, correspondent, document_type, storage_path, tags, archive_serial_number

Motivation

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 the created date.

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_documents is unchanged — it remains the right tool for setting correspondent/type/tags across many documents at once. update_document is 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

  • New Features
    • Document metadata can now be updated, including title, creation date, correspondent, document type, storage location, tags, and archive serial number. Input validation ensures data consistency and integrity throughout the update process.

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>
@coderabbitai

coderabbitai Bot commented Apr 15, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@hannahswain has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 40 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 22566963-2d00-4378-9b33-10db3c1d0c66

📥 Commits

Reviewing files that changed from the base of the PR and between 9157c5b and 0fb8aa0.

📒 Files selected for processing (5)
  • README.md
  • src/tools/correspondents.ts
  • src/tools/documentTypes.ts
  • src/tools/documents.ts
  • src/tools/tags.ts
📝 Walkthrough

Walkthrough

A new document update feature is introduced, comprising an updateDocument() method in the PaperlessAPI class that issues a PATCH request, and a corresponding update_document tool that validates input metadata fields and invokes the API method.

Changes

Cohort / File(s) Summary
API Layer
src/api/PaperlessAPI.ts
Added updateDocument(id, data) method that issues a PATCH request to /documents/{id}/ with JSON-stringified data, with error handling delegated to existing request() helper.
Tools Layer
src/tools/documents.ts
Added update_document tool registration with Zod validation for optional metadata fields (title, created, correspondent, document_type, storage_path, tags, archive_serial_number). Tool extracts document id and forwards remaining data to api.updateDocument().

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A new tool hops into the warren,
With patches aplenty, no time to tarry—
Documents dance with renewed delight,
Metadata shuffles left and right,
Update them swiftly, tidy and bright! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding an update_document tool for editing document metadata. It is concise, specific, and clearly summarizes the primary contribution.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4ba7457 and 9157c5b.

📒 Files selected for processing (2)
  • src/api/PaperlessAPI.ts
  • src/tools/documents.ts

Comment thread src/tools/documents.ts Outdated
Comment thread src/tools/documents.ts
hannahswain and others added 6 commits April 15, 2026 19:39
- 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant