up-mcp-bridge 1.0.0 → 1.0.1
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.
- package/README.md +11 -15
- package/dist/{chunk-EOYXIWZ7.js → chunk-7F6PRZCR.js} +49 -7
- package/dist/client.js +1 -1
- package/dist/proxy.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -209,9 +209,7 @@ You can specify multiple `--ignore-tool` flags to ignore different patterns. Exa
|
|
|
209
209
|
]
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
-
### Auto-Reconnect
|
|
213
|
-
|
|
214
|
-
⚠️ **This feature is experimental and has known issues being investigated.**
|
|
212
|
+
### Auto-Reconnect
|
|
215
213
|
|
|
216
214
|
To enable automatic reconnection when the remote server restarts, add the `--auto-reconnect` flag:
|
|
217
215
|
|
|
@@ -231,11 +229,20 @@ To enable automatic reconnection when the remote server restarts, add the `--aut
|
|
|
231
229
|
```
|
|
232
230
|
|
|
233
231
|
This enables:
|
|
232
|
+
- **Session-aware ping**: Detects stale SSE sessions via `/ping?sessionId=XXX` endpoint
|
|
234
233
|
- Automatic reconnection with exponential backoff when the server becomes unavailable
|
|
235
234
|
- Message queuing during reconnection attempts
|
|
236
235
|
- MCP session re-initialization after successful reconnection
|
|
237
236
|
- Transparent handling for the MCP client
|
|
238
237
|
|
|
238
|
+
**How session-aware ping works:**
|
|
239
|
+
1. When connected, the bridge extracts the `sessionId` from the SSE endpoint URL
|
|
240
|
+
2. Periodic health checks include the `sessionId` parameter
|
|
241
|
+
3. If the server returns `410 Gone`, the session has expired (e.g., server restarted)
|
|
242
|
+
4. The bridge immediately triggers reconnection instead of waiting for SSE errors
|
|
243
|
+
|
|
244
|
+
This ensures fast recovery when the remote server restarts, avoiding tool call timeouts.
|
|
245
|
+
|
|
239
246
|
**Default parameters:**
|
|
240
247
|
- Max reconnection attempts: 20
|
|
241
248
|
- Base delay: 1000ms
|
|
@@ -254,18 +261,7 @@ This enables:
|
|
|
254
261
|
]
|
|
255
262
|
```
|
|
256
263
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
1. **MCP calls may hang after server restart**: In some scenarios, MCP tool calls hang indefinitely after the remote server restarts, even though the proxy reports a successful reconnection. A full restart cycle (stop client → restart server → start client) resolves the issue, but this defeats the purpose of auto-reconnect.
|
|
260
|
-
|
|
261
|
-
2. **npx execution issues**: When using `npx up-mcp-bridge`, environment variables (like `NODE_TLS_REJECT_UNAUTHORIZED`) may not propagate correctly to the spawned process, causing SSL issues with self-signed certificates.
|
|
262
|
-
|
|
263
|
-
3. **HTTP vs HTTPS behavior**: The proxy behaves differently when connecting via HTTP vs HTTPS. HTTP connections (even on localhost) may exhibit different hanging behavior.
|
|
264
|
-
|
|
265
|
-
**Status**: These issues are being actively investigated. If you encounter problems, please report them with:
|
|
266
|
-
- Your configuration (sanitized)
|
|
267
|
-
- Steps to reproduce
|
|
268
|
-
- Logs from the proxy (`--debug` flag)
|
|
264
|
+
**Note:** Session-aware ping requires server support (e.g., ultraPRO Desktop v1.0+). Servers without this feature will still work with standard ping-based health checks.
|
|
269
265
|
|
|
270
266
|
### Transport Strategies
|
|
271
267
|
|
|
@@ -20026,10 +20026,13 @@ var PING_TIMEOUT_MS = 2e3;
|
|
|
20026
20026
|
var MAX_PING_FAILURES = 3;
|
|
20027
20027
|
var MAX_SSE_ERRORS_BEFORE_RECONNECT = 2;
|
|
20028
20028
|
var SSE_ERROR_PATTERNS = ["timeout", "terminated", "aborted", "network", "ECONNRESET", "ECONNREFUSED"];
|
|
20029
|
-
async function pingServer(serverUrl) {
|
|
20029
|
+
async function pingServer(serverUrl, sessionId) {
|
|
20030
20030
|
try {
|
|
20031
20031
|
const url2 = new URL(serverUrl);
|
|
20032
|
-
|
|
20032
|
+
let pingUrl = `${url2.protocol}//${url2.host}/ping`;
|
|
20033
|
+
if (sessionId) {
|
|
20034
|
+
pingUrl += `?sessionId=${encodeURIComponent(sessionId)}`;
|
|
20035
|
+
}
|
|
20033
20036
|
const controller = new AbortController();
|
|
20034
20037
|
const timeout = setTimeout(() => controller.abort(), PING_TIMEOUT_MS);
|
|
20035
20038
|
try {
|
|
@@ -20038,13 +20041,25 @@ async function pingServer(serverUrl) {
|
|
|
20038
20041
|
method: "GET"
|
|
20039
20042
|
});
|
|
20040
20043
|
clearTimeout(timeout);
|
|
20041
|
-
|
|
20044
|
+
if (response.status === 410) {
|
|
20045
|
+
return { alive: true, sessionExpired: true };
|
|
20046
|
+
}
|
|
20047
|
+
return { alive: response.ok, sessionExpired: false };
|
|
20042
20048
|
} catch (e) {
|
|
20043
20049
|
clearTimeout(timeout);
|
|
20044
|
-
return false;
|
|
20050
|
+
return { alive: false, sessionExpired: false };
|
|
20045
20051
|
}
|
|
20046
20052
|
} catch (e) {
|
|
20047
|
-
return false;
|
|
20053
|
+
return { alive: false, sessionExpired: false };
|
|
20054
|
+
}
|
|
20055
|
+
}
|
|
20056
|
+
function extractSessionId(transport) {
|
|
20057
|
+
try {
|
|
20058
|
+
const endpoint = transport._endpoint;
|
|
20059
|
+
if (!endpoint) return void 0;
|
|
20060
|
+
return endpoint.searchParams?.get("sessionId") || void 0;
|
|
20061
|
+
} catch {
|
|
20062
|
+
return void 0;
|
|
20048
20063
|
}
|
|
20049
20064
|
}
|
|
20050
20065
|
var pid = process.pid;
|
|
@@ -20154,6 +20169,17 @@ function mcpProxy({
|
|
|
20154
20169
|
let consecutiveTimeouts = 0;
|
|
20155
20170
|
let consecutiveSseErrors = 0;
|
|
20156
20171
|
const pendingRequests = /* @__PURE__ */ new Map();
|
|
20172
|
+
let currentSessionId;
|
|
20173
|
+
let sessionIdExtracted = false;
|
|
20174
|
+
function tryExtractSessionId() {
|
|
20175
|
+
if (sessionIdExtracted) return;
|
|
20176
|
+
const sessionId = extractSessionId(currentTransportToServer);
|
|
20177
|
+
if (sessionId) {
|
|
20178
|
+
currentSessionId = sessionId;
|
|
20179
|
+
sessionIdExtracted = true;
|
|
20180
|
+
log(`[Session] Extracted sessionId from transport: ${sessionId}`);
|
|
20181
|
+
}
|
|
20182
|
+
}
|
|
20157
20183
|
let savedInitializeMessage = null;
|
|
20158
20184
|
let initializeIdCounter = 1e6;
|
|
20159
20185
|
async function reinitializeMcpSession(transport) {
|
|
@@ -20287,8 +20313,20 @@ function mcpProxy({
|
|
|
20287
20313
|
return;
|
|
20288
20314
|
}
|
|
20289
20315
|
debugLog(`[Ping] Checking server health for request ${message.id}...`);
|
|
20290
|
-
const
|
|
20291
|
-
if (
|
|
20316
|
+
const pingResult = await pingServer(serverUrl, currentSessionId);
|
|
20317
|
+
if (pingResult.sessionExpired) {
|
|
20318
|
+
log(`[Ping] Session expired for request ${message.id}, triggering immediate reconnection`);
|
|
20319
|
+
clearInterval(pingInterval);
|
|
20320
|
+
connectionHealthy = false;
|
|
20321
|
+
const pending = pendingRequests.get(message.id);
|
|
20322
|
+
if (pending) {
|
|
20323
|
+
pendingRequests.delete(message.id);
|
|
20324
|
+
queueMessageForRetry(pending.message);
|
|
20325
|
+
}
|
|
20326
|
+
currentTransportToServer.close().catch(onServerError);
|
|
20327
|
+
return;
|
|
20328
|
+
}
|
|
20329
|
+
if (pingResult.alive) {
|
|
20292
20330
|
if (pingFailures > 0) {
|
|
20293
20331
|
debugLog(`[Ping] Server recovered, resetting failure counter (was ${pingFailures})`);
|
|
20294
20332
|
pingFailures = 0;
|
|
@@ -20386,6 +20424,7 @@ function mcpProxy({
|
|
|
20386
20424
|
serverTransport.onmessage = (_message) => {
|
|
20387
20425
|
const message = messageTransformer.interceptResponse(_message);
|
|
20388
20426
|
log("[Remote\u2192Local]", message.method || message.id);
|
|
20427
|
+
tryExtractSessionId();
|
|
20389
20428
|
if (message.id !== void 0) {
|
|
20390
20429
|
clearRequestTracking(message.id);
|
|
20391
20430
|
}
|
|
@@ -20437,6 +20476,9 @@ function mcpProxy({
|
|
|
20437
20476
|
connectionHealthy = true;
|
|
20438
20477
|
consecutiveTimeouts = 0;
|
|
20439
20478
|
consecutiveSseErrors = 0;
|
|
20479
|
+
sessionIdExtracted = false;
|
|
20480
|
+
currentSessionId = void 0;
|
|
20481
|
+
tryExtractSessionId();
|
|
20440
20482
|
log(`Flushing ${pendingMessages.length} queued messages...`);
|
|
20441
20483
|
while (pendingMessages.length > 0) {
|
|
20442
20484
|
const pending = pendingMessages.shift();
|
package/dist/client.js
CHANGED
package/dist/proxy.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
mcpProxy,
|
|
11
11
|
parseCommandLineArgs,
|
|
12
12
|
setupSignalHandlers
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-7F6PRZCR.js";
|
|
14
14
|
|
|
15
15
|
// src/proxy.ts
|
|
16
16
|
import { EventEmitter } from "events";
|
|
@@ -172,8 +172,8 @@ async function runProxy(serverUrl, callbackPort, headers, transportStrategy = "h
|
|
|
172
172
|
}
|
|
173
173
|
log("Press Ctrl+C to exit");
|
|
174
174
|
const cleanup = async () => {
|
|
175
|
-
await remoteTransport.close();
|
|
176
175
|
await localTransport.close();
|
|
176
|
+
await remoteTransport.close();
|
|
177
177
|
if (server) {
|
|
178
178
|
server.close();
|
|
179
179
|
}
|