fix: wrap all tool responses in MCP content format - #25
Conversation
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.
|
Warning Rate limit exceeded
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 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)
📝 WalkthroughWalkthroughFour tool registration modules introduce a standardized ChangesTool Output Standardization
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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.
🧹 Nitpick comments (2)
src/tools/documents.ts (2)
4-5: ⚡ Quick winConsider 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.tsorsrc/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 liftConsider 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
📒 Files selected for processing (4)
src/tools/correspondents.tssrc/tools/documentTypes.tssrc/tools/documents.tssrc/tools/tags.ts
|
Fixed in latest commit — extracted wrap to src/tools/utils.ts and updated all |
All tool handlers were returning raw Paperless API objects. The MCP
protocol requires responses as { content: [{ type, text }] }.
Two failure modes with Claude Code:
Fix: add a one-line wrap() helper to each tool file and use it on
every return statement.
Summary by CodeRabbit