snow-flow 2.8.6 โ 2.8.7
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/mcp/start-all-mcp-servers.d.ts +1 -1
- package/dist/mcp/start-all-mcp-servers.js +9 -2
- package/dist/test-singleton-protection.d.ts +8 -0
- package/dist/test-singleton-protection.js +110 -0
- package/dist/utils/mcp-server-manager.js +7 -0
- package/dist/utils/mcp-singleton-lock.d.ts +35 -0
- package/dist/utils/mcp-singleton-lock.js +132 -0
- package/package.json +1 -1
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
* Start all ServiceNow MCP servers
|
|
4
|
+
* Start all ServiceNow MCP servers (WITH SINGLETON PROTECTION)
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const logger_js_1 = require("../utils/logger.js");
|
|
9
|
+
const mcp_singleton_lock_js_1 = require("../utils/mcp-singleton-lock.js");
|
|
9
10
|
const logger = new logger_js_1.Logger('MCPServerLauncher');
|
|
10
11
|
async function startAllServers() {
|
|
11
|
-
|
|
12
|
+
// ๐ SINGLETON CHECK - Prevent duplicate instances
|
|
13
|
+
const singletonLock = (0, mcp_singleton_lock_js_1.getMCPSingletonLock)();
|
|
14
|
+
if (!singletonLock.acquire()) {
|
|
15
|
+
logger.error('โ MCP servers already running. Exiting.');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
logger.info('โ
Starting all ServiceNow MCP servers (singleton protected)...');
|
|
12
19
|
const servers = [
|
|
13
20
|
{
|
|
14
21
|
name: 'ServiceNow MCP Server',
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test Singleton Protection for MCP Servers
|
|
4
|
+
* Verifies that duplicate MCP server instances cannot start
|
|
5
|
+
*/
|
|
6
|
+
declare function testSingletonProtection(): Promise<boolean>;
|
|
7
|
+
export { testSingletonProtection };
|
|
8
|
+
//# sourceMappingURL=test-singleton-protection.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Test Singleton Protection for MCP Servers
|
|
5
|
+
* Verifies that duplicate MCP server instances cannot start
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.testSingletonProtection = testSingletonProtection;
|
|
9
|
+
const mcp_singleton_lock_js_1 = require("./utils/mcp-singleton-lock.js");
|
|
10
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
11
|
+
const logger = new logger_js_1.Logger('SingletonTest');
|
|
12
|
+
async function testSingletonProtection() {
|
|
13
|
+
console.log('๐งช Testing MCP Singleton Protection\n');
|
|
14
|
+
console.log('โ'.repeat(60));
|
|
15
|
+
try {
|
|
16
|
+
// Test 1: First instance should acquire lock successfully
|
|
17
|
+
console.log('\n1๏ธโฃ Testing first instance lock acquisition...');
|
|
18
|
+
const firstLock = (0, mcp_singleton_lock_js_1.getMCPSingletonLock)();
|
|
19
|
+
const firstResult = firstLock.acquire();
|
|
20
|
+
if (firstResult) {
|
|
21
|
+
console.log('โ
First instance successfully acquired lock');
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.log('โ First instance failed to acquire lock');
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
// Test 2: Second instance should fail to acquire lock
|
|
28
|
+
console.log('\n2๏ธโฃ Testing second instance prevention...');
|
|
29
|
+
const secondLock = new mcp_singleton_lock_js_1.MCPSingletonLock();
|
|
30
|
+
const secondResult = secondLock.acquire();
|
|
31
|
+
if (!secondResult) {
|
|
32
|
+
console.log('โ
Second instance correctly prevented (singleton working!)');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log('โ Second instance acquired lock (singleton BROKEN!)');
|
|
36
|
+
secondLock.release();
|
|
37
|
+
firstLock.release();
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Test 3: Lock release and re-acquisition
|
|
41
|
+
console.log('\n3๏ธโฃ Testing lock release and re-acquisition...');
|
|
42
|
+
firstLock.release();
|
|
43
|
+
// Wait a moment for cleanup
|
|
44
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
45
|
+
const thirdLock = new mcp_singleton_lock_js_1.MCPSingletonLock();
|
|
46
|
+
const thirdResult = thirdLock.acquire();
|
|
47
|
+
if (thirdResult) {
|
|
48
|
+
console.log('โ
Lock successfully re-acquired after release');
|
|
49
|
+
thirdLock.release();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log('โ Failed to re-acquire lock after release');
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
// Test 4: Force release functionality
|
|
56
|
+
console.log('\n4๏ธโฃ Testing force release functionality...');
|
|
57
|
+
const fourthLock = new mcp_singleton_lock_js_1.MCPSingletonLock();
|
|
58
|
+
fourthLock.acquire();
|
|
59
|
+
const forceResult = mcp_singleton_lock_js_1.MCPSingletonLock.forceRelease();
|
|
60
|
+
if (forceResult) {
|
|
61
|
+
console.log('โ
Force release successful');
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
console.log('โ ๏ธ No lock to force release (expected if cleanup worked)');
|
|
65
|
+
}
|
|
66
|
+
// Clean up
|
|
67
|
+
fourthLock.release();
|
|
68
|
+
// Summary
|
|
69
|
+
console.log('\n' + 'โ'.repeat(60));
|
|
70
|
+
console.log('\n๐ SINGLETON PROTECTION TEST RESULTS:\n');
|
|
71
|
+
console.log('โ
First instance acquires lock correctly');
|
|
72
|
+
console.log('โ
Duplicate instances are prevented');
|
|
73
|
+
console.log('โ
Lock release works correctly');
|
|
74
|
+
console.log('โ
Lock re-acquisition works after release');
|
|
75
|
+
console.log('โ
Force release works for cleanup');
|
|
76
|
+
console.log('\n๐ฏ Practical Impact:');
|
|
77
|
+
console.log('โข No more duplicate MCP servers');
|
|
78
|
+
console.log('โข No more memory exhaustion (1.5GB+ usage)');
|
|
79
|
+
console.log('โข No more random timeouts in swarm operations');
|
|
80
|
+
console.log('โข MCP servers cleanly prevent conflicts');
|
|
81
|
+
console.log('\n๐ง Usage:');
|
|
82
|
+
console.log('โข MCP servers automatically use singleton protection');
|
|
83
|
+
console.log('โข If stuck: run "npm run cleanup-mcp"');
|
|
84
|
+
console.log('โข Or kill manually: "pkill -f mcp"');
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error('\nโ Singleton test failed:', error.message);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Run test if executed directly
|
|
93
|
+
if (require.main === module) {
|
|
94
|
+
testSingletonProtection()
|
|
95
|
+
.then(success => {
|
|
96
|
+
if (success) {
|
|
97
|
+
console.log('\n๐ All singleton protection tests PASSED!');
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log('\n๐ฅ Singleton protection tests FAILED!');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
.catch(error => {
|
|
106
|
+
console.error('\n๐ฅ Test execution failed:', error);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=test-singleton-protection.js.map
|
|
@@ -13,6 +13,7 @@ const path_1 = require("path");
|
|
|
13
13
|
const events_1 = require("events");
|
|
14
14
|
const os_1 = __importDefault(require("os"));
|
|
15
15
|
const unified_auth_store_js_1 = require("./unified-auth-store.js");
|
|
16
|
+
const mcp_singleton_lock_js_1 = require("./mcp-singleton-lock.js");
|
|
16
17
|
class MCPServerManager extends events_1.EventEmitter {
|
|
17
18
|
constructor(configPath) {
|
|
18
19
|
super();
|
|
@@ -281,6 +282,12 @@ class MCPServerManager extends events_1.EventEmitter {
|
|
|
281
282
|
* Start all configured MCP servers
|
|
282
283
|
*/
|
|
283
284
|
async startAllServers() {
|
|
285
|
+
// ๐ SINGLETON CHECK - Prevent duplicate instances
|
|
286
|
+
const singletonLock = (0, mcp_singleton_lock_js_1.getMCPSingletonLock)();
|
|
287
|
+
if (!singletonLock.acquire()) {
|
|
288
|
+
throw new Error('โ MCP servers already running. Cannot start duplicate instances.');
|
|
289
|
+
}
|
|
290
|
+
console.log('โ
Starting all MCP servers (singleton protected)...');
|
|
284
291
|
const promises = Array.from(this.servers.keys()).map(name => this.startServer(name).catch(error => {
|
|
285
292
|
console.error(`Failed to start server '${name}':`, error);
|
|
286
293
|
return false;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Singleton Lock Utility
|
|
3
|
+
* Prevents duplicate MCP server instances across all start mechanisms
|
|
4
|
+
*/
|
|
5
|
+
export declare class MCPSingletonLock {
|
|
6
|
+
private lockDir;
|
|
7
|
+
private lockFile;
|
|
8
|
+
private acquired;
|
|
9
|
+
constructor();
|
|
10
|
+
/**
|
|
11
|
+
* Acquire singleton lock to prevent duplicate MCP server instances
|
|
12
|
+
*/
|
|
13
|
+
acquire(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Release the singleton lock
|
|
16
|
+
*/
|
|
17
|
+
release(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Check if lock is currently held by this process
|
|
20
|
+
*/
|
|
21
|
+
isAcquired(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Setup cleanup handlers to release lock on process exit
|
|
24
|
+
*/
|
|
25
|
+
private setupCleanupHandlers;
|
|
26
|
+
/**
|
|
27
|
+
* Force release any existing lock (for cleanup scripts)
|
|
28
|
+
*/
|
|
29
|
+
static forceRelease(): boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the global MCP singleton lock instance
|
|
33
|
+
*/
|
|
34
|
+
export declare function getMCPSingletonLock(): MCPSingletonLock;
|
|
35
|
+
//# sourceMappingURL=mcp-singleton-lock.d.ts.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP Singleton Lock Utility
|
|
4
|
+
* Prevents duplicate MCP server instances across all start mechanisms
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MCPSingletonLock = void 0;
|
|
8
|
+
exports.getMCPSingletonLock = getMCPSingletonLock;
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const os_1 = require("os");
|
|
12
|
+
const logger_js_1 = require("./logger.js");
|
|
13
|
+
const logger = new logger_js_1.Logger('MCPSingleton');
|
|
14
|
+
class MCPSingletonLock {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.acquired = false;
|
|
17
|
+
this.lockDir = (0, path_1.join)((0, os_1.homedir)(), '.claude');
|
|
18
|
+
this.lockFile = (0, path_1.join)(this.lockDir, 'mcp-servers.lock');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Acquire singleton lock to prevent duplicate MCP server instances
|
|
22
|
+
*/
|
|
23
|
+
acquire() {
|
|
24
|
+
// Create .claude directory if it doesn't exist
|
|
25
|
+
if (!(0, fs_1.existsSync)(this.lockDir)) {
|
|
26
|
+
(0, fs_1.mkdirSync)(this.lockDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
// Check if lock exists and process is still running
|
|
29
|
+
if ((0, fs_1.existsSync)(this.lockFile)) {
|
|
30
|
+
try {
|
|
31
|
+
const existingPid = parseInt((0, fs_1.readFileSync)(this.lockFile, 'utf8'));
|
|
32
|
+
// Check if process is still running
|
|
33
|
+
try {
|
|
34
|
+
process.kill(existingPid, 0); // Signal 0 just checks if process exists
|
|
35
|
+
logger.warn(`MCP servers already running with PID: ${existingPid}`);
|
|
36
|
+
logger.info('Use "pkill -f mcp" to stop existing servers first');
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
// Process not running, remove stale lock
|
|
41
|
+
logger.info('Removing stale lock file from dead process');
|
|
42
|
+
(0, fs_1.unlinkSync)(this.lockFile);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
// Invalid lock file, remove it
|
|
47
|
+
logger.info('Removing invalid lock file');
|
|
48
|
+
(0, fs_1.unlinkSync)(this.lockFile);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Create new lock with current PID
|
|
52
|
+
(0, fs_1.writeFileSync)(this.lockFile, process.pid.toString());
|
|
53
|
+
logger.info(`๐ MCP singleton lock acquired (PID: ${process.pid})`);
|
|
54
|
+
this.acquired = true;
|
|
55
|
+
// Set up cleanup handlers
|
|
56
|
+
this.setupCleanupHandlers();
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Release the singleton lock
|
|
61
|
+
*/
|
|
62
|
+
release() {
|
|
63
|
+
if (this.acquired && (0, fs_1.existsSync)(this.lockFile)) {
|
|
64
|
+
try {
|
|
65
|
+
(0, fs_1.unlinkSync)(this.lockFile);
|
|
66
|
+
logger.info('๐ MCP singleton lock released');
|
|
67
|
+
this.acquired = false;
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
logger.warn('Failed to release lock file:', e);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if lock is currently held by this process
|
|
76
|
+
*/
|
|
77
|
+
isAcquired() {
|
|
78
|
+
return this.acquired;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Setup cleanup handlers to release lock on process exit
|
|
82
|
+
*/
|
|
83
|
+
setupCleanupHandlers() {
|
|
84
|
+
const cleanup = () => {
|
|
85
|
+
this.release();
|
|
86
|
+
};
|
|
87
|
+
process.on('exit', cleanup);
|
|
88
|
+
process.on('SIGINT', cleanup);
|
|
89
|
+
process.on('SIGTERM', cleanup);
|
|
90
|
+
process.on('uncaughtException', (error) => {
|
|
91
|
+
logger.error('Uncaught exception, releasing MCP lock:', error);
|
|
92
|
+
cleanup();
|
|
93
|
+
process.exit(1);
|
|
94
|
+
});
|
|
95
|
+
process.on('unhandledRejection', (reason) => {
|
|
96
|
+
logger.error('Unhandled rejection, releasing MCP lock:', reason);
|
|
97
|
+
cleanup();
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Force release any existing lock (for cleanup scripts)
|
|
103
|
+
*/
|
|
104
|
+
static forceRelease() {
|
|
105
|
+
const lockFile = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'mcp-servers.lock');
|
|
106
|
+
if ((0, fs_1.existsSync)(lockFile)) {
|
|
107
|
+
try {
|
|
108
|
+
(0, fs_1.unlinkSync)(lockFile);
|
|
109
|
+
logger.info('๐ Forced release of MCP singleton lock');
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
logger.error('Failed to force release lock:', e);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return false; // No lock existed
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.MCPSingletonLock = MCPSingletonLock;
|
|
121
|
+
// Global singleton instance
|
|
122
|
+
let globalLock = null;
|
|
123
|
+
/**
|
|
124
|
+
* Get the global MCP singleton lock instance
|
|
125
|
+
*/
|
|
126
|
+
function getMCPSingletonLock() {
|
|
127
|
+
if (!globalLock) {
|
|
128
|
+
globalLock = new MCPSingletonLock();
|
|
129
|
+
}
|
|
130
|
+
return globalLock;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=mcp-singleton-lock.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.7",
|
|
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",
|