Skip to content

Comments

Fix post_document: use Buffer instead of browser File API#55

Merged
baruchiro merged 7 commits intobaruchiro:mainfrom
psoldunov:main
Feb 22, 2026
Merged

Fix post_document: use Buffer instead of browser File API#55
baruchiro merged 7 commits intobaruchiro:mainfrom
psoldunov:main

Conversation

@psoldunov
Copy link

@psoldunov psoldunov commented Feb 20, 2026

Summary

  • Fixed post_document action failing in Node.js environments by replacing the browser File API with Buffer for FormData file uploads
  • Ensured metadata fields are properly stringified before appending to FormData

Details

The post_document tool was using new File() which is a browser-only API not available in Node.js runtimes. This caused the action to fail when called through the MCP server. The fix uses Buffer with a filename option instead, which is compatible with Node.js FormData.

Testing

Tested manually using npm run inspect (MCP Inspector) — uploaded a document via post_document and it worked like a charm.

Summary by CodeRabbit

  • Bug Fixes

    • Improved document uploads: accept raw/base64 file payloads with explicit filenames and validate base64 inputs.
    • Metadata handling fixed: numeric serial numbers and tag identifiers are now normalized for consistent upload.
  • Chores

    • Cleaned metadata construction to exclude undefined values and align field types with the upload interface.

claude and others added 3 commits February 20, 2026 21:56
The form-data npm package doesn't support browser File/Blob objects.
postDocument was creating File from Buffer unnecessarily, causing the
upload data to be serialized incorrectly. Pass Buffer directly to
form-data with filename option, and stringify metadata values for
proper multipart form encoding.

https://claude.ai/code/session_01SyvNqE1R7m6LmSpkfMUiXz
…x7TEr

Claude/fix post document action x7 t er
Copilot AI review requested due to automatic review settings February 20, 2026 22:13
@changeset-bot
Copy link

changeset-bot bot commented Feb 20, 2026

🦋 Changeset detected

Latest commit: 9fcdb74

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@baruchiro/paperless-mcp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

The PaperlessAPI.postDocument signature changed from accepting a browser File to accepting a Buffer and a filename (postDocument(document: Buffer, filename: string, ...)). FormData now appends the document with an explicit filename. In the API implementation metadata values are stringified before appending; custom_fields are appended as strings (numbers coerced to strings). The post_document tool now validates base64 input, converts it to a Buffer, constructs metadata excluding undefined fields, passes filename separately, and its schema changes archive_serial_number to number. A changeset records these edits.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main change: replacing the browser File API with Buffer for the post_document functionality.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request fixes the document upload functionality in the Paperless-NGX MCP server by replacing browser-specific File API usage with Node.js Buffer objects, improving server-side compatibility. The changes also add explicit type conversions for metadata fields when constructing FormData requests.

Changes:

  • Modified PaperlessAPI.postDocument to accept Buffer and explicit filename instead of File object
  • Added String() conversions for all metadata fields appended to FormData for type consistency
  • Updated post_document tool to pass raw Buffer and filename directly, removing unnecessary Blob/File intermediary conversions

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/api/PaperlessAPI.ts Changed postDocument signature from File to Buffer+filename parameters, added explicit String() conversions for all FormData metadata fields
src/tools/documents.ts Simplified document upload by passing Buffer and filename directly to API, removed Blob and File object creation
package-lock.json Version bump from 0.3.0 to 0.4.0

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/api/PaperlessAPI.ts (1)

129-138: ⚠️ Potential issue | 🟠 Major

postDocument is missing the required Accept: application/json; version=5 header.

The direct axios.post call at line 129 only includes Authorization and the form-data boundary headers. The Accept: application/json; version=5 header is absent, which violates the project guideline and may cause the Paperless API to respond with an unexpected content type or API version.

🔧 Proposed fix
  const response = await axios.post<string>(
    `${this.baseUrl}/api/documents/post_document/`,
    formData,
    {
      headers: {
        Authorization: `Token ${this.token}`,
+       Accept: "application/json; version=5",
        ...formData.getHeaders(),
      },
    }
  );

As per coding guidelines: "Include Accept: application/json; version=5 header in all API requests in src/api/PaperlessAPI.ts".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/api/PaperlessAPI.ts` around lines 129 - 138, The axios.post call inside
postDocument is missing the required Accept header; update the request options
for the call in method postDocument to include "Accept: application/json;
version=5" alongside Authorization and the formData boundary headers (i.e.,
merge it into the headers object used with axios.post on
`${this.baseUrl}/api/documents/post_document/` so the request sends
Authorization: `Token ${this.token}`, Accept: application/json; version=5, and
...formData.getHeaders()).
🤖 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`:
- Around line 136-138: Validate and sanitize the incoming args.file and metadata
before calling api.postDocument: first, guard against invalid base64 by checking
args.file with a strict base64 validation (or attempt a safe decode+re-encode
roundtrip) and throw/return a clear error if it fails instead of calling
Buffer.from blindly (replace the Buffer.from(args.file, "base64") usage in the
post_document handler with this validation step); second, build a metadata
Record explicitly by copying only defined properties from the destructured
metadata (the const { file: _, filename, ...metadata } = args) into a new object
and cast/shape it to Record<string, string | string[] | number | number[]> so
undefined values are excluded before passing to api.postDocument(filename,
document, metadata).

---

Outside diff comments:
In `@src/api/PaperlessAPI.ts`:
- Around line 129-138: The axios.post call inside postDocument is missing the
required Accept header; update the request options for the call in method
postDocument to include "Accept: application/json; version=5" alongside
Authorization and the formData boundary headers (i.e., merge it into the headers
object used with axios.post on `${this.baseUrl}/api/documents/post_document/` so
the request sends Authorization: `Token ${this.token}`, Accept:
application/json; version=5, and ...formData.getHeaders()).

@psoldunov
Copy link
Author

@copilot open a new pull request to apply changes based on the comments in this thread

claude and others added 2 commits February 20, 2026 22:24
… input, and sanitize metadata

- Change archive_serial_number schema from z.string() to z.number() to match
  the Paperless-ngx API spec (integer, 0–4294967295)
- Add base64 regex validation before decoding the file buffer
- Build metadata record explicitly, excluding undefined values
- Add patch changeset

Tested via `npm run inspect` (MCP Inspector) — worked like a charm.

https://claude.ai/code/session_01SyvNqE1R7m6LmSpkfMUiXz
…x7TEr

Harden post_document: fix archive_serial_number type, validate base64…
Comment on lines 104 to 114
if (metadata.title) formData.append("title", String(metadata.title));
if (metadata.created) formData.append("created", String(metadata.created));
if (metadata.correspondent)
formData.append("correspondent", metadata.correspondent);
formData.append("correspondent", String(metadata.correspondent));
if (metadata.document_type)
formData.append("document_type", metadata.document_type);
formData.append("document_type", String(metadata.document_type));
if (metadata.storage_path)
formData.append("storage_path", metadata.storage_path);
formData.append("storage_path", String(metadata.storage_path));
if (metadata.tags) {
(metadata.tags as string[]).forEach((tag) =>
formData.append("tags", tag)
(metadata.tags as number[]).forEach((tag) =>
formData.append("tags", String(tag))
Copy link
Owner

Choose a reason for hiding this comment

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

Is formData required to be a string value?

psoldunov and others added 2 commits February 22, 2026 17:53
Fair enough!

Co-authored-by: Baruch Odem (Rothkoff) <baruchiro@gmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/api/PaperlessAPI.ts (1)

129-138: ⚠️ Potential issue | 🟠 Major

Add the required Accept: application/json; version=5 header.

Every API call in this file must include Accept: application/json; version=5, but postDocument bypasses the request() wrapper and its direct axios.post only sends Authorization + form-data headers, omitting Accept.

🛠️ Proposed fix
     {
       headers: {
         Authorization: `Token ${this.token}`,
+        Accept: "application/json; version=5",
         ...formData.getHeaders(),
       },
     }

As per coding guidelines: "Include Accept: application/json; version=5 header in all API requests in src/api/PaperlessAPI.ts".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/api/PaperlessAPI.ts` around lines 129 - 138, The postDocument call is
missing the required Accept header; update the headers passed to the axios.post
in the postDocument (the block constructing response = await
axios.post<string>(...)) to include "Accept": "application/json; version=5"
alongside Authorization and formData.getHeaders() so the request matches other
API calls that use the request() wrapper.
🤖 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/api/PaperlessAPI.ts`:
- Around line 117-122: The current guard in PaperlessAPI (the block that appends
"archive_serial_number" to formData) uses a truthy check on
metadata.archive_serial_number which drops 0; change the conditional to an
explicit nullish check (e.g., check metadata.archive_serial_number !== null &&
metadata.archive_serial_number !== undefined or use
metadata.archive_serial_number ?? undefined) so that numeric zero is preserved
before calling formData.append("archive_serial_number",
String(metadata.archive_serial_number)).

---

Outside diff comments:
In `@src/api/PaperlessAPI.ts`:
- Around line 129-138: The postDocument call is missing the required Accept
header; update the headers passed to the axios.post in the postDocument (the
block constructing response = await axios.post<string>(...)) to include
"Accept": "application/json; version=5" alongside Authorization and
formData.getHeaders() so the request matches other API calls that use the
request() wrapper.

@psoldunov psoldunov requested a review from baruchiro February 22, 2026 16:23
@baruchiro baruchiro enabled auto-merge (squash) February 22, 2026 16:28
@baruchiro baruchiro merged commit 9783e4c into baruchiro:main Feb 22, 2026
2 checks passed
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.

3 participants