Skip to content

fix: wrap tool handler results in MCP content blocks (#4) - #5

Merged
smichaelsen merged 1 commit into
mainfrom
fix/wrap-tool-results
May 28, 2026
Merged

fix: wrap tool handler results in MCP content blocks (#4)#5
smichaelsen merged 1 commit into
mainfrom
fix/wrap-tool-results

Conversation

@smichaelsen

Copy link
Copy Markdown
Owner

Summary

Fixes #4. Every tool handler in src/tools/*.ts returned the raw Paperless API response directly. MCP requires CallToolResult shape { content: [{ type, text }, ...] } — bare objects pass the SDK at runtime but compliant clients (Claude Code) silently render an empty result.

Adds a shared toTextResult(data) helper in src/tools/result.ts and threads it through every handler in documents, tags, correspondents, and documentTypes.

Notes

Test

Smoke-tested against the live Paperless-NGX instance via the reproducer from #4:

printf '%s\n%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_tags","arguments":{}}}' \
  | node build/index.js <url> <token>

Result: result.content[0].type === "text", content[0].text contains the JSON-stringified Paperless payload (83 tags). ✅

Test plan for reviewer

  • Pull, npm run build, restart MCP client
  • Run list_tags / list_correspondents / list_document_types / search_documents — results should now render in the client instead of appearing empty
  • update_document, create_*, bulk_edit_* — confirm they still take effect and return their (now-visible) response

🤖 Generated with Claude Code

All handlers in src/tools/*.ts returned raw Paperless API responses
directly. The MCP spec requires CallToolResult shape
`{ content: [{ type, text }, ...] }`; bare objects pass the SDK at
runtime but are silently dropped by compliant clients (Claude Code
renders an empty tool result).

Adds a shared `toTextResult(data)` helper that JSON-stringifies any
value into a single text content block, and threads it through every
handler in documents, tags, correspondents, and documentTypes.
download_document is wrapped the same way — base64 blob + filename
get stringified into the text block. A `resource`-typed content block
would be a more proper fit for binary but is not blocking.

Verified against a live Paperless-NGX instance: `tools/call list_tags`
now returns `result.content[0].type === "text"` with the full payload
in `text`.

Fixes #4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@smichaelsen
smichaelsen merged commit 88d6b98 into main May 28, 2026
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.

Tool handlers return raw API responses — empty results in MCP clients

1 participant