fix: route MCP tool errors as failed ToolReturnParts instead of raising ModelRetry (#5217)

Draft
SH
5mo ago
+103-58

Description

Replaces the rejected implementation from PR #5293 to align with maintainer feedback.

Previously, when a FastMCP or MCP tool encountered an error (isError=True), the framework raised a ToolError or ModelRetry. This forced the agent into an infinite retry loop for non-retryable tool failures (e.g., trying to read a missing file).

This PR alters the behavior so that tool errors are treated as informational context rather than framework crashes:

  1. mcp.py & fastmcp.py: Intercepts the error and returns a standard dictionary containing the error message, flagged internally with _tool_error=True. fastmcp.py now correctly honors the tool_error_behavior configuration.
  2. _agent_graph.py: Detects the internal _tool_error flag on the raw tool_result. It extracts the error message to keep the prompt clean, and constructs the ToolReturnPart with outcome='failed'.

This allows the LLM to receive the failure message directly in the conversation history, allowing it to adapt its strategy rather than triggering a framework-level retry loop.

Type Safety

Maintained strict mypy compliance by returning standard dicts that satisfy the Any return type on direct_call_tool. outcome='failed' aligns with the allowed literal values for BaseToolReturnPart.

Checklist

  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • No breaking changes in accordance with the version policy.
  • PR title is fit for the release changelog.