{"record":{"id":"dd894c97b628e58f","repo":"koala73/worldmonitor","slug":"invalid-mcp-server-response","errorCode":null,"errorMessage":"Invalid MCP server response","messagePattern":"Invalid MCP server response","errorType":"exception","errorClass":"McpProxyUpstreamError","httpStatus":null,"severity":"error","filePath":"api/mcp-proxy.ts","lineNumber":556,"sourceCode":"      for (const line of lines) {\n        if (line.startsWith('data: ')) {\n          try {\n            const parsed = parseMcpProxyJson(line.slice(6));\n            if (parsed.result !== undefined || parsed.error !== undefined) return parsed;\n          } catch (error) {\n            if (error instanceof McpProxyJsonDepthError) throw error;\n          }\n        }\n      }\n      throw new McpProxyUpstreamError('No result found in SSE response');\n    }\n    return parseMcpProxyJson(text);\n  } catch (error) {\n    if (error instanceof McpProxyUpstreamError\n      || error instanceof ResponseBodyTooLargeError\n      || error instanceof McpProxyJsonDepthError\n      || proxyFailureFor(error).isTimeout) throw error;\n    throw new McpProxyUpstreamError('Invalid MCP server response', { cause: error });\n  }\n}\n\nasync function sendInitialized(serverUrl, headers, sessionId) {\n  try {\n    const { response } = await postJson(serverUrl, {\n      jsonrpc: '2.0',\n      method: 'notifications/initialized',\n      params: {},\n    }, headers, sessionId);\n    await cancelResponseBody(response);\n  } catch (error) {\n    if (error instanceof McpProxySsrfError) throw error;\n    /* non-fatal */\n  }\n}\n\nasync function mcpListTools(serverUrl, customHeaders) {","sourceCodeStart":538,"sourceCodeEnd":574,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/api/mcp-proxy.ts#L538-L574","documentation":"The MCP server answered the proxy's JSON-RPC request, but its body could not be parsed as a valid JSON-RPC message. parseJsonRpcResponse reads the (size-bounded) response body and hands it to the strict JSON parser; any parse failure that is not a size/depth/timeout condition is wrapped as McpProxyUpstreamError('Invalid MCP server response'). This guards callers against upstreams that return HTML error pages, truncated bodies, or non-JSON-RPC payloads.","triggerScenarios":"Thrown from parseJsonRpcResponse (called by initData, listData, callData) when the upstream HTTP 2xx body fails strict JSON parsing (parseMcpProxyJson throws), e.g. an HTML error page, an empty body, malformed JSON, or a body exceeding the configured JSON nesting depth when that error is not the dedicated McpProxyJsonDepthError path.","commonSituations":"Pointing the proxy at a regular web page or an endpoint that answers errors as HTML with status 200; a reverse proxy (Cloudflare, nginx) intercepting the request and returning a challenge/interstitial page; an MCP server that half-closes the connection mid-body leaving truncated JSON; a server that responds 200 with a non-JSONRPC JSON object.","solutions":["Verify the configured MCP server URL is the actual JSON-RPC/MCP endpoint and is reachable without an HTML interstitial (open it and check what it returns for a POST).","Check whether a proxy/WAF (Cloudflare, corporate proxy) is intercepting upstream responses and returning HTML challenge pages with 200 status; allowlist the upstream.","Inspect the cause attached to the McpProxyUpstreamError — it carries the underlying parse error identifying the malformed payload.","Confirm the upstream MCP server is healthy and running a spec-compliant transport; test with a raw curl POST of an initialize payload.","If bodies are large or deeply nested, confirm the response is under MAX_MCP_PROXY_RESPONSE_BYTES and within JSON depth limits."],"exampleFix":"// before: pointing at a landing page\nconst url = 'https://mcp.example.com/';\n// after: point at the MCP JSON-RPC endpoint\nconst url = 'https://mcp.example.com/mcp';","handlingStrategy":"try-catch","validationCode":"const u = new URL(serverUrl);\nif (u.protocol !== 'https:') throw new Error('MCP server URL must be https');\n// a quick probe that the endpoint answers JSON, not HTML:\nconst probe = await fetch(u, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 0, method: 'ping' }) });\nconst ct = probe.headers.get('content-type') || '';\nif (!ct.includes('json') && !ct.includes('event-stream')) throw new Error('Endpoint did not return JSON: ' + ct);","typeGuard":null,"tryCatchPattern":"try {\n  const tools = await mcpProxy.tools(serverUrl);\n} catch (error) {\n  if (error instanceof McpProxyUpstreamError && error.message === 'Invalid MCP server response') {\n    console.error('Upstream returned a non-JSON-RPC body', { cause: error.cause });\n    return { tools: [], degraded: true };\n  }\n  throw error;\n}","preventionTips":["Always configure the direct JSON-RPC/MCP endpoint URL, not a landing page or docs route.","Allowlist the upstream in any WAF/CDN so challenge pages never masquerade as 200 JSON responses.","Smoke-test the server with a raw curl initialize POST before wiring it into the dashboard.","Monitor the error's cause field to distinguish HTML interstitials from truncated bodies."],"tags":["mcp","json-rpc","upstream","json-parsing","network"],"backgroundTag":"invalid-json-response","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}