The resources/read Method
Implement reading resource contents with text and blob support.
Purpose of resources/read
The resources/read method retrieves the actual content of a resource.
After listing resources, clients use this method to fetch the data they need.
Request Format
The client specifies which resource to read by its URI:
{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/read",
"params": {
"uri": "file:///project/config.json"
}
}
Response Format
The server returns a contents array. Each item contains the resource
URI, optional MIME type, and the content as either text or blob.
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"contents": [
{
"uri": "file:///project/config.json",
"mimeType": "application/json",
"text": "{\n \"debug\": true,\n \"port\": 8080\n}"
}
]
}
}
Text vs Blob Content
MCP supports two content types: text for human-readable content and blob for binary data.
| Field | Type | Use For |
|---|---|---|
text |
UTF-8 string | Source code, JSON, markdown, plain text |
blob |
Base64-encoded string | Images, PDFs, binary files |
Each content item must have either text or blob,
never both. Use text when possible — it's more efficient
and the AI can read it directly.
Text Content Example
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"contents": [
{
"uri": "file:///project/main.py",
"mimeType": "text/x-python",
"text": "def main():\n print('Hello, World!')\n\nif __name__ == '__main__':\n main()"
}
]
}
}
Blob Content Example
For binary content, encode it as Base64:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"contents": [
{
"uri": "file:///project/logo.png",
"mimeType": "image/png",
"blob": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
}
]
}
}
Implementing resources/read in Python
"""
resources_read.py - Implementing resources/read
"""
import base64
from pathlib import Path
# Resource registry with content providers
RESOURCES = {
"file:///project/config.json": {
"name": "config.json",
"mimeType": "application/json"
},
"file:///project/README.md": {
"name": "README.md",
"mimeType": "text/markdown"
},
"file:///project/logo.png": {
"name": "logo.png",
"mimeType": "image/png"
}
}
def is_binary_mime_type(mime_type: str) -> bool:
"""Check if a MIME type represents binary content."""
binary_types = [
"image/", "audio/", "video/",
"application/octet-stream",
"application/pdf",
"application/zip"
]
return any(mime_type.startswith(t) for t in binary_types)
def read_file_content(uri: str) -> dict:
"""Read file content and return appropriate format."""
# Extract file path from URI
if uri.startswith("file://"):
file_path = Path(uri[7:]) # Remove "file://" prefix
else:
raise ValueError(f"Unsupported URI scheme: {uri}")
resource = RESOURCES.get(uri)
if not resource:
raise ValueError(f"Unknown resource: {uri}")
mime_type = resource.get("mimeType", "application/octet-stream")
# Read the file
if is_binary_mime_type(mime_type):
# Binary content - encode as base64
with open(file_path, "rb") as f:
content = base64.b64encode(f.read()).decode("utf-8")
return {
"uri": uri,
"mimeType": mime_type,
"blob": content
}
else:
# Text content - read as UTF-8
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
return {
"uri": uri,
"mimeType": mime_type,
"text": content
}
def handle_resources_read(params: dict) -> dict:
"""Handle resources/read requests."""
uri = params.get("uri", "")
if not uri:
raise ValueError("Missing required parameter: uri")
content = read_file_content(uri)
return {
"contents": [content]
}
Error Handling
When a resource cannot be read, return a JSON-RPC error with an appropriate code:
"""
error_handling.py - Proper error responses for resources/read
"""
def handle_resources_read(params: dict) -> dict:
"""Handle resources/read with proper error handling."""
uri = params.get("uri", "")
if not uri:
raise MCPError(-32602, "Missing required parameter: uri")
if uri not in RESOURCES:
raise MCPError(-32602, f"Resource not found: {uri}")
try:
content = read_file_content(uri)
return {"contents": [content]}
except FileNotFoundError:
raise MCPError(-32002, f"File not found: {uri}")
except PermissionError:
raise MCPError(-32003, f"Permission denied: {uri}")
except UnicodeDecodeError:
raise MCPError(-32004, f"Cannot decode file as text: {uri}")
class MCPError(Exception):
"""Custom exception for MCP errors."""
def __init__(self, code: int, message: str):
self.code = code
self.message = message
super().__init__(message)
// Error response when resource not found
{
"jsonrpc": "2.0",
"id": 2,
"error": {
"code": -32602,
"message": "Resource not found: file:///nonexistent.txt"
}
}
Complete Handler Integration
Here's how to integrate both methods into your server:
"""
complete_handler.py - Full resources implementation
"""
def route_request(method: str, params: dict) -> dict:
"""Route requests to appropriate handlers."""
handlers = {
"initialize": handle_initialize,
"resources/list": handle_resources_list,
"resources/read": handle_resources_read,
}
handler = handlers.get(method)
if not handler:
raise MCPError(-32601, f"Method not found: {method}")
return handler(params)
def handle_initialize(params: dict) -> dict:
"""Handle initialization with resource capability."""
return {
"protocolVersion": "2024-11-05",
"capabilities": {
"resources": {} # Advertise resource support
},
"serverInfo": {
"name": "resource-server",
"version": "1.0.0"
}
}
Testing resources/read
# Read a text resource
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/read",
"params": {
"uri": "file:///project/config.json"
}
}'
For very large files, consider implementing streaming or chunked responses.
The standard resources/read method is best suited for resources
that fit comfortably in memory.