Skip to content

Commit 9dec4ba

Browse files
committed
fix: respect resource mime type in responses
The server was ignoring mime types set on resources, defaulting to text/plain for strings and application/octet-stream for bytes. Now properly preserves the specified mime type in both FastMCP and low-level server implementations. Reported-by: eiseleMichael
1 parent 2108ecf commit 9dec4ba

File tree

4 files changed

+78
-13
lines changed

4 files changed

+78
-13
lines changed

CLAUDE.md

+14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,20 @@ This file is intended to be used by an LLM such as Claude.
2323
git commit -am "\"fix: my commit message\""
2424
```
2525

26+
- For commits fixing bugs or adding features based on user reports add:
27+
```bash
28+
git commit --trailer "Reported-by:<name>"
29+
```
30+
Where `<name>` is the name of the user.
31+
32+
- For commits related to a Github issue, add
33+
```bash
34+
git commit --trailer "Github-Issue:<number>"
35+
```
36+
- NEVER ever mention a `co-authored-by` or similar aspects. In particular, never
37+
mention the tool used to create the commit message or PR.
38+
39+
2640
## Python Tools
2741

2842
### Ruff

src/mcp/server/fastmcp/server.py

+27-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424
from mcp.server.stdio import stdio_server
2525
from mcp.shared.context import RequestContext
2626
from mcp.types import (
27+
BlobResourceContents,
2728
EmbeddedResource,
2829
GetPromptResult,
2930
ImageContent,
31+
ReadResourceResult,
3032
TextContent,
33+
TextResourceContents,
3134
)
3235
from mcp.types import (
3336
Prompt as MCPPrompt,
@@ -197,14 +200,36 @@ async def list_resource_templates(self) -> list[MCPResourceTemplate]:
197200
for template in templates
198201
]
199202

200-
async def read_resource(self, uri: AnyUrl | str) -> str | bytes:
203+
async def read_resource(
204+
self, uri: AnyUrl | str
205+
) -> str | bytes | ReadResourceResult:
201206
"""Read a resource by URI."""
207+
import base64
208+
202209
resource = await self._resource_manager.get_resource(uri)
203210
if not resource:
204211
raise ResourceError(f"Unknown resource: {uri}")
205212

206213
try:
207-
return await resource.read()
214+
match await resource.read():
215+
case str() as s:
216+
return ReadResourceResult(
217+
contents=[
218+
TextResourceContents(
219+
uri=resource.uri, mimeType=resource.mime_type, text=s
220+
)
221+
]
222+
)
223+
case bytes() as b:
224+
return ReadResourceResult(
225+
contents=[
226+
BlobResourceContents(
227+
uri=resource.uri,
228+
mimeType=resource.mime_type,
229+
blob=base64.b64encode(b).decode(),
230+
)
231+
]
232+
)
208233
except Exception as e:
209234
logger.error(f"Error reading resource {uri}: {e}")
210235
raise ResourceError(str(e))

src/mcp/server/lowlevel/server.py

+21-9
Original file line numberDiff line numberDiff line change
@@ -252,32 +252,44 @@ async def handler(_: Any):
252252
return decorator
253253

254254
def read_resource(self):
255-
def decorator(func: Callable[[AnyUrl], Awaitable[str | bytes]]):
255+
def decorator(
256+
func: Callable[[AnyUrl], Awaitable[types.ReadResourceResult | str | bytes]],
257+
):
256258
logger.debug("Registering handler for ReadResourceRequest")
257259

258260
async def handler(req: types.ReadResourceRequest):
259261
result = await func(req.params.uri)
260262
match result:
261-
case str(s):
263+
case types.ReadResourceResult() as result:
264+
return types.ServerResult(result)
265+
case str() as s:
262266
content = types.TextResourceContents(
263267
uri=req.params.uri,
264268
text=s,
265269
mimeType="text/plain",
266270
)
267-
case bytes(b):
271+
return types.ServerResult(
272+
types.ReadResourceResult(
273+
contents=[content],
274+
)
275+
)
276+
case bytes() as b:
268277
import base64
269278

270279
content = types.BlobResourceContents(
271280
uri=req.params.uri,
272281
blob=base64.urlsafe_b64encode(b).decode(),
273282
mimeType="application/octet-stream",
274283
)
275-
276-
return types.ServerResult(
277-
types.ReadResourceResult(
278-
contents=[content],
279-
)
280-
)
284+
return types.ServerResult(
285+
types.ReadResourceResult(
286+
contents=[content],
287+
)
288+
)
289+
case _:
290+
raise ValueError(
291+
f"Unexpected return type from read_resource: {type(result)}"
292+
)
281293

282294
self.request_handlers[types.ReadResourceRequest] = handler
283295
return func

tests/issues/test_152_resource_mime_type.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,23 @@ async def handle_list_resources():
9898
@server.read_resource()
9999
async def handle_read_resource(uri: AnyUrl):
100100
if str(uri) == "test://image":
101-
return base64_string
101+
return types.ReadResourceResult(
102+
contents=[
103+
types.TextResourceContents(
104+
uri=uri, mimeType="image/png", text=base64_string
105+
)
106+
]
107+
)
102108
elif str(uri) == "test://image_bytes":
103-
return image_bytes
109+
return types.ReadResourceResult(
110+
contents=[
111+
types.BlobResourceContents(
112+
uri=uri,
113+
mimeType="image/png",
114+
blob=base64.b64encode(image_bytes).decode(),
115+
)
116+
]
117+
)
104118
raise Exception(f"Resource not found: {uri}")
105119

106120
# Test that resources are listed with correct mime type

0 commit comments

Comments
 (0)