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
- 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.
- If the server cannot be changed, connect the client using the same origin the server advertises (same scheme, host, and port).
- Update reverse-proxy configuration so it does not rewrite or inject absolute URLs into the SSE stream.
- In containerized environments, align the advertised endpoint with the externally reachable address.
- 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
- Servers should announce relative endpoint paths per the legacy SSE spec.
- Connect with the exact origin (scheme/host/port) the server advertises.
- In containers, advertise externally reachable addresses, not internal ones.
- Keep TLS-terminating proxies from rewriting endpoint events to absolute URLs.
- Never weaken the client-side origin check for convenience.
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
- HTTP ${response.status}: ${text}
- Legacy SSE response did not include a body
- Legacy SSE endpoint timeout after ${timeout}ms
- ENXIO
- Refusing to download onto a non-regular file: ${absolutePath
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/87783941c7301b88.
Report an issue: GitHub.