Skip to content

fix(core): keep unsupported MCP media out of tool attachments - #57640

Merged
rekram1-node merged 2 commits into
devfrom
mcp-media-types
Oct 21, 2026
Merged

rekram1-node merged 2 commits into
devfrom
mcp-media-types

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #54798
Fixes #52448

Problem

MCP tool calls stored every non-text result as a tool attachment with whatever media type the server declared. A Jira attachment arriving as text/markdown or text/x-patch, or a .parquet file, ended up as a file item that no provider route can send. The next request failed before it left the client:

Anthropic Messages does not support media type text/markdown

That attachment stays in history, so every later turn in the session failed the same way.

Every other path (the read tool, webfetch, user attachments) only ever sends PNG, JPEG, GIF, WebP and PDF. The MCP tool path was the only one with no filter.

Change

MCP tool results now follow the same rule as the read tool:

for each media part in an MCP tool result:
  png, jpeg, gif, webp, pdf   -> file attachment (unchanged)
  text types (md, patch, json) -> decoded and sent as text
                                  "[Resource file:///README.md (text/markdown)]\n..."
  anything else               -> written to tool-output/, model gets
                                  "[Binary resource file:///data.parquet (application/vnd.apache.parquet, 4 bytes) saved to <path>]"
  • ToolOutput.save(bytes, extension) writes to the same tool-output/ folder as truncated output, with the same tool_<id> names, so the existing 7 day cleanup removes these files too.
  • MCP resource blobs now carry their uri so the note can name the resource.
  • read_mcp_resource uses the same image/PDF allowlist, so image/svg+xml and other image types no longer get attached.
  • isTextual moved from webfetch into Mime so both callers share it.
 McpTool execute
   mcp.callTool
-  every media part -> { type: "file", mime: <server mime> }
+  toContent(part)
+    allowlisted media -> file
+    text media        -> text
+    other media       -> ToolOutput.save -> text note

Out of scope

  • Recovering automatically when a provider rejects an attachment that we do send (a corrupt PDF, for example).
  • read_mcp_resource still returns non-image blobs as base64 in its structured output.
  • Sessions that already have one of these attachments in history are not repaired; they still need a revert past the tool call.
  • Third-party plugin tools can still return arbitrary file types.

Verification


Added by the opencode bot (AI-generated).

Related: #55271

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I reproduced #54798 on the base branch: the request after the MCP tool call failed with Anthropic Messages does not support media type text/markdown, and so did every later turn. With this branch both turns go through. The markdown arrives as text in the tool result and the PNG is still sent as an image. A session broken on the base branch also recovers when continued on this branch, thanks to the request-time fallback.

bun test test/mcp.test.ts test/session-model-request.test.ts passes, except for the MCP descendant-termination test, which fails on dev too. packages/core type-checks.

Public API is unchanged: ToolOutput.save and the optional uri on MCP media content are internal to core.

No blocking concerns.

Related: #55271

@rekram1-node
rekram1-node merged commit 94bc566 into dev Oct 21, 2026
13 of 14 checks passed
@rekram1-node
rekram1-node deleted the mcp-media-types branch October 21, 2026 05:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant