diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 51cc568653c..2b808e3aedf 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -5368,28 +5368,15 @@ def _extract_response_obj_and_hidden_params( return response_obj, hidden_params -def _extract_mcp_tool_error_message(response_obj: dict) -> Optional[str]: - """Return an upstream MCP tool error message when result.isError is true.""" +MCP_TOOL_ERROR_LOGGING_MESSAGE = "MCP tool returned isError=true" + + +def _has_mcp_tool_error(response_obj: dict) -> bool: + """Return True when an MCP tool result reports isError.""" result_obj = response_obj.get("result", response_obj) if not isinstance(result_obj, dict) or result_obj.get("isError") is not True: - return None - - content = result_obj.get("content") - messages: List[str] = [] - if isinstance(content, list): - for item in content: - if isinstance(item, dict): - text = item.get("text") or item.get("message") - if isinstance(text, str) and text.strip(): - messages.append(text.strip()) - elif isinstance(item, str) and item.strip(): - messages.append(item.strip()) - elif isinstance(content, str) and content.strip(): - messages.append(content.strip()) - - if messages: - return " ".join(messages) - return "MCP tool returned isError=true" + return False + return True def _get_mcp_tool_call_logging_status( @@ -5406,8 +5393,7 @@ def _get_mcp_tool_call_logging_status( if call_type != CallTypes.call_mcp_tool.value: return call_type, status, error_str, None - mcp_tool_error_message = _extract_mcp_tool_error_message(response_obj) - if mcp_tool_error_message is None: + if _has_mcp_tool_error(response_obj) is False: return call_type, status, error_str, None error_information = StandardLoggingPayloadErrorInformation( @@ -5415,9 +5401,14 @@ def _get_mcp_tool_call_logging_status( error_class="MCPToolError", llm_provider="mcp", traceback="", - error_message=mcp_tool_error_message, + error_message=MCP_TOOL_ERROR_LOGGING_MESSAGE, + ) + return ( + call_type, + "failure", + error_str or MCP_TOOL_ERROR_LOGGING_MESSAGE, + error_information, ) - return call_type, "failure", error_str or mcp_tool_error_message, error_information def get_standard_logging_object_payload( diff --git a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py index 717c61c8e68..78e50aadf23 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -2729,12 +2729,12 @@ def test_mcp_tool_is_error_marks_standard_logging_payload_as_failure(logging_obj assert payload is not None assert payload["status"] == "failure" assert payload["status_fields"]["llm_api_status"] == "failure" - assert payload["error_str"] == "Input validation error: 'query' is required" + assert payload["error_str"] == "MCP tool returned isError=true" assert payload["error_information"] is not None assert payload["error_information"]["error_class"] == "MCPToolError" assert ( payload["error_information"]["error_message"] - == "Input validation error: 'query' is required" + == "MCP tool returned isError=true" ) assert payload["metadata"]["mcp_tool_call_metadata"] is not None assert ( @@ -2766,7 +2766,7 @@ def test_mcp_tool_json_rpc_is_error_envelope_marks_payload_as_failure(logging_ob "content": [ { "type": "text", - "text": "Permission denied for repository", + "text": "Permission denied for repository sk-live-secret", } ], "isError": True, @@ -2784,8 +2784,11 @@ def test_mcp_tool_json_rpc_is_error_envelope_marks_payload_as_failure(logging_ob assert payload["error_information"]["error_class"] == "MCPToolError" assert ( payload["error_information"]["error_message"] - == "Permission denied for repository" + == "MCP tool returned isError=true" ) + assert payload["error_str"] == "MCP tool returned isError=true" + assert "sk-live-secret" not in payload["error_str"] + assert "sk-live-secret" not in payload["error_information"]["error_message"] def test_mcp_tool_success_keeps_standard_logging_payload_success(logging_obj):