Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/strands/tools/mcp/mcp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,8 @@ def _handle_tool_result(self, tool_use_id: str, call_tool_result: MCPCallToolRes
result["structuredContent"] = call_tool_result.structuredContent
if call_tool_result.meta:
result["metadata"] = call_tool_result.meta
if call_tool_result.isError:
Comment thread
mkmeral marked this conversation as resolved.
Outdated
result["isError"] = True

return result

Expand Down
6 changes: 6 additions & 0 deletions src/strands/tools/mcp/mcp_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ class MCPToolResult(ToolResult):
metadata: Optional arbitrary metadata returned by the MCP tool. This field allows
MCP servers to attach custom metadata to tool results (e.g., token usage,
performance metrics, or business-specific tracking information).
isError: Whether the MCP tool reported an application-level error via
``CallToolResult.isError``. ``True`` means the tool executed but its logic
returned a failure. Absent when the tool succeeded or when the error was a
protocol/client exception rather than a tool-reported failure, letting
callers distinguish application errors from transport/protocol errors.
Comment thread
mkmeral marked this conversation as resolved.
"""

structuredContent: NotRequired[dict[str, Any]]
metadata: NotRequired[dict[str, Any]]
isError: NotRequired[bool]
Comment thread
mkmeral marked this conversation as resolved.
13 changes: 13 additions & 0 deletions tests/strands/tools/mcp/test_mcp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ def test_call_tool_sync_status(mock_transport, mock_session, is_error, expected_
assert result["content"][0]["text"] == "Test message"
# No structured content should be present when not provided by MCP
assert result.get("structuredContent") is None
# isError flag should be True only when the tool reported an application error
assert result.get("isError") == (True if is_error else None)
Comment thread
mkmeral marked this conversation as resolved.
Outdated


def test_call_tool_sync_session_not_active():
Expand Down Expand Up @@ -261,6 +263,8 @@ async def mock_awaitable():
assert result["toolUseId"] == "test-123"
assert len(result["content"]) == 1
assert result["content"][0]["text"] == "Test message"
# isError flag should be True only when the tool reported an application error
assert result.get("isError") == (True if is_error else None)


@pytest.mark.asyncio
Expand Down Expand Up @@ -408,6 +412,15 @@ def test_mcp_tool_result_type():

assert result_with_structured["structuredContent"] == {"key": "value"}

# isError is optional — absent by default
assert "isError" not in result or result.get("isError") is None

# isError can be set to flag tool-reported application errors
result_with_is_error = MCPToolResult(
status="error", toolUseId="test-789", content=[{"text": "Tool failed"}], isError=True
)
assert result_with_is_error["isError"] is True
Comment thread
mkmeral marked this conversation as resolved.


def test_call_tool_sync_without_structured_content(mock_transport, mock_session):
"""Test that call_tool_sync works correctly when no structured content is provided."""
Expand Down