Version: v1.92.0-dev.2 (same code path on main).
Summary
Reading a binary MCP resource through the proxy returns TextResourceContents (base64 in text, mimeType e.g. application/gzip) instead of BlobResourceContents (base64 in blob). The bytes are intact but mislabeled: a client that reads blob for binary resources sees it empty, and one that treats text as UTF-8 mangles it.
Root cause
_normalize_resource_contents forwards a blob's base64 string as content:
elif isinstance(content, BlobResourceContents):
normalized.append(ReadResourceContents(content=content.blob, ...)) # content.blob is a str
The MCP SDK then re-serializes ReadResourceContents via create_content (mcp/server/lowlevel/server.py), where ReadResourceContents.content: str | bytes is the discriminator: str -> TextResourceContents, bytes -> BlobResourceContents. Handed a str, it emits text. (The sibling TextResourceContents branch correctly passes content.text, a str.)
Reproduce
- Upstream MCP server exposing a binary resource (e.g. an
application/gzip blob).
resources/read through the proxy.
- Result is
TextResourceContents with the base64 in text; blob is absent. Expected BlobResourceContents.
Expected
Binary resources round-trip as BlobResourceContents (base64 in blob), preserving the upstream text/blob distinction.
Suggested fix
Decode the blob to bytes so the SDK re-emits a blob (symmetric with the text branch; the SDK re-base64-encodes, so it round-trips exactly):
elif isinstance(content, BlobResourceContents):
normalized.append(ReadResourceContents(content=base64.b64decode(content.blob), ...))
Version: v1.92.0-dev.2 (same code path on
main).Summary
Reading a binary MCP resource through the proxy returns
TextResourceContents(base64 intext, mimeType e.g.application/gzip) instead ofBlobResourceContents(base64 inblob). The bytes are intact but mislabeled: a client that readsblobfor binary resources sees it empty, and one that treatstextas UTF-8 mangles it.Root cause
_normalize_resource_contentsforwards a blob's base64 string ascontent:The MCP SDK then re-serializes
ReadResourceContentsviacreate_content(mcp/server/lowlevel/server.py), whereReadResourceContents.content: str | bytesis the discriminator:str->TextResourceContents,bytes->BlobResourceContents. Handed astr, it emits text. (The siblingTextResourceContentsbranch correctly passescontent.text, a str.)Reproduce
application/gzipblob).resources/readthrough the proxy.TextResourceContentswith the base64 intext;blobis absent. ExpectedBlobResourceContents.Expected
Binary resources round-trip as
BlobResourceContents(base64 inblob), preserving the upstream text/blob distinction.Suggested fix
Decode the blob to bytes so the SDK re-emits a blob (symmetric with the text branch; the SDK re-base64-encodes, so it round-trips exactly):