Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion apps/mcping/src/main/mcp/listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const BACKOFF_FACTOR = 2;
const HEALTH_CHECK_INTERVAL_MS = 5000;
const HEALTH_CHECK_TIMEOUT_MS = 4000;
const HTTP_UNAUTHORIZED = 401;
const HTTP_NOT_FOUND = 404;

const AUTH_REQUIRED_DETAIL = 'Authorization required — click Connect';
const AWAITING_AUTH_DETAIL = 'Waiting for browser authorization…';
Expand Down Expand Up @@ -82,6 +83,22 @@ function isAuthError(error: unknown): boolean {
return /unauthorized|authentication required|\b401\b/i.test(error.message);
}

// Auth failures and 404s are terminal: retrying the connection can't fix a
// missing endpoint or rejected credentials, so we surface the error and stop
// the reconnect/ping loop instead of backing off forever.
function isTerminalConnectError(error: unknown): boolean {
if (isAuthError(error)) {
return true;
}
if (!(error instanceof Error)) {
return false;
}
if ((error as { code?: unknown }).code === HTTP_NOT_FOUND) {
return true;
}
return /\bnot found\b|\b404\b/i.test(error.message);
}

// mcp-use exposes no ping helper and its raw `request` hands the SDK an
// undefined result schema (which throws while parsing every response), so reach
// the underlying MCP SDK client. Its `ping` is a protocol-level liveness probe
Expand Down Expand Up @@ -215,6 +232,11 @@ class ServerConnection {
}
const detail = error instanceof Error ? error.message : String(error);
log({ level: 'warn', message: `[${this.server.name}] Connection lost: ${detail}` });
if (isTerminalConnectError(error)) {
this.setStatus({ state: 'error', detail });
await this.disposeClient();
return;
}
this.setStatus({ state: 'error', detail: 'Connection lost' });
void this.reconnectAfterDrop();
} finally {
Expand Down Expand Up @@ -288,7 +310,7 @@ class ServerConnection {
log({ level: 'error', message: `[${this.server.name}] Connection failed: ${detail}` });
this.setStatus({ state: 'error', detail });
await this.disposeClient();
if (this.desiredConnected) {
if (this.desiredConnected && !isTerminalConnectError(error)) {
this.scheduleReconnect();
}
}
Expand Down