koala73/worldmonitor · error · McpProxyUpstreamError
MCP server request failed
Error message
MCP server request failed
What it means
fetchMcpUpstream wraps the proxy's upstream fetch and converts network-level rejections into McpProxyUpstreamError('MCP server request failed', { cause }). Timeouts are rethrown as-is (isTimeout check) so timeout handling stays distinct; everything else — DNS failure, connection refused, TLS error, abort mid-connect — is wrapped with a generic message.
Solutions
- Check the error.cause to identify the root cause (ECONNREFUSED, ENOTFOUND, TLS alert, etc.)
- Verify the MCP server is running and reachable at the configured serverUrl/port from the proxy's network
- Test with curl from the same network; fix DNS, firewall, or TLS configuration
- If it is a timeout, handle it via the timeout path (the original error is preserved)
Example fix
// before
const upstream = await fetchMcpUpstream(url, init); // throws opaque McpProxyUpstreamError
// after
try { return await fetchMcpUpstream(url, init); }
catch (e) { if (e instanceof McpProxyUpstreamError) log.error('mcp upstream', e.cause); throw e; } Defensive patterns
Strategy: try-catch
Validate before calling
// before configuring the proxy
const u = new URL(serverUrl); if (u.protocol !== 'https:' && u.hostname !== 'localhost') throw new Error('https required for remote MCP servers'); Type guard
null
Try / catch
try { return await fetchMcpUpstream(url, init); } catch (e) { if (e instanceof McpProxyUpstreamError) { log('mcp upstream failed', { cause: e.cause }); return new Response(JSON.stringify({ error: 'upstream_unreachable' }), { status: 502 }); } throw e; } Prevention
- Health-check the MCP server URL at registration time
- Log e.cause — the wrapper message is intentionally generic
- Verify the MCP server is reachable from the proxy's network (edge != localhost)
- Use TLS-valid certificates; self-signed certs surface here
When it happens
Trigger: POST/GET through /api/mcp-proxy where the configured serverUrl host is unreachable: wrong port, server down, TLS misconfiguration, IPv6-only host on IPv4 network, or the connection resets during the request. Inspect error.cause for the underlying reason.
Common situations: MCP server not running or crashed; serverUrl pointing at localhost from a deployed edge function; firewall blocking egress; self-signed/expired certificates.
Related errors
- HTTP ${resp.status}
- HTTP ${resp.status}
- HTTP ${res.status}
- serverUrl DNS resolution failed
- Invalid MCP server response
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/1905f1b3842a17c3.
Report an issue: GitHub.
Appendix: source
Thrown at api/mcp-proxy.ts:271
const isTimeout = (error instanceof Error && error.name === 'TimeoutError')
|| message.includes('TimeoutError')
|| message.includes('timed out');
const isExpectedExternal = error instanceof McpProxyUpstreamError
|| error instanceof McpProxySsrfError
|| error instanceof ResponseBodyTooLargeError
|| error instanceof McpProxyJsonDepthError;
return {
isTimeout,
level: isTimeout || isExpectedExternal ? 'warning' : 'error',
};
}
async function fetchMcpUpstream(input, init) {
try {
return await fetch(input, init);
} catch (error) {
if (proxyFailureFor(error).isTimeout) throw error;
throw new McpProxyUpstreamError('MCP server request failed', { cause: error });
}
}
// Generic message surfaced to the caller when a serverUrl resolves to a
// private/reserved address. The specific blocked IP is deliberately NOT echoed
// back: returning it turns the proxy into an address oracle (the caller could
// enumerate internal IPs by observing which hostnames get blocked). SSRF review
// finding — log the concrete IP server-side for debugging, tell the caller only
// that the host is disallowed.
const SSRF_BLOCKED_PUBLIC_MESSAGE = 'serverUrl host is not allowed';
function throwBlockedAddress(blockedAddress) {
// Server-side audit/debug log with the concrete blocked address. This is the
// only place the resolved internal IP appears; it never reaches the response.
console.error('[mcp-proxy]', {
event: 'mcp_proxy_ssrf_blocked',
ts: new Date().toISOString(),
blocked_address: blockedAddress,View on GitHub (pinned to 7d06c8633d)