nuwax-mcp-stdio-proxy 1.4.5 → 1.4.6

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/dist/bridge.js CHANGED
@@ -49,17 +49,36 @@ export class PersistentMcpBridge {
49
49
  this.log.warn(`${LOG_TAG} Already running, stopping first`);
50
50
  await this.stop();
51
51
  }
52
- this.log.info(`${LOG_TAG} Starting with ${Object.keys(servers).length} persistent servers`);
53
- // 1. Spawn each persistent server + create MCP Client
54
- for (const [id, config] of Object.entries(servers)) {
55
- await this.startServer(id, config);
52
+ const serverCount = Object.keys(servers).length;
53
+ this.log.info(`${LOG_TAG} Starting with ${serverCount} persistent servers (parallel)`);
54
+ // 1. 并行启动 HTTP server 和所有 MCP server 子进程。
55
+ // 两者完全独立:HTTP server 在请求时才查询 this.servers,
56
+ // startServer 会在异步 spawnAndConnect 前先同步写入 this.servers,
57
+ // 所以 HTTP server 收到第一个请求时所有 entry 都已注册。
58
+ //
59
+ // 提前保存 serverPromises 引用:若 startHttpServer 抛出,在 catch 中先
60
+ // await Promise.allSettled(serverPromises) 等所有 spawnAndConnect 跑完
61
+ // (保证 entry.client / entry.transport 已赋值),再调用 stopServer,
62
+ // 才能可靠关闭子进程,防止孤儿进程残留。
63
+ const serverPromises = Object.entries(servers).map(([id, config]) => this.startServer(id, config));
64
+ try {
65
+ await Promise.all([
66
+ this.startHttpServer(options?.port),
67
+ ...serverPromises,
68
+ ]);
69
+ }
70
+ catch (e) {
71
+ this.log.error(`${LOG_TAG} 启动失败,清理已 spawn 的子进程:`, e);
72
+ // 等待所有 spawnAndConnect 完成,确保 entry.client/transport 已赋值后再清理
73
+ await Promise.allSettled(serverPromises);
74
+ await Promise.all(Array.from(this.servers.keys()).map((id) => this.stopServer(id)));
75
+ this.servers.clear();
76
+ throw e;
56
77
  }
57
- // 2. Start HTTP server
58
- await this.startHttpServer(options?.port);
59
78
  this.running = true;
60
- // 3. Start periodic session cleanup
79
+ // 2. Start periodic session cleanup
61
80
  this.sessionCleanupTimer = setInterval(() => this.cleanupStaleSessions(), SESSION_CLEANUP_INTERVAL_MS);
62
- this.log.info(`${LOG_TAG} Bridge ready on port ${this.port}`);
81
+ this.log.info(`${LOG_TAG} Bridge ready on port ${this.port} (${serverCount} servers)`);
63
82
  }
64
83
  /**
65
84
  * Stop bridge: close HTTP, kill all child processes
@@ -72,15 +91,16 @@ export class PersistentMcpBridge {
72
91
  clearInterval(this.sessionCleanupTimer);
73
92
  this.sessionCleanupTimer = null;
74
93
  }
75
- // Close all HTTP sessions
76
- for (const [key, session] of this.httpSessions) {
94
+ // 并行关闭所有 HTTP sessions(各 session transport 独立,无顺序依赖)。
95
+ // 内层 async 函数自行 catch 错误后不再 throw,Promise.all 足够,不需要 allSettled。
96
+ await Promise.all(Array.from(this.httpSessions.entries()).map(async ([key, session]) => {
77
97
  try {
78
98
  await session.transport.close();
79
99
  }
80
100
  catch (e) {
81
101
  this.log.warn(`${LOG_TAG} Error closing HTTP session ${key}:`, e);
82
102
  }
83
- }
103
+ }));
84
104
  this.httpSessions.clear();
85
105
  // Close HTTP server
86
106
  if (this.httpServer) {
@@ -90,10 +110,8 @@ export class PersistentMcpBridge {
90
110
  this.httpServer = null;
91
111
  this.port = 0;
92
112
  }
93
- // Stop all persistent servers
94
- for (const [id] of this.servers) {
95
- await this.stopServer(id);
96
- }
113
+ // 并行关闭所有 MCP server 子进程(各自独立,无顺序依赖)
114
+ await Promise.all(Array.from(this.servers.keys()).map((id) => this.stopServer(id)));
97
115
  this.servers.clear();
98
116
  this.log.info(`${LOG_TAG} Stopped`);
99
117
  }
package/dist/index.js CHANGED
@@ -23767,7 +23767,7 @@ var StdioServerTransport = class {
23767
23767
 
23768
23768
  // src/constants.ts
23769
23769
  var PKG_NAME = "nuwax-mcp-stdio-proxy";
23770
- var PKG_VERSION = "1.4.5";
23770
+ var PKG_VERSION = "1.4.6";
23771
23771
 
23772
23772
  // src/shared.ts
23773
23773
  async function discoverTools(client) {
@@ -25382,14 +25382,24 @@ var PersistentMcpBridge = class {
25382
25382
  this.log.warn(`${LOG_TAG} Already running, stopping first`);
25383
25383
  await this.stop();
25384
25384
  }
25385
- this.log.info(`${LOG_TAG} Starting with ${Object.keys(servers).length} persistent servers`);
25386
- for (const [id, config2] of Object.entries(servers)) {
25387
- await this.startServer(id, config2);
25385
+ const serverCount = Object.keys(servers).length;
25386
+ this.log.info(`${LOG_TAG} Starting with ${serverCount} persistent servers (parallel)`);
25387
+ const serverPromises = Object.entries(servers).map(([id, config2]) => this.startServer(id, config2));
25388
+ try {
25389
+ await Promise.all([
25390
+ this.startHttpServer(options?.port),
25391
+ ...serverPromises
25392
+ ]);
25393
+ } catch (e) {
25394
+ this.log.error(`${LOG_TAG} \u542F\u52A8\u5931\u8D25\uFF0C\u6E05\u7406\u5DF2 spawn \u7684\u5B50\u8FDB\u7A0B:`, e);
25395
+ await Promise.allSettled(serverPromises);
25396
+ await Promise.all(Array.from(this.servers.keys()).map((id) => this.stopServer(id)));
25397
+ this.servers.clear();
25398
+ throw e;
25388
25399
  }
25389
- await this.startHttpServer(options?.port);
25390
25400
  this.running = true;
25391
25401
  this.sessionCleanupTimer = setInterval(() => this.cleanupStaleSessions(), SESSION_CLEANUP_INTERVAL_MS);
25392
- this.log.info(`${LOG_TAG} Bridge ready on port ${this.port}`);
25402
+ this.log.info(`${LOG_TAG} Bridge ready on port ${this.port} (${serverCount} servers)`);
25393
25403
  }
25394
25404
  /**
25395
25405
  * Stop bridge: close HTTP, kill all child processes
@@ -25401,13 +25411,15 @@ var PersistentMcpBridge = class {
25401
25411
  clearInterval(this.sessionCleanupTimer);
25402
25412
  this.sessionCleanupTimer = null;
25403
25413
  }
25404
- for (const [key, session] of this.httpSessions) {
25405
- try {
25406
- await session.transport.close();
25407
- } catch (e) {
25408
- this.log.warn(`${LOG_TAG} Error closing HTTP session ${key}:`, e);
25409
- }
25410
- }
25414
+ await Promise.all(
25415
+ Array.from(this.httpSessions.entries()).map(async ([key, session]) => {
25416
+ try {
25417
+ await session.transport.close();
25418
+ } catch (e) {
25419
+ this.log.warn(`${LOG_TAG} Error closing HTTP session ${key}:`, e);
25420
+ }
25421
+ })
25422
+ );
25411
25423
  this.httpSessions.clear();
25412
25424
  if (this.httpServer) {
25413
25425
  await new Promise((resolve) => {
@@ -25416,9 +25428,9 @@ var PersistentMcpBridge = class {
25416
25428
  this.httpServer = null;
25417
25429
  this.port = 0;
25418
25430
  }
25419
- for (const [id] of this.servers) {
25420
- await this.stopServer(id);
25421
- }
25431
+ await Promise.all(
25432
+ Array.from(this.servers.keys()).map((id) => this.stopServer(id))
25433
+ );
25422
25434
  this.servers.clear();
25423
25435
  this.log.info(`${LOG_TAG} Stopped`);
25424
25436
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuwax-mcp-stdio-proxy",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
4
4
  "description": "TypeScript MCP proxy — aggregates multiple MCP servers (stdio + streamable-http + SSE) with convert & proxy modes",
5
5
  "type": "module",
6
6
  "bin": {