conductor-oss/conductor · error · RuntimeException
Failed to list MCP tools from
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.
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
- HTTP error from MCP server
- MCP server exceeded the redirect limit
- MCP server returned a redirect without a valid Location
- A2A call ' ' to failed
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/677461b768cb38e9.
Report an issue: GitHub.
Appendix: 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)