snow-flow 1.3.23 โ 1.3.25
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/intelligence/multi-pass-requirements-analyzer.js +477 -0
- package/dist/intelligence/performance-recommendations-engine.js +485 -0
- package/dist/mcp/base-mcp-server.js +183 -15
- package/dist/mcp/servicenow-flow-composer-mcp.js +696 -46
- package/dist/mcp/servicenow-intelligent-mcp.js +354 -25
- package/dist/mcp/servicenow-security-compliance-mcp-refactored.js +9 -9
- package/dist/memory/memory-system.js +89 -17
- package/dist/utils/servicenow-client.js +79 -18
- package/dist/utils/xml-first-flow-generator.js +29 -8
- package/npm-publish-summary.txt +35 -0
- package/package.json +1 -1
|
@@ -26,6 +26,8 @@ class BaseMCPServer {
|
|
|
26
26
|
this.tools = new Map();
|
|
27
27
|
// Performance tracking
|
|
28
28
|
this.toolMetrics = new Map();
|
|
29
|
+
// ๐ด SNOW-003 FIX: Circuit breaker implementation
|
|
30
|
+
this.circuitBreakers = new Map();
|
|
29
31
|
/**
|
|
30
32
|
* Tool handlers map
|
|
31
33
|
*/
|
|
@@ -159,48 +161,214 @@ class BaseMCPServer {
|
|
|
159
161
|
}
|
|
160
162
|
}
|
|
161
163
|
/**
|
|
162
|
-
*
|
|
164
|
+
* ๐ด SNOW-003 FIX: Enhanced retry logic with intelligent backoff and circuit breaker
|
|
165
|
+
* Addresses the 19% failure rate with better retry strategies and failure prevention
|
|
163
166
|
*/
|
|
164
167
|
async executeWithRetry(toolName, args, attempt = 1) {
|
|
165
|
-
|
|
166
|
-
const
|
|
168
|
+
// ๐ด CRITICAL: Increased retries from 3 to 6 for better resilience
|
|
169
|
+
const maxRetries = 6;
|
|
170
|
+
// ๐ด CRITICAL: Intelligent backoff based on error type
|
|
171
|
+
const backoffMs = this.calculateBackoff(attempt, toolName);
|
|
172
|
+
// ๐ด CRITICAL: Dynamic timeout based on tool complexity
|
|
173
|
+
const timeout = this.calculateTimeout(toolName);
|
|
167
174
|
try {
|
|
168
175
|
// Get tool handler
|
|
169
176
|
const handler = this.getToolHandler(toolName);
|
|
170
177
|
if (!handler) {
|
|
171
178
|
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${toolName}`);
|
|
172
179
|
}
|
|
173
|
-
//
|
|
174
|
-
|
|
180
|
+
// ๐ด CRITICAL: Memory usage check before execution
|
|
181
|
+
if (attempt === 1) {
|
|
182
|
+
await this.checkMemoryUsage();
|
|
183
|
+
}
|
|
184
|
+
// Execute with dynamic timeout
|
|
175
185
|
const result = await Promise.race([
|
|
176
186
|
handler(args),
|
|
177
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error(
|
|
187
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Tool execution timeout after ${timeout}ms`)), timeout)),
|
|
178
188
|
]);
|
|
189
|
+
// ๐ด SUCCESS: Reset circuit breaker on success
|
|
190
|
+
this.resetCircuitBreaker(toolName);
|
|
179
191
|
return result;
|
|
180
192
|
}
|
|
181
193
|
catch (error) {
|
|
182
|
-
this.logger.error(
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
194
|
+
this.logger.error(`๐ด Tool ${toolName} execution failed (attempt ${attempt}/${maxRetries}):`, error);
|
|
195
|
+
// ๐ด CRITICAL: Update circuit breaker
|
|
196
|
+
this.updateCircuitBreaker(toolName, error);
|
|
197
|
+
// Check if retryable and within limits
|
|
198
|
+
if (attempt < maxRetries && this.isRetryableError(error) && !this.isCircuitBreakerOpen(toolName)) {
|
|
199
|
+
this.logger.info(`๐ Retrying ${toolName} after ${backoffMs}ms (attempt ${attempt + 1}/${maxRetries})...`);
|
|
186
200
|
await new Promise(resolve => setTimeout(resolve, backoffMs));
|
|
187
201
|
return this.executeWithRetry(toolName, args, attempt + 1);
|
|
188
202
|
}
|
|
189
|
-
//
|
|
190
|
-
|
|
203
|
+
// ๐ด FINAL FAILURE: Enhanced error reporting
|
|
204
|
+
const errorMessage = this.createEnhancedErrorMessage(toolName, error, attempt, maxRetries);
|
|
205
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, errorMessage);
|
|
191
206
|
}
|
|
192
207
|
}
|
|
193
208
|
/**
|
|
194
|
-
*
|
|
209
|
+
* ๐ด SNOW-003 FIX: Calculate intelligent backoff based on error type and attempt
|
|
210
|
+
*/
|
|
211
|
+
calculateBackoff(attempt, toolName) {
|
|
212
|
+
// Base exponential backoff: 1s, 2s, 4s, 8s, 16s, 32s
|
|
213
|
+
const baseBackoff = 1000 * Math.pow(2, attempt - 1);
|
|
214
|
+
// Add jitter to prevent thundering herd (ยฑ25%)
|
|
215
|
+
const jitter = baseBackoff * 0.25 * (Math.random() - 0.5);
|
|
216
|
+
// Cap maximum backoff at 30 seconds
|
|
217
|
+
const maxBackoff = 30000;
|
|
218
|
+
return Math.min(baseBackoff + jitter, maxBackoff);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* ๐ด SNOW-003 FIX: Calculate dynamic timeout based on tool complexity
|
|
222
|
+
*/
|
|
223
|
+
calculateTimeout(toolName) {
|
|
224
|
+
// Tool-specific timeouts based on complexity
|
|
225
|
+
const timeoutMap = {
|
|
226
|
+
'snow_create_flow': 90000, // Flow creation: 90s
|
|
227
|
+
'snow_deploy': 120000, // Deployment: 2 minutes
|
|
228
|
+
'snow_comprehensive_search': 45000, // Search: 45s
|
|
229
|
+
'snow_find_artifact': 30000, // Find: 30s
|
|
230
|
+
'snow_validate_live_connection': 15000, // Validation: 15s
|
|
231
|
+
};
|
|
232
|
+
// Default timeout for unknown tools
|
|
233
|
+
return timeoutMap[toolName] || 60000; // 60s default (increased from 30s)
|
|
234
|
+
}
|
|
235
|
+
updateCircuitBreaker(toolName, error) {
|
|
236
|
+
const breaker = this.circuitBreakers.get(toolName) || { failures: 0, lastFailure: 0, isOpen: false };
|
|
237
|
+
breaker.failures++;
|
|
238
|
+
breaker.lastFailure = Date.now();
|
|
239
|
+
// Open circuit breaker after 5 failures within 5 minutes
|
|
240
|
+
if (breaker.failures >= 5 && (Date.now() - breaker.lastFailure) < 300000) {
|
|
241
|
+
breaker.isOpen = true;
|
|
242
|
+
this.logger.warn(`๐จ Circuit breaker opened for ${toolName} due to repeated failures`);
|
|
243
|
+
}
|
|
244
|
+
this.circuitBreakers.set(toolName, breaker);
|
|
245
|
+
}
|
|
246
|
+
isCircuitBreakerOpen(toolName) {
|
|
247
|
+
const breaker = this.circuitBreakers.get(toolName);
|
|
248
|
+
if (!breaker || !breaker.isOpen)
|
|
249
|
+
return false;
|
|
250
|
+
// Auto-reset circuit breaker after 10 minutes
|
|
251
|
+
if (Date.now() - breaker.lastFailure > 600000) {
|
|
252
|
+
breaker.isOpen = false;
|
|
253
|
+
breaker.failures = 0;
|
|
254
|
+
this.circuitBreakers.set(toolName, breaker);
|
|
255
|
+
this.logger.info(`โ
Circuit breaker reset for ${toolName}`);
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
resetCircuitBreaker(toolName) {
|
|
261
|
+
const breaker = this.circuitBreakers.get(toolName);
|
|
262
|
+
if (breaker) {
|
|
263
|
+
breaker.failures = 0;
|
|
264
|
+
breaker.isOpen = false;
|
|
265
|
+
this.circuitBreakers.set(toolName, breaker);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* ๐ด SNOW-003 FIX: Memory usage monitoring to prevent memory exhaustion failures
|
|
270
|
+
*/
|
|
271
|
+
async checkMemoryUsage() {
|
|
272
|
+
try {
|
|
273
|
+
const memUsage = process.memoryUsage();
|
|
274
|
+
const heapUsedMB = Math.round(memUsage.heapUsed / 1024 / 1024);
|
|
275
|
+
const heapTotalMB = Math.round(memUsage.heapTotal / 1024 / 1024);
|
|
276
|
+
// Log memory usage if high (>200MB)
|
|
277
|
+
if (heapUsedMB > 200) {
|
|
278
|
+
this.logger.warn(`โ ๏ธ High memory usage: ${heapUsedMB}MB heap used, ${heapTotalMB}MB total`);
|
|
279
|
+
}
|
|
280
|
+
// Trigger garbage collection if memory usage is very high (>500MB)
|
|
281
|
+
if (heapUsedMB > 500 && global.gc) {
|
|
282
|
+
this.logger.info('๐งน Triggering garbage collection due to high memory usage');
|
|
283
|
+
global.gc();
|
|
284
|
+
}
|
|
285
|
+
// Fail fast if memory usage is critical (>800MB)
|
|
286
|
+
if (heapUsedMB > 800) {
|
|
287
|
+
throw new Error(`Critical memory usage: ${heapUsedMB}MB. Operation aborted to prevent system instability.`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
this.logger.warn('Could not check memory usage:', error);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* ๐ด SNOW-003 FIX: Enhanced error message with troubleshooting guidance
|
|
296
|
+
*/
|
|
297
|
+
createEnhancedErrorMessage(toolName, error, attempts, maxRetries) {
|
|
298
|
+
const baseMessage = `Tool '${toolName}' failed after ${attempts}/${maxRetries} attempts`;
|
|
299
|
+
const errorDetail = error instanceof Error ? error.message : String(error);
|
|
300
|
+
let troubleshooting = '';
|
|
301
|
+
// Add specific troubleshooting based on error type
|
|
302
|
+
if (error.response?.status === 401) {
|
|
303
|
+
troubleshooting = '\n๐ก Authentication issue: Run "snow-flow auth login" to re-authenticate';
|
|
304
|
+
}
|
|
305
|
+
else if (error.response?.status === 403) {
|
|
306
|
+
troubleshooting = '\n๐ก Permission issue: Check ServiceNow user permissions and OAuth scopes';
|
|
307
|
+
}
|
|
308
|
+
else if (error.response?.status >= 500) {
|
|
309
|
+
troubleshooting = '\n๐ก ServiceNow server issue: Try again later or contact ServiceNow administrator';
|
|
310
|
+
}
|
|
311
|
+
else if (errorDetail.includes('timeout')) {
|
|
312
|
+
troubleshooting = '\n๐ก Timeout issue: ServiceNow instance may be slow - try again later';
|
|
313
|
+
}
|
|
314
|
+
else if (errorDetail.includes('network') || errorDetail.includes('connection')) {
|
|
315
|
+
troubleshooting = '\n๐ก Network issue: Check internet connection and ServiceNow instance availability';
|
|
316
|
+
}
|
|
317
|
+
return `${baseMessage}: ${errorDetail}${troubleshooting}`;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* ๐ด SNOW-003 FIX: Enhanced error classification for ServiceNow specific errors
|
|
321
|
+
* Addresses the 19% failure rate by properly categorizing retryable errors
|
|
195
322
|
*/
|
|
196
323
|
isRetryableError(error) {
|
|
197
324
|
if (error instanceof Error) {
|
|
198
325
|
const message = error.message.toLowerCase();
|
|
199
|
-
|
|
326
|
+
// ๐ด CRITICAL: ServiceNow specific retryable errors
|
|
327
|
+
const serviceNowRetryable = (message.includes('timeout') ||
|
|
200
328
|
message.includes('econnreset') ||
|
|
201
329
|
message.includes('socket hang up') ||
|
|
202
330
|
message.includes('enotfound') ||
|
|
203
|
-
message.includes('rate limit')
|
|
331
|
+
message.includes('rate limit') ||
|
|
332
|
+
message.includes('service unavailable') ||
|
|
333
|
+
message.includes('bad gateway') ||
|
|
334
|
+
message.includes('gateway timeout') ||
|
|
335
|
+
message.includes('connection refused') ||
|
|
336
|
+
message.includes('network error') ||
|
|
337
|
+
message.includes('dns lookup failed') ||
|
|
338
|
+
message.includes('connect etimedout') ||
|
|
339
|
+
message.includes('index not available') ||
|
|
340
|
+
message.includes('search index updating') ||
|
|
341
|
+
message.includes('temporary failure') ||
|
|
342
|
+
message.includes('server is busy') ||
|
|
343
|
+
message.includes('database lock') ||
|
|
344
|
+
message.includes('deadlock detected'));
|
|
345
|
+
// ๐ด CRITICAL: HTTP status code based retry logic
|
|
346
|
+
if (error.response?.status) {
|
|
347
|
+
const status = error.response.status;
|
|
348
|
+
const httpRetryable = (status === 429 || // Rate limit
|
|
349
|
+
status === 502 || // Bad Gateway
|
|
350
|
+
status === 503 || // Service Unavailable
|
|
351
|
+
status === 504 || // Gateway Timeout
|
|
352
|
+
status === 507 || // Insufficient Storage
|
|
353
|
+
status === 520 || // CloudFlare unknown error
|
|
354
|
+
status === 521 || // Web server is down
|
|
355
|
+
status === 522 || // Connection timed out
|
|
356
|
+
status === 523 || // Origin is unreachable
|
|
357
|
+
status === 524 // A timeout occurred
|
|
358
|
+
);
|
|
359
|
+
// 401 is retryable only once (for token refresh)
|
|
360
|
+
const authRetryable = status === 401 && !error.config?._retry;
|
|
361
|
+
return httpRetryable || authRetryable;
|
|
362
|
+
}
|
|
363
|
+
return serviceNowRetryable;
|
|
364
|
+
}
|
|
365
|
+
// Handle specific error types
|
|
366
|
+
if (error.code) {
|
|
367
|
+
const retryableCodes = [
|
|
368
|
+
'ECONNRESET', 'ENOTFOUND', 'ECONNREFUSED', 'ETIMEDOUT',
|
|
369
|
+
'ESOCKETTIMEDOUT', 'EHOSTUNREACH', 'EPIPE', 'EAI_AGAIN'
|
|
370
|
+
];
|
|
371
|
+
return retryableCodes.includes(error.code);
|
|
204
372
|
}
|
|
205
373
|
return false;
|
|
206
374
|
}
|