How do text vs. binary resource contents differ in MCP's data model?
Quick Answer
A resources/read result's contents array holds one entry per returned item, and each entry is either a TextResourceContents (a text field with UTF-8 string content, appropriate for source code, markdown, JSON, CSV, logs) or a BlobResourceContents (a blob field holding base64-encoded binary data, appropriate for images, PDFs, audio, or any non-text format). Both variants share uri and mimeType; the distinction is purely which of text/blob is populated. A server decides which shape to use based on the resource's actual content type — text-based formats should always use text rather than base64-wrapping them unnecessarily, since that keeps the payload smaller and directly readable without a decode step.
Detailed Answer
JSON has no native binary type, so MCP needs an explicit convention for anything that isn't plain text.
The two shapes
// Text resource
{"uri": "file:///notes.md", "mimeType": "text/markdown", "text": "# Notes\n..."}
// Binary resource
{"uri": "file:///diagram.png", "mimeType": "image/png", "blob": "iVBORw0KGgoAAAANSUhEUg..."}
text is a plain JSON string — readable, diffable, directly usable. blob is a base64-encoded string, since JSON strings can't hold arbitrary binary bytes; the consumer must base64-decode it back into raw bytes before use.
Why this distinction matters for cost and correctness
Base64 inflates payload size by roughly 33%, and for genuinely text-based content it adds an unnecessary encode/decode round trip on both ends for no benefit. A server should only reach for blob when the underlying data truly isn't text — always base64-wrapping everything "to be safe" needlessly bloats every resource read. Getting mimeType right matters too, since it's the signal a host or model uses to decide how to interpret the content:
import base64, mimetypes
def read_resource(path: str):
mime, _ = mimetypes.guess_type(path)
if mime and mime.startswith("text/"):
with open(path, "r", encoding="utf-8") as f:
return {"uri": f"file://{path}", "mimeType": mime, "text": f.read()}
else:
with open(path, "rb") as f:
data = f.read()
return {"uri": f"file://{path}", "mimeType": mime or "application/octet-stream", "blob": base64.b64encode(data).decode()}