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
- 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'.
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
- 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.
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
- Failed to call MCP tool '{toolName}' on {serverUrl}: {messag
- HTTP %d error from MCP server: %s
- MCP server returned a redirect without a valid Location
- MCP server exceeded the redirect limit
- A2A stream to {endpoint} failed: {message}
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/677461b768cb38e9.
Report an issue: GitHub.