snow-flow 8.3.2 โ 8.4.0
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/OPENCODE-SETUP.md +312 -0
- package/OPENCODE-TROUBLESHOOTING.md +381 -0
- package/dist/agents/index.d.ts +2 -2
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -4
- package/dist/agents/index.js.map +1 -1
- package/dist/cli.js +208 -244
- package/dist/cli.js.map +1 -1
- package/dist/memory/session-memory.d.ts +80 -0
- package/dist/memory/session-memory.d.ts.map +1 -0
- package/dist/memory/session-memory.js +468 -0
- package/dist/memory/session-memory.js.map +1 -0
- package/dist/sdk/claude-agent-sdk-integration.d.ts +4 -1
- package/dist/sdk/claude-agent-sdk-integration.d.ts.map +1 -1
- package/dist/sdk/claude-agent-sdk-integration.js.map +1 -1
- package/dist/sdk/index.d.ts +2 -7
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +2 -7
- package/dist/sdk/index.js.map +1 -1
- package/dist/snow-flow-system.d.ts +3 -7
- package/dist/snow-flow-system.d.ts.map +1 -1
- package/dist/snow-flow-system.js +59 -40
- package/dist/snow-flow-system.js.map +1 -1
- package/dist/utils/mcp-output-formatter.d.ts +128 -0
- package/dist/utils/mcp-output-formatter.d.ts.map +1 -0
- package/dist/utils/mcp-output-formatter.js +442 -0
- package/dist/utils/mcp-output-formatter.js.map +1 -0
- package/dist/utils/opencode-output-interceptor.d.ts +40 -0
- package/dist/utils/opencode-output-interceptor.d.ts.map +1 -0
- package/dist/utils/opencode-output-interceptor.js +258 -0
- package/dist/utils/opencode-output-interceptor.js.map +1 -0
- package/package.json +4 -2
- package/scripts/bulk-optimize-tools.js +486 -0
- package/scripts/cleanup-mcp-servers.js +115 -0
- package/scripts/generate-mcp-config.js +45 -0
- package/scripts/mcp-server-manager.sh +320 -0
- package/scripts/optimize-mcp-tools.ts +410 -0
- package/scripts/reset-mcp-servers.js +266 -0
- package/scripts/safe-mcp-cleanup.js +151 -0
- package/scripts/setup-mcp.js +106 -0
- package/scripts/start-mcp-proper.js +76 -0
- package/scripts/start-opencode.sh +123 -0
- package/scripts/start-sysprops-mcp.js +43 -0
- package/scripts/test-todowrite-timeout.js +108 -0
- package/scripts/update-version.js +31 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bulk MCP Tool Optimization Script
|
|
4
|
+
*
|
|
5
|
+
* Optimizes ALL Snow-Flow MCP tools:
|
|
6
|
+
* - Compresses verbose descriptions โ concise
|
|
7
|
+
* - Adds categorization metadata
|
|
8
|
+
* - Calculates token savings
|
|
9
|
+
* - Validates changes
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// CONFIGURATION
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
const TOOL_DIRS = [
|
|
20
|
+
'dist/mcp/servicenow-mcp-unified/tools',
|
|
21
|
+
'dist/mcp/snow-flow/tools'
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const DRY_RUN = process.argv.includes('--dry-run');
|
|
25
|
+
const VERBOSE = process.argv.includes('--verbose');
|
|
26
|
+
|
|
27
|
+
// Description optimization patterns
|
|
28
|
+
const OPTIMIZATIONS = {
|
|
29
|
+
// Remove verbose phrases
|
|
30
|
+
removes: [
|
|
31
|
+
/Executes all \d+ steps automatically:/gi,
|
|
32
|
+
/with automatic/gi,
|
|
33
|
+
/comprehensive/gi,
|
|
34
|
+
/advanced/gi,
|
|
35
|
+
/flexible/gi,
|
|
36
|
+
/powerful/gi,
|
|
37
|
+
/complete/gi,
|
|
38
|
+
/full support for/gi,
|
|
39
|
+
/provides capability to/gi,
|
|
40
|
+
/allows you to/gi,
|
|
41
|
+
/enables you to/gi,
|
|
42
|
+
/enables/gi
|
|
43
|
+
],
|
|
44
|
+
|
|
45
|
+
// Shorten common phrases
|
|
46
|
+
replaces: [
|
|
47
|
+
['Create a new', 'Create'],
|
|
48
|
+
['Create an', 'Create'],
|
|
49
|
+
['Update an existing', 'Update'],
|
|
50
|
+
['Update the', 'Update'],
|
|
51
|
+
['Delete an existing', 'Delete'],
|
|
52
|
+
['Delete the', 'Delete'],
|
|
53
|
+
['Query for', 'Query'],
|
|
54
|
+
['Search for', 'Search'],
|
|
55
|
+
['Retrieve information about', 'Get'],
|
|
56
|
+
['Retrieve', 'Get'],
|
|
57
|
+
['with filtering and pagination', ': filter, paginate'],
|
|
58
|
+
['with pagination and filtering', ': filter, paginate'],
|
|
59
|
+
['with role-based access control', 'with RBAC'],
|
|
60
|
+
['Now Experience Framework', 'UX'],
|
|
61
|
+
['UI Builder', 'UIB'],
|
|
62
|
+
['Service Portal', 'SP'],
|
|
63
|
+
['Configuration Management Database', 'CMDB'],
|
|
64
|
+
['Predictive Intelligence', 'PI'],
|
|
65
|
+
['Performance Analytics', 'PA'],
|
|
66
|
+
['and configuration', ''],
|
|
67
|
+
[' and ', ', ']
|
|
68
|
+
]
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Property description shortcuts
|
|
72
|
+
const PROPERTY_SHORTCUTS = {
|
|
73
|
+
'Table name to query': 'Table name',
|
|
74
|
+
'Table name to create record in': 'Table name',
|
|
75
|
+
'Table name to update': 'Table name',
|
|
76
|
+
'Encoded query string': 'Encoded query',
|
|
77
|
+
'Fields to return': 'Fields',
|
|
78
|
+
'Maximum number of records': 'Max records',
|
|
79
|
+
'Number of records to skip': 'Records to skip',
|
|
80
|
+
'Field to order by': 'Order by',
|
|
81
|
+
'Return display values': 'Display values',
|
|
82
|
+
'Workspace name': 'Name',
|
|
83
|
+
'Page name': 'Name',
|
|
84
|
+
'Component name': 'Name',
|
|
85
|
+
'Description of the': 'Description',
|
|
86
|
+
'System ID of the': 'System ID'
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Metadata taxonomy by directory
|
|
90
|
+
const CATEGORY_TAXONOMY = {
|
|
91
|
+
'operations': {
|
|
92
|
+
category: 'core-operations',
|
|
93
|
+
subcategory: 'general',
|
|
94
|
+
complexity: 'beginner',
|
|
95
|
+
frequency: 'very-high'
|
|
96
|
+
},
|
|
97
|
+
'ui-builder': {
|
|
98
|
+
category: 'ui-frameworks',
|
|
99
|
+
subcategory: 'ui-builder',
|
|
100
|
+
complexity: 'intermediate',
|
|
101
|
+
frequency: 'high'
|
|
102
|
+
},
|
|
103
|
+
'workspace': {
|
|
104
|
+
category: 'ui-frameworks',
|
|
105
|
+
subcategory: 'workspace',
|
|
106
|
+
complexity: 'intermediate',
|
|
107
|
+
frequency: 'high'
|
|
108
|
+
},
|
|
109
|
+
'service-portal': {
|
|
110
|
+
category: 'ui-frameworks',
|
|
111
|
+
subcategory: 'service-portal',
|
|
112
|
+
complexity: 'intermediate',
|
|
113
|
+
frequency: 'medium'
|
|
114
|
+
},
|
|
115
|
+
'update-sets': {
|
|
116
|
+
category: 'development',
|
|
117
|
+
subcategory: 'update-sets',
|
|
118
|
+
complexity: 'beginner',
|
|
119
|
+
frequency: 'very-high'
|
|
120
|
+
},
|
|
121
|
+
'deployment': {
|
|
122
|
+
category: 'development',
|
|
123
|
+
subcategory: 'deployment',
|
|
124
|
+
complexity: 'intermediate',
|
|
125
|
+
frequency: 'high'
|
|
126
|
+
},
|
|
127
|
+
'development': {
|
|
128
|
+
category: 'development',
|
|
129
|
+
subcategory: 'platform',
|
|
130
|
+
complexity: 'intermediate',
|
|
131
|
+
frequency: 'high'
|
|
132
|
+
},
|
|
133
|
+
'automation': {
|
|
134
|
+
category: 'automation',
|
|
135
|
+
subcategory: 'script-execution',
|
|
136
|
+
complexity: 'intermediate',
|
|
137
|
+
frequency: 'high'
|
|
138
|
+
},
|
|
139
|
+
'integration': {
|
|
140
|
+
category: 'integration',
|
|
141
|
+
subcategory: 'rest-soap',
|
|
142
|
+
complexity: 'advanced',
|
|
143
|
+
frequency: 'medium'
|
|
144
|
+
},
|
|
145
|
+
'cmdb': {
|
|
146
|
+
category: 'cmdb',
|
|
147
|
+
subcategory: 'ci-management',
|
|
148
|
+
complexity: 'intermediate',
|
|
149
|
+
frequency: 'medium'
|
|
150
|
+
},
|
|
151
|
+
'knowledge': {
|
|
152
|
+
category: 'itsm',
|
|
153
|
+
subcategory: 'knowledge',
|
|
154
|
+
complexity: 'beginner',
|
|
155
|
+
frequency: 'medium'
|
|
156
|
+
},
|
|
157
|
+
'change': {
|
|
158
|
+
category: 'itsm',
|
|
159
|
+
subcategory: 'change',
|
|
160
|
+
complexity: 'intermediate',
|
|
161
|
+
frequency: 'high'
|
|
162
|
+
},
|
|
163
|
+
'catalog': {
|
|
164
|
+
category: 'itsm',
|
|
165
|
+
subcategory: 'catalog',
|
|
166
|
+
complexity: 'intermediate',
|
|
167
|
+
frequency: 'medium'
|
|
168
|
+
},
|
|
169
|
+
'predictive-intelligence': {
|
|
170
|
+
category: 'ml-analytics',
|
|
171
|
+
subcategory: 'predictive-intelligence',
|
|
172
|
+
complexity: 'advanced',
|
|
173
|
+
frequency: 'medium'
|
|
174
|
+
},
|
|
175
|
+
'performance-analytics': {
|
|
176
|
+
category: 'ml-analytics',
|
|
177
|
+
subcategory: 'performance-analytics',
|
|
178
|
+
complexity: 'intermediate',
|
|
179
|
+
frequency: 'medium'
|
|
180
|
+
},
|
|
181
|
+
'ai-ml': {
|
|
182
|
+
category: 'ml-analytics',
|
|
183
|
+
subcategory: 'machine-learning',
|
|
184
|
+
complexity: 'advanced',
|
|
185
|
+
frequency: 'low'
|
|
186
|
+
},
|
|
187
|
+
'flow-designer': {
|
|
188
|
+
category: 'automation',
|
|
189
|
+
subcategory: 'flow-designer',
|
|
190
|
+
complexity: 'intermediate',
|
|
191
|
+
frequency: 'medium'
|
|
192
|
+
},
|
|
193
|
+
'local-sync': {
|
|
194
|
+
category: 'development',
|
|
195
|
+
subcategory: 'local-sync',
|
|
196
|
+
complexity: 'intermediate',
|
|
197
|
+
frequency: 'high'
|
|
198
|
+
},
|
|
199
|
+
'system-properties': {
|
|
200
|
+
category: 'core-operations',
|
|
201
|
+
subcategory: 'properties',
|
|
202
|
+
complexity: 'beginner',
|
|
203
|
+
frequency: 'high'
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// ============================================================================
|
|
208
|
+
// UTILITY FUNCTIONS
|
|
209
|
+
// ============================================================================
|
|
210
|
+
|
|
211
|
+
function estimateTokens(text) {
|
|
212
|
+
// Rough estimation: 1 token โ 4 characters
|
|
213
|
+
return Math.ceil(text.length / 4);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function optimizeDescription(desc) {
|
|
217
|
+
let optimized = desc;
|
|
218
|
+
|
|
219
|
+
// Apply removals
|
|
220
|
+
OPTIMIZATIONS.removes.forEach(pattern => {
|
|
221
|
+
optimized = optimized.replace(pattern, '');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Apply replacements
|
|
225
|
+
OPTIMIZATIONS.replaces.forEach(([from, to]) => {
|
|
226
|
+
optimized = optimized.replace(new RegExp(from, 'gi'), to);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Clean up whitespace
|
|
230
|
+
optimized = optimized.replace(/\s+/g, ' ').trim();
|
|
231
|
+
optimized = optimized.replace(/,\s*,/g, ',');
|
|
232
|
+
optimized = optimized.replace(/:\s*,/g, ':');
|
|
233
|
+
|
|
234
|
+
return optimized;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function optimizePropertyDesc(desc) {
|
|
238
|
+
let optimized = desc;
|
|
239
|
+
|
|
240
|
+
// Try exact matches first
|
|
241
|
+
for (const [from, to] of Object.entries(PROPERTY_SHORTCUTS)) {
|
|
242
|
+
if (desc.includes(from)) {
|
|
243
|
+
optimized = optimized.replace(from, to);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Remove common redundancies
|
|
248
|
+
optimized = optimized
|
|
249
|
+
.replace(/\(e\.g\.,.*?\)/g, '')
|
|
250
|
+
.replace(/\(default:.*?\)/g, '')
|
|
251
|
+
.replace(/\(optional\)/g, '')
|
|
252
|
+
.trim();
|
|
253
|
+
|
|
254
|
+
return optimized;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function inferMetadata(filePath) {
|
|
258
|
+
// Extract directory name
|
|
259
|
+
const parts = filePath.split(path.sep);
|
|
260
|
+
const toolsIndex = parts.findIndex(p => p === 'tools');
|
|
261
|
+
const dirName = parts[toolsIndex + 1] || 'general';
|
|
262
|
+
|
|
263
|
+
const metadata = CATEGORY_TAXONOMY[dirName] || {
|
|
264
|
+
category: 'advanced',
|
|
265
|
+
subcategory: 'specialized',
|
|
266
|
+
complexity: 'intermediate',
|
|
267
|
+
frequency: 'low'
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
...metadata,
|
|
272
|
+
use_cases: [dirName.replace(/-/g, '_')]
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ============================================================================
|
|
277
|
+
// FILE PROCESSING
|
|
278
|
+
// ============================================================================
|
|
279
|
+
|
|
280
|
+
function processToolFile(filePath) {
|
|
281
|
+
try {
|
|
282
|
+
let content = fs.readFileSync(filePath, 'utf-8');
|
|
283
|
+
|
|
284
|
+
// Skip index files and non-tool files
|
|
285
|
+
if (filePath.includes('index.js') || !content.includes('toolDefinition')) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Extract toolDefinition
|
|
290
|
+
const toolDefStart = content.indexOf('exports.toolDefinition = {');
|
|
291
|
+
const toolDefEnd = content.indexOf('};', toolDefStart);
|
|
292
|
+
|
|
293
|
+
if (toolDefStart === -1 || toolDefEnd === -1) {
|
|
294
|
+
if (VERBOSE) console.warn(`โ ๏ธ Could not find toolDefinition in ${filePath}`);
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const toolDefSection = content.substring(toolDefStart, toolDefEnd + 2);
|
|
299
|
+
|
|
300
|
+
// Extract current description
|
|
301
|
+
const descMatch = toolDefSection.match(/description:\s*['"](.*?)['"]/);
|
|
302
|
+
if (!descMatch) {
|
|
303
|
+
if (VERBOSE) console.warn(`โ ๏ธ No description found in ${filePath}`);
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const originalDesc = descMatch[1];
|
|
308
|
+
const optimizedDesc = optimizeDescription(originalDesc);
|
|
309
|
+
|
|
310
|
+
// Calculate token savings
|
|
311
|
+
const originalTokens = estimateTokens(originalDesc);
|
|
312
|
+
const optimizedTokens = estimateTokens(optimizedDesc);
|
|
313
|
+
const savings = originalTokens - optimizedTokens;
|
|
314
|
+
|
|
315
|
+
// Get metadata
|
|
316
|
+
const metadata = inferMetadata(filePath);
|
|
317
|
+
|
|
318
|
+
// Build new toolDefinition with metadata
|
|
319
|
+
let newToolDef = toolDefSection.replace(
|
|
320
|
+
/description:\s*['"].*?['"]/,
|
|
321
|
+
`description: '${optimizedDesc}',\n // Metadata for tool discovery (not sent to LLM)\n category: '${metadata.category}',\n subcategory: '${metadata.subcategory}',\n use_cases: ${JSON.stringify(metadata.use_cases)},\n complexity: '${metadata.complexity}',\n frequency: '${metadata.frequency}'`
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// Optimize property descriptions
|
|
325
|
+
newToolDef = newToolDef.replace(/description:\s*['"]([^'"]+)['"]/g, (match, desc) => {
|
|
326
|
+
if (match.includes('category:') || match.includes('subcategory:')) {
|
|
327
|
+
return match; // Don't optimize metadata
|
|
328
|
+
}
|
|
329
|
+
const optimized = optimizePropertyDesc(desc);
|
|
330
|
+
return `description: '${optimized}'`;
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Replace in content
|
|
334
|
+
const newContent = content.substring(0, toolDefStart) + newToolDef + content.substring(toolDefEnd + 2);
|
|
335
|
+
|
|
336
|
+
// Write back
|
|
337
|
+
if (!DRY_RUN) {
|
|
338
|
+
fs.writeFileSync(filePath, newContent, 'utf-8');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
file: path.relative(process.cwd(), filePath),
|
|
343
|
+
originalDesc,
|
|
344
|
+
optimizedDesc,
|
|
345
|
+
originalTokens,
|
|
346
|
+
optimizedTokens,
|
|
347
|
+
savings,
|
|
348
|
+
metadata
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
} catch (error) {
|
|
352
|
+
console.error(`โ Error processing ${filePath}:`, error.message);
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function findToolFiles(dir) {
|
|
358
|
+
const files = [];
|
|
359
|
+
|
|
360
|
+
function traverse(currentDir) {
|
|
361
|
+
try {
|
|
362
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
363
|
+
|
|
364
|
+
for (const entry of entries) {
|
|
365
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
366
|
+
|
|
367
|
+
if (entry.isDirectory()) {
|
|
368
|
+
traverse(fullPath);
|
|
369
|
+
} else if (entry.isFile() && entry.name.endsWith('.js') && !entry.name.endsWith('.map.js')) {
|
|
370
|
+
files.push(fullPath);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.warn(`โ ๏ธ Could not read directory ${currentDir}:`, error.message);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
traverse(dir);
|
|
379
|
+
return files;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ============================================================================
|
|
383
|
+
// MAIN EXECUTION
|
|
384
|
+
// ============================================================================
|
|
385
|
+
|
|
386
|
+
async function main() {
|
|
387
|
+
console.log('๐ Starting Bulk MCP Tool Optimization...\n');
|
|
388
|
+
console.log(`Mode: ${DRY_RUN ? 'DRY RUN (no files will be modified)' : 'LIVE (files will be modified)'}\n`);
|
|
389
|
+
|
|
390
|
+
const results = [];
|
|
391
|
+
let totalOriginalTokens = 0;
|
|
392
|
+
let totalOptimizedTokens = 0;
|
|
393
|
+
let totalFiles = 0;
|
|
394
|
+
|
|
395
|
+
for (const toolDir of TOOL_DIRS) {
|
|
396
|
+
const fullPath = path.join(process.cwd(), toolDir);
|
|
397
|
+
|
|
398
|
+
if (!fs.existsSync(fullPath)) {
|
|
399
|
+
console.warn(`โ ๏ธ Directory not found: ${toolDir}`);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
console.log(`๐ Processing: ${toolDir}`);
|
|
404
|
+
const files = findToolFiles(fullPath);
|
|
405
|
+
console.log(` Found ${files.length} JavaScript files`);
|
|
406
|
+
|
|
407
|
+
let processed = 0;
|
|
408
|
+
for (const file of files) {
|
|
409
|
+
const result = processToolFile(file);
|
|
410
|
+
if (result) {
|
|
411
|
+
results.push(result);
|
|
412
|
+
totalOriginalTokens += result.originalTokens;
|
|
413
|
+
totalOptimizedTokens += result.optimizedTokens;
|
|
414
|
+
processed++;
|
|
415
|
+
|
|
416
|
+
if (VERBOSE) {
|
|
417
|
+
console.log(` โ ${result.file} (${result.savings} tokens saved)`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
totalFiles += processed;
|
|
423
|
+
console.log(` โ
Processed ${processed} tool files\n`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Print summary
|
|
427
|
+
console.log('='.repeat(80));
|
|
428
|
+
console.log('๐ OPTIMIZATION SUMMARY');
|
|
429
|
+
console.log('='.repeat(80));
|
|
430
|
+
console.log(`Total tools optimized: ${totalFiles}`);
|
|
431
|
+
console.log(`Original description tokens: ${totalOriginalTokens.toLocaleString()}`);
|
|
432
|
+
console.log(`Optimized description tokens: ${totalOptimizedTokens.toLocaleString()}`);
|
|
433
|
+
console.log(`Total savings: ${(totalOriginalTokens - totalOptimizedTokens).toLocaleString()} tokens`);
|
|
434
|
+
console.log(`Reduction: ${((1 - totalOptimizedTokens / totalOriginalTokens) * 100).toFixed(1)}%`);
|
|
435
|
+
console.log('='.repeat(80));
|
|
436
|
+
|
|
437
|
+
// Category breakdown
|
|
438
|
+
const byCategory = {};
|
|
439
|
+
results.forEach(r => {
|
|
440
|
+
const cat = r.metadata.category;
|
|
441
|
+
if (!byCategory[cat]) {
|
|
442
|
+
byCategory[cat] = { count: 0, savings: 0 };
|
|
443
|
+
}
|
|
444
|
+
byCategory[cat].count++;
|
|
445
|
+
byCategory[cat].savings += r.savings;
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
console.log('\n๐ BY CATEGORY:');
|
|
449
|
+
Object.entries(byCategory)
|
|
450
|
+
.sort((a, b) => b[1].savings - a[1].savings)
|
|
451
|
+
.forEach(([cat, data]) => {
|
|
452
|
+
console.log(` ${cat.padEnd(25)} ${data.count.toString().padStart(3)} tools, ${data.savings.toString().padStart(5)} tokens saved`);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// Save detailed report
|
|
456
|
+
const report = {
|
|
457
|
+
timestamp: new Date().toISOString(),
|
|
458
|
+
mode: DRY_RUN ? 'dry-run' : 'live',
|
|
459
|
+
summary: {
|
|
460
|
+
totalTools: totalFiles,
|
|
461
|
+
originalTokens: totalOriginalTokens,
|
|
462
|
+
optimizedTokens: totalOptimizedTokens,
|
|
463
|
+
savings: totalOriginalTokens - totalOptimizedTokens,
|
|
464
|
+
reductionPercent: parseFloat(((1 - totalOptimizedTokens / totalOriginalTokens) * 100).toFixed(1))
|
|
465
|
+
},
|
|
466
|
+
byCategory,
|
|
467
|
+
results: results.map(r => ({
|
|
468
|
+
file: r.file,
|
|
469
|
+
category: r.metadata.category,
|
|
470
|
+
originalDesc: r.originalDesc,
|
|
471
|
+
optimizedDesc: r.optimizedDesc,
|
|
472
|
+
savings: r.savings
|
|
473
|
+
}))
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const reportPath = path.join(process.cwd(), 'docs/optimization-report.json');
|
|
477
|
+
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
478
|
+
console.log(`\nโ
Detailed report saved to: ${reportPath}`);
|
|
479
|
+
|
|
480
|
+
if (DRY_RUN) {
|
|
481
|
+
console.log('\nโ ๏ธ DRY RUN MODE: No files were modified. Run without --dry-run to apply changes.');
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Run
|
|
486
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP Server Cleanup Script
|
|
5
|
+
* Prevents duplicate MCP servers and memory exhaustion
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { exec } = require('child_process');
|
|
9
|
+
const { promisify } = require('util');
|
|
10
|
+
const execAsync = promisify(exec);
|
|
11
|
+
|
|
12
|
+
async function cleanupMCPServers() {
|
|
13
|
+
console.log('๐งน Cleaning up MCP servers...\n');
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// 1. Check for running MCP processes
|
|
17
|
+
const { stdout: psOutput } = await execAsync('ps aux | grep -E "node.*mcp" | grep -v grep | wc -l');
|
|
18
|
+
const processCount = parseInt(psOutput.trim());
|
|
19
|
+
|
|
20
|
+
if (processCount > 0) {
|
|
21
|
+
console.log(`โ ๏ธ Found ${processCount} running MCP processes`);
|
|
22
|
+
console.log('๐ช Killing all MCP processes...');
|
|
23
|
+
|
|
24
|
+
// Kill all MCP processes
|
|
25
|
+
await execAsync('pkill -f "node.*mcp"').catch(() => {
|
|
26
|
+
// Ignore errors if no processes found
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Wait for processes to terminate
|
|
30
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
31
|
+
|
|
32
|
+
console.log('โ
All MCP processes terminated\n');
|
|
33
|
+
} else {
|
|
34
|
+
console.log('โ
No MCP processes running\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. Clean up any stale lock files
|
|
38
|
+
console.log('๐ Cleaning up lock files...');
|
|
39
|
+
await execAsync('rm -f /tmp/mcp-*.lock').catch(() => {});
|
|
40
|
+
await execAsync('rm -f ~/.claude/mcp-*.lock').catch(() => {});
|
|
41
|
+
await execAsync('rm -f ~/.claude/mcp-servers.lock').catch(() => {});
|
|
42
|
+
|
|
43
|
+
// 3. Check memory usage
|
|
44
|
+
const { stdout: memOutput } = await execAsync('ps aux | grep node | awk \'{sum+=$6} END {print sum/1024}\'');
|
|
45
|
+
const nodeMemoryMB = parseFloat(memOutput.trim());
|
|
46
|
+
|
|
47
|
+
console.log(`๐ Node.js memory usage: ${nodeMemoryMB.toFixed(2)} MB\n`);
|
|
48
|
+
|
|
49
|
+
if (nodeMemoryMB > 1000) {
|
|
50
|
+
console.log('โ ๏ธ High memory usage detected!');
|
|
51
|
+
console.log('๐ก Recommendation: Restart your terminal or run "killall node"\n');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 4. Create singleton lock mechanism
|
|
55
|
+
console.log('๐ Setting up singleton lock mechanism...');
|
|
56
|
+
const lockScript = `
|
|
57
|
+
const fs = require('fs');
|
|
58
|
+
const path = require('path');
|
|
59
|
+
|
|
60
|
+
const lockFile = path.join(process.env.HOME, '.claude', 'mcp-singleton.lock');
|
|
61
|
+
const pid = process.pid;
|
|
62
|
+
|
|
63
|
+
// Check if lock exists
|
|
64
|
+
if (fs.existsSync(lockFile)) {
|
|
65
|
+
const existingPid = fs.readFileSync(lockFile, 'utf8');
|
|
66
|
+
try {
|
|
67
|
+
// Check if process is still running
|
|
68
|
+
process.kill(existingPid, 0);
|
|
69
|
+
console.error('MCP servers already running with PID:', existingPid);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
// Process not running, remove stale lock
|
|
73
|
+
fs.unlinkSync(lockFile);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Create lock
|
|
78
|
+
fs.mkdirSync(path.dirname(lockFile), { recursive: true });
|
|
79
|
+
fs.writeFileSync(lockFile, pid.toString());
|
|
80
|
+
|
|
81
|
+
// Clean up on exit
|
|
82
|
+
process.on('exit', () => {
|
|
83
|
+
try {
|
|
84
|
+
fs.unlinkSync(lockFile);
|
|
85
|
+
} catch (e) {}
|
|
86
|
+
});
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
// Save singleton script
|
|
90
|
+
const fs = require('fs');
|
|
91
|
+
const path = require('path');
|
|
92
|
+
const singletonPath = path.join(__dirname, '..', 'dist', 'mcp', 'singleton-check.js');
|
|
93
|
+
fs.writeFileSync(singletonPath, lockScript);
|
|
94
|
+
console.log('โ
Singleton mechanism created\n');
|
|
95
|
+
|
|
96
|
+
// 5. Report status
|
|
97
|
+
console.log('๐ Cleanup Summary:');
|
|
98
|
+
console.log('โ'.repeat(40));
|
|
99
|
+
console.log('โ
All MCP processes terminated');
|
|
100
|
+
console.log('โ
Lock files cleaned');
|
|
101
|
+
console.log('โ
Singleton mechanism installed');
|
|
102
|
+
console.log(`โ
Memory usage: ${nodeMemoryMB.toFixed(2)} MB`);
|
|
103
|
+
console.log('\n๐ฏ Next Steps:');
|
|
104
|
+
console.log('1. Run "npm run build" to rebuild');
|
|
105
|
+
console.log('2. Use "snow-flow mcp start" to start servers properly');
|
|
106
|
+
console.log('3. Monitor with "snow-flow mcp status"');
|
|
107
|
+
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error('โ Cleanup failed:', error.message);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Run cleanup
|
|
115
|
+
cleanupMCPServers().catch(console.error);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const dotenv = require('dotenv');
|
|
6
|
+
|
|
7
|
+
// Load environment variables
|
|
8
|
+
const envPath = path.join(__dirname, '..', '.env');
|
|
9
|
+
if (fs.existsSync(envPath)) {
|
|
10
|
+
dotenv.config({ path: envPath });
|
|
11
|
+
} else {
|
|
12
|
+
console.error('โ .env file not found!');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Read template
|
|
17
|
+
const templatePath = path.join(__dirname, '..', '.mcp.json.template');
|
|
18
|
+
const template = JSON.parse(fs.readFileSync(templatePath, 'utf-8'));
|
|
19
|
+
|
|
20
|
+
// Process template - replace variables
|
|
21
|
+
const config = JSON.parse(JSON.stringify(template)
|
|
22
|
+
.replace(/{{PROJECT_ROOT}}/g, path.resolve(__dirname, '..'))
|
|
23
|
+
.replace(/{{SNOW_INSTANCE}}/g, process.env.SNOW_INSTANCE || '')
|
|
24
|
+
.replace(/{{SNOW_CLIENT_ID}}/g, process.env.SNOW_CLIENT_ID || '')
|
|
25
|
+
.replace(/{{SNOW_CLIENT_SECRET}}/g, process.env.SNOW_CLIENT_SECRET || '')
|
|
26
|
+
.replace(/{{SNOW_DEPLOYMENT_TIMEOUT}}/g, process.env.SNOW_DEPLOYMENT_TIMEOUT || '300000')
|
|
27
|
+
.replace(/{{MCP_DEPLOYMENT_TIMEOUT}}/g, process.env.MCP_DEPLOYMENT_TIMEOUT || '360000')
|
|
28
|
+
.replace(/{{SNOW_FLOW_ENV}}/g, process.env.SNOW_FLOW_ENV || 'production')
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Write config
|
|
32
|
+
const outputPath = path.join(__dirname, '..', '.mcp.json');
|
|
33
|
+
fs.writeFileSync(outputPath, JSON.stringify(config, null, 2));
|
|
34
|
+
|
|
35
|
+
console.log('โ
Generated .mcp.json with all ServiceNow MCP servers');
|
|
36
|
+
console.log(`๐ Location: ${outputPath}`);
|
|
37
|
+
console.log(`๐ง Servers configured: ${Object.keys(config.servers).length}`);
|
|
38
|
+
console.log('\nServers:');
|
|
39
|
+
Object.keys(config.servers).forEach(server => {
|
|
40
|
+
console.log(` โ ${server}`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log('\n๐ก Next steps:');
|
|
44
|
+
console.log('1. Restart Claude Code to load the new configuration');
|
|
45
|
+
console.log('2. Run /doctor again to verify everything is working');
|