up-mcp-bridge 1.0.21 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20163,13 +20163,9 @@ function mcpProxy({
20163
20163
  let inPersistentRetry = false;
20164
20164
  let connectionHealthy = true;
20165
20165
  let mcpSessionInitialized = false;
20166
- const HANDSHAKE_METHODS = /* @__PURE__ */ new Set([
20166
+ const PRE_INIT_METHODS = /* @__PURE__ */ new Set([
20167
20167
  "initialize",
20168
20168
  "notifications/initialized",
20169
- "tools/list",
20170
- "resources/list",
20171
- "prompts/list",
20172
- "resources/templates/list",
20173
20169
  "ping"
20174
20170
  ]);
20175
20171
  const pendingMessages = [];
@@ -20195,6 +20191,7 @@ function mcpProxy({
20195
20191
  const maxDelayMs = reconnectOptions.maxDelayMs ?? 15e3;
20196
20192
  const connectionTimeoutMs = reconnectOptions.connectionTimeoutMs ?? 5e3;
20197
20193
  const persistentRetryDelayMs = reconnectOptions.persistentRetryDelayMs ?? 3e4;
20194
+ const maxReconnectDurationMs = reconnectOptions.maxReconnectDurationMs ?? 3e5;
20198
20195
  const messageTimeoutMs = 6e4;
20199
20196
  const requestTimeoutMs = 5e3;
20200
20197
  const maxConsecutiveTimeouts = 3;
@@ -20654,11 +20651,21 @@ function mcpProxy({
20654
20651
  log(`Remote transport closed. Starting reconnection loop...`);
20655
20652
  debugLog("Remote transport closed, starting reconnection loop", { attempt: reconnectAttempts + 1 });
20656
20653
  isReconnecting = true;
20654
+ let persistentStartTime;
20657
20655
  while (!transportToClientClosed) {
20658
20656
  reconnectAttempts++;
20659
20657
  if (!inPersistentRetry && reconnectAttempts > reconnectOptions.maxAttempts) {
20660
20658
  inPersistentRetry = true;
20661
- log(`[Persistent] Entering persistent retry mode after ${reconnectOptions.maxAttempts} fast attempts. Will retry every ${persistentRetryDelayMs / 1e3}s indefinitely until gateway recovers.`);
20659
+ persistentStartTime = Date.now();
20660
+ log(`[Persistent] Entering persistent retry mode after ${reconnectOptions.maxAttempts} fast attempts. Will retry every ${persistentRetryDelayMs / 1e3}s for up to ${maxReconnectDurationMs / 1e3}s before self-destruct.`);
20661
+ }
20662
+ if (inPersistentRetry && persistentStartTime && Date.now() - persistentStartTime > maxReconnectDurationMs) {
20663
+ const elapsedSec = Math.round((Date.now() - persistentStartTime) / 1e3);
20664
+ log(`[BUG-006 Self-destruct] Reconnection failed for ${elapsedSec}s (limit: ${maxReconnectDurationMs / 1e3}s). Exiting process for clean restart.`);
20665
+ flushAllPendingMessagesWithError("Bridge self-destructing after prolonged reconnection failure");
20666
+ clearInterval(statusInterval);
20667
+ stopBackgroundHeartbeat();
20668
+ process.exit(1);
20662
20669
  }
20663
20670
  let delay;
20664
20671
  let logThisAttempt;
@@ -20735,8 +20742,10 @@ function mcpProxy({
20735
20742
  log(`[Queue] Flush complete: ${flushedCount} sent, ${expiredCount} expired`);
20736
20743
  return;
20737
20744
  } catch (error2) {
20738
- log(`Reconnection attempt ${reconnectAttempts} failed:`, error2);
20739
- debugLog("Reconnection attempt failed", { error: error2, attempts: reconnectAttempts });
20745
+ const errorCode = error2?.code || error2?.event?.code || "unknown";
20746
+ const errorStr = String(error2).substring(0, 200);
20747
+ log(`Reconnection attempt ${reconnectAttempts} failed (HTTP ${errorCode}): ${errorStr}`);
20748
+ debugLog("Reconnection attempt failed", { error: error2, errorCode, attempts: reconnectAttempts, serverUrl });
20740
20749
  }
20741
20750
  }
20742
20751
  log(`Reconnection loop ended: client disconnected after ${reconnectAttempts} attempts.`);
@@ -20853,12 +20862,12 @@ function mcpProxy({
20853
20862
  });
20854
20863
  return;
20855
20864
  }
20856
- if (!mcpSessionInitialized && !HANDSHAKE_METHODS.has(message.method)) {
20865
+ if (!mcpSessionInitialized && !PRE_INIT_METHODS.has(message.method)) {
20857
20866
  log(`[BUG-005] Session not initialized, queuing message ${message.id || "(no id)"} (${message.method})`);
20858
20867
  log("[Local\u2192Remote] (queuing - session not initialized)", message.method || message.id);
20859
20868
  queueMessageForRetry(message);
20860
20869
  if (pendingMessages.length === 1) {
20861
- log("[BUG-005] Messages will be sent after server confirms session is initialized.");
20870
+ log("[BUG-005] Messages will be sent after notifications/initialized confirms session.");
20862
20871
  }
20863
20872
  return;
20864
20873
  }
@@ -20880,10 +20889,28 @@ function mcpProxy({
20880
20889
  log(`[BUG-003 SendSlow] Message ${message.id || "(no id)"} (${message.method}) send() took ${sendDuration}ms`);
20881
20890
  }
20882
20891
  if (message.method === "notifications/initialized") {
20883
- log("[MCP Init] notifications/initialized sent to server, awaiting confirmation via first successful response...");
20892
+ mcpSessionInitialized = true;
20893
+ log("[MCP Init] notifications/initialized sent to server, session marked as initialized");
20884
20894
  const queueSize = pendingMessages.length;
20885
20895
  if (queueSize > 0) {
20886
- log(`[MCP Init] ${queueSize} messages queued, will flush after server confirms session`);
20896
+ log(`[MCP Init] Flushing ${queueSize} queued messages after session confirmed`);
20897
+ let flushedCount = 0;
20898
+ while (pendingMessages.length > 0) {
20899
+ const pending = pendingMessages.shift();
20900
+ if (pending.message.id) {
20901
+ queuedMessageIds.delete(pending.message.id);
20902
+ }
20903
+ if (Date.now() - pending.timestamp < messageTimeoutMs) {
20904
+ log(`[MCP Init] Sending queued message ${pending.message.id || "(no id)"} (${pending.message.method})`);
20905
+ trackRequest(pending.message);
20906
+ currentTransportToServer.send(pending.message).catch(onServerError);
20907
+ flushedCount++;
20908
+ } else {
20909
+ log(`[MCP Init] Message ${pending.message.id} expired during init wait`);
20910
+ sendErrorForPendingMessage(pending, "Request timed out waiting for MCP session initialization");
20911
+ }
20912
+ }
20913
+ log(`[MCP Init] Flush complete: ${flushedCount} messages sent`);
20887
20914
  }
20888
20915
  }
20889
20916
  }).catch((error2) => {
@@ -21318,16 +21345,28 @@ async function parseCommandLineArgs(args, usage) {
21318
21345
  log(`Warning: Ignoring invalid --persistent-retry-delay value: ${args[persistentRetryDelayIndex + 1]}. Must be a positive number.`);
21319
21346
  }
21320
21347
  }
21348
+ let maxReconnectDurationMs = 3e5;
21349
+ const maxReconnectDurationIndex = args.indexOf("--max-reconnect-duration");
21350
+ if (maxReconnectDurationIndex !== -1 && maxReconnectDurationIndex < args.length - 1) {
21351
+ const value = parseInt(args[maxReconnectDurationIndex + 1], 10);
21352
+ if (!isNaN(value) && value > 0) {
21353
+ maxReconnectDurationMs = value;
21354
+ log(`Using max reconnect duration: ${maxReconnectDurationMs}ms (${maxReconnectDurationMs / 1e3}s)`);
21355
+ } else {
21356
+ log(`Warning: Ignoring invalid --max-reconnect-duration value: ${args[maxReconnectDurationIndex + 1]}. Must be a positive number.`);
21357
+ }
21358
+ }
21321
21359
  const reconnectOptions = {
21322
21360
  enabled: autoReconnect,
21323
21361
  maxAttempts: maxReconnectAttempts,
21324
21362
  baseDelayMs: reconnectDelayMs,
21325
21363
  maxDelayMs: maxReconnectDelayMs,
21326
21364
  connectionTimeoutMs,
21327
- persistentRetryDelayMs
21365
+ persistentRetryDelayMs,
21366
+ maxReconnectDurationMs
21328
21367
  };
21329
21368
  if (autoReconnect) {
21330
- log(`Auto-reconnect enabled: fast phase: ${maxReconnectAttempts} attempts (${reconnectDelayMs}ms\u2192${maxReconnectDelayMs}ms backoff), persistent phase: every ${persistentRetryDelayMs / 1e3}s indefinitely`);
21369
+ log(`Auto-reconnect enabled: fast phase: ${maxReconnectAttempts} attempts (${reconnectDelayMs}ms\u2192${maxReconnectDelayMs}ms backoff), persistent phase: every ${persistentRetryDelayMs / 1e3}s for up to ${maxReconnectDurationMs / 1e3}s`);
21331
21370
  }
21332
21371
  if (!serverUrl) {
21333
21372
  log(usage);
package/dist/client.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  parseCommandLineArgs,
13
13
  setupSignalHandlers,
14
14
  version
15
- } from "./chunk-AEDNEZ76.js";
15
+ } from "./chunk-FN3ZSOWT.js";
16
16
 
17
17
  // src/client.ts
18
18
  import { EventEmitter } from "events";
package/dist/proxy.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  parseCommandLineArgs,
12
12
  setupSignalHandlers,
13
13
  version
14
- } from "./chunk-AEDNEZ76.js";
14
+ } from "./chunk-FN3ZSOWT.js";
15
15
 
16
16
  // src/proxy.ts
17
17
  import { EventEmitter } from "events";
@@ -131,6 +131,7 @@ Options:
131
131
  --reconnect-delay N Base delay between fast attempts in ms (default: 1000)
132
132
  --max-reconnect-delay N Maximum delay in fast phase in ms (default: 15000)
133
133
  --persistent-retry-delay N Fixed delay in persistent phase in ms (default: 30000)
134
+ --max-reconnect-duration N Max time in persistent phase before self-destruct in ms (default: 300000)
134
135
  --connection-timeout N Connection timeout in ms (default: 5000)
135
136
  --transport STRATEGY Transport strategy: sse-only, http-only, sse-first, http-first
136
137
  --header "Key: Value" Add custom header (can be repeated)
@@ -138,8 +139,10 @@ Options:
138
139
 
139
140
  Reconnection behavior (two-phase):
140
141
  Fast phase: Attempts 1-N with exponential backoff (1s\u21922s\u21924s\u2192...\u219215s)
141
- Persistent phase: After N attempts, retries every 30s indefinitely until
142
- the gateway recovers or the client disconnects.
142
+ Persistent phase: After N attempts, retries every 30s for up to 5 minutes
143
+ (configurable via --max-reconnect-duration). If the gateway
144
+ doesn't recover, the bridge exits so the MCP client can
145
+ spawn a fresh process that connects cleanly.
143
146
 
144
147
  Example:
145
148
  npx up-mcp-bridge http://localhost:3000/sse --auto-reconnect
@@ -203,7 +206,7 @@ async function runProxy(serverUrl, callbackPort, headers, transportStrategy = "h
203
206
  log("Local STDIO server running");
204
207
  log(`Proxy established successfully between local STDIO and remote ${remoteTransport.constructor.name}`);
205
208
  if (reconnectOptions.enabled) {
206
- log(`Auto-reconnect enabled (fast: ${reconnectOptions.maxAttempts} attempts, persistent: every ${(reconnectOptions.persistentRetryDelayMs ?? 3e4) / 1e3}s, never gives up)`);
209
+ log(`Auto-reconnect enabled (fast: ${reconnectOptions.maxAttempts} attempts, persistent: every ${(reconnectOptions.persistentRetryDelayMs ?? 3e4) / 1e3}s, max duration: ${(reconnectOptions.maxReconnectDurationMs ?? 3e5) / 1e3}s)`);
207
210
  }
208
211
  log("Press Ctrl+C to exit");
209
212
  const cleanup = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "up-mcp-bridge",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "Remote proxy for MCP with auto-reconnect support. Fork of mcp-remote with transparent server restart handling.",
5
5
  "keywords": [
6
6
  "mcp",