conductor-oss/conductor · error · RuntimeException

Failed to list MCP tools from {serverUrl}: {message}

Error message

Failed to list MCP tools from {serverUrl}: {message}

What it means

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.

Source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java:186

            List<McpSchema.Tool> tools =
                    objectMapper.convertValue(
                            toolsNode,
                            objectMapper
                                    .getTypeFactory()
                                    .constructCollectionType(List.class, McpSchema.Tool.class));

            log.debug(
                    "Successfully listed {} tools via direct JSON-RPC from {}",
                    tools.size(),
                    serverUrl);
            return tools;

        } catch (Exception e) {
            log.error(
                    "Failed to list tools via direct JSON-RPC from {}: {}",
                    serverUrl,
                    e.getMessage());
            throw new RuntimeException(
                    "Failed to list MCP tools from " + serverUrl + ": " + e.getMessage(), e);
        }
    }

    /** Calls a tool on an HTTP/HTTPS MCP server. */
    private Map<String, Object> callToolHttp(
            String serverUrl,
            String toolName,
            Map<String, Object> arguments,
            Map<String, String> headers) {

        // Use direct JSON-RPC since many MCP servers don't support full SDK
        // initialization
        log.debug("Calling tool '{}' on MCP server via direct JSON-RPC: {}", toolName, serverUrl);
        return callToolDirectHttp(serverUrl, toolName, arguments, headers);
    }

    /**

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Inspect e.getCause() and the inner message — the real failure is nested, not the wrapper text.
  2. If the cause is a network/timeout error, retry with backoff or fix connectivity/DNS.
  3. If the cause is one of errors 160-163/169-176, apply the fix documented for that specific error.
  4. Add logging that unwraps the cause chain so operators see the root reason, not just 'Failed to list MCP tools'.

Example fix

// before
catch (RuntimeException e) { log.error("list tools failed", e); throw e; }
// after
catch (RuntimeException e) {
    Throwable root = e;
    while (root.getCause() != null && root.getCause() != root) root = root.getCause();
    log.error("list tools failed for {}: root cause: {}", url, root.toString());
    throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    List<McpSchema.Tool> tools = mcpService.listTools(serverUrl, headers);
} catch (RuntimeException e) {
    Throwable root = e;
    while (root.getCause() != null && root.getCause() != root) root = root.getCause();
    log.error("listTools failed for {}: rootCause={}", serverUrl, root.toString());
    // decide retry vs fail based on root type (IOException -> retry; protocol -> fix config)
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/677461b768cb38e9. Report an issue: GitHub.