[BUG] MCP tool output not rendered in terminal when using native TextContent (fastmcp 3.x)
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?
MCP tool output is not displayed in the terminal ⎿ block when the server returns native TextContent via fastmcp 3.x. The text is consumed silently by the model — users cannot see what the tool returned.
When the same text is returned as -> str (which fastmcp wraps in structuredContent: {"result": "..."} automatically), it IS shown — but as a JSON blob with escaped \n characters, not as formatted multi-line text.
Built-in tools like Bash render multi-line output with real newlines. MCP tools have no equivalent path.
What Should Happen?
MCP tool text output should be rendered in the terminal identically to Bash stdout — with real newlines, passed directly to the uK display component. Specifically:
- When CallToolResult.content contains TextContent items, their .text should be displayed in the ⎿ block with newlines preserved.
- When structuredContent.result is a string, it should be displayed as plain text, not JSON-serialized.
Error Messages/Logs
No errors. The behavior is silent — tool output simply doesn't appear.
Steps to Reproduce
Minimal repro — two tools, same text, different visibility:
#server.py — requires: pip install fastmcp==3.2.4
from fastmcp import FastMCP
from mcp.types import TextContent
mcp = FastMCP("repro")
@mcp.tool()
async def visible() -> str:
"""Visible but JSON-escaped."""
return "Line 1\n Line 2\n Line 3"
@mcp.tool()
async def hidden() -> TextContent:
"""Hidden from terminal entirely."""
return TextContent(type="text", text="Line 1\n Line 2\n Line 3")
if __name__ == "__main__":
mcp.run(transport="stdio")
Add to .mcp.json:
{"mcpServers": {"repro": {"command": "python3", "args": ["server.py"]}}}
- Call visible — terminal shows ⎿ {"result":"Line 1\n Line 2\n Line 3"} (JSON blob, newlines escaped)
- Call hidden — terminal shows nothing (output consumed by model, invisible to user)
Expected for both: terminal shows:
⎿ Line 1
Line 2
Line 3
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.116
Platform
AWS Bedrock
Operating System
Other Linux
Terminal/Shell
Other
Additional Information
Root cause from source analysis (cli.js):
The MCP tool renderer in C2B() has two paths:
// Path 1: structuredContent present → JSON.stringify → visible but escaped
if ("toolResult" in W) {
return String(W.toolResult); // loses newlines when toString'd from object
}
// Path 2: content array → mapped but displayed differently
if ("content" in W && Array.isArray(W.content)) {
let F = W.content.map((V) => H2B(V, B)).flat();
return F; // array → renderer called but output not shown in terminal
}
The MCP renderer renderToolResultMessage correctly handles TextContent[] arrays and passes .text to uK. However, in practice the output is not displayed when structuredContent is absent from the wire response.
For comparison, Bash's renderer (mu component) passes stdout directly to uK as a plain string — newlines render correctly.
Suggested fix: When the MCP tool result contains only TextContent items, extract and join their .text values and pass directly to uK as a plain string (same as Bash stdout), instead of JSON-serializing the response object.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗