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
- Inspect the JSON-RPC error object (code/message) returned by the server — it states why initialize was rejected (version, auth, or protocol).
- Align the protocolVersion sent in buildInitPayload with what the upstream server supports (check its advertised version in the error data).
- Verify required auth: pass the needed custom headers (API key / bearer token) to the proxy request if the server expects them at initialize.
- Confirm the server URL/transport: a path ending in /sse must use the SSE transport, not the streamable-HTTP POST flow.
- 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
- Pin and document the MCP protocol version each upstream server supports.
- Store and pass auth headers via environment configuration before first use.
- Test the handshake with the official MCP inspector against each registered server.
- Keep upstream servers on a spec revision compatible with the proxy's initialize payload.
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
- Invalid MCP server response
- tools/list error: MCP server rejected request
- tools/call error: MCP server rejected request
- ${label} HTTP ${response.status}
- ${label} HTTP 400
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)