snow-flow 2.9.5 → 2.9.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.
@@ -0,0 +1,70 @@
1
+ /**
2
+ * MCP Process Manager - SAFE VERSION
3
+ * Emergency fix for memory crash issues
4
+ * Implements graceful shutdown and memory-safe cleanup
5
+ */
6
+ export declare class MCPProcessManager {
7
+ private static instance;
8
+ private readonly MAX_MCP_SERVERS;
9
+ private readonly MAX_MEMORY_MB;
10
+ private readonly CLEANUP_ENABLED;
11
+ private readonly CLEANUP_INTERVAL;
12
+ private cleanupTimer?;
13
+ private isCleaningUp;
14
+ private constructor();
15
+ static getInstance(): MCPProcessManager;
16
+ /**
17
+ * Check if we can spawn a new MCP server
18
+ */
19
+ canSpawnServer(): boolean;
20
+ /**
21
+ * Get current MCP system status - SAFER VERSION
22
+ */
23
+ getSystemStatus(): {
24
+ processCount: number;
25
+ memoryUsageMB: number;
26
+ processes: Array<{
27
+ pid: number;
28
+ memory: number;
29
+ name: string;
30
+ }>;
31
+ };
32
+ /**
33
+ * Gracefully shutdown a process with timeout
34
+ */
35
+ private gracefulKill;
36
+ /**
37
+ * Kill duplicate MCP servers - SAFER VERSION
38
+ */
39
+ killDuplicates(): Promise<void>;
40
+ /**
41
+ * Emergency cleanup - only for critical situations
42
+ */
43
+ emergencyCleanup(): Promise<void>;
44
+ /**
45
+ * Safe cleanup - only when absolutely necessary
46
+ */
47
+ cleanup(): Promise<void>;
48
+ /**
49
+ * Start periodic cleanup - MUCH SAFER
50
+ */
51
+ private startPeriodicCleanup;
52
+ /**
53
+ * Stop periodic cleanup
54
+ */
55
+ stopPeriodicCleanup(): void;
56
+ /**
57
+ * Kill all MCP servers - USE WITH CAUTION
58
+ */
59
+ killAll(): Promise<void>;
60
+ /**
61
+ * Get resource usage summary
62
+ */
63
+ getResourceSummary(): string;
64
+ /**
65
+ * Get health status
66
+ */
67
+ getHealthStatus(): 'healthy' | 'warning' | 'critical';
68
+ }
69
+ export declare const mcpProcessManager: MCPProcessManager;
70
+ //# sourceMappingURL=mcp-process-manager-safe.d.ts.map
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ /**
3
+ * MCP Process Manager - SAFE VERSION
4
+ * Emergency fix for memory crash issues
5
+ * Implements graceful shutdown and memory-safe cleanup
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.mcpProcessManager = exports.MCPProcessManager = void 0;
9
+ const child_process_1 = require("child_process");
10
+ const logger_js_1 = require("./logger.js");
11
+ const util_1 = require("util");
12
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
13
+ const logger = new logger_js_1.Logger('MCPProcessManager');
14
+ class MCPProcessManager {
15
+ constructor() {
16
+ // INCREASED LIMITS TO PREVENT AGGRESSIVE CLEANUP
17
+ this.MAX_MCP_SERVERS = parseInt(process.env.SNOW_MAX_MCP_SERVERS || '30'); // Increased from 10
18
+ this.MAX_MEMORY_MB = parseInt(process.env.SNOW_MCP_MEMORY_LIMIT || '3000'); // Increased from 1500
19
+ // DISABLED AUTOMATIC CLEANUP BY DEFAULT
20
+ this.CLEANUP_ENABLED = process.env.SNOW_MCP_CLEANUP_ENABLED === 'true'; // Off by default
21
+ this.CLEANUP_INTERVAL = parseInt(process.env.SNOW_MCP_CLEANUP_INTERVAL || '300000'); // 5 minutes instead of 1
22
+ this.isCleaningUp = false;
23
+ // Only start cleanup if explicitly enabled
24
+ if (this.CLEANUP_ENABLED) {
25
+ logger.warn('⚠️ MCP cleanup is ENABLED - monitor for memory issues');
26
+ this.startPeriodicCleanup();
27
+ }
28
+ else {
29
+ logger.info('✅ MCP cleanup is DISABLED for stability');
30
+ }
31
+ }
32
+ static getInstance() {
33
+ if (!MCPProcessManager.instance) {
34
+ MCPProcessManager.instance = new MCPProcessManager();
35
+ }
36
+ return MCPProcessManager.instance;
37
+ }
38
+ /**
39
+ * Check if we can spawn a new MCP server
40
+ */
41
+ canSpawnServer() {
42
+ const status = this.getSystemStatus();
43
+ // More lenient limits
44
+ if (status.processCount >= this.MAX_MCP_SERVERS) {
45
+ logger.warn(`⚠️ At server limit (${status.processCount}/${this.MAX_MCP_SERVERS}) - consider manual cleanup`);
46
+ // Still allow spawning unless critically high
47
+ if (status.processCount >= this.MAX_MCP_SERVERS * 1.5) {
48
+ return false;
49
+ }
50
+ }
51
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB) {
52
+ logger.warn(`⚠️ High memory usage (${status.memoryUsageMB}MB > ${this.MAX_MEMORY_MB}MB)`);
53
+ // Still allow spawning unless critically high
54
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB * 1.5) {
55
+ return false;
56
+ }
57
+ }
58
+ return true;
59
+ }
60
+ /**
61
+ * Get current MCP system status - SAFER VERSION
62
+ */
63
+ getSystemStatus() {
64
+ try {
65
+ // More careful process detection
66
+ const psOutput = (0, child_process_1.execSync)('ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep || true', {
67
+ encoding: 'utf8',
68
+ maxBuffer: 1024 * 1024 // 1MB buffer limit
69
+ }).trim();
70
+ if (!psOutput) {
71
+ return {
72
+ processCount: 0,
73
+ memoryUsageMB: 0,
74
+ processes: []
75
+ };
76
+ }
77
+ const lines = psOutput.split('\n').slice(0, 100); // Limit to 100 processes
78
+ const processes = [];
79
+ let totalMemory = 0;
80
+ for (const line of lines) {
81
+ try {
82
+ const parts = line.split(/\s+/);
83
+ if (parts.length > 10) {
84
+ const pid = parseInt(parts[1]);
85
+ const memory = Math.round(parseInt(parts[5]) / 1024); // Convert KB to MB
86
+ const name = parts.slice(10).join(' ').substring(0, 100); // Limit name length
87
+ if (!isNaN(pid) && !isNaN(memory)) {
88
+ processes.push({ pid, memory, name });
89
+ totalMemory += memory;
90
+ }
91
+ }
92
+ }
93
+ catch (e) {
94
+ // Skip malformed lines
95
+ }
96
+ }
97
+ return {
98
+ processCount: processes.length,
99
+ memoryUsageMB: totalMemory,
100
+ processes
101
+ };
102
+ }
103
+ catch (error) {
104
+ logger.error('Failed to get system status:', error);
105
+ return {
106
+ processCount: 0,
107
+ memoryUsageMB: 0,
108
+ processes: []
109
+ };
110
+ }
111
+ }
112
+ /**
113
+ * Gracefully shutdown a process with timeout
114
+ */
115
+ async gracefulKill(pid, name) {
116
+ try {
117
+ // First try SIGTERM for graceful shutdown
118
+ process.kill(pid, 'SIGTERM');
119
+ // Wait up to 5 seconds for graceful shutdown
120
+ let waited = 0;
121
+ while (waited < 5000) {
122
+ try {
123
+ process.kill(pid, 0); // Check if still alive
124
+ await new Promise(resolve => setTimeout(resolve, 500));
125
+ waited += 500;
126
+ }
127
+ catch {
128
+ // Process terminated
129
+ logger.info(`✅ Gracefully stopped ${name} (PID: ${pid})`);
130
+ return true;
131
+ }
132
+ }
133
+ // Force kill if still alive
134
+ process.kill(pid, 'SIGKILL');
135
+ logger.warn(`⚠️ Force killed ${name} (PID: ${pid})`);
136
+ return true;
137
+ }
138
+ catch (error) {
139
+ if (error.code === 'ESRCH') {
140
+ // Process already dead
141
+ return true;
142
+ }
143
+ logger.error(`Failed to kill ${name} (PID: ${pid}):`, error);
144
+ return false;
145
+ }
146
+ }
147
+ /**
148
+ * Kill duplicate MCP servers - SAFER VERSION
149
+ */
150
+ async killDuplicates() {
151
+ if (this.isCleaningUp) {
152
+ logger.warn('Cleanup already in progress, skipping...');
153
+ return;
154
+ }
155
+ this.isCleaningUp = true;
156
+ try {
157
+ const status = this.getSystemStatus();
158
+ // Group processes by server type
159
+ const serverGroups = new Map();
160
+ for (const proc of status.processes) {
161
+ const match = proc.name.match(/servicenow-([^-]+)-mcp\.js/);
162
+ if (match) {
163
+ const serverType = match[1];
164
+ if (!serverGroups.has(serverType)) {
165
+ serverGroups.set(serverType, []);
166
+ }
167
+ serverGroups.get(serverType).push({
168
+ pid: proc.pid,
169
+ memory: proc.memory
170
+ });
171
+ }
172
+ }
173
+ // Kill duplicates gracefully
174
+ for (const [serverType, procs] of serverGroups) {
175
+ if (procs.length > 2) { // Only clean if more than 2 duplicates
176
+ logger.info(`Found ${procs.length} instances of ${serverType}-mcp`);
177
+ // Sort by memory usage (kill highest consumers first)
178
+ procs.sort((a, b) => b.memory - a.memory);
179
+ // Keep 2 instances, kill the rest
180
+ for (let i = 2; i < procs.length; i++) {
181
+ await this.gracefulKill(procs[i].pid, `${serverType}-mcp`);
182
+ // Wait between kills to avoid memory spike
183
+ await new Promise(resolve => setTimeout(resolve, 1000));
184
+ }
185
+ }
186
+ }
187
+ }
188
+ finally {
189
+ this.isCleaningUp = false;
190
+ }
191
+ }
192
+ /**
193
+ * Emergency cleanup - only for critical situations
194
+ */
195
+ async emergencyCleanup() {
196
+ logger.warn('🚨 EMERGENCY CLEANUP INITIATED');
197
+ const status = this.getSystemStatus();
198
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB * 2) {
199
+ logger.error(`🔴 CRITICAL: Memory usage ${status.memoryUsageMB}MB - killing highest consumers`);
200
+ // Sort by memory usage
201
+ const sorted = status.processes.sort((a, b) => b.memory - a.memory);
202
+ // Kill top 3 memory consumers
203
+ for (let i = 0; i < Math.min(3, sorted.length); i++) {
204
+ await this.gracefulKill(sorted[i].pid, sorted[i].name);
205
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s between kills
206
+ }
207
+ // Force garbage collection if available
208
+ if (global.gc) {
209
+ global.gc();
210
+ logger.info('Forced garbage collection');
211
+ }
212
+ }
213
+ }
214
+ /**
215
+ * Safe cleanup - only when absolutely necessary
216
+ */
217
+ async cleanup() {
218
+ if (!this.CLEANUP_ENABLED) {
219
+ logger.info('Cleanup disabled for stability');
220
+ return;
221
+ }
222
+ if (this.isCleaningUp) {
223
+ logger.warn('Cleanup already in progress');
224
+ return;
225
+ }
226
+ const status = this.getSystemStatus();
227
+ // Only cleanup if REALLY necessary
228
+ if (status.processCount > this.MAX_MCP_SERVERS * 1.5) {
229
+ logger.warn(`🧹 Too many MCP servers (${status.processCount}), cleaning duplicates...`);
230
+ await this.killDuplicates();
231
+ }
232
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB * 1.5) {
233
+ await this.emergencyCleanup();
234
+ }
235
+ }
236
+ /**
237
+ * Start periodic cleanup - MUCH SAFER
238
+ */
239
+ startPeriodicCleanup() {
240
+ if (this.cleanupTimer) {
241
+ clearInterval(this.cleanupTimer);
242
+ }
243
+ // Only run cleanup when critically necessary
244
+ this.cleanupTimer = setInterval(async () => {
245
+ try {
246
+ const status = this.getSystemStatus();
247
+ // Only cleanup if CRITICALLY high
248
+ if (status.processCount > this.MAX_MCP_SERVERS * 2 ||
249
+ status.memoryUsageMB > this.MAX_MEMORY_MB * 2) {
250
+ logger.warn('🔄 Critical resource usage detected, running cleanup...');
251
+ await this.cleanup();
252
+ }
253
+ }
254
+ catch (error) {
255
+ logger.error('Cleanup failed:', error);
256
+ }
257
+ }, this.CLEANUP_INTERVAL);
258
+ // Don't block process exit
259
+ this.cleanupTimer.unref();
260
+ }
261
+ /**
262
+ * Stop periodic cleanup
263
+ */
264
+ stopPeriodicCleanup() {
265
+ if (this.cleanupTimer) {
266
+ clearInterval(this.cleanupTimer);
267
+ this.cleanupTimer = undefined;
268
+ logger.info('✅ Periodic cleanup stopped');
269
+ }
270
+ }
271
+ /**
272
+ * Kill all MCP servers - USE WITH CAUTION
273
+ */
274
+ async killAll() {
275
+ logger.warn('🔴 KILLING ALL MCP PROCESSES');
276
+ try {
277
+ // First try graceful shutdown
278
+ await execAsync('pkill -TERM -f mcp');
279
+ await new Promise(resolve => setTimeout(resolve, 2000));
280
+ // Then force kill any remaining
281
+ await execAsync('pkill -KILL -f mcp');
282
+ logger.info('✅ All MCP processes terminated');
283
+ }
284
+ catch (error) {
285
+ // pkill returns non-zero if no processes found
286
+ logger.info('No MCP processes to kill');
287
+ }
288
+ }
289
+ /**
290
+ * Get resource usage summary
291
+ */
292
+ getResourceSummary() {
293
+ const status = this.getSystemStatus();
294
+ return `MCP Resources:
295
+ Processes: ${status.processCount}/${this.MAX_MCP_SERVERS} (${Math.round(status.processCount / this.MAX_MCP_SERVERS * 100)}%)
296
+ Memory: ${status.memoryUsageMB}MB/${this.MAX_MEMORY_MB}MB (${Math.round(status.memoryUsageMB / this.MAX_MEMORY_MB * 100)}%)
297
+ Cleanup: ${this.CLEANUP_ENABLED ? 'ENABLED' : 'DISABLED'}
298
+ Status: ${this.getHealthStatus()}`;
299
+ }
300
+ /**
301
+ * Get health status
302
+ */
303
+ getHealthStatus() {
304
+ const status = this.getSystemStatus();
305
+ const processPercent = status.processCount / this.MAX_MCP_SERVERS;
306
+ const memoryPercent = status.memoryUsageMB / this.MAX_MEMORY_MB;
307
+ if (processPercent > 1.5 || memoryPercent > 1.5) {
308
+ return 'critical';
309
+ }
310
+ if (processPercent > 1.0 || memoryPercent > 1.0) {
311
+ return 'warning';
312
+ }
313
+ return 'healthy';
314
+ }
315
+ }
316
+ exports.MCPProcessManager = MCPProcessManager;
317
+ // Export singleton instance
318
+ exports.mcpProcessManager = MCPProcessManager.getInstance();
319
+ //# sourceMappingURL=mcp-process-manager-safe.js.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * MCP Process Manager
3
+ * Prevents excessive MCP server spawning and manages resource limits
4
+ */
5
+ export declare class MCPProcessManager {
6
+ private static instance;
7
+ private readonly MAX_MCP_SERVERS;
8
+ private readonly MAX_MEMORY_MB;
9
+ private readonly CLEANUP_INTERVAL;
10
+ private cleanupTimer?;
11
+ private constructor();
12
+ static getInstance(): MCPProcessManager;
13
+ /**
14
+ * Check if we can spawn a new MCP server
15
+ */
16
+ canSpawnServer(): boolean;
17
+ /**
18
+ * Get current MCP system status
19
+ */
20
+ getSystemStatus(): {
21
+ processCount: number;
22
+ memoryUsageMB: number;
23
+ processes: Array<{
24
+ pid: number;
25
+ memory: number;
26
+ name: string;
27
+ }>;
28
+ };
29
+ /**
30
+ * Kill duplicate MCP servers (keep only the newest)
31
+ */
32
+ killDuplicates(): void;
33
+ /**
34
+ * Kill all MCP servers
35
+ */
36
+ killAll(): void;
37
+ /**
38
+ * Clean up excessive resources
39
+ */
40
+ cleanup(): void;
41
+ /**
42
+ * Start periodic cleanup
43
+ */
44
+ private startPeriodicCleanup;
45
+ /**
46
+ * Stop periodic cleanup
47
+ */
48
+ stopPeriodicCleanup(): void;
49
+ /**
50
+ * Get resource usage summary
51
+ */
52
+ getResourceSummary(): string;
53
+ /**
54
+ * Get health status
55
+ */
56
+ getHealthStatus(): 'healthy' | 'warning' | 'critical';
57
+ }
58
+ //# sourceMappingURL=mcp-process-manager.backup.d.ts.map
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ /**
3
+ * MCP Process Manager
4
+ * Prevents excessive MCP server spawning and manages resource limits
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.MCPProcessManager = void 0;
8
+ const child_process_1 = require("child_process");
9
+ const logger_js_1 = require("./logger.js");
10
+ const logger = new logger_js_1.Logger('MCPProcessManager');
11
+ class MCPProcessManager {
12
+ constructor() {
13
+ this.MAX_MCP_SERVERS = parseInt(process.env.SNOW_MAX_MCP_SERVERS || '10');
14
+ this.MAX_MEMORY_MB = parseInt(process.env.SNOW_MCP_MEMORY_LIMIT || '1500');
15
+ this.CLEANUP_INTERVAL = 60000; // 1 minute
16
+ // Start periodic cleanup
17
+ this.startPeriodicCleanup();
18
+ }
19
+ static getInstance() {
20
+ if (!MCPProcessManager.instance) {
21
+ MCPProcessManager.instance = new MCPProcessManager();
22
+ }
23
+ return MCPProcessManager.instance;
24
+ }
25
+ /**
26
+ * Check if we can spawn a new MCP server
27
+ */
28
+ canSpawnServer() {
29
+ const status = this.getSystemStatus();
30
+ if (status.processCount >= this.MAX_MCP_SERVERS) {
31
+ logger.warn(`❌ Cannot spawn: Already at max servers (${status.processCount}/${this.MAX_MCP_SERVERS})`);
32
+ return false;
33
+ }
34
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB) {
35
+ logger.warn(`❌ Cannot spawn: Memory limit exceeded (${status.memoryUsageMB}MB > ${this.MAX_MEMORY_MB}MB)`);
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+ /**
41
+ * Get current MCP system status
42
+ */
43
+ getSystemStatus() {
44
+ try {
45
+ // Count MCP processes
46
+ const psOutput = (0, child_process_1.execSync)('ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep', {
47
+ encoding: 'utf8'
48
+ }).trim();
49
+ if (!psOutput) {
50
+ return {
51
+ processCount: 0,
52
+ memoryUsageMB: 0,
53
+ processes: []
54
+ };
55
+ }
56
+ const lines = psOutput.split('\n');
57
+ const processes = [];
58
+ let totalMemory = 0;
59
+ for (const line of lines) {
60
+ const parts = line.split(/\s+/);
61
+ if (parts.length > 10) {
62
+ const pid = parseInt(parts[1]);
63
+ const memory = Math.round(parseInt(parts[5]) / 1024); // Convert KB to MB
64
+ const name = parts.slice(10).join(' ');
65
+ processes.push({ pid, memory, name });
66
+ totalMemory += memory;
67
+ }
68
+ }
69
+ return {
70
+ processCount: processes.length,
71
+ memoryUsageMB: totalMemory,
72
+ processes
73
+ };
74
+ }
75
+ catch (error) {
76
+ // No MCP processes found
77
+ return {
78
+ processCount: 0,
79
+ memoryUsageMB: 0,
80
+ processes: []
81
+ };
82
+ }
83
+ }
84
+ /**
85
+ * Kill duplicate MCP servers (keep only the newest)
86
+ */
87
+ killDuplicates() {
88
+ const status = this.getSystemStatus();
89
+ // Group processes by server type
90
+ const serverGroups = new Map();
91
+ for (const proc of status.processes) {
92
+ // Extract server type from process name
93
+ const match = proc.name.match(/servicenow-([^-]+)-mcp\.js/);
94
+ if (match) {
95
+ const serverType = match[1];
96
+ if (!serverGroups.has(serverType)) {
97
+ serverGroups.set(serverType, []);
98
+ }
99
+ serverGroups.get(serverType).push({
100
+ pid: proc.pid,
101
+ memory: proc.memory
102
+ });
103
+ }
104
+ }
105
+ // Kill duplicates (keep the first one)
106
+ for (const [serverType, procs] of serverGroups) {
107
+ if (procs.length > 1) {
108
+ logger.warn(`Found ${procs.length} instances of ${serverType}-mcp, killing duplicates...`);
109
+ // Sort by PID (older PIDs first) and keep the first one
110
+ procs.sort((a, b) => a.pid - b.pid);
111
+ for (let i = 1; i < procs.length; i++) {
112
+ try {
113
+ process.kill(procs[i].pid, 'SIGTERM');
114
+ logger.info(`Killed duplicate ${serverType}-mcp (PID: ${procs[i].pid})`);
115
+ }
116
+ catch (error) {
117
+ // Process might already be dead
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ /**
124
+ * Kill all MCP servers
125
+ */
126
+ killAll() {
127
+ try {
128
+ (0, child_process_1.execSync)('pkill -f mcp', { encoding: 'utf8' });
129
+ logger.info('✅ Killed all MCP processes');
130
+ }
131
+ catch (error) {
132
+ // pkill returns non-zero if no processes found
133
+ }
134
+ }
135
+ /**
136
+ * Clean up excessive resources
137
+ */
138
+ cleanup() {
139
+ const status = this.getSystemStatus();
140
+ if (status.processCount > this.MAX_MCP_SERVERS) {
141
+ logger.warn(`🧹 Cleaning up excessive MCP servers (${status.processCount} > ${this.MAX_MCP_SERVERS})`);
142
+ this.killDuplicates();
143
+ }
144
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB) {
145
+ logger.warn(`🧹 Memory usage too high (${status.memoryUsageMB}MB), killing oldest processes...`);
146
+ // Sort by memory usage and kill the highest consumers
147
+ const sorted = status.processes.sort((a, b) => b.memory - a.memory);
148
+ let memoryFreed = 0;
149
+ for (const proc of sorted) {
150
+ if (status.memoryUsageMB - memoryFreed <= this.MAX_MEMORY_MB * 0.8) {
151
+ break; // Stop when we're at 80% of limit
152
+ }
153
+ try {
154
+ process.kill(proc.pid, 'SIGTERM');
155
+ memoryFreed += proc.memory;
156
+ logger.info(`Killed high-memory process (PID: ${proc.pid}, ${proc.memory}MB)`);
157
+ }
158
+ catch (error) {
159
+ // Process might already be dead
160
+ }
161
+ }
162
+ }
163
+ }
164
+ /**
165
+ * Start periodic cleanup
166
+ */
167
+ startPeriodicCleanup() {
168
+ // Clear existing timer
169
+ if (this.cleanupTimer) {
170
+ clearInterval(this.cleanupTimer);
171
+ }
172
+ // Run cleanup periodically
173
+ this.cleanupTimer = setInterval(() => {
174
+ const status = this.getSystemStatus();
175
+ if (status.processCount > this.MAX_MCP_SERVERS * 0.8 ||
176
+ status.memoryUsageMB > this.MAX_MEMORY_MB * 0.8) {
177
+ logger.info('🔄 Running periodic MCP cleanup...');
178
+ this.cleanup();
179
+ }
180
+ }, this.CLEANUP_INTERVAL);
181
+ // Don't block process exit
182
+ this.cleanupTimer.unref();
183
+ }
184
+ /**
185
+ * Stop periodic cleanup
186
+ */
187
+ stopPeriodicCleanup() {
188
+ if (this.cleanupTimer) {
189
+ clearInterval(this.cleanupTimer);
190
+ this.cleanupTimer = undefined;
191
+ }
192
+ }
193
+ /**
194
+ * Get resource usage summary
195
+ */
196
+ getResourceSummary() {
197
+ const status = this.getSystemStatus();
198
+ return `MCP Resources:
199
+ Processes: ${status.processCount}/${this.MAX_MCP_SERVERS} (${Math.round(status.processCount / this.MAX_MCP_SERVERS * 100)}%)
200
+ Memory: ${status.memoryUsageMB}MB/${this.MAX_MEMORY_MB}MB (${Math.round(status.memoryUsageMB / this.MAX_MEMORY_MB * 100)}%)
201
+ Status: ${this.getHealthStatus()}`;
202
+ }
203
+ /**
204
+ * Get health status
205
+ */
206
+ getHealthStatus() {
207
+ const status = this.getSystemStatus();
208
+ const processPercent = status.processCount / this.MAX_MCP_SERVERS;
209
+ const memoryPercent = status.memoryUsageMB / this.MAX_MEMORY_MB;
210
+ if (processPercent > 0.9 || memoryPercent > 0.9) {
211
+ return 'critical';
212
+ }
213
+ if (processPercent > 0.7 || memoryPercent > 0.7) {
214
+ return 'warning';
215
+ }
216
+ return 'healthy';
217
+ }
218
+ }
219
+ exports.MCPProcessManager = MCPProcessManager;
220
+ //# sourceMappingURL=mcp-process-manager.backup.js.map
@@ -1,13 +1,16 @@
1
1
  /**
2
- * MCP Process Manager
3
- * Prevents excessive MCP server spawning and manages resource limits
2
+ * MCP Process Manager - SAFE VERSION
3
+ * Emergency fix for memory crash issues
4
+ * Implements graceful shutdown and memory-safe cleanup
4
5
  */
5
6
  export declare class MCPProcessManager {
6
7
  private static instance;
7
8
  private readonly MAX_MCP_SERVERS;
8
9
  private readonly MAX_MEMORY_MB;
10
+ private readonly CLEANUP_ENABLED;
9
11
  private readonly CLEANUP_INTERVAL;
10
12
  private cleanupTimer?;
13
+ private isCleaningUp;
11
14
  private constructor();
12
15
  static getInstance(): MCPProcessManager;
13
16
  /**
@@ -15,7 +18,7 @@ export declare class MCPProcessManager {
15
18
  */
16
19
  canSpawnServer(): boolean;
17
20
  /**
18
- * Get current MCP system status
21
+ * Get current MCP system status - SAFER VERSION
19
22
  */
20
23
  getSystemStatus(): {
21
24
  processCount: number;
@@ -27,25 +30,33 @@ export declare class MCPProcessManager {
27
30
  }>;
28
31
  };
29
32
  /**
30
- * Kill duplicate MCP servers (keep only the newest)
33
+ * Gracefully shutdown a process with timeout
31
34
  */
32
- killDuplicates(): void;
35
+ private gracefulKill;
33
36
  /**
34
- * Kill all MCP servers
37
+ * Kill duplicate MCP servers - SAFER VERSION
35
38
  */
36
- killAll(): void;
39
+ killDuplicates(): Promise<void>;
37
40
  /**
38
- * Clean up excessive resources
41
+ * Emergency cleanup - only for critical situations
39
42
  */
40
- cleanup(): void;
43
+ emergencyCleanup(): Promise<void>;
41
44
  /**
42
- * Start periodic cleanup
45
+ * Safe cleanup - only when absolutely necessary
46
+ */
47
+ cleanup(): Promise<void>;
48
+ /**
49
+ * Start periodic cleanup - MUCH SAFER
43
50
  */
44
51
  private startPeriodicCleanup;
45
52
  /**
46
53
  * Stop periodic cleanup
47
54
  */
48
55
  stopPeriodicCleanup(): void;
56
+ /**
57
+ * Kill all MCP servers - USE WITH CAUTION
58
+ */
59
+ killAll(): Promise<void>;
49
60
  /**
50
61
  * Get resource usage summary
51
62
  */
@@ -55,4 +66,5 @@ export declare class MCPProcessManager {
55
66
  */
56
67
  getHealthStatus(): 'healthy' | 'warning' | 'critical';
57
68
  }
69
+ export declare const mcpProcessManager: MCPProcessManager;
58
70
  //# sourceMappingURL=mcp-process-manager.d.ts.map
@@ -1,20 +1,33 @@
1
1
  "use strict";
2
2
  /**
3
- * MCP Process Manager
4
- * Prevents excessive MCP server spawning and manages resource limits
3
+ * MCP Process Manager - SAFE VERSION
4
+ * Emergency fix for memory crash issues
5
+ * Implements graceful shutdown and memory-safe cleanup
5
6
  */
6
7
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.MCPProcessManager = void 0;
8
+ exports.mcpProcessManager = exports.MCPProcessManager = void 0;
8
9
  const child_process_1 = require("child_process");
9
10
  const logger_js_1 = require("./logger.js");
11
+ const util_1 = require("util");
12
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
10
13
  const logger = new logger_js_1.Logger('MCPProcessManager');
11
14
  class MCPProcessManager {
12
15
  constructor() {
13
- this.MAX_MCP_SERVERS = parseInt(process.env.SNOW_MAX_MCP_SERVERS || '10');
14
- this.MAX_MEMORY_MB = parseInt(process.env.SNOW_MCP_MEMORY_LIMIT || '1500');
15
- this.CLEANUP_INTERVAL = 60000; // 1 minute
16
- // Start periodic cleanup
17
- this.startPeriodicCleanup();
16
+ // INCREASED LIMITS TO PREVENT AGGRESSIVE CLEANUP
17
+ this.MAX_MCP_SERVERS = parseInt(process.env.SNOW_MAX_MCP_SERVERS || '30'); // Increased from 10
18
+ this.MAX_MEMORY_MB = parseInt(process.env.SNOW_MCP_MEMORY_LIMIT || '3000'); // Increased from 1500
19
+ // DISABLED AUTOMATIC CLEANUP BY DEFAULT
20
+ this.CLEANUP_ENABLED = process.env.SNOW_MCP_CLEANUP_ENABLED === 'true'; // Off by default
21
+ this.CLEANUP_INTERVAL = parseInt(process.env.SNOW_MCP_CLEANUP_INTERVAL || '300000'); // 5 minutes instead of 1
22
+ this.isCleaningUp = false;
23
+ // Only start cleanup if explicitly enabled
24
+ if (this.CLEANUP_ENABLED) {
25
+ logger.warn('⚠️ MCP cleanup is ENABLED - monitor for memory issues');
26
+ this.startPeriodicCleanup();
27
+ }
28
+ else {
29
+ logger.info('✅ MCP cleanup is DISABLED for stability');
30
+ }
18
31
  }
19
32
  static getInstance() {
20
33
  if (!MCPProcessManager.instance) {
@@ -27,24 +40,32 @@ class MCPProcessManager {
27
40
  */
28
41
  canSpawnServer() {
29
42
  const status = this.getSystemStatus();
43
+ // More lenient limits
30
44
  if (status.processCount >= this.MAX_MCP_SERVERS) {
31
- logger.warn(`❌ Cannot spawn: Already at max servers (${status.processCount}/${this.MAX_MCP_SERVERS})`);
32
- return false;
45
+ logger.warn(`⚠️ At server limit (${status.processCount}/${this.MAX_MCP_SERVERS}) - consider manual cleanup`);
46
+ // Still allow spawning unless critically high
47
+ if (status.processCount >= this.MAX_MCP_SERVERS * 1.5) {
48
+ return false;
49
+ }
33
50
  }
34
51
  if (status.memoryUsageMB > this.MAX_MEMORY_MB) {
35
- logger.warn(`❌ Cannot spawn: Memory limit exceeded (${status.memoryUsageMB}MB > ${this.MAX_MEMORY_MB}MB)`);
36
- return false;
52
+ logger.warn(`⚠️ High memory usage (${status.memoryUsageMB}MB > ${this.MAX_MEMORY_MB}MB)`);
53
+ // Still allow spawning unless critically high
54
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB * 1.5) {
55
+ return false;
56
+ }
37
57
  }
38
58
  return true;
39
59
  }
40
60
  /**
41
- * Get current MCP system status
61
+ * Get current MCP system status - SAFER VERSION
42
62
  */
43
63
  getSystemStatus() {
44
64
  try {
45
- // Count MCP processes
46
- const psOutput = (0, child_process_1.execSync)('ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep', {
47
- encoding: 'utf8'
65
+ // More careful process detection
66
+ const psOutput = (0, child_process_1.execSync)('ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep || true', {
67
+ encoding: 'utf8',
68
+ maxBuffer: 1024 * 1024 // 1MB buffer limit
48
69
  }).trim();
49
70
  if (!psOutput) {
50
71
  return {
@@ -53,17 +74,24 @@ class MCPProcessManager {
53
74
  processes: []
54
75
  };
55
76
  }
56
- const lines = psOutput.split('\n');
77
+ const lines = psOutput.split('\n').slice(0, 100); // Limit to 100 processes
57
78
  const processes = [];
58
79
  let totalMemory = 0;
59
80
  for (const line of lines) {
60
- const parts = line.split(/\s+/);
61
- if (parts.length > 10) {
62
- const pid = parseInt(parts[1]);
63
- const memory = Math.round(parseInt(parts[5]) / 1024); // Convert KB to MB
64
- const name = parts.slice(10).join(' ');
65
- processes.push({ pid, memory, name });
66
- totalMemory += memory;
81
+ try {
82
+ const parts = line.split(/\s+/);
83
+ if (parts.length > 10) {
84
+ const pid = parseInt(parts[1]);
85
+ const memory = Math.round(parseInt(parts[5]) / 1024); // Convert KB to MB
86
+ const name = parts.slice(10).join(' ').substring(0, 100); // Limit name length
87
+ if (!isNaN(pid) && !isNaN(memory)) {
88
+ processes.push({ pid, memory, name });
89
+ totalMemory += memory;
90
+ }
91
+ }
92
+ }
93
+ catch (e) {
94
+ // Skip malformed lines
67
95
  }
68
96
  }
69
97
  return {
@@ -73,7 +101,7 @@ class MCPProcessManager {
73
101
  };
74
102
  }
75
103
  catch (error) {
76
- // No MCP processes found
104
+ logger.error('Failed to get system status:', error);
77
105
  return {
78
106
  processCount: 0,
79
107
  memoryUsageMB: 0,
@@ -82,100 +110,149 @@ class MCPProcessManager {
82
110
  }
83
111
  }
84
112
  /**
85
- * Kill duplicate MCP servers (keep only the newest)
113
+ * Gracefully shutdown a process with timeout
86
114
  */
87
- killDuplicates() {
88
- const status = this.getSystemStatus();
89
- // Group processes by server type
90
- const serverGroups = new Map();
91
- for (const proc of status.processes) {
92
- // Extract server type from process name
93
- const match = proc.name.match(/servicenow-([^-]+)-mcp\.js/);
94
- if (match) {
95
- const serverType = match[1];
96
- if (!serverGroups.has(serverType)) {
97
- serverGroups.set(serverType, []);
115
+ async gracefulKill(pid, name) {
116
+ try {
117
+ // First try SIGTERM for graceful shutdown
118
+ process.kill(pid, 'SIGTERM');
119
+ // Wait up to 5 seconds for graceful shutdown
120
+ let waited = 0;
121
+ while (waited < 5000) {
122
+ try {
123
+ process.kill(pid, 0); // Check if still alive
124
+ await new Promise(resolve => setTimeout(resolve, 500));
125
+ waited += 500;
126
+ }
127
+ catch {
128
+ // Process terminated
129
+ logger.info(`✅ Gracefully stopped ${name} (PID: ${pid})`);
130
+ return true;
98
131
  }
99
- serverGroups.get(serverType).push({
100
- pid: proc.pid,
101
- memory: proc.memory
102
- });
103
132
  }
133
+ // Force kill if still alive
134
+ process.kill(pid, 'SIGKILL');
135
+ logger.warn(`⚠️ Force killed ${name} (PID: ${pid})`);
136
+ return true;
104
137
  }
105
- // Kill duplicates (keep the first one)
106
- for (const [serverType, procs] of serverGroups) {
107
- if (procs.length > 1) {
108
- logger.warn(`Found ${procs.length} instances of ${serverType}-mcp, killing duplicates...`);
109
- // Sort by PID (older PIDs first) and keep the first one
110
- procs.sort((a, b) => a.pid - b.pid);
111
- for (let i = 1; i < procs.length; i++) {
112
- try {
113
- process.kill(procs[i].pid, 'SIGTERM');
114
- logger.info(`Killed duplicate ${serverType}-mcp (PID: ${procs[i].pid})`);
115
- }
116
- catch (error) {
117
- // Process might already be dead
118
- }
119
- }
138
+ catch (error) {
139
+ if (error.code === 'ESRCH') {
140
+ // Process already dead
141
+ return true;
120
142
  }
143
+ logger.error(`Failed to kill ${name} (PID: ${pid}):`, error);
144
+ return false;
121
145
  }
122
146
  }
123
147
  /**
124
- * Kill all MCP servers
148
+ * Kill duplicate MCP servers - SAFER VERSION
125
149
  */
126
- killAll() {
150
+ async killDuplicates() {
151
+ if (this.isCleaningUp) {
152
+ logger.warn('Cleanup already in progress, skipping...');
153
+ return;
154
+ }
155
+ this.isCleaningUp = true;
127
156
  try {
128
- (0, child_process_1.execSync)('pkill -f mcp', { encoding: 'utf8' });
129
- logger.info('✅ Killed all MCP processes');
157
+ const status = this.getSystemStatus();
158
+ // Group processes by server type
159
+ const serverGroups = new Map();
160
+ for (const proc of status.processes) {
161
+ const match = proc.name.match(/servicenow-([^-]+)-mcp\.js/);
162
+ if (match) {
163
+ const serverType = match[1];
164
+ if (!serverGroups.has(serverType)) {
165
+ serverGroups.set(serverType, []);
166
+ }
167
+ serverGroups.get(serverType).push({
168
+ pid: proc.pid,
169
+ memory: proc.memory
170
+ });
171
+ }
172
+ }
173
+ // Kill duplicates gracefully
174
+ for (const [serverType, procs] of serverGroups) {
175
+ if (procs.length > 2) { // Only clean if more than 2 duplicates
176
+ logger.info(`Found ${procs.length} instances of ${serverType}-mcp`);
177
+ // Sort by memory usage (kill highest consumers first)
178
+ procs.sort((a, b) => b.memory - a.memory);
179
+ // Keep 2 instances, kill the rest
180
+ for (let i = 2; i < procs.length; i++) {
181
+ await this.gracefulKill(procs[i].pid, `${serverType}-mcp`);
182
+ // Wait between kills to avoid memory spike
183
+ await new Promise(resolve => setTimeout(resolve, 1000));
184
+ }
185
+ }
186
+ }
130
187
  }
131
- catch (error) {
132
- // pkill returns non-zero if no processes found
188
+ finally {
189
+ this.isCleaningUp = false;
133
190
  }
134
191
  }
135
192
  /**
136
- * Clean up excessive resources
193
+ * Emergency cleanup - only for critical situations
137
194
  */
138
- cleanup() {
195
+ async emergencyCleanup() {
196
+ logger.warn('🚨 EMERGENCY CLEANUP INITIATED');
139
197
  const status = this.getSystemStatus();
140
- if (status.processCount > this.MAX_MCP_SERVERS) {
141
- logger.warn(`🧹 Cleaning up excessive MCP servers (${status.processCount} > ${this.MAX_MCP_SERVERS})`);
142
- this.killDuplicates();
143
- }
144
- if (status.memoryUsageMB > this.MAX_MEMORY_MB) {
145
- logger.warn(`🧹 Memory usage too high (${status.memoryUsageMB}MB), killing oldest processes...`);
146
- // Sort by memory usage and kill the highest consumers
198
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB * 2) {
199
+ logger.error(`🔴 CRITICAL: Memory usage ${status.memoryUsageMB}MB - killing highest consumers`);
200
+ // Sort by memory usage
147
201
  const sorted = status.processes.sort((a, b) => b.memory - a.memory);
148
- let memoryFreed = 0;
149
- for (const proc of sorted) {
150
- if (status.memoryUsageMB - memoryFreed <= this.MAX_MEMORY_MB * 0.8) {
151
- break; // Stop when we're at 80% of limit
152
- }
153
- try {
154
- process.kill(proc.pid, 'SIGTERM');
155
- memoryFreed += proc.memory;
156
- logger.info(`Killed high-memory process (PID: ${proc.pid}, ${proc.memory}MB)`);
157
- }
158
- catch (error) {
159
- // Process might already be dead
160
- }
202
+ // Kill top 3 memory consumers
203
+ for (let i = 0; i < Math.min(3, sorted.length); i++) {
204
+ await this.gracefulKill(sorted[i].pid, sorted[i].name);
205
+ await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s between kills
206
+ }
207
+ // Force garbage collection if available
208
+ if (global.gc) {
209
+ global.gc();
210
+ logger.info('Forced garbage collection');
161
211
  }
162
212
  }
163
213
  }
164
214
  /**
165
- * Start periodic cleanup
215
+ * Safe cleanup - only when absolutely necessary
216
+ */
217
+ async cleanup() {
218
+ if (!this.CLEANUP_ENABLED) {
219
+ logger.info('Cleanup disabled for stability');
220
+ return;
221
+ }
222
+ if (this.isCleaningUp) {
223
+ logger.warn('Cleanup already in progress');
224
+ return;
225
+ }
226
+ const status = this.getSystemStatus();
227
+ // Only cleanup if REALLY necessary
228
+ if (status.processCount > this.MAX_MCP_SERVERS * 1.5) {
229
+ logger.warn(`🧹 Too many MCP servers (${status.processCount}), cleaning duplicates...`);
230
+ await this.killDuplicates();
231
+ }
232
+ if (status.memoryUsageMB > this.MAX_MEMORY_MB * 1.5) {
233
+ await this.emergencyCleanup();
234
+ }
235
+ }
236
+ /**
237
+ * Start periodic cleanup - MUCH SAFER
166
238
  */
167
239
  startPeriodicCleanup() {
168
- // Clear existing timer
169
240
  if (this.cleanupTimer) {
170
241
  clearInterval(this.cleanupTimer);
171
242
  }
172
- // Run cleanup periodically
173
- this.cleanupTimer = setInterval(() => {
174
- const status = this.getSystemStatus();
175
- if (status.processCount > this.MAX_MCP_SERVERS * 0.8 ||
176
- status.memoryUsageMB > this.MAX_MEMORY_MB * 0.8) {
177
- logger.info('🔄 Running periodic MCP cleanup...');
178
- this.cleanup();
243
+ // Only run cleanup when critically necessary
244
+ this.cleanupTimer = setInterval(async () => {
245
+ try {
246
+ const status = this.getSystemStatus();
247
+ // Only cleanup if CRITICALLY high
248
+ if (status.processCount > this.MAX_MCP_SERVERS * 2 ||
249
+ status.memoryUsageMB > this.MAX_MEMORY_MB * 2) {
250
+ logger.warn('🔄 Critical resource usage detected, running cleanup...');
251
+ await this.cleanup();
252
+ }
253
+ }
254
+ catch (error) {
255
+ logger.error('Cleanup failed:', error);
179
256
  }
180
257
  }, this.CLEANUP_INTERVAL);
181
258
  // Don't block process exit
@@ -188,6 +265,25 @@ class MCPProcessManager {
188
265
  if (this.cleanupTimer) {
189
266
  clearInterval(this.cleanupTimer);
190
267
  this.cleanupTimer = undefined;
268
+ logger.info('✅ Periodic cleanup stopped');
269
+ }
270
+ }
271
+ /**
272
+ * Kill all MCP servers - USE WITH CAUTION
273
+ */
274
+ async killAll() {
275
+ logger.warn('🔴 KILLING ALL MCP PROCESSES');
276
+ try {
277
+ // First try graceful shutdown
278
+ await execAsync('pkill -TERM -f mcp');
279
+ await new Promise(resolve => setTimeout(resolve, 2000));
280
+ // Then force kill any remaining
281
+ await execAsync('pkill -KILL -f mcp');
282
+ logger.info('✅ All MCP processes terminated');
283
+ }
284
+ catch (error) {
285
+ // pkill returns non-zero if no processes found
286
+ logger.info('No MCP processes to kill');
191
287
  }
192
288
  }
193
289
  /**
@@ -198,6 +294,7 @@ class MCPProcessManager {
198
294
  return `MCP Resources:
199
295
  Processes: ${status.processCount}/${this.MAX_MCP_SERVERS} (${Math.round(status.processCount / this.MAX_MCP_SERVERS * 100)}%)
200
296
  Memory: ${status.memoryUsageMB}MB/${this.MAX_MEMORY_MB}MB (${Math.round(status.memoryUsageMB / this.MAX_MEMORY_MB * 100)}%)
297
+ Cleanup: ${this.CLEANUP_ENABLED ? 'ENABLED' : 'DISABLED'}
201
298
  Status: ${this.getHealthStatus()}`;
202
299
  }
203
300
  /**
@@ -207,14 +304,16 @@ class MCPProcessManager {
207
304
  const status = this.getSystemStatus();
208
305
  const processPercent = status.processCount / this.MAX_MCP_SERVERS;
209
306
  const memoryPercent = status.memoryUsageMB / this.MAX_MEMORY_MB;
210
- if (processPercent > 0.9 || memoryPercent > 0.9) {
307
+ if (processPercent > 1.5 || memoryPercent > 1.5) {
211
308
  return 'critical';
212
309
  }
213
- if (processPercent > 0.7 || memoryPercent > 0.7) {
310
+ if (processPercent > 1.0 || memoryPercent > 1.0) {
214
311
  return 'warning';
215
312
  }
216
313
  return 'healthy';
217
314
  }
218
315
  }
219
316
  exports.MCPProcessManager = MCPProcessManager;
317
+ // Export singleton instance
318
+ exports.mcpProcessManager = MCPProcessManager.getInstance();
220
319
  //# sourceMappingURL=mcp-process-manager.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "2.9.5",
3
+ "version": "2.9.6",
4
4
  "description": "Snow-Flow: ServiceNow Advanced Intelligence Platform - 100+ real MCP tools with AI-powered swarm orchestration and neural networks. Dynamic task categorization using AI. Machine learning for incident classification, change risk prediction, and anomaly detection. Zero Mock Data, 100% Real API Integration.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
@@ -22,6 +22,8 @@
22
22
  "reset-mcp": "node scripts/reset-mcp-servers.js",
23
23
  "reset-mcp:restart": "node scripts/reset-mcp-servers.js --restart",
24
24
  "cleanup-mcp": "node scripts/cleanup-mcp-servers.js",
25
+ "mcp:safe-cleanup": "node scripts/safe-mcp-cleanup.js",
26
+ "mcp:emergency-stop": "pkill -f mcp || true",
25
27
  "mcp:clean": "node scripts/cleanup-mcp-servers.js && npm run build",
26
28
  "mcp:start": "node scripts/start-mcp-proper.js",
27
29
  "mcp:start-proper": "node scripts/start-mcp-proper.js",