v3-notes/resource-internal-types.md
Version: 3.0.0 Impact: Breaking change for resources returning dict/list
ResourceResult now enforces strict typing to catch errors at development time (via type checker) rather than at runtime (when a client reads a resource).
@mcp.resource("data://config")
def get_config() -> dict: # Auto-serialized to JSON
return {"key": "value"}
@mcp.resource("data://items")
def get_items() -> list: # Each item auto-wrapped
return ["item1", "item2"]
ResourceResult({"key": "value"}) # Dict auto-converted
ResourceResult(["a", "b"]) # List split into items
@mcp.resource("data://config")
def get_config() -> str: # Explicit JSON serialization
import json
return json.dumps({"key": "value"})
@mcp.resource("data://items")
def get_items() -> ResourceResult: # Explicit multi-item response
return ResourceResult([
ResourceContent("item1"),
ResourceContent("item2"),
])
ResourceResult([ResourceContent(...)]) # Explicit list wrapping
# Dict/list raises TypeError
str | bytes | ResourceResult
Valid:
return "text content"return b"binary data"return ResourceResult([ResourceContent(...)])Invalid (now raises TypeError):
return {"key": "value"} → Use json.dumps() insteadreturn ["item1", "item2"] → Use ResourceResult([ResourceContent(...)])return ResourceContent(...) → Use ResourceResult([ResourceContent(...)])ResourceResult(
contents: str | bytes | list[ResourceContent],
meta: dict[str, Any] | None = None
)
Valid:
ResourceResult("plain text")ResourceResult(b"binary")ResourceResult([ResourceContent(...), ResourceContent(...)])Invalid (now raises TypeError):
ResourceResult({"key": "value"}) → Dict not supportedResourceResult(["a", "b"]) → Bare list not supported (must be list[ResourceContent])ResourceResult(resource_content_obj) → Single item must be in listResourceContent(
content: Any, # Auto-serializes non-str/bytes to JSON
mime_type: str | None = None,
meta: dict[str, Any] | None = None
)
Auto-Serialization in ResourceContent.init:
str → passes through (mime_type defaults to "text/plain")bytes → passes through (mime_type defaults to "application/octet-stream")dict → JSON-serialized string (mime_type defaults to "application/json")list → JSON-serialized string (mime_type defaults to "application/json")BaseModel → JSON-serialized string (mime_type defaults to "application/json")The old auto-conversion behavior was convenient but hid errors:
# Old behavior - silent failure
return ["item1", "item2"] # Client sees 2 items OR JSON array?
# Ambiguous! Users would discover issues only when client reads resource
# New behavior - caught at dev time
return ["item1", "item2"] # Type checker error immediately
# Must explicitly write:
return json.dumps(["item1", "item2"]) # Clear intent
# OR:
return ResourceResult([ResourceContent("item1"), ResourceContent("item2")])
Type checkers now catch return type mismatches during development rather than at runtime.
Before:
def get_config() -> dict:
return {"key": "value", "nested": {"a": 1}}
After:
import json
def get_config() -> str:
return json.dumps({"key": "value", "nested": {"a": 1}})
Before:
def get_items() -> list:
return ["user1", "user2", "user3"]
After (Option 1: Single JSON array):
import json
def get_items() -> str:
return json.dumps(["user1", "user2", "user3"])
After (Option 2: Multiple content items):
from fastmcp.resources import ResourceContent, ResourceResult
def get_items() -> ResourceResult:
return ResourceResult([
ResourceContent("user1"),
ResourceContent("user2"),
ResourceContent("user3"),
])
Before:
def get_html() -> dict:
return {"html": "<div>content</div>"}
After:
from fastmcp.resources import ResourceContent, ResourceResult
def get_html() -> ResourceResult:
return ResourceResult([
ResourceContent(
content="<div>content</div>",
mime_type="text/html"
)
])
Your type checker will now catch these errors:
@mcp.resource("data://test")
def bad_resource() -> dict: # ← Type error: should be str | bytes | ResourceResult
return {"key": "value"}
This is intentional. The type system enforces correct typing at development time.
This is a breaking change. Code that returns dict or list from resources will:
TypeError when client reads the resourceMigrate to explicit JSON serialization or ResourceResult.