You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Image/Audio format= emits image/jpg, audio/mp3, audio/m4a — unregistered MIME types that disagree with the path= suffix table #3585
I confirm that I'm using the newest release of my release line (2.2.0)
I have searched GitHub issues and found no similar issue
Release Line
2.x (current stable). The same code exists on v1.x (src/mcp/server/fastmcp/utilities/types.py).
Description
Image and Audio compute the MIME type two different ways. With path= they use a suffix table (.jpg -> image/jpeg, .mp3 -> audio/mpeg, .m4a -> audio/mp4). With format=, which docs/servers/media.md says to pass whenever you use data=, they just do f"image/{format}" / f"audio/{format}". So the same name gives different results depending on which keyword it arrives through, and format= puts non-registered types on the wire:
Image(data=..., format="jpg") -> mimeType: "image/jpg" (should be image/jpeg)
Audio(data=..., format="mp3") -> audio/mp3 (should be audio/mpeg)
Audio(data=..., format="m4a") -> audio/m4a (should be audio/mp4)
This is easy to hit: switching a tool from Image(path="chart.jpg") to Image(data=buf.getvalue(), format="jpg") silently changes the wire output from image/jpeg to image/jpg, and a consumer that validates image MIME types rejects it.
Expected: format="jpg" and path="x.jpg" give the same registered type.
Proposed fix: route format= through the same table the path= branch already has, keeping image/<format> / audio/<format> as the fallback for names outside it (so format="bmp" etc. are unchanged). I have this fix with parametrized tests ready locally (100% branch coverage on types.py, ruff/pyright clean) and would like to open the PR if this is accepted.
Example Code
frompathlibimportPathfrommcp.server.mcpserverimportAudio, Imageprint(Image(data=b"\x00", format="jpg").to_image_content().mime_type) # image/jpg (expected image/jpeg)print(Audio(data=b"\x00", format="mp3").to_audio_content().mime_type) # audio/mp3 (expected audio/mpeg)print(Audio(data=b"\x00", format="m4a").to_audio_content().mime_type) # audio/m4a (expected audio/mp4)# The same helpers get it right when the name arrives as a file suffix:print(Image(path=Path("x.jpg"))._mime_type) # image/jpeg
Python & MCP Python SDK
Python 3.12.13
mcp 2.2.0 (also reproduced on main @ f1b6589)
Disclosure: found, reproduced and the draft fix tested locally with the help of an AI coding agent (Claude Code); I reviewed the report before filing it.
I reproduced this locally: Image(data=..., format="jpg") and Audio(data=..., format="mp3") emit unregistered MIME types (image/jpg, audio/mp3), differing from the suffix mapping used by path=.
I have a fix with tests ready that normalizes format= through the same mapping table as path= while preserving generic fallbacks. If this approach looks good, could you assign this issue to me? (Assigning will also automatically reopen PR #3593, which was closed by the bot).
Disclosure: Prepared with AI assistance under human review.
The format= branch is an unchecked interpolation — it never consults the suffix table two lines below it, so format="jpg" emits image/jpg while path="x.jpg" emits image/jpeg. Same for Audio (audio/mp3 vs audio/mpeg, audio/m4a vs audio/mp4).
One thing worth flagging for whoever picks this up, since it affects the scope: the path in this report is the v1 location. On main (v2) the file has moved to src/mcp/server/mcpserver/utilities/types.py — fastmcp no longer exists. So the fix needs landing on both lines (the v1.x maintenance branch and v2), and a v2-only fix would leave v1 emitting the bad types.
Suggested shape, if useful: a small module-level suffix→MIME map that both branches share, plus mimetypes.guess_type() as the fallback for formats outside the table, so format= and path= cannot disagree by construction rather than by convention. Happy to also add the regression tests (format="jpg" == path=".jpg") if this gets assigned.
I don't want to open a PR blind — AGENTS.md closes PRs that aren't linked to an issue assigned to the author, so assigning this to me first is the efficient path.
Activity
MustafaKemal0146 commentedon Sep 29, 2026
Hi maintainers,
I reproduced this locally:
Image(data=..., format="jpg")andAudio(data=..., format="mp3")emit unregistered MIME types (image/jpg,audio/mp3), differing from the suffix mapping used bypath=.I have a fix with tests ready that normalizes
format=through the same mapping table aspath=while preserving generic fallbacks. If this approach looks good, could you assign this issue to me? (Assigning will also automatically reopen PR #3593, which was closed by the bot).Disclosure: Prepared with AI assistance under human review.
Jaswanth1902 commentedon Oct 2, 2026
i20000109 commentedon Oct 2, 2026
Confirmed on
main. The asymmetry is exactly as described.src/mcp/server/mcpserver/utilities/types.py,Image._get_mime_type():The
format=branch is an unchecked interpolation — it never consults the suffix table two lines below it, soformat="jpg"emitsimage/jpgwhilepath="x.jpg"emitsimage/jpeg. Same forAudio(audio/mp3vsaudio/mpeg,audio/m4avsaudio/mp4).One thing worth flagging for whoever picks this up, since it affects the scope: the path in this report is the v1 location. On
main(v2) the file has moved tosrc/mcp/server/mcpserver/utilities/types.py—fastmcpno longer exists. So the fix needs landing on both lines (thev1.xmaintenance branch and v2), and a v2-only fix would leave v1 emitting the bad types.Suggested shape, if useful: a small module-level suffix→MIME map that both branches share, plus
mimetypes.guess_type()as the fallback for formats outside the table, soformat=andpath=cannot disagree by construction rather than by convention. Happy to also add the regression tests (format="jpg" == path=".jpg") if this gets assigned.I don't want to open a PR blind —
AGENTS.mdcloses PRs that aren't linked to an issue assigned to the author, so assigning this to me first is the efficient path.