conductor-oss/conductor · error · RuntimeException

HTTP %d error from MCP server: %s

Error message

HTTP %d error from MCP server: %s

What it means

Thrown by MCPService.listToolsDirectHttp when the remote MCP server returns an HTTP status outside the 200-299 success range for a tools/list JSON-RPC POST. The %d is the status code and %s is the raw response body (read and size-bounded first). It signals the server rejected the request at the transport layer before any JSON-RPC semantics apply.

Source

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

            request.put("id", 1);

            Request.Builder requestBuilder =
                    new Request.Builder()
                            .url(serverUrl)
                            .post(
                                    RequestBody.create(
                                            objectMapper.writeValueAsString(request),
                                            MediaType.get("application/json")))
                            .header("Content-Type", "application/json")
                            .header("Accept", "application/json, text/event-stream");

            // Add custom headers
            addHeaders(requestBuilder, headers);

            ResponsePayload response = execute(requestBuilder.build());
            // Check response status
            if (response.statusCode < 200 || response.statusCode >= 300) {
                throw new RuntimeException(
                        String.format(
                                "HTTP %d error from MCP server: %s",
                                response.statusCode, response.body));
            }

            // Get response body and content type
            String responseBody = response.body;
            String contentType = response.contentType;

            // Parse response based on content type
            JsonNode responseJson;
            if (contentType != null && contentType.contains("text/event-stream")) {
                // Parse SSE format
                responseJson = parseSseResponse(responseBody);
            } else {
                // Parse as JSON directly
                responseJson = objectMapper.readTree(responseBody);
            }

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Verify serverUrl points at the MCP server's streamable-HTTP JSON-RPC endpoint, not a browser or SSE-only URL.
  2. Confirm the headers map contains the auth the server expects (Bearer token, API key) and that it is not expired.
  3. Reproduce with curl: POST the same tools/list JSON-RPC body with the same headers and inspect the status/body.
  4. If the server requires MCP initialize, use a client/transport that performs the handshake instead of this raw-JSON-RPC path.

Example fix

// before
List<McpSchema.Tool> tools = mcpService.listTools(url, headers);
// after
if (headers == null || headers.get("Authorization") == null) {
    throw new IllegalStateException("MCP server requires Authorization; header missing");
}
List<McpSchema.Tool> tools;
try {
    tools = mcpService.listTools(url, headers);
} catch (RuntimeException e) {
    throw new RuntimeException("tools/list failed for " + url + " (check URL/auth): " + e.getMessage(), e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the URL is a plausible MCP JSON-RPC endpoint before calling.
java.net.URI u = java.net.URI.create(serverUrl);
if (!u.getScheme().matches("https?") || u.getHost() == null) {
    throw new IllegalArgumentException("Invalid MCP server URL: " + serverUrl);
}

Try / catch

try {
    List<McpSchema.Tool> tools = mcpService.listTools(serverUrl, headers);
} catch (RuntimeException e) {
    // Message contains 'HTTP <code> error'; parse the code for retry/backoff decisions.
    if (e.getMessage().contains("HTTP 5") || e.getMessage().contains("HTTP 429")) {
        // transient: retry with backoff
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling MCPService.listTools(serverUrl, headers) against an MCP endpoint that responds 4xx/5xx (e.g. 401 Unauthorized when headers lack a valid token, 404 for a wrong URL, 500 from a crashed server, 413 if the request itself is large). Also a server that requires the full MCP initialize handshake and returns 400 to the raw JSON-RPC call this code makes.

Common situations: Wrong base URL (pointed at an HTML page or the SSE endpoint instead of the streamable-HTTP endpoint); missing or expired Authorization header; MCP server behind a gateway that returns 403; server that only accepts the initialized session and rejects bare tools/list.

Related errors


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