@@ -4,20 +4,25 @@ import (
44 "context"
55 "encoding/json"
66 "fmt"
7+ "regexp"
8+ "strconv"
79 "strings"
810 "time"
911
10- "github.com/mark3labs/mcp-go/mcp"
11- mcpserver "github.com/mark3labs/mcp-go/server"
12- "go.uber.org/zap"
13-
1412 "mcpproxy-go/internal/cache"
1513 "mcpproxy-go/internal/config"
1614 "mcpproxy-go/internal/index"
1715 "mcpproxy-go/internal/logs"
1816 "mcpproxy-go/internal/storage"
17+ "mcpproxy-go/internal/transport"
1918 "mcpproxy-go/internal/truncate"
2019 "mcpproxy-go/internal/upstream"
20+
21+ "errors"
22+
23+ "github.com/mark3labs/mcp-go/mcp"
24+ mcpserver "github.com/mark3labs/mcp-go/server"
25+ "go.uber.org/zap"
2126)
2227
2328const (
@@ -430,7 +435,7 @@ func (p *MCPProxyServer) handleCallTool(ctx context.Context, request mcp.CallToo
430435 zap .String ("server_name" , serverName ),
431436 zap .String ("actual_tool" , actualToolName ))
432437
433- return mcp . NewToolResultError (err . Error () ), nil
438+ return p . createDetailedErrorResponse (err , serverName , actualToolName ), nil
434439 }
435440
436441 // Increment usage stats
@@ -1398,6 +1403,150 @@ func (p *MCPProxyServer) handleTailLog(_ context.Context, request mcp.CallToolRe
13981403 return mcp .NewToolResultText (string (jsonResult )), nil
13991404}
14001405
1406+ // createDetailedErrorResponse creates an enhanced error response with HTTP and troubleshooting context
1407+ func (p * MCPProxyServer ) createDetailedErrorResponse (err error , serverName , toolName string ) * mcp.CallToolResult {
1408+ // Try to extract HTTP error details
1409+ var httpErr * transport.HTTPError
1410+ var jsonRPCErr * transport.JSONRPCError
1411+
1412+ // Check if it's our enhanced error types
1413+ if errors .As (err , & httpErr ) {
1414+ // We have HTTP error details
1415+ errorDetails := map [string ]interface {}{
1416+ "error" : httpErr .Error (),
1417+ "http_details" : map [string ]interface {}{
1418+ "status_code" : httpErr .StatusCode ,
1419+ "response_body" : httpErr .Body ,
1420+ "server_url" : httpErr .URL ,
1421+ "method" : httpErr .Method ,
1422+ },
1423+ "troubleshooting" : p .generateTroubleshootingAdvice (httpErr .StatusCode , httpErr .Body ),
1424+ }
1425+
1426+ jsonResponse , _ := json .Marshal (errorDetails )
1427+ return mcp .NewToolResultError (string (jsonResponse ))
1428+ }
1429+
1430+ if errors .As (err , & jsonRPCErr ) {
1431+ // We have JSON-RPC error details
1432+ errorDetails := map [string ]interface {}{
1433+ "error" : jsonRPCErr .Message ,
1434+ "error_code" : jsonRPCErr .Code ,
1435+ "error_data" : jsonRPCErr .Data ,
1436+ }
1437+
1438+ if jsonRPCErr .HTTPError != nil {
1439+ errorDetails ["http_details" ] = map [string ]interface {}{
1440+ "status_code" : jsonRPCErr .HTTPError .StatusCode ,
1441+ "response_body" : jsonRPCErr .HTTPError .Body ,
1442+ "server_url" : jsonRPCErr .HTTPError .URL ,
1443+ }
1444+ errorDetails ["troubleshooting" ] = p .generateTroubleshootingAdvice (jsonRPCErr .HTTPError .StatusCode , jsonRPCErr .HTTPError .Body )
1445+ }
1446+
1447+ jsonResponse , _ := json .Marshal (errorDetails )
1448+ return mcp .NewToolResultError (string (jsonResponse ))
1449+ }
1450+
1451+ // Extract status codes and helpful info from error message for enhanced responses
1452+ errStr := err .Error ()
1453+ if strings .Contains (errStr , "status code" ) || strings .Contains (errStr , "HTTP" ) {
1454+ // Try to extract HTTP status code for troubleshooting advice
1455+ statusCode := p .extractStatusCodeFromError (errStr )
1456+
1457+ errorDetails := map [string ]interface {}{
1458+ "error" : errStr ,
1459+ "server_name" : serverName ,
1460+ "tool_name" : toolName ,
1461+ }
1462+
1463+ if statusCode > 0 {
1464+ errorDetails ["http_status" ] = statusCode
1465+ errorDetails ["troubleshooting" ] = p .generateTroubleshootingAdvice (statusCode , errStr )
1466+ }
1467+
1468+ jsonResponse , _ := json .Marshal (errorDetails )
1469+ return mcp .NewToolResultError (string (jsonResponse ))
1470+ }
1471+
1472+ // Fallback to enhanced error message
1473+ errorDetails := map [string ]interface {}{
1474+ "error" : errStr ,
1475+ "server_name" : serverName ,
1476+ "tool_name" : toolName ,
1477+ "troubleshooting" : "Check server configuration, connectivity, and authentication credentials" ,
1478+ }
1479+
1480+ jsonResponse , _ := json .Marshal (errorDetails )
1481+ return mcp .NewToolResultError (string (jsonResponse ))
1482+ }
1483+
1484+ // extractStatusCodeFromError attempts to extract HTTP status code from error message
1485+ func (p * MCPProxyServer ) extractStatusCodeFromError (errStr string ) int {
1486+ // Common patterns for status codes in error messages
1487+ patterns := []string {
1488+ `status code (\d+)` ,
1489+ `HTTP (\d+)` ,
1490+ `(\d+) [A-Za-z\s]+$` , // "400 Bad Request" pattern
1491+ }
1492+
1493+ for _ , pattern := range patterns {
1494+ if matches := regexp .MustCompile (pattern ).FindStringSubmatch (errStr ); len (matches ) > 1 {
1495+ if code , err := strconv .Atoi (matches [1 ]); err == nil {
1496+ return code
1497+ }
1498+ }
1499+ }
1500+
1501+ return 0
1502+ }
1503+
1504+ // generateTroubleshootingAdvice provides specific troubleshooting advice based on HTTP status codes and error content
1505+ func (p * MCPProxyServer ) generateTroubleshootingAdvice (statusCode int , errorBody string ) string {
1506+ switch statusCode {
1507+ case 400 :
1508+ if strings .Contains (strings .ToLower (errorBody ), "api key" ) || strings .Contains (strings .ToLower (errorBody ), "key" ) {
1509+ return "Check API key configuration. Ensure the API key is correctly set in server environment variables or configuration."
1510+ }
1511+ if strings .Contains (strings .ToLower (errorBody ), "auth" ) {
1512+ return "Authentication issue. Verify authentication credentials and configuration."
1513+ }
1514+ return "Bad request. Check tool parameters, API endpoint configuration, and request format."
1515+
1516+ case 401 :
1517+ return "Authentication required. Check API keys, tokens, or authentication credentials in server configuration."
1518+
1519+ case 403 :
1520+ return "Access forbidden. Verify API key permissions, user authorization, or check if the service requires additional authentication."
1521+
1522+ case 404 :
1523+ return "Resource not found. Check API endpoint URL, server configuration, or verify the requested resource exists."
1524+
1525+ case 429 :
1526+ return "Rate limit exceeded. Wait before retrying or check if you need a higher rate limit plan."
1527+
1528+ case 500 :
1529+ return "Internal server error. The upstream service is experiencing issues. Try again later or contact the service provider."
1530+
1531+ case 502 , 503 , 504 :
1532+ return "Service unavailable or timeout. The upstream service may be down or overloaded. Check service status and try again later."
1533+
1534+ default :
1535+ if strings .Contains (strings .ToLower (errorBody ), "api key" ) {
1536+ return "API key issue detected. Check environment variables and server configuration for correct API key setup."
1537+ }
1538+ if strings .Contains (strings .ToLower (errorBody ), "timeout" ) {
1539+ return "Request timeout. The server may be slow or overloaded. Check network connectivity and server responsiveness."
1540+ }
1541+ if strings .Contains (strings .ToLower (errorBody ), "connection" ) {
1542+ return "Connection issue. Check network connectivity, server URL, and firewall settings."
1543+ }
1544+ return "Check server configuration, network connectivity, and authentication settings. Review server logs for more details."
1545+ }
1546+ }
1547+
1548+ // getServerErrorContext extracts relevant context information for error reporting
1549+
14011550// GetMCPServer returns the underlying MCP server for serving
14021551func (p * MCPProxyServer ) GetMCPServer () * mcpserver.MCPServer {
14031552 return p .server
0 commit comments