koala73/worldmonitor · error · McpProxyUpstreamError

tools/list failed: HTTP

Error message

tools/list failed: HTTP ${listResp.status}

What it means

Thrown by mcpListTools after a successful initialize when the follow-up tools/list request to the MCP server returned a non-2xx HTTP status. The session was established but the tools enumeration call was rejected (expired/missing Mcp-Session-Id, auth, or upstream failure).

Solutions

  1. Check the returned status: a 404 often means the Mcp-Session-Id expired or was not forwarded
  2. Ensure custom headers required by the server are applied to every request, not just initialize
  3. Retry the whole connect-and-list flow to obtain a fresh session
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at api/mcp-proxy.ts:320 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-15). Data as JSON: /api/errors/f3c36082aba08d8c. Report an issue: GitHub.

Appendix: source

Thrown at api/mcp-proxy.ts:587

  } catch (error) {
    if (error instanceof McpProxySsrfError) throw error;
    /* non-fatal */
  }
}

async function mcpListTools(serverUrl, customHeaders) {
  const { response: initResp, url: sessionUrl, headers } = await postJson(
    serverUrl, buildInitPayload(), buildHeaders(customHeaders), null,
  );
  if (!initResp.ok) throw new McpProxyUpstreamError(`Initialize failed: HTTP ${initResp.status}`);
  const sessionId = initResp.headers.get('Mcp-Session-Id') || initResp.headers.get('mcp-session-id');
  const initData = await parseJsonRpcResponse(initResp);
  if (initData.error) throw new McpProxyUpstreamError('Initialize error: MCP server rejected request');
  await sendInitialized(sessionUrl, headers, sessionId);
  const { response: listResp } = await postJson(sessionUrl, {
    jsonrpc: '2.0', id: 2, method: 'tools/list', params: {},
  }, headers, sessionId);
  if (!listResp.ok) throw new McpProxyUpstreamError(`tools/list failed: HTTP ${listResp.status}`);
  const listData = await parseJsonRpcResponse(listResp);
  if (listData.error) throw new McpProxyUpstreamError('tools/list error: MCP server rejected request');
  return listData.result?.tools || [];
}

async function mcpCallTool(serverUrl, toolName, toolArgs, customHeaders) {
  const { response: initResp, url: sessionUrl, headers } = await postJson(
    serverUrl, buildInitPayload(), buildHeaders(customHeaders), null,
  );
  if (!initResp.ok) throw new McpProxyUpstreamError(`Initialize failed: HTTP ${initResp.status}`);
  const sessionId = initResp.headers.get('Mcp-Session-Id') || initResp.headers.get('mcp-session-id');
  const initData = await parseJsonRpcResponse(initResp);
  if (initData.error) throw new McpProxyUpstreamError('Initialize error: MCP server rejected request');
  await sendInitialized(sessionUrl, headers, sessionId);
  const { response: callResp } = await postJson(sessionUrl, {
    jsonrpc: '2.0', id: 3, method: 'tools/call',
    params: { name: toolName, arguments: toolArgs || {} },
  }, headers, sessionId);

View on GitHub (pinned to e586b8b4b8)