fix: handle CallToolResult in _convert_to_content to prevent double-serialization#2136
Closed
fix: handle CallToolResult in _convert_to_content to prevent double-serialization#2136
Conversation
…erialization
When a tool returns a CallToolResult nested inside a list (or any other
sequence), _convert_to_content would fall through to the pydantic_core.to_json
fallback, serializing the entire CallToolResult object as a JSON string and
wrapping it in a TextContent. This caused the client to receive:
TextContent(text='{"_meta": null, "content": [...], "isError": false}')
instead of the actual content blocks from the CallToolResult.
The fix adds an explicit guard in _convert_to_content for CallToolResult objects:
when one is encountered, its content blocks are extracted and flattened into
the result sequence rather than being JSON-serialized.
The direct return case (tool returning CallToolResult at the top level) was
already correctly handled by the isinstance check in convert_result.
Reported-by: wilson-urdaneta
Github-Issue: #592
Reported-by: wilson-urdaneta
Github-Issue: #592
Contributor
Author
|
just wanted to get claude to reproduce, didn't mean for it to actually post a pr |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a tool returns a
CallToolResultnested inside a list (e.g.,return [CallToolResult(...)]), the_convert_to_contentfunction infunc_metadata.pywould fall through to thepydantic_core.to_jsonfallback, serializing the entireCallToolResultobject as a JSON string and wrapping it in aTextContent. This caused the client to receive:instead of the actual content blocks from the
CallToolResult:The root cause is that
CallToolResultis not aContentBlock(which isTextContent | ImageContent | AudioContent | ...), so it would miss theisinstance(result, ContentBlock)guard and hit the generic JSON serializer.Fix
Added an explicit guard in
_convert_to_contentforCallToolResultobjects: when one is encountered, its.contentblocks are extracted and flattened into the result sequence rather than being JSON-serialized.The direct return case (a tool returning
CallToolResultat the top level) was already correctly handled by theisinstance(result, CallToolResult)check inconvert_result.Tests
Added regression tests in
tests/issues/test_592_call_tool_result_primitive.pycovering:CallToolResultdirectly with-> CallToolResultannotation[CallToolResult(...)](nested in a list) — this was the broken caseFixes #592