{"record":{"id":"677461b768cb38e9","repo":"conductor-oss/conductor","slug":"failed-to-list-mcp-tools-from-serverurl-messag","errorCode":null,"errorMessage":"Failed to list MCP tools from {serverUrl}: {message}","messagePattern":"Failed to list MCP tools from (.+?): (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java","lineNumber":186,"sourceCode":"            List<McpSchema.Tool> tools =\n                    objectMapper.convertValue(\n                            toolsNode,\n                            objectMapper\n                                    .getTypeFactory()\n                                    .constructCollectionType(List.class, McpSchema.Tool.class));\n\n            log.debug(\n                    \"Successfully listed {} tools via direct JSON-RPC from {}\",\n                    tools.size(),\n                    serverUrl);\n            return tools;\n\n        } catch (Exception e) {\n            log.error(\n                    \"Failed to list tools via direct JSON-RPC from {}: {}\",\n                    serverUrl,\n                    e.getMessage());\n            throw new RuntimeException(\n                    \"Failed to list MCP tools from \" + serverUrl + \": \" + e.getMessage(), e);\n        }\n    }\n\n    /** Calls a tool on an HTTP/HTTPS MCP server. */\n    private Map<String, Object> callToolHttp(\n            String serverUrl,\n            String toolName,\n            Map<String, Object> arguments,\n            Map<String, String> headers) {\n\n        // Use direct JSON-RPC since many MCP servers don't support full SDK\n        // initialization\n        log.debug(\"Calling tool '{}' on MCP server via direct JSON-RPC: {}\", toolName, serverUrl);\n        return callToolDirectHttp(serverUrl, toolName, arguments, headers);\n    }\n\n    /**","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java#L168-L204","documentation":"Catch-all wrapper thrown by listToolsDirectHttp's outer catch (Exception). It re-wraps ANY exception from the whole listTools flow (HTTP errors 160/161/162/163, SSE parse failures 169/170, redirect failures 171-173, payload-limit 175/176, or raw IOException from the HTTP client) with the serverUrl and original message, preserving the cause. This is the exception callers of listTools actually see for nearly every failure.","triggerScenarios":"Any uncaught exception inside the try block of listToolsDirectHttp: network timeout, DNS failure, SSL handshake error, JSON-RPC protocol error, redirect/CRLF/payload-limit violation, or one of errors 160-163/169/170.","commonSituations":"Server unreachable (DNS/connection refused); TLS misconfiguration; any of the inner errors above; the caller sees only this wrapper and must inspect getCause() for the real reason.","solutions":["Inspect e.getCause() and the inner message — the real failure is nested, not the wrapper text.","If the cause is a network/timeout error, retry with backoff or fix connectivity/DNS.","If the cause is one of errors 160-163/169-176, apply the fix documented for that specific error.","Add logging that unwraps the cause chain so operators see the root reason, not just 'Failed to list MCP tools'."],"exampleFix":"// before\ncatch (RuntimeException e) { log.error(\"list tools failed\", e); throw e; }\n// after\ncatch (RuntimeException e) {\n    Throwable root = e;\n    while (root.getCause() != null && root.getCause() != root) root = root.getCause();\n    log.error(\"list tools failed for {}: root cause: {}\", url, root.toString());\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    List<McpSchema.Tool> tools = mcpService.listTools(serverUrl, headers);\n} catch (RuntimeException e) {\n    Throwable root = e;\n    while (root.getCause() != null && root.getCause() != root) root = root.getCause();\n    log.error(\"listTools failed for {}: rootCause={}\", serverUrl, root.toString());\n    // decide retry vs fail based on root type (IOException -> retry; protocol -> fix config)\n    throw e;\n}","preventionTips":["Always unwrap the cause chain — the wrapper text is generic.","Classify root cause as network (retry), protocol (fix config), or security (investigate).","Surface root causes in dashboards, not the wrapper message."],"tags":["mcp","wrapper","network","error-handling","diagnostics"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}