Skip to content

fix: wrap all tool responses in MCP content format - #25

Open
samperk1 wants to merge 2 commits into
nloui:mainfrom
samperk1:fix/wrap-tool-responses-in-mcp-content-format
Open

fix: wrap all tool responses in MCP content format#25
samperk1 wants to merge 2 commits into
nloui:mainfrom
samperk1:fix/wrap-tool-responses-in-mcp-content-format

Conversation

@samperk1

@samperk1 samperk1 commented May 10, 2026

Copy link
Copy Markdown

All tool handlers were returning raw Paperless API objects. The MCP
protocol requires responses as { content: [{ type, text }] }.

Two failure modes with Claude Code:

  • get_document threw a Zod error because the Paperless 'content' field (OCR text string) collided with CallToolResult.content (array)
  • search_documents, list_tags, list_document_types silently returned nothing because the raw objects had no 'content' array

Fix: add a one-line wrap() helper to each tool file and use it on
every return statement.

Summary by CodeRabbit

  • Refactor
    • Standardized tool result formatting across API integrations to use a consistent JSON text payload structure for correspondents, document types, documents, and tags operations. Tool functionality remains unchanged; this improves internal consistency and maintainability.

Review Change Stack

  All tool handlers were returning raw Paperless API objects. The MCP
  protocol requires responses as { content: [{ type, text }] }.

  Two failure modes with Claude Code:
  - get_document threw a Zod error because the Paperless 'content' field
    (OCR text string) collided with CallToolResult.content (array)
  - search_documents, list_tags, list_document_types silently returned
    nothing because the raw objects had no 'content' array

  Fix: add a one-line wrap() helper to each tool file and use it on
  every return statement.
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

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

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ 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: 74b6f207-64df-47d7-8c8e-24c018cdf416

📥 Commits

Reviewing files that changed from the base of the PR and between 5f30b8f and 4f6ba80.

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

Walkthrough

Four tool registration modules introduce a standardized wrap() helper to format API results into MCP text payloads. Each handler across correspondents, document types, documents, and tags now returns wrap(await api.method(...)) instead of raw results.

Changes

Tool Output Standardization

Layer / File(s) Summary
Wrapper Pattern
src/tools/correspondents.ts, src/tools/documentTypes.ts, src/tools/documents.ts, src/tools/tags.ts
Local wrap() helpers introduced to convert tool results into { content: [{ type: "text", text: JSON.stringify(..., null, 2) }] } MCP payloads.
Correspondent Tools
src/tools/correspondents.ts
create_correspondent and bulk_edit_correspondents return wrapped API results; list_correspondents also updated in the initial wrapper layer.
Document Type Tools
src/tools/documentTypes.ts
list_document_types, create_document_type, and bulk_edit_document_types return wrapped API results.
Document Tools
src/tools/documents.ts
bulk_edit_documents, post_document, get_document, search_documents, and download_document return wrapped API results; download_document preserves filename extraction from content-disposition header.
Tag Tools
src/tools/tags.ts
list_tags, create_tag, update_tag, delete_tag, and bulk_edit_tags return wrapped API results.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A wrapper's gentle touch so fine,
Makes JSON dance in format's line,
Four modules bloom with text so bright,
Tool results now shine with standardized light!
API calls wrapped up neat and tight. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: wrap all tool responses in MCP content format' clearly and concisely describes the main change across all modified files, directly matching the PR's core objective.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

🧹 Nitpick comments (2)
src/tools/documents.ts (2)

4-5: ⚡ Quick win

Consider extracting the wrap helper to reduce duplication.

The wrap helper is identical across all four tool files (tags.ts, documentTypes.ts, correspondents.ts, documents.ts). Consider extracting it to a shared utility module (e.g., src/tools/utils.ts or src/tools/wrap.ts) to follow the DRY principle and ensure consistency if the format needs to change.

♻️ Suggested refactor

Create src/tools/utils.ts:

export const wrap = (d: unknown) => ({ 
  content: [{ type: "text" as const, text: JSON.stringify(d, null, 2) }] 
});

Then import in each tool file:

import { wrap } from "./utils";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/documents.ts` around lines 4 - 5, The helper function wrap used in
documents.ts (and duplicated in tags.ts, documentTypes.ts, correspondents.ts)
should be extracted to a shared module to remove duplication and keep formatting
consistent; create a new module (e.g., src/tools/utils.ts or src/tools/wrap.ts)
that exports wrap and replace the local definitions in each file with an import
of that exported wrap function, ensuring the exported signature matches the
existing usage ({ content: [{ type: "text" as const, text: JSON.stringify(d,
null, 2) }] }) so callers like documents.ts still work without changes.

124-130: 🏗️ Heavy lift

Consider memory and payload size implications for large document downloads.

The current implementation base64-encodes the entire document into a JSON string, which can lead to:

  • Memory pressure: Large files (multi-MB PDFs) will create correspondingly large in-memory strings (base64 adds ~33% overhead, plus JSON stringification overhead).
  • MCP message size limits: The MCP protocol or client may have message size limits that could be exceeded by large documents.
  • Performance: Encoding/decoding large payloads can impact responsiveness.

If users frequently download large documents, consider:

  • Documenting recommended file size limits for this tool
  • Adding a file size check with a helpful error message for oversized downloads
  • Investigating whether MCP supports streaming or resource-based file transfer as an alternative to inline base64
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/documents.ts` around lines 124 - 130, The current code in the
return that reads the full response into Buffer.from(await
response.arrayBuffer()).toString("base64") (used with wrap and args.id) can OOM
and exceed message-size limits for large files; update this logic to check the
Content-Length (or the arrayBuffer byteLength) before encoding and fail fast
with a clear error if the file is larger than a configured MAX_INLINE_SIZE
(document this limit), and alter wrap usage to return an error message for
oversized files; additionally add a TODO or switch to a streaming/resource-based
transfer path (or reference MCP streaming) for large downloads instead of inline
base64 so callers can opt into chunked/streamed transfer.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/tools/documents.ts`:
- Around line 4-5: The helper function wrap used in documents.ts (and duplicated
in tags.ts, documentTypes.ts, correspondents.ts) should be extracted to a shared
module to remove duplication and keep formatting consistent; create a new module
(e.g., src/tools/utils.ts or src/tools/wrap.ts) that exports wrap and replace
the local definitions in each file with an import of that exported wrap
function, ensuring the exported signature matches the existing usage ({ content:
[{ type: "text" as const, text: JSON.stringify(d, null, 2) }] }) so callers like
documents.ts still work without changes.
- Around line 124-130: The current code in the return that reads the full
response into Buffer.from(await response.arrayBuffer()).toString("base64") (used
with wrap and args.id) can OOM and exceed message-size limits for large files;
update this logic to check the Content-Length (or the arrayBuffer byteLength)
before encoding and fail fast with a clear error if the file is larger than a
configured MAX_INLINE_SIZE (document this limit), and alter wrap usage to return
an error message for oversized files; additionally add a TODO or switch to a
streaming/resource-based transfer path (or reference MCP streaming) for large
downloads instead of inline base64 so callers can opt into chunked/streamed
transfer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ea54a100-1ddb-4079-a032-c88d75cc4195

📥 Commits

Reviewing files that changed from the base of the PR and between 4ba7457 and 5f30b8f.

📒 Files selected for processing (4)
  • src/tools/correspondents.ts
  • src/tools/documentTypes.ts
  • src/tools/documents.ts
  • src/tools/tags.ts

@samperk1

Copy link
Copy Markdown
Author

Fixed in latest commit — extracted wrap to src/tools/utils.ts and updated all
four tool files to import from there.

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