[BUG] MCP error results (isError) silently truncated to first/last 5,000 characters, even when the tool sets anthropic/maxResultSizeChars
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 tool returns an error (isError: true) whose text is longer than about 11,000 characters, Claude Code keeps the first and last 5,000 characters and replaces the middle with … [N characters truncated] …. Nothing is saved to a file, so the middle is unrecoverable. This is not documented, and anthropic/maxResultSizeChars on the tool does not change it.
What Should Happen?
Any of:
- document the error-result limit next to the other MCP output limits;
- honor
anthropic/maxResultSizeCharsfor error results as well; - or persist the full error text to a file, as is done for successful results, instead of dropping the middle.
Error Messages/Logs
<first 5,000 characters of the error> … [1500 characters truncated] … <last 5,000 characters of the error>
Steps to Reproduce
- Save the minimal server below as
synth_server.py(MCP Python SDK / FastMCP 1.28.1). mcp.json:{"mcpServers":{"synth":{"type":"stdio","command":"python","args":["synth_server.py"]}}}- Run one tool call per case:
claude -p "<call the tool with n=…>" --strict-mcp-config --mcp-config mcp.json --output-format stream-json --verbose - Inspect
tool_resultin the stream-json log.
raise_error(n) raises an exception so that the client-visible text Error executing tool raise_error: <message> is exactly n characters.
| Case | n (chars) | Result in the model's context |
|---|---|---|
| latin | 9,900 / 10,000 / 10,100 / 10,500 / 11,000 | full error text |
| latin | 11,500 | first 5,000 + … [1500 characters truncated] … + last 5,000 |
| latin | 12,000 / 15,000 / 17,500 / 20,000 | same pattern; truncated 2,000 / 5,000 / 7,500 / 10,000 |
| greek | 11,000 / 11,500 | full / truncated 1,500 (same boundary as Latin) |
| latin, tool with anthropic/maxResultSizeChars: 250000 (raise_error_meta) | 20,000 | still truncated 10,000 |
"""Minimal MCP server to reproduce Claude Code tool-result size limits.
echo_text(n, alphabet) -> success result of exactly n characters
raise_error(n) -> error result whose text is exactly n characters
*_meta variants -> same, with _meta["anthropic/maxResultSizeChars"] = 250000
"""
import random
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("synth")
ALPHABETS = {
"latin": "abcdefghijklmnopqrstuvwxyz",
"greek": "αβγδεζηθικλμνξοπρστυφχψω",
}
META = {"anthropic/maxResultSizeChars": 250000}
def make_text(n: int, alphabet: str, line_len: int = 0) -> str:
"""Deterministic random words, exactly n characters, ending with a marker.
line_len > 0: a newline replaces the space roughly every line_len characters."""
letters = ALPHABETS[alphabet]
rnd = random.Random(n)
marker = " END-OF-TEXT"
parts, size = [], 0
while size < n - len(marker):
w = "".join(rnd.choice(letters) for _ in range(rnd.randint(2, 9))) + " "
parts.append(w)
size += len(w)
body = "".join(parts)[: n - len(marker)]
if line_len > 0:
chars = list(body)
for i in range(line_len, len(chars), line_len):
chars[i] = "\n"
body = "".join(chars)
text = body + marker
assert len(text) == n
return text
def error_text(n: int, tool: str, alphabet: str = "latin") -> str:
"""Message such that the client-visible text 'Error executing tool <tool>: <msg>' is exactly n chars."""
prefix = f"Error executing tool {tool}: "
return make_text(n - len(prefix), alphabet)
@mcp.tool(structured_output=False)
def echo_text(n: int, alphabet: str = "latin", line_len: int = 0) -> str:
"""Return a text of exactly n characters (alphabet: latin | greek; line_len > 0 - multi-line)."""
return make_text(n, alphabet, line_len)
@mcp.tool(structured_output=False, meta=META)
def echo_text_meta(n: int, alphabet: str = "latin", line_len: int = 0) -> str:
"""Same as echo_text, tool carries anthropic/maxResultSizeChars=250000."""
return make_text(n, alphabet, line_len)
@mcp.tool(structured_output=False)
def raise_error(n: int, alphabet: str = "latin") -> str:
"""Fail with an error whose client-visible text is exactly n characters."""
raise ValueError(error_text(n, "raise_error", alphabet))
@mcp.tool(structured_output=False, meta=META)
def raise_error_meta(n: int, alphabet: str = "latin") -> str:
"""Same as raise_error, tool carries anthropic/maxResultSizeChars=250000."""
raise ValueError(error_text(n, "raise_error_meta", alphabet))
if __name__ == "__main__":
mcp.run()
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.283 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Non-interactive/CI environment
Additional Information
Impact: errors that carry actionable lists (e.g. "ambiguous — candidates: …") lose the middle of the list with no indication from the server; the model sees a plausible but incomplete message.
A related undocumented limit on successful results is filed separately: #97622
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗