claude.ai Zoho Mail MCP: binary email attachments (PDFs, images) are corrupted due to UTF-8 decoding
Environment
- Claude Code (latest)
- MCP:
claude.airemote Zoho Mail integration (mcp__claude_ai_Zoho_Mail_Reading_Search__*) - Tool:
ZohoMail_getMessageAttachmentContent - Platform: macOS
Bug description
Calling ZohoMail_getMessageAttachmentContent on any binary attachment (PDF, image, Office doc) returns corrupted data. The binary content is being run through a UTF-8 decoder instead of being returned as base64. This makes it impossible to reconstruct the original file.
Reproduction
- Set up the
claude.aiZoho Mail MCP integration - Find any email with a PDF attachment
- Call
ZohoMail_getMessageAttachmentContentwith the correct account/folder/message/attachment IDs - Attempt to save the returned content to disk as a binary file
Expected behaviour
Binary attachment content should be returned as a base64-encoded string, allowing the caller to decode it to the original bytes.
Actual behaviour
Raw binary bytes are decoded as UTF-8 text:
- Valid multi-byte UTF-8 sequences collapse N bytes into 1 character (reducing apparent content length)
- Invalid UTF-8 byte sequences (extremely common in zlib/FlateDecode-compressed PDF streams) are silently replaced with U+FFFD (
?)
Observed on a 31,581-byte QuickBooks-generated PDF:
- Returned string length: 25,916 characters (not 31,581)
- Characters with code point > 255: 11,418 (36% of content destroyed)
- Code point 0xFFFD (replacement char) dominates the high-value characters
The resulting file is completely unreadable by any PDF viewer.
Suggested fix
In the MCP connector that calls the Zoho attachment content API endpoint, base64-encode the raw HTTP response body before serialising it into the JSON tool result. One line change. On the consumer side, the caller then base64-decodes to get the original bytes.
Impact
Every binary attachment retrieved through this MCP integration is silently corrupted. There is no fallback or error — the tool returns 200 OK with broken content. Users have no way to know the file is damaged until they try to open it.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗