Kuberwastaken/claurst · error
MCP server ' ': legacy SSE POST request failed
Error message
MCP server '{}': legacy SSE POST request failed: {} What it means
This error wraps a reqwest transport failure that occurred while POSTing a JSON-RPC message to a legacy (HTTP+SSE) MCP server's message endpoint. It is raised in the `send` implementation of `LegacySseRmcpTransport`, which serializes the outgoing rmcp message as JSON and sends it to the previously discovered post endpoint. Any error at the HTTP layer (DNS, connect, TLS, timeout, body encode, request build) is wrapped with the MCP server name so the operator can identify which configured server is unreachable. The inner reqwest error is preserved in the message, so the actual root cause (e.g. 'connection refused', 'timed out') appears after the colon.
Solutions
- Verify the MCP server is running and reachable: `curl -v <post-endpoint-URL>` from the same host.
- Check the mcpServers config entry (command/args or url) for a wrong host, port, or path.
- If the server was restarted, reconnect — the discovered post endpoint may be stale.
- Inspect the inner reqwest error text in the message (connection refused / timed out / dns error) to pick the right fix.
- If timeouts are the cause, increase the reqwest client timeout or check server load/proxy settings.
Example fix
// before: transport constructed with a hardcoded port that nothing listens on
let backend = RmcpClientBackend::connect_legacy_sse("http://127.0.0.1:9999/sse", ...).await?;
// after: start the MCP server first and derive the port/URL from its startup log
let server = spawn_mcp_server(port_file)?;
let port = read_port(port_file).await?;
let backend = RmcpClientBackend::connect_legacy_sse(&format!("http://127.0.0.1:{port}/sse"), ...).await?; Defensive patterns
Strategy: retry
Validate before calling
async fn mcp_server_reachable(sse_url: &str) -> bool {
reqwest::get(sse_url).await.map(|r| r.status().is_success()).unwrap_or(false)
} Try / catch
match backend.list_tools().await {
Ok(t) => t,
Err(e) if e.to_string().contains("legacy SSE POST request failed") => {
eprintln!("MCP server unreachable, check config/process: {e}");
Vec::new()
}
Err(e) => return Err(e),
} Prevention
- Health-check the SSE endpoint before establishing the backend session.
- Reconnect (rebuild the backend) whenever the MCP server process restarts; never reuse stale endpoints.
- Keep MCP server URLs in config with explicit host/port and validate them at startup.
- Set a sane reqwest timeout so hangs surface as timeouts rather than indefinite stalls.
When it happens
Trigger: Calling any MCP operation through a backend configured with the legacy SSE transport when `request.send().await` fails: server process is down, wrong host/port in the MCP server config, the SSE-post endpoint URL is stale after a server restart, TLS certificate problems, or the network drops mid-request.
Common situations: MCP server binary not running or crashed; `mcpServers` entry points at a port nothing listens on; server restarted and returned a different message endpoint; corporate proxy or firewall blocking the connection; request timeout because the server is overloaded and slow to accept POSTs.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- MCP server ' ': legacy SSE stream returned HTTP
- MCP server ' ' connection failed
- failed to resolve legacy SSE endpoint
- MCP server ' ': failed to read legacy SSE HTTP response body
- legacy SSE endpoint event did not include a POST endpoint
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/68ac458f3313dcf8.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/mcp/src/rmcp_backend.rs:388
anyhow::anyhow!(
"MCP server '{}': legacy SSE POST endpoint has not been discovered",
server_name
)
})?;
let mut request = client
.post(endpoint)
.header(reqwest::header::CONTENT_TYPE, "application/json")
.json(&item);
if let Some(token) = &auth_token {
request = request.header(
reqwest::header::AUTHORIZATION,
transport::bearer_header_value(token)?,
);
}
let response = request.send().await.map_err(|e| {
anyhow::anyhow!(
"MCP server '{}': legacy SSE POST request failed: {}",
server_name,
e
)
})?;
handle_legacy_sse_http_response(server_name, response, incoming_tx, background_tasks)
.await
.map_err(Into::into)
}
}
fn receive(
&mut self,
) -> impl std::future::Future<Output = Option<rmcp::service::RxJsonRpcMessage<RoleClient>>> + Send {
let incoming_rx = Arc::clone(&self.incoming_rx);
async move {
let mut rx = incoming_rx.lock().await;View on GitHub (pinned to b0637c97ec)