Hosted connector drops `blob` from embedded resources: -32602 invalid_union for any binary document (same payload accepted by the engine client)
Summary
A tool result that carries a binary document as a standard MCP embedded resource ({"type":"resource","resource":{"uri":…,"mimeType":"application/pdf","blob":"<base64>"}}) is rejected by the hosted-connector MCP client with MCP error -32602: Invalid tools/call result (invalid_union on content[N]). The same response, byte for byte, is accepted when the same server is reached by the Claude Code engine client.
This is the same failure signature as #72621 (Slack connector, slack_read_file, PDF). Filing again because that one was closed as not planned while the failure is still reproducible, and because this report carries a same-wire control that isolates it to the connector.
Environment
- Claude Desktop 2.110.0 (macOS), connector added against a self-hosted remote MCP server (Streamable HTTP, OAuth).
- Server: a Python MCP server on
mcp2.x that returns, for a PDF document:
[EmbeddedResource(type="resource", resource=BlobResourceContents(uri="…", mimeType="application/pdf", blob=<base64>)), TextContent(type="text", text='{"filename":…,"mimetype":…,"size":…}')]
Reproduction
- In a Claude Desktop conversation backed by the connector, call the tool that returns a PDF.
- Observe:
MCP error -32602: Invalid tools/call result: [
{ "code": "invalid_union",
"errors": [
[ {"path":["content",0,"type"],"message":"Invalid input: expected \"text\""},
{"path":["content",0,"text"],"message":"Invalid input: expected string, received undefined"} ],
[ {"path":["content",0,"type"],"message":"Invalid input: expected \"image\""}, … ],
[ {"path":["content",0,"type"],"message":"Invalid input: expected \"audio\""}, … ],
[ {"path":["content",0,"type"],"message":"Invalid input: expected \"resource_link\""}, … ],
[ {"path":["content",0,"resource","text"],"message":"Invalid input: expected string"},
{"path":["content",0,"resource","blob"],"message":"Invalid input: expected string"} ]
],
"path": ["content", 0], "message": "Invalid input" } ]
Note the last branch: content[0].resource parsed as an object, and neither text nor blob survived to validation — i.e. the blob field does not reach the validator through the connector, while uri / mimeType do.
Control: same wire, engine client
Reaching the same server from a Claude Code session (engine client) returns the identical payload and succeeds: the client reports Binary content (application/pdf, …) saved to …/tool-results/…pdf, and the bytes are intact. Nothing differs between the two runs except which client carried the call.
The server records the calling client's self-reported initialize name. Same host, same tool, minutes apart:
- connector:
client=Anthropic/ClaudeAI/1.0.0 proto=2026-07-28→-32602 invalid_union(client-side; the server answered 200) - engine:
client=claude-code/2.1.273 proto=2026-07-28→ payload accepted, file written
Server-side validation
The payload matches CallToolResultSchema in the MCP TypeScript SDK (the same schema the desktop client bundles): EmbeddedResource → resource union of TextResourceContents{text} / BlobResourceContents{blob} over a base {uri, mimeType?, _meta?}. The blob is base64 with no line breaks. So it is not a server-side shape error.
Related
- #72621 — same
invalid_unionfor binary files through a hosted connector; closed as not planned, no maintainer reply. - anthropics/claude-ai-mcp#287 — Claude Desktop does not render
EmbeddedResourceblocks at all (open). - #53453 —
resource_linkresults silently ignored (open).
Ask
Fix or document the connector's handling of binary content blocks. The engine client handles the exact same payload, so the gap is connector-specific, and today the only workarounds available to a server author are to stop using blob (inline base64 text, which overflows for real documents) or to hand the caller an out-of-band download URL.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗