[BUG] MCP ImageContent returned as text in tool results instead of native image blocks (10-20x token waste)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When an MCP server returns ImageContent (e.g., a matplotlib chart from the Jupyter MCP server), Claude Code does not convert it into a native image content block for the Anthropic API. Instead, the base64 data appears to be treated as text in the tool result, consuming ~15,000-25,000 tokens per image.
The same image, when attached directly as a user message (e.g., pasting a screenshot), is processed as a native image and costs only ~1,600 tokens — roughly 10-20x less.
This makes iterative notebook workflows (where multiple charts are produced) impractical, as a notebook with 10 charts can consume 150K-250K tokens just on image data that Claude can't even interpret (it's just base64 text characters).
What Should Happen?
When an MCP tool returns ImageContent per the MCP spec:
{
"type": "image",
"data": "<base64-encoded-data>",
"mimeType": "image/png"
}
Claude Code should convert this into a native image content block in the API request:
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "<base64-encoded-data>"
}
}
This way the model receives the image as an actual image (~1,600 tokens for a typical chart) and can visually interpret it, rather than receiving a wall of base64 text (~20,000 tokens) that it cannot interpret.
Observed Behavior
In the Claude Code terminal, large MCP image results show messages like:
Error: result (62,162 characters) exceeds maximum allowed tokens. Output has been saved to ~/.claude/projects/.../tool-results/mcp-jupyter-execute_cell-XXX.txt
The base64 image data is being saved as a text file and treated as text content. Claude sees the raw base64 characters but cannot interpret them as an image.
Reproduction Steps
- Set up the Jupyter MCP server with Claude Code
- Connect to a notebook and execute a cell that produces a matplotlib chart
- Observe that the result is treated as text (base64 string), not as a native image
Impact
This affects any MCP server that returns images: Jupyter (charts/plots), Playwright (screenshots), Figma, etc. For data science workflows in particular, this makes Claude Code impractical for iterative notebook development with visualizations.
Related Closed Issues
- #14150 — Same core issue (base64 saved to JSON file instead of rendered as image). Closed by inactivity bot, not by a fix.
- #9152 — Token limit exceeded for MCP image responses. Closed as duplicate of #4002.
- #4002 — File content exceeds 25K token limit. Closed.
Claude Code Version
Latest
Platform
macOS
Additional Context
The MCP server (datalayer/jupyter-mcp-server) correctly returns ImageContent objects with type="image", mimeType="image/png", and base64 data. The issue is entirely in how Claude Code handles these objects when constructing the API request.
This issue has 11 comments on GitHub. Read the full discussion on GitHub ↗