fix(core): handle ExceptionGroup-wrapped ToolError and McpError in MCP tool execution (#9328)
* fix(core): handle ExceptionGroup-wrapped ToolError and McpError in MCP tool execution Fixes 3 related Datadog bugs (all fastmcp.exceptions.ToolError): - 75d43daa-ff04-11f0-81b2-da7ad0900000 - 7af6373e-0080-11f1-9855-da7ad0900000 - a322edc8-fffa-11f0-b26c-da7ad0900000 These errors were caused by ToolError and McpError exceptions bubbling up unhandled from the MCP REST endpoint. This fix combines the approaches from PRs #9320 and #9321: 1. Handle ExceptionGroup wrapping (Python 3.11+ async TaskGroup) 2. Check for ToolError by class name to handle module variations 3. Convert ToolError to LettaInvalidArgumentError for proper client response 4. Catch McpError and return HTTP 500 with proper error message Issue-IDs: 75d43daa-ff04-11f0-81b2-da7ad0900000, 7af6373e-0080-11f1-9855-da7ad0900000, a322edc8-fffa-11f0-b26c-da7ad0900000 🐾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta <noreply@letta.com> * fix: return 422 instead of 500 for McpError (user config issue) * fix: use LettaMCPConnectionError instead of HTTPException for McpError --------- Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Literal, Optional, Union
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Query, Request
|
||||
from fastmcp.exceptions import ToolError as FastMCPToolError
|
||||
from httpx import ConnectError, HTTPStatusError
|
||||
from mcp.shared.exceptions import McpError
|
||||
from pydantic import BaseModel, Field
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
@@ -822,8 +823,21 @@ async def execute_mcp_tool(
|
||||
# Execute the tool
|
||||
try:
|
||||
result, success = await client.execute_tool(tool_name, request.args)
|
||||
except FastMCPToolError as e:
|
||||
raise LettaInvalidArgumentError(f"Invalid arguments for MCP tool '{tool_name}': {str(e)}", argument_name="args")
|
||||
except Exception as e:
|
||||
# Handle ExceptionGroup wrapping (Python 3.11+ async TaskGroup can wrap exceptions)
|
||||
exception_to_check = e
|
||||
if hasattr(e, "exceptions") and e.exceptions:
|
||||
if len(e.exceptions) == 1:
|
||||
exception_to_check = e.exceptions[0]
|
||||
|
||||
# Check by class name to handle both fastmcp.exceptions.ToolError and potential module variations
|
||||
if exception_to_check.__class__.__name__ == "ToolError":
|
||||
raise LettaInvalidArgumentError(
|
||||
f"Invalid arguments for MCP tool '{tool_name}': {str(exception_to_check)}", argument_name="args"
|
||||
)
|
||||
elif isinstance(exception_to_check, McpError):
|
||||
raise LettaMCPConnectionError(f"MCP tool execution failed: {str(exception_to_check)}", server_name=tool_name)
|
||||
raise
|
||||
|
||||
return {
|
||||
"result": result,
|
||||
|
||||
Reference in New Issue
Block a user