{"record":{"id":"90bdaecd22915a68","repo":"koala73/worldmonitor","slug":"tools-list-error-mcp-server-rejected-request","errorCode":null,"errorMessage":"tools/list error: MCP server rejected request","messagePattern":"tools/list error: MCP server rejected request","errorType":"exception","errorClass":"McpProxyUpstreamError","httpStatus":null,"severity":"error","filePath":"api/mcp-proxy.ts","lineNumber":588,"sourceCode":"    /* non-fatal */\n  }\n}\n\nasync function mcpListTools(serverUrl, customHeaders) {\n  const { response: initResp, url: sessionUrl, headers } = await postJson(\n    serverUrl, buildInitPayload(), buildHeaders(customHeaders), null,\n  );\n  if (!initResp.ok) throw new McpProxyUpstreamError(`Initialize failed: HTTP ${initResp.status}`);\n  const sessionId = initResp.headers.get('Mcp-Session-Id') || initResp.headers.get('mcp-session-id');\n  const initData = await parseJsonRpcResponse(initResp);\n  if (initData.error) throw new McpProxyUpstreamError('Initialize error: MCP server rejected request');\n  await sendInitialized(sessionUrl, headers, sessionId);\n  const { response: listResp } = await postJson(sessionUrl, {\n    jsonrpc: '2.0', id: 2, method: 'tools/list', params: {},\n  }, headers, sessionId);\n  if (!listResp.ok) throw new McpProxyUpstreamError(`tools/list failed: HTTP ${listResp.status}`);\n  const listData = await parseJsonRpcResponse(listResp);\n  if (listData.error) throw new McpProxyUpstreamError('tools/list error: MCP server rejected request');\n  return listData.result?.tools || [];\n}\n\nasync function mcpCallTool(serverUrl, toolName, toolArgs, customHeaders) {\n  const { response: initResp, url: sessionUrl, headers } = await postJson(\n    serverUrl, buildInitPayload(), buildHeaders(customHeaders), null,\n  );\n  if (!initResp.ok) throw new McpProxyUpstreamError(`Initialize failed: HTTP ${initResp.status}`);\n  const sessionId = initResp.headers.get('Mcp-Session-Id') || initResp.headers.get('mcp-session-id');\n  const initData = await parseJsonRpcResponse(initResp);\n  if (initData.error) throw new McpProxyUpstreamError('Initialize error: MCP server rejected request');\n  await sendInitialized(sessionUrl, headers, sessionId);\n  const { response: callResp } = await postJson(sessionUrl, {\n    jsonrpc: '2.0', id: 3, method: 'tools/call',\n    params: { name: toolName, arguments: toolArgs || {} },\n  }, headers, sessionId);\n  if (!callResp.ok) throw new McpProxyUpstreamError(`tools/call failed: HTTP ${callResp.status}`);\n  const callData = await parseJsonRpcResponse(callResp);","sourceCodeStart":570,"sourceCodeEnd":606,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/api/mcp-proxy.ts#L570-L606","documentation":"After a successful MCP initialize handshake, the tools/list JSON-RPC request completed at the HTTP level (2xx) but the server returned a JSON-RPC error object instead of a tool list. mcpListTools throws McpProxyUpstreamError('tools/list error: MCP server rejected request') because the session is up but the server refuses to enumerate tools.","triggerScenarios":"Thrown from mcpListTools (called by the tools handler) when listData = await parseJsonRpcResponse(listResp) contains { error: ... } for the tools/list request (id: 2), despite initialize having succeeded.","commonSituations":"The server expired or invalidated the Mcp-Session-Id between initialize and tools/list (idle timeout, multi-instance deployment without sticky sessions); the server requires the notifications/initialized round-trip the proxy sends best-effort and rejects tools/list as 'not initialized'; tools capability was not negotiated in initialize; server-side authorization denies tool listing for the supplied credentials.","solutions":["Read the JSON-RPC error code/message from the upstream response — '-32602' style errors or 'not initialized' point to session/capability problems.","Ensure the Mcp-Session-Id from the initialize response is being replayed on the tools/list POST (deployments behind load balancers need sticky sessions).","Check that the initialize response declared tools capability; servers reject tools/list when tools were not negotiated.","Re-run initialize + notifications/initialized and retry tools/list promptly in case of session idle timeout.","Verify the supplied auth headers grant permission to list tools on this server."],"exampleFix":"// before: load balancer routes tools/list to a different instance, session lost\n// nginx: upstream mcp { server a:8080; server b:8080; }\n// after: pin the session to one backend\n// nginx: upstream mcp { hash $http_mcp_session_id; server a:8080; server b:8080; }","handlingStrategy":"retry","validationCode":"// ensure a fresh session before listing tools\nconst session = await initializeSession(serverUrl, headers); // returns { sessionId } or throws\nif (!session.sessionId) console.warn('Server did not issue Mcp-Session-Id; tools/list may be rejected');","typeGuard":"function isJsonRpcError(msg) {\n  return typeof msg === 'object' && msg !== null && 'error' in msg && msg.error !== undefined;\n}","tryCatchPattern":"let lastError;\nfor (let attempt = 0; attempt < 2; attempt++) {\n  try {\n    return await mcpProxy.tools(serverUrl, headers); // retry re-runs initialize, getting a fresh session\n  } catch (error) {\n    lastError = error;\n    if (error instanceof McpProxyUpstreamError && error.message.startsWith('tools/list error')) continue;\n    throw error;\n  }\n}\nthrow lastError;","preventionTips":["Use sticky sessions (hash on Mcp-Session-Id) when the MCP server is load-balanced across instances.","Call tools/list immediately after initialize to avoid session idle expiry.","Verify the server advertises the tools capability in its initialize result.","Log the JSON-RPC error code to distinguish auth denials from session loss."],"tags":["mcp","json-rpc","tools-list","session","upstream"],"backgroundTag":"upstream-api-error","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"}