@loop_ouroboros/mcp-hub-lite 1.2.7 → 1.2.8

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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.2.8] - 2026-05-09
6
+
7
+ ### Gateway
8
+
9
+ - prevent concurrent request counter drift from SSE timeout causing 503 for new clients
10
+ - add GET /health/connections diagnostic endpoint for connection stats
11
+
5
12
  ## [1.2.7] - 2026-05-09
6
13
 
7
14
  ### Gateway
@@ -1 +1 @@
1
- {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../../../src/api/web/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,iBAK7D"}
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../../../src/api/web/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG1C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,iBAU7D"}
@@ -1,3 +1,4 @@
1
+ import { getConnectionStats } from '../../app.js';
1
2
  /**
2
3
  * Health Check API Routes
3
4
  *
@@ -21,4 +22,8 @@ export async function webHealthRoutes(fastify) {
21
22
  fastify.get('/web/health', async () => {
22
23
  return { status: 'ok', timestamp: new Date().toISOString() };
23
24
  });
25
+ // GET /health/connections - diagnostic endpoint for connection counters
26
+ fastify.get('/health/connections', async () => {
27
+ return getConnectionStats();
28
+ });
24
29
  }
@@ -1,3 +1,10 @@
1
+ /** Expose connection stats for diagnostic endpoints */
2
+ export declare function getConnectionStats(): {
3
+ currentConnections: number;
4
+ currentConcurrentRequests: number;
5
+ maxConnections: number;
6
+ maxConcurrentConnections: number;
7
+ };
1
8
  /**
2
9
  * Creates and configures a Fastify application instance for the MCP Hub Lite service.
3
10
  *
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/app.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,QAAQ,mWAmH7B"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/app.ts"],"names":[],"mappings":"AAmCA,uDAAuD;AACvD,wBAAgB,kBAAkB;;;;;EAQjC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,QAAQ,mWAyJ7B"}
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  import { configManager } from './config/config-manager.js';
6
6
  import { setJsonPrettyConfigGetter } from './utils/json-utils.js';
7
7
  import { isIpAllowed } from './utils/network-security.js';
8
+ import { logger, LOG_MODULES } from './utils/logger.js';
8
9
  // MCP Protocol Routes
9
10
  import { mcpGatewayRoutes } from './api/mcp/gateway.js';
10
11
  // Web API Routes
@@ -24,6 +25,17 @@ const __dirname = path.dirname(__filename);
24
25
  // Connection tracking counters (module-scoped, shared across all requests)
25
26
  let currentConnections = 0;
26
27
  let currentConcurrentRequests = 0;
28
+ const CONCURRENT_DECREMENTED = Symbol('concurrentDecremented');
29
+ /** Expose connection stats for diagnostic endpoints */
30
+ export function getConnectionStats() {
31
+ const cfg = configManager.getConfig().security;
32
+ return {
33
+ currentConnections,
34
+ currentConcurrentRequests,
35
+ maxConnections: cfg.maxConnections,
36
+ maxConcurrentConnections: cfg.maxConcurrentConnections
37
+ };
38
+ }
27
39
  /**
28
40
  * Creates and configures a Fastify application instance for the MCP Hub Lite service.
29
41
  *
@@ -69,6 +81,9 @@ export async function buildApp() {
69
81
  socket.on('close', () => {
70
82
  currentConnections--;
71
83
  });
84
+ socket.on('error', (err) => {
85
+ logger.debug(`Socket error (will be closed): ${err.message}`, LOG_MODULES.SERVER);
86
+ });
72
87
  if (currentConnections > config.security.maxConnections) {
73
88
  socket.destroy();
74
89
  }
@@ -95,10 +110,36 @@ export async function buildApp() {
95
110
  return;
96
111
  }
97
112
  currentConcurrentRequests++;
113
+ // Safety net: decrement on premature connection close (e.g. SSE timeout).
114
+ // Prevents counter drift for hijacked long-lived connections whose
115
+ // onResponse hook may not fire when the socket is destroyed by timeout.
116
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
+ reply.raw[CONCURRENT_DECREMENTED] = false;
118
+ reply.raw.on('close', () => {
119
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
+ if (!reply.raw[CONCURRENT_DECREMENTED]) {
121
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
+ reply.raw[CONCURRENT_DECREMENTED] = true;
123
+ currentConcurrentRequests--;
124
+ }
125
+ });
126
+ // Warn when approaching the configured limit
127
+ if (currentConcurrentRequests > config.security.maxConcurrentConnections * 0.8) {
128
+ logger.warn(`High concurrent requests: ${currentConcurrentRequests}/${config.security.maxConcurrentConnections}`, LOG_MODULES.SERVER);
129
+ }
98
130
  done();
99
131
  });
100
- fastify.addHook('onResponse', (_request, _reply, done) => {
101
- currentConcurrentRequests = Math.max(0, currentConcurrentRequests - 1);
132
+ fastify.addHook('onResponse', (_request, reply, done) => {
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ if (!reply.raw[CONCURRENT_DECREMENTED]) {
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ reply.raw[CONCURRENT_DECREMENTED] = true;
137
+ currentConcurrentRequests--;
138
+ if (currentConcurrentRequests < 0) {
139
+ logger.warn('currentConcurrentRequests went negative in onResponse, resetting to 0', LOG_MODULES.SERVER);
140
+ currentConcurrentRequests = 0;
141
+ }
142
+ }
102
143
  done();
103
144
  });
104
145
  // Simple CORS for dev
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loop_ouroboros/mcp-hub-lite",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
4
4
  "description": "A lightweight MCP management platform designed for independent developers",
5
5
  "license": "MIT",
6
6
  "author": "loop_ouroboros",