diff --git a/src/google/adk/models/anthropic_llm.py b/src/google/adk/models/anthropic_llm.py index 7d28165f710..7c03a6d1eff 100644 --- a/src/google/adk/models/anthropic_llm.py +++ b/src/google/adk/models/anthropic_llm.py @@ -621,6 +621,12 @@ def _part_to_message_block( else: tool_result_content = content + # An MCP CallToolResult reports a failed call with `isError`, spelled + # `is_error` in dumps made with MCP SDK 2.x field names. + is_error = bool( + response_data.get("isError") or response_data.get("is_error") + ) + return anthropic_types.ToolResultBlockParam( tool_use_id=sanitizer.sanitize( function_response.id, @@ -629,7 +635,7 @@ def _part_to_message_block( ), type="tool_result", content=tool_result_content, - is_error=False, + is_error=is_error, ) elif _is_image_part(part): inline_data = part.inline_data diff --git a/tests/unittests/models/test_anthropic_llm.py b/tests/unittests/models/test_anthropic_llm.py index f57e7bcd689..c72495fb8b1 100644 --- a/tests/unittests/models/test_anthropic_llm.py +++ b/tests/unittests/models/test_anthropic_llm.py @@ -1379,6 +1379,24 @@ def test_part_to_message_block_with_multiple_content_items(): assert result["content"] == "First part\nSecond part" +@pytest.mark.parametrize("error_key", ["isError", "is_error"]) +def test_part_to_message_block_marks_mcp_error_result_as_error(error_key): + """An MCP CallToolResult with isError reaches Claude as an error result.""" + response_part = types.Part.from_function_response( + name="mcp_tool", + response={ + "content": [{"type": "text", "text": "file not found"}], + error_key: True, + }, + ) + response_part.function_response.id = "test_id_mcp_error" + + result = part_to_message_block(response_part) + + assert result["content"] == "file not found" + assert result["is_error"] is True + + def test_part_to_message_block_tool_result_with_image(): """Media a tool attached to its response reaches Claude as an image block.""" image_data = b"chart-bytes"