Hosted connector drops `blob` from embedded resources: -32602 invalid_union for any binary document (same payload accepted by the engine client)

Status Fixed / completed
Reported on v2.1.273
Maintainer reply None cached
Activity 2 comments · opened Sep 16, 2026 · closed Oct 6, 2026

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 mcp 2.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

  1. In a Claude Desktop conversation backed by the connector, call the tool that returns a PDF.
  2. 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_union for binary files through a hosted connector; closed as not planned, no maintainer reply.
  • anthropics/claude-ai-mcp#287 — Claude Desktop does not render EmbeddedResource blocks at all (open).
  • #53453 — resource_link results 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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗