From a5108c96b40982286498cd1ad0e7101b51da467b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 17:11:44 -0800 Subject: [PATCH] fix: handle ToolError exceptions in MCP clients to reduce production alerts (#8599) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ToolError to exception handling alongside McpError in MCP client classes. ToolError is raised by fastmcp for input validation errors (e.g., missing required properties like 'filename'). Both error types are expected user-facing errors from external MCP servers and should be logged at warning/debug level to avoid triggering production alerts. Fixes issue with production error: "fastmcp.exceptions.ToolError: Input validation error: 'filename' is a required property" 🤖 Generated with [Letta Code](https://letta.com) Co-authored-by: letta-code <248085862+letta-code@users.noreply.github.com> Co-authored-by: Letta Co-authored-by: datadog-official[bot] Co-authored-by: Kian Jones <11655409+kianjones9@users.noreply.github.com> --- letta/services/mcp/base_client.py | 9 +++++---- letta/services/mcp/fastmcp_client.py | 10 ++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/letta/services/mcp/base_client.py b/letta/services/mcp/base_client.py index 28f541ca..d297e83f 100644 --- a/letta/services/mcp/base_client.py +++ b/letta/services/mcp/base_client.py @@ -81,10 +81,11 @@ class AsyncBaseMCPClient: try: result = await self.session.call_tool(tool_name, tool_args) except Exception as e: - if e.__class__.__name__ == "McpError": - # MCP errors are typically user-facing issues from external MCP servers - # (e.g., resource not found, invalid arguments, permission errors) - # Log at debug level to avoid triggering production alerts for expected failures + # ToolError is raised by fastmcp for input validation errors (e.g., missing required properties) + # McpError is raised for other MCP-related errors + # Both are expected user-facing issues from external MCP servers + # Log at debug level to avoid triggering production alerts for expected failures + if e.__class__.__name__ in ("McpError", "ToolError"): logger.debug(f"MCP tool '{tool_name}' execution failed: {str(e)}") raise diff --git a/letta/services/mcp/fastmcp_client.py b/letta/services/mcp/fastmcp_client.py index d6766803..54b7bf46 100644 --- a/letta/services/mcp/fastmcp_client.py +++ b/letta/services/mcp/fastmcp_client.py @@ -128,7 +128,10 @@ class AsyncFastMCPSSEClient: try: result = await self.client.call_tool(tool_name, tool_args) except Exception as e: - if e.__class__.__name__ == "McpError": + # ToolError is raised by fastmcp for input validation errors (e.g., missing required properties) + # McpError is raised for other MCP-related errors + # Both are expected user-facing errors from MCP tools, log at warning level + if e.__class__.__name__ in ("McpError", "ToolError"): logger.warning(f"MCP tool '{tool_name}' execution failed: {str(e)}") raise @@ -270,7 +273,10 @@ class AsyncFastMCPStreamableHTTPClient: try: result = await self.client.call_tool(tool_name, tool_args) except Exception as e: - if e.__class__.__name__ == "McpError": + # ToolError is raised by fastmcp for input validation errors (e.g., missing required properties) + # McpError is raised for other MCP-related errors + # Both are expected user-facing errors from MCP tools, log at warning level + if e.__class__.__name__ in ("McpError", "ToolError"): logger.warning(f"MCP tool '{tool_name}' execution failed: {str(e)}") raise