From 7106509ef059eaa86d6d1274b73d435819e3295b Mon Sep 17 00:00:00 2001 From: YutaSaito <36355491+uc4w6c@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:19:00 +0900 Subject: [PATCH] chore: add stack trace to mcp error message (#17269) --- .../mcp_server/rest_endpoints.py | 8 +++- .../mcp_tools/mcp_connection_status.tsx | 37 +++++++++++++++++-- .../src/components/networking.tsx | 3 +- .../src/hooks/useTestMCPConnection.tsx | 7 ++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py index d284e747364..6f293a298c3 100644 --- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py +++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py @@ -1,4 +1,5 @@ import importlib +import traceback from typing import Dict, List, Optional, Union from fastapi import APIRouter, Depends, Query, Request @@ -326,7 +327,12 @@ if MCP_AVAILABLE: except Exception as e: verbose_logger.error(f"Error in MCP operation: {e}", exc_info=True) - return {"status": "error", "message": "An internal error has occurred."} + stack_trace = traceback.format_exc() + return { + "status": "error", + "message": f"An internal error has occurred: {str(e)}", + "stack_trace": stack_trace, + } @router.post("/test/connection") async def test_connection( diff --git a/ui/litellm-dashboard/src/components/mcp_tools/mcp_connection_status.tsx b/ui/litellm-dashboard/src/components/mcp_tools/mcp_connection_status.tsx index 5e4c93e0cb7..601895d29d0 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/mcp_connection_status.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/mcp_connection_status.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { Button, Spin, Alert } from "antd"; +import { Button, Spin, Alert, Collapse } from "antd"; import { CheckCircleOutlined, ExclamationCircleOutlined, ReloadOutlined, ToolOutlined } from "@ant-design/icons"; import { Card, Title, Text } from "@tremor/react"; import { useTestMCPConnection } from "../../hooks/useTestMCPConnection"; @@ -12,7 +12,7 @@ interface MCPConnectionStatusProps { } const MCPConnectionStatus: React.FC = ({ accessToken, oauthAccessToken, formValues, onToolsLoaded }) => { - const { tools, isLoadingTools, toolsError, canFetchTools, fetchTools } = useTestMCPConnection({ + const { tools, isLoadingTools, toolsError, toolsErrorStackTrace, canFetchTools, fetchTools } = useTestMCPConnection({ accessToken, oauthAccessToken, formValues, @@ -95,7 +95,38 @@ const MCPConnectionStatus: React.FC = ({ accessToken, {toolsError && ( +
{toolsError}
+ {toolsErrorStackTrace && ( + + {toolsErrorStackTrace} + + ), + }, + ]} + style={{ marginTop: "12px" }} + /> + )} + + } type="error" showIcon action={ diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 0e45c0f3a91..c5dc3114ff6 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -5964,7 +5964,7 @@ export const listMCPTools = async (accessToken: string, serverId: string) => { throw new Error("Failed to fetch MCP tools"); } - // Return the full response object which includes tools, error, and message + // Return the full response object which includes tools, error, message, and stack_trace return data; } catch (error) { console.error("Failed to fetch MCP tools:", error); @@ -5973,6 +5973,7 @@ export const listMCPTools = async (accessToken: string, serverId: string) => { tools: [], error: "network_error", message: error instanceof Error ? error.message : "Failed to fetch MCP tools", + stack_trace: null, }; } }; diff --git a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx index a82bb2fa45b..7354b1a5364 100644 --- a/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx +++ b/ui/litellm-dashboard/src/hooks/useTestMCPConnection.tsx @@ -29,6 +29,7 @@ interface UseTestMCPConnectionReturn { tools: any[]; isLoadingTools: boolean; toolsError: string | null; + toolsErrorStackTrace: string | null; hasShownSuccessMessage: boolean; canFetchTools: boolean; fetchTools: () => Promise; @@ -44,6 +45,7 @@ export const useTestMCPConnection = ({ const [tools, setTools] = useState([]); const [isLoadingTools, setIsLoadingTools] = useState(false); const [toolsError, setToolsError] = useState(null); + const [toolsErrorStackTrace, setToolsErrorStackTrace] = useState(null); const [hasShownSuccessMessage, setHasShownSuccessMessage] = useState(false); // Check if we have the minimum required fields to fetch tools @@ -137,18 +139,21 @@ export const useTestMCPConnection = ({ if (toolsResponse.tools && !toolsResponse.error) { setTools(toolsResponse.tools); setToolsError(null); + setToolsErrorStackTrace(null); if (toolsResponse.tools.length > 0 && !hasShownSuccessMessage) { setHasShownSuccessMessage(true); } } else { const errorMessage = toolsResponse.message || "Failed to retrieve tools list"; setToolsError(errorMessage); + setToolsErrorStackTrace(toolsResponse.stack_trace || null); setTools([]); setHasShownSuccessMessage(false); } } catch (error) { console.error("Tools fetch error:", error); setToolsError(error instanceof Error ? error.message : String(error)); + setToolsErrorStackTrace(null); setTools([]); setHasShownSuccessMessage(false); } finally { @@ -159,6 +164,7 @@ export const useTestMCPConnection = ({ const clearTools = () => { setTools([]); setToolsError(null); + setToolsErrorStackTrace(null); setHasShownSuccessMessage(false); }; @@ -190,6 +196,7 @@ export const useTestMCPConnection = ({ tools, isLoadingTools, toolsError, + toolsErrorStackTrace, hasShownSuccessMessage, canFetchTools, fetchTools,