Skip to content

Python: serialize Anthropic MCP tool result content on replay - #9972

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 5 commits into
microsoft:mainfrom
uragoodman:fix/anthropic-mcp-tool-result-replay
Nov 4, 2026
Merged

Eduard van Valkenburg (eavanvalkenburg) merged 5 commits into
microsoft:mainfrom
uragoodman:fix/anthropic-mcp-tool-result-replay

Conversation

@uragoodman

Copy link
Copy Markdown
Contributor

Motivation & Context

Anthropic's provider-hosted MCP connector returns mcp_tool_result blocks whose content is parsed into list[Content]. On the next turn, _prepare_message_for_anthropic copied content.output directly into the request body, so the Anthropic SDK failed with:

TypeError: Object of type Content is not JSON serializable

The first request succeeds; every subsequent turn that replays the MCP result fails before the request is sent.

This change normalizes MCP tool result output into the shapes Anthropic accepts for mcp_tool_result.content: a string or a list of text blocks.

Description & Review Guide

  • What are the major changes?
    • Added _prepare_mcp_tool_result_content to convert None, strings, top-level mappings, and Sequence[Content] | Sequence[Mapping] into Anthropic-compatible content.
    • mcp_server_tool_result now uses this helper instead of emitting content.output unchanged.
    • Text Content items become {"type": "text", "text": ...}; non-text items are skipped with a debug log because the Anthropic mcp_tool_result schema only accepts text.
    • Top-level text mappings become a one-item text-block list; other mappings are JSON-encoded.
  • What is the impact of these changes?
    • Multi-turn conversations using Anthropic's hosted MCP connector now replay their tool results instead of raising TypeError.
    • Local MCP tools and normal function tools are unchanged; they use function_result.
    • Existing string and None outputs keep their previous wire shape.
  • What do you want reviewers to focus on?
    • The accepted input shapes and the decision to skip non-text items, since the Anthropic schema cannot represent image/file blocks in mcp_tool_result.
    • The top-level Mapping handling and whether JSON-encoding non-text mappings is the right fallback.
    • Test coverage for parsed list content, top-level mappings, and persisted-history replay.

The diff also contains one ruff format adjustment to an existing over-wrapped call in the same file; it is required by the repository formatter.

Testing

  • pytest packages/anthropic/tests -m "not integration" -q — 259 passed
  • ruff format --check — pass
  • ruff check --no-fix — pass
  • pyright --pythonpath .venv/Scripts/python.exe packages/anthropic/agent_framework_anthropic — 0 errors
  • mypy packages/anthropic/tests/test_anthropic_client.py — no issues

Full workspace build and the full unit-test suite were not run locally; CI will run them.

Related Issue

Fixes #9970

The issue is filed; opening this focused fix for maintainer review. No ready-for-implementation label was present when this PR was opened.

AI Assistance

  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: implementation, tests, and PR description were drafted with an AI coding assistant; I reproduced and verified the failure and the fix locally.

Contribution Checklist

  • The changed package imports cleanly and the focused Anthropic test suite passes locally.
  • New regression tests were added and pass.
  • Ruff format, Ruff lint, Pyright source check, and Mypy test check pass locally.
  • Full workspace build and full unit-test suite were not run locally; CI will run them.
  • This is not a breaking change.
  • The PR links to issue Python: [Bug]: Anthropic MCP tool result replay sends Content objects instead of text blocks #9970; no ready-for-implementation label was present when opened, and the PR is submitted for early direction feedback.

Render parsed Anthropic MCP results as JSON-serializable text blocks instead of Content objects, and cover list, top-level mapping, and persisted-history replay with regression tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Nov 2, 2026
Kip (uragoodman) and others added 2 commits November 3, 2026 19:44
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Persist the provider's is_error flag in Anthropic metadata so MCP tool
results replay with the same error status after serialization. Tighten
the replay output contract to a concrete union and reject unsupported
shapes instead of stringifying them.

Add streaming, non-streaming, persistence, and unsupported-output
regression tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merged via the queue into microsoft:main with commit f6a7335 Nov 4, 2026
50 checks passed

This branch was previously deployed

1 inactive deployment
github-app-auth — 4d3ad2fc Deployed Nov 3, 2026 by uragoodman via add_label #26960
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: Anthropic MCP tool result replay sends Content objects instead of text blocks

3 participants