koala73/worldmonitor · error · McpProxyUpstreamError

Initialize error: MCP server rejected request

Error message

Initialize error: MCP server rejected request

What it means

During the MCP streamable-HTTP handshake, the server accepted the initialize request at the HTTP level (2xx) but answered with a JSON-RPC error object instead of a result. The proxy treats this as the upstream rejecting the initialization itself, so the session can never be established, and throws McpProxyUpstreamError('Initialize error: MCP server rejected request') in mcpListTools.

Solutions

  1. Inspect the JSON-RPC error object (code/message) returned by the server — it states why initialize was rejected (version, auth, or protocol).
  2. Align the protocolVersion sent in buildInitPayload with what the upstream server supports (check its advertised version in the error data).
  3. Verify required auth: pass the needed custom headers (API key / bearer token) to the proxy request if the server expects them at initialize.
  4. Confirm the server URL/transport: a path ending in /sse must use the SSE transport, not the streamable-HTTP POST flow.
  5. Update the upstream MCP server to a spec revision compatible with the proxy's initialize payload.

Example fix

// before: proxy advertises a newer spec version than the server accepts
{ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2025-06-18', ... } }
// after: negotiate the version the server supports
{ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2024-11-05', ... } }
Defensive patterns

Strategy: try-catch

Validate before calling

if (!serverUrl || !/^https:\/\//.test(serverUrl)) throw new Error('A valid https MCP server URL is required');
if (requiredAuthHeaders && !Object.keys(customHeaders || {}).length) throw new Error('This MCP server requires auth headers at initialize');

Type guard

function isJsonRpcError(msg) {
  return typeof msg === 'object' && msg !== null && 'error' in msg && msg.error !== undefined;
}

Try / catch

try {
  const tools = await mcpProxy.tools(serverUrl, headers);
} catch (error) {
  if (error instanceof McpProxyUpstreamError && error.message.startsWith('Initialize error')) {
    // protocol-version or auth rejection: surface a config hint to the operator
    return { error: 'MCP server rejected initialize — check protocol version and auth headers' };
  }
  throw error;
}

Prevention

When it happens

Trigger: Thrown from mcpListTools (called by the tools handler) when initData = await parseJsonRpcResponse(initResp) returns { error: ... } — i.e. HTTP 200 but a JSON-RPC error for the initialize request.

Common situations: The upstream MCP server does not support the protocol version the proxy sends in buildInitPayload (spec version mismatch); the server requires authentication and returns a JSON-RPC auth error instead of HTTP 401; the URL targets a transport the server doesn't speak at that path (e.g. POSTing to an SSE-only endpoint); the server is a different MCP spec revision that rejects the payload shape.

Related errors


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

Appendix: source

Thrown at api/mcp-proxy.ts:581

      jsonrpc: '2.0',
      method: 'notifications/initialized',
      params: {},
    }, headers, sessionId);
    await cancelResponseBody(response);
  } 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');

View on GitHub (pinned to 7d06c8633d)