[BUG] Undocumented breaking change in MCP tool output display (sometime between 2.0.10-2.0.22): structured content now prioritized over TextContent
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?
Issue
In 2.0.22 and possibly earlier, but after 2.0.9, MCP tool results are displayed differently:
- Before (≤2.0.9): TextContent was displayed cleanly for human readability
- After (in 2.0.22 possibly earlier): Structured content is wrapped in JSON format, even when TextContent is available
Impact
- MCP servers designed with pretty-printed output (code, formatted data) now show JSON-wrapped results
- Significantly degrades readability and user experience
- Affects all servers using FastMCP 2.10+ (which returns both formats per MCP spec)
Example
Expected (2.0.9):
f:{[x]
:x+1
}
Actual (2.0.22):
{
"result": "f:{[x]\n :x+1\n }"
}
MCP Spec Context
The MCP 2025-06-18 spec states:
"For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block."
This suggests TextContent should be the primary display format for human readability.
What Should Happen?
Questions
- Was this display change intentional?
- If yes, why wasn't it documented in release notes?
- Should Claude Code prioritize TextContent when both formats are available?
Workaround
Using structured_output=False in FastMCP forces text-only output.
Error Messages/Logs
Steps to Reproduce
Use a plain Python MCP server without the additional decorator described and return string
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
No response
Claude Code Version
2.0.22
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
WSL (Windows Subsystem for Linux)
Additional Information
No response
Thanks for raising this! Let me address your questions:
1. Was this intentional?
Yes, this was an intentional change. We added support for structuredContent in v2.0.21 to fix a compatibility issue: the previous behavior (never using structuredContent) broke servers that only provided structuredContent. At the same time, we made structuredContent the default when both formats are present.
2. Was it documented?
The structuredContent changes were mentioned in the changelog, though I acknowledge we could have been more explicit about the behavior change when both formats are available.
3. Should TextContent be prioritized?
The MCP spec doesn't indicate a preference between the two formats. The "SHOULD also return" language you mentioned is guidance for servers (about backwards compatibility), not a preference for clients.
For Claude Code, we're optimizing for agent performance rather than human readability. We've found that structuredContent is often easier for models to work with for composability and consistency, which is why we default to it.
We're generally willing to make this tradeoff, but if there are specific workflows that have become significantly worse or broken as a result, please share details and we can take a look.
I think there are two different issues -- 1) the format of the data being transferred, 2) its on-screen rendering.
Would it be possible to render \n in structuredContent as a new-line in both tool inputs and outputs?
The built-in tools do do that i.e. when code is edited or bash commands are run, new-lines are displayed instead of \n , whereas for custom tools \n does not get rendered. This is why the unannounced switch was particularly jarring to me -- suddenly many tool outputs started displaying \n instead of new-line.
Another case: YouTube Transcript MCP
We're experiencing this exact issue with a custom YouTube transcript MCP server.
What we tried (all failed):
- Removing metadata from response
- Removing
toolResultwrapper - Removing
isErrorfield - Using minimal
{ content: [{ type: "text", text: "..." }] }format
Our JSON output is correct:
{"result":{"content":[{"type":"text","text":"[actual transcript text here]"}]},"jsonrpc":"2.0","id":1}
When tested directly via JSON-RPC stdin, the MCP returns proper text content. But Claude Code displays [object Object] instead of the text.
Environment:
- Claude Code v2.0.56 (Windows)
- MCP SDK
@modelcontextprotocol/sdk - Node.js stdio transport
Workaround:
We're forced to use Bash to call the MCP directly instead of using the tool integration:
node -e "require('./dist/youtube-fetcher').getSubtitles({videoID:'xxx'}).then(r => console.log(r.lines.map(x => x.text).join(' ')))"
This defeats the purpose of MCP tool integration. Would appreciate a fix or documented workaround for text-only tool responses.
UPDATE: Found the fix!
After hours of debugging, we discovered the solution:
The issue: Claude Code v2.0.21+ reads structuredContent from MCP responses, NOT the content array.
The fix: Return BOTH fields in your tool response:
return {
content: [{
type: "text",
text: yourTextHere
}],
structuredContent: {
content: yourTextHere // <-- THIS IS REQUIRED
}
};
We found this by comparing our MCP to the official @modelcontextprotocol/server-filesystem which returns both fields.
Before: [object Object]
After: Proper text display ✅
This should probably be documented somewhere - we spent hours thinking our code was broken when it was just a missing field that isn't mentioned in the MCP SDK docs.
Correction: This IS documented in the MCP spec
After further investigation, we found that structuredContent is actually documented in the official MCP specification:
"For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block."
So the spec says structuredContent is the primary field, and content is for backwards compatibility - we had it backwards.
Our mistake: We assumed content was primary because that's what most MCP SDK examples show. The fix is still correct, but this isn't a Claude Code bug - it's following the spec.
Suggestion for MCP SDK maintainers: The SDK examples and docs could be clearer about when to use structuredContent vs content. Most tutorials only show content, which leads to confusion like ours.
Apologies for the noise - RTFM lesson learned! 📖
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.