can1357/oh-my-pi · error · Error

Legacy SSE endpoint origin mismatch: expected ${configuredUr

Error message

Legacy SSE endpoint origin mismatch: expected ${configuredUrl.origin}, received ${endpointUrl.origin}

What it means

The legacy SSE spec requires the server's first event to announce the POST endpoint, which may be a relative URL. As a security measure the transport resolves it against the configured URL and rejects any endpoint whose origin differs from the configured server's origin, preventing a compromised or misbehaving server from redirecting JSON-RPC POSTs (and any credentials) to a different host. The mismatch throws and fails connect().

Source

Thrown at packages/coding-agent/src/mcp/transports/sse.ts:124

			throw error;
		}
	}

	async #readSSEStream(
		body: ReadableStream<Uint8Array>,
		operation: MCPTimeoutOperation,
		endpointReady: PromiseWithResolvers<void>,
	): Promise<void> {
		const signal = operation.signal ?? getNeverAbortSignal();
		let endpointReceived = false;
		try {
			for await (const event of readSseEvents(body, signal)) {
				if (event.event === "endpoint") {
					if (!this.#endpointUrl) {
						const endpointUrl = new URL(event.data, this.#config.url);
						const configuredUrl = new URL(this.#config.url);
						if (endpointUrl.origin !== configuredUrl.origin) {
							throw new Error(
								`Legacy SSE endpoint origin mismatch: expected ${configuredUrl.origin}, received ${endpointUrl.origin}`,
							);
						}
						this.#endpointUrl = endpointUrl.href;
						this.#connected = true;
						endpointReceived = true;
						operation.clear();
						endpointReady.resolve();
					}
					continue;
				}
				if (event.data === "" || event.data === "[DONE]") continue;

				let payload: unknown;
				try {
					payload = JSON.parse(event.data) as unknown;
				} catch (error) {
					if (error instanceof SyntaxError) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Fix the server to announce a relative endpoint path (e.g. '/messages') in the endpoint event, as the spec recommends, so it always resolves to the same origin.
  2. If the server cannot be changed, connect the client using the same origin the server advertises (same scheme, host, and port).
  3. Update reverse-proxy configuration so it does not rewrite or inject absolute URLs into the SSE stream.
  4. In containerized environments, align the advertised endpoint with the externally reachable address.
  5. As a last resort for trusted same-machine servers, bind client and server to matching origins — do not bypass the check in the client.

Example fix

// server (before): absolute internal URL leaks the container host
res.write(`event: endpoint\ndata: http://localhost:3001/messages\n\n`);
// after: relative path resolves against the client's configured URL
res.write(`event: endpoint\ndata: /messages\n\n`);
Defensive patterns

Strategy: validation

Validate before calling

// validate origins match before connecting
const configured = new URL(sseUrl);
const probe = await fetch(sseUrl, { headers: { Accept: 'text/event-stream' } });
// read the first 'endpoint' event and check:
// new URL(endpointData, sseUrl).origin === configured.origin
await probe.body?.cancel();

Type guard

function isSameOriginEndpoint(endpointData: string, configuredUrl: string): boolean {
  try { return new URL(endpointData, configuredUrl).origin === new URL(configuredUrl).origin; }
  catch { return false; }
}

Try / catch

try {
  transport = await createSseTransport(config);
} catch (e) {
  if (e instanceof Error && e.message.includes('origin mismatch')) {
    logger.error('MCP server advertises a cross-origin endpoint; fix server to send a relative path');
    throw new Error('Refusing cross-origin MCP endpoint (credential-leak risk)');
  }
  throw e;
}

Prevention

When it happens

Trigger: Server sends a 'endpoint' event containing an absolute URL on another host/port (e.g. http://localhost:3001/messages while the client connected via https://mcp.example.com/sse, or a different port); a proxy rewrites the endpoint event to its own address; a misconfigured server advertising an internal hostname.

Common situations: Docker/container setups where the server advertises its internal address (localhost:PORT inside the container) while the client reaches it via a mapped port or hostname; TLS-terminating proxies where the server emits an http:// endpoint while the client uses https://; copied example configs with mismatched hosts.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/87783941c7301b88. Report an issue: GitHub.