From a3293d3402790c3ae1651aecf022dbeceb7a5bfb Mon Sep 17 00:00:00 2001 From: Nithin Date: Wed, 14 Oct 2026 15:47:29 -0400 Subject: [PATCH 1/4] fix(tools): preserve UTF-8 characters without unicode escaping in tool results --- python/packages/core/agent_framework/_mcp.py | 2 +- .../packages/core/agent_framework/_tools.py | 6 ++-- .../packages/core/agent_framework/_types.py | 2 +- python/packages/core/tests/core/test_mcp.py | 19 ++++++++++++ python/packages/core/tests/core/test_types.py | 31 +++++++++++++++++++ 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index c4f93237f03..46f8dfc6629 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -1390,7 +1390,7 @@ def _parse_tool_result_from_mcp( structured_block: Content | None = None if mcp_type.structured_content is not None: structured_block = Content.from_text( - json.dumps(mcp_type.structured_content, default=str), **additional_kwargs + json.dumps(mcp_type.structured_content, default=str, ensure_ascii=False), **additional_kwargs ) # Select model-visible content per explicit policy (#7866). Host payload still diff --git a/python/packages/core/agent_framework/_tools.py b/python/packages/core/agent_framework/_tools.py index 59aaf472803..3436bbcd25c 100644 --- a/python/packages/core/agent_framework/_tools.py +++ b/python/packages/core/agent_framework/_tools.py @@ -1413,13 +1413,15 @@ def parse_result(result: Any) -> list[Content]: parsed_items.append(item) else: dumpable = FunctionTool._make_dumpable(item) - text = dumpable if isinstance(dumpable, str) else json.dumps(dumpable, default=str) + text = ( + dumpable if isinstance(dumpable, str) else json.dumps(dumpable, default=str, ensure_ascii=False) + ) parsed_items.append(Content.from_text(text)) return parsed_items dumpable = FunctionTool._make_dumpable(result) if isinstance(dumpable, str): return [Content.from_text(dumpable)] - return [Content.from_text(json.dumps(dumpable, default=str))] + return [Content.from_text(json.dumps(dumpable, default=str, ensure_ascii=False))] def to_json_schema_spec(self) -> dict[str, Any]: """Convert a FunctionTool to the JSON Schema function specification format. diff --git a/python/packages/core/agent_framework/_types.py b/python/packages/core/agent_framework/_types.py index 629593b8839..7b55a4fddcf 100644 --- a/python/packages/core/agent_framework/_types.py +++ b/python/packages/core/agent_framework/_types.py @@ -1202,7 +1202,7 @@ def from_function_result( items_list = [Content.from_text(result)] elif result is not None: try: - text = json.dumps(result, default=str) + text = json.dumps(result, default=str, ensure_ascii=False) except (TypeError, ValueError): text = str(result) items_list = [Content.from_text(text)] diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index 757e2a15f88..326154c4ffc 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -1254,6 +1254,25 @@ def test_parse_tool_result_from_mcp_structured_content_only(): assert parsed == {"Tables": [{"Name": "Sales", "Columns": ["Amount", "Date"]}]} +def test_parse_tool_result_from_mcp_structured_content_unicode_unescaped(): + """Test that non-ASCII unicode characters in structuredContent are not escaped to \\uXXXX.""" + mcp_result = types.CallToolResult( + content=[], + structuredContent={"columns": ["Año", "Género", "Categoría"]}, + ) + result = _HELPER_MCP_TOOL._parse_tool_result_from_mcp(mcp_result) + + assert isinstance(result, list) + assert len(result) == 1 + assert result[0].text is not None + assert "Año" in result[0].text + assert "Género" in result[0].text + assert "Categoría" in result[0].text + assert "\\u00f1" not in result[0].text + assert "\\u00e9" not in result[0].text + assert "\\u00ed" not in result[0].text + + def test_parse_tool_result_from_mcp_structured_content_with_text(): """Default structured_first prefers structuredContent when both are present (#7866).""" mcp_result = types.CallToolResult( diff --git a/python/packages/core/tests/core/test_types.py b/python/packages/core/tests/core/test_types.py index 771e94fb799..2cc915493af 100644 --- a/python/packages/core/tests/core/test_types.py +++ b/python/packages/core/tests/core/test_types.py @@ -4653,6 +4653,37 @@ def test_parse_result_mixed_content_list(): assert result[1].type == "data" +def test_parse_result_non_ascii_unicode(): + """Test that non-ASCII unicode characters in dict results are not escaped to \\uXXXX.""" + data = {"columns": ["Año", "Género", "Categoría"]} + parsed = FunctionTool.parse_result(data) + assert len(parsed) == 1 + assert "Año" in parsed[0].text + assert "Género" in parsed[0].text + assert "\\u00f1" not in parsed[0].text + assert "\\u00e9" not in parsed[0].text + + +def test_parse_result_list_non_ascii_unicode(): + """Test that non-ASCII unicode in list-of-items results are not escaped to \\uXXXX.""" + data = [{"name": "Año"}, {"name": "Género"}] + parsed = FunctionTool.parse_result(data) + assert len(parsed) == 1 + assert "Año" in parsed[0].text + assert "Género" in parsed[0].text + assert "\\u00f1" not in parsed[0].text + + +def test_from_function_result_non_ascii_unicode(): + """Test that Content.from_function_result preserves UTF-8 unicode in dict results.""" + data = {"city": "München", "country": "España"} + content = Content.from_function_result(call_id="call-1", result=data) + assert "München" in content.result + assert "España" in content.result + assert "\\u00fc" not in content.result + assert "\\u00f1" not in content.result + + def test_from_function_result_with_content_list(): """Test Content.from_function_result stores all items uniformly.""" content_list = [ From 28f585d5b610947cbf093153f84ab9b0f563e06a Mon Sep 17 00:00:00 2001 From: Nithin Date: Wed, 14 Oct 2026 16:00:12 -0400 Subject: [PATCH 2/4] test(types): narrow Content.text and result before membership assertions --- python/packages/core/tests/core/test_types.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/packages/core/tests/core/test_types.py b/python/packages/core/tests/core/test_types.py index 2cc915493af..faa43d4c178 100644 --- a/python/packages/core/tests/core/test_types.py +++ b/python/packages/core/tests/core/test_types.py @@ -4658,6 +4658,7 @@ def test_parse_result_non_ascii_unicode(): data = {"columns": ["Año", "Género", "Categoría"]} parsed = FunctionTool.parse_result(data) assert len(parsed) == 1 + assert parsed[0].text is not None assert "Año" in parsed[0].text assert "Género" in parsed[0].text assert "\\u00f1" not in parsed[0].text @@ -4669,6 +4670,7 @@ def test_parse_result_list_non_ascii_unicode(): data = [{"name": "Año"}, {"name": "Género"}] parsed = FunctionTool.parse_result(data) assert len(parsed) == 1 + assert parsed[0].text is not None assert "Año" in parsed[0].text assert "Género" in parsed[0].text assert "\\u00f1" not in parsed[0].text @@ -4678,6 +4680,7 @@ def test_from_function_result_non_ascii_unicode(): """Test that Content.from_function_result preserves UTF-8 unicode in dict results.""" data = {"city": "München", "country": "España"} content = Content.from_function_result(call_id="call-1", result=data) + assert content.result is not None assert "München" in content.result assert "España" in content.result assert "\\u00fc" not in content.result From ddb4a179641f8a8562c692d1b5a4b235974c142c Mon Sep 17 00:00:00 2001 From: Nithin Date: Wed, 14 Oct 2026 16:40:30 -0400 Subject: [PATCH 3/4] fix(mcp): preserve UTF-8 characters in multi-message prompt results --- python/packages/core/agent_framework/_mcp.py | 2 +- python/packages/core/tests/core/test_mcp.py | 25 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index 46f8dfc6629..091342c5243 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -1304,7 +1304,7 @@ def _parse_prompt_result_from_mcp( return "" if len(parts) == 1: return parts[0] - return json.dumps(parts, default=str) + return json.dumps(parts, default=str, ensure_ascii=False) def _parse_message_from_mcp( self, diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index 326154c4ffc..a17b1b06006 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -1104,6 +1104,31 @@ def test_mcp_tool_str_and_parse_prompt_result_rich_content() -> None: } +def test_parse_prompt_result_from_mcp_unicode_unescaped(): + """Test that non-ASCII unicode characters in multi-message prompt results are not escaped to \\uXXXX.""" + tool = _HELPER_MCP_TOOL + prompt_result = types.GetPromptResult( + description="Unicode prompt", + messages=[ + types.PromptMessage( + role="user", + content=types.TextContent(type="text", text="Año"), + ), + types.PromptMessage( + role="assistant", + content=types.TextContent(type="text", text="Género"), + ), + ], + ) + + result = tool._parse_prompt_result_from_mcp(prompt_result) + + assert "Año" in result + assert "Género" in result + assert "\\u00f1" not in result + assert "\\u00e9" not in result + + def test_parse_tool_result_from_mcp(): """Test conversion from MCP tool result with images preserves original order.""" mcp_result = types.CallToolResult( From 762d1d4f7808976e641db93079c18f68bed8cc39 Mon Sep 17 00:00:00 2001 From: Nithin Date: Thu, 22 Oct 2026 11:00:24 -0400 Subject: [PATCH 4/4] test(mcp): align structured_content kwarg with MCP 2.x CallToolResult --- python/packages/core/tests/core/test_mcp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index a17b1b06006..2f173abd7ae 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -1280,10 +1280,10 @@ def test_parse_tool_result_from_mcp_structured_content_only(): def test_parse_tool_result_from_mcp_structured_content_unicode_unescaped(): - """Test that non-ASCII unicode characters in structuredContent are not escaped to \\uXXXX.""" + """Test that non-ASCII unicode characters in structured_content are not escaped to \\uXXXX.""" mcp_result = types.CallToolResult( content=[], - structuredContent={"columns": ["Año", "Género", "Categoría"]}, + structured_content={"columns": ["Año", "Género", "Categoría"]}, ) result = _HELPER_MCP_TOOL._parse_tool_result_from_mcp(mcp_result)