snow-flow 3.5.9 → 3.5.11

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.
@@ -21,6 +21,7 @@ export declare class ServiceNowLocalDevelopmentMCP extends EnhancedBaseMCPServer
21
21
  private validateArtifactCoherence;
22
22
  private syncCleanup;
23
23
  private convertToES5;
24
+ private debugWidgetFetch;
24
25
  private pullWidget;
25
26
  private pushWidget;
26
27
  }
@@ -143,6 +143,20 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
143
143
  required: ['code']
144
144
  }
145
145
  },
146
+ {
147
+ name: 'snow_debug_widget_fetch',
148
+ description: 'Debug widget fetching to diagnose API issues',
149
+ inputSchema: {
150
+ type: 'object',
151
+ properties: {
152
+ sys_id: {
153
+ type: 'string',
154
+ description: 'Widget sys_id to debug'
155
+ }
156
+ },
157
+ required: ['sys_id']
158
+ }
159
+ },
146
160
  // Legacy compatibility tools
147
161
  {
148
162
  name: 'snow_pull_widget',
@@ -176,41 +190,56 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
176
190
  }));
177
191
  this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
178
192
  const { name, arguments: args } = request.params;
193
+ // Add timeout protection for MCP tool execution
194
+ const TOOL_TIMEOUT = 10000; // 10 seconds max per tool call
179
195
  try {
180
196
  this.logger.info(`šŸ”§ Executing tool: ${name}`, args);
181
- let result;
182
- switch (name) {
183
- case 'snow_pull_artifact':
184
- result = await this.pullArtifact(args);
185
- break;
186
- case 'snow_push_artifact':
187
- result = await this.pushArtifact(args);
188
- break;
189
- case 'snow_validate_artifact_coherence':
190
- result = await this.validateArtifactCoherence(args);
191
- break;
192
- case 'snow_sync_status':
193
- result = await this.getSyncStatus(args);
194
- break;
195
- case 'snow_list_supported_artifacts':
196
- result = await this.listSupportedArtifacts(args);
197
- break;
198
- case 'snow_sync_cleanup':
199
- result = await this.syncCleanup(args);
200
- break;
201
- case 'snow_convert_to_es5':
202
- result = await this.convertToES5(args);
203
- break;
204
- // Legacy compatibility
205
- case 'snow_pull_widget':
206
- result = await this.pullWidget(args);
207
- break;
208
- case 'snow_push_widget':
209
- result = await this.pushWidget(args);
210
- break;
211
- default:
212
- throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
213
- }
197
+ // Create timeout promise
198
+ const timeoutPromise = new Promise((_, reject) => {
199
+ setTimeout(() => reject(new Error(`Tool ${name} timed out after ${TOOL_TIMEOUT / 1000}s`)), TOOL_TIMEOUT);
200
+ });
201
+ // Execute tool with timeout protection
202
+ const toolPromise = (async () => {
203
+ let result;
204
+ switch (name) {
205
+ case 'snow_pull_artifact':
206
+ result = await this.pullArtifact(args);
207
+ break;
208
+ case 'snow_push_artifact':
209
+ result = await this.pushArtifact(args);
210
+ break;
211
+ case 'snow_validate_artifact_coherence':
212
+ result = await this.validateArtifactCoherence(args);
213
+ break;
214
+ case 'snow_sync_status':
215
+ result = await this.getSyncStatus(args);
216
+ break;
217
+ case 'snow_list_supported_artifacts':
218
+ result = await this.listSupportedArtifacts(args);
219
+ break;
220
+ case 'snow_sync_cleanup':
221
+ result = await this.syncCleanup(args);
222
+ break;
223
+ case 'snow_convert_to_es5':
224
+ result = await this.convertToES5(args);
225
+ break;
226
+ case 'snow_debug_widget_fetch':
227
+ result = await this.debugWidgetFetch(args);
228
+ break;
229
+ // Legacy compatibility
230
+ case 'snow_pull_widget':
231
+ result = await this.pullWidget(args);
232
+ break;
233
+ case 'snow_push_widget':
234
+ result = await this.pushWidget(args);
235
+ break;
236
+ default:
237
+ throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
238
+ }
239
+ return result;
240
+ })();
241
+ // Race between tool execution and timeout
242
+ const result = await Promise.race([toolPromise, timeoutPromise]);
214
243
  this.logger.info(`āœ… Tool ${name} completed successfully`);
215
244
  return {
216
245
  content: result.content
@@ -232,16 +261,25 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
232
261
  }
233
262
  async pullArtifact(args) {
234
263
  const { sys_id, table } = args;
264
+ // Add timeout for pull operations
265
+ const PULL_TIMEOUT = 15000; // 15 seconds for pull operations
235
266
  try {
236
- let artifact;
237
- if (table) {
238
- // Use specified table
239
- artifact = await this.syncManager.pullArtifact(table, sys_id);
240
- }
241
- else {
242
- // Auto-detect table
243
- artifact = await this.syncManager.pullArtifactBySysId(sys_id);
244
- }
267
+ const pullPromise = (async () => {
268
+ let artifact;
269
+ if (table) {
270
+ // Use specified table
271
+ artifact = await this.syncManager.pullArtifact(table, sys_id);
272
+ }
273
+ else {
274
+ // Auto-detect table
275
+ artifact = await this.syncManager.pullArtifactBySysId(sys_id);
276
+ }
277
+ return artifact;
278
+ })();
279
+ const timeoutPromise = new Promise((_, reject) => {
280
+ setTimeout(() => reject(new Error(`Pull operation timed out after ${PULL_TIMEOUT / 1000}s`)), PULL_TIMEOUT);
281
+ });
282
+ const artifact = await Promise.race([pullPromise, timeoutPromise]);
245
283
  return {
246
284
  content: [
247
285
  {
@@ -412,6 +450,64 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
412
450
  ]
413
451
  };
414
452
  }
453
+ async debugWidgetFetch(args) {
454
+ const { sys_id } = args;
455
+ // Debug operations get extra time
456
+ const DEBUG_TIMEOUT = 20000; // 20 seconds for debug operations
457
+ try {
458
+ const debugPromise = this.syncManager['smartFetcher'].debugFetchWidget(sys_id);
459
+ const timeoutPromise = new Promise((_, reject) => {
460
+ setTimeout(() => reject(new Error(`Debug operation timed out after ${DEBUG_TIMEOUT / 1000}s`)), DEBUG_TIMEOUT);
461
+ });
462
+ const debugResults = await Promise.race([debugPromise, timeoutPromise]);
463
+ let summaryText = `šŸ” Debug Results for Widget ${sys_id}\n\n`;
464
+ // Check which methods worked
465
+ const methods = ['searchRecords', 'getRecord', 'searchRecordsWithFields'];
466
+ for (const method of methods) {
467
+ if (debugResults[method]) {
468
+ const widget = debugResults[method];
469
+ summaryText += `āœ… ${method}:\n`;
470
+ summaryText += ` - Fields: ${Object.keys(widget).length}\n`;
471
+ summaryText += ` - Has script: ${!!widget.script}\n`;
472
+ summaryText += ` - Has client_script: ${!!widget.client_script}\n`;
473
+ summaryText += ` - Has template: ${!!widget.template}\n`;
474
+ summaryText += ` - Script size: ${widget.script?.length || 0} chars\n`;
475
+ summaryText += ` - Client script size: ${widget.client_script?.length || 0} chars\n`;
476
+ summaryText += ` - Template size: ${widget.template?.length || 0} chars\n\n`;
477
+ }
478
+ else {
479
+ summaryText += `āŒ ${method}: Failed\n\n`;
480
+ }
481
+ }
482
+ // Recommend best approach
483
+ const workingMethods = methods.filter(m => debugResults[m]);
484
+ if (workingMethods.length > 0) {
485
+ summaryText += `\nšŸ“Š Recommendation: Use ${workingMethods[0]} for fetching this widget.`;
486
+ }
487
+ else {
488
+ summaryText += `\nāš ļø All fetch methods failed. There may be an authentication or permission issue.`;
489
+ }
490
+ return {
491
+ content: [
492
+ {
493
+ type: 'text',
494
+ text: summaryText
495
+ }
496
+ ]
497
+ };
498
+ }
499
+ catch (error) {
500
+ const errorMessage = error instanceof Error ? error.message : String(error);
501
+ return {
502
+ content: [
503
+ {
504
+ type: 'text',
505
+ text: `āŒ Debug failed: ${errorMessage}`
506
+ }
507
+ ]
508
+ };
509
+ }
510
+ }
415
511
  // Legacy compatibility methods
416
512
  async pullWidget(args) {
417
513
  return this.pullArtifact({ ...args, table: 'sp_widget' });
@@ -421,13 +517,25 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
421
517
  }
422
518
  }
423
519
  exports.ServiceNowLocalDevelopmentMCP = ServiceNowLocalDevelopmentMCP;
424
- // Start the server
520
+ // Start the server with timeout protection
425
521
  async function main() {
426
- const mcpServer = new ServiceNowLocalDevelopmentMCP();
427
- const transport = new stdio_js_1.StdioServerTransport();
428
- // Access server through a public method
429
- await mcpServer.server.connect(transport);
430
- console.error('šŸš€ ServiceNow Local Development MCP Server started');
522
+ try {
523
+ const mcpServer = new ServiceNowLocalDevelopmentMCP();
524
+ const transport = new stdio_js_1.StdioServerTransport();
525
+ // Add timeout for server initialization
526
+ const INIT_TIMEOUT = 5000; // 5 seconds to start
527
+ const connectPromise = mcpServer.server.connect(transport);
528
+ const timeoutPromise = new Promise((_, reject) => {
529
+ setTimeout(() => reject(new Error('Server initialization timeout')), INIT_TIMEOUT);
530
+ });
531
+ await Promise.race([connectPromise, timeoutPromise]);
532
+ console.error('šŸš€ ServiceNow Local Development MCP Server started');
533
+ }
534
+ catch (error) {
535
+ console.error('āŒ Server failed to start within timeout:', error);
536
+ // Still allow server to run even if initial connection takes time
537
+ console.error('ā³ Server may still be initializing...');
538
+ }
431
539
  }
432
540
  if (require.main === module) {
433
541
  main().catch((error) => {
@@ -46,30 +46,36 @@ exports.ArtifactLocalSync = void 0;
46
46
  const fs = __importStar(require("fs"));
47
47
  const path = __importStar(require("path"));
48
48
  const smart_field_fetcher_js_1 = require("./smart-field-fetcher.js");
49
+ const mcp_timeout_config_js_1 = require("./mcp-timeout-config.js");
49
50
  const artifact_registry_1 = require("./artifact-sync/artifact-registry");
50
51
  class ArtifactLocalSync {
51
52
  constructor(client, customBaseDir) {
52
53
  this.artifacts = new Map();
53
54
  this.client = client;
54
55
  this.smartFetcher = new smart_field_fetcher_js_1.SmartFieldFetcher(client);
56
+ // Version check for debugging
57
+ console.log(`šŸ”§ ArtifactLocalSync v3.5.10 initializing...`);
55
58
  // Use custom directory, environment variable, or default to current project's servicenow folder
56
59
  if (customBaseDir) {
57
60
  this.baseDir = customBaseDir;
61
+ console.log(` šŸ“ Using custom directory: ${customBaseDir}`);
58
62
  }
59
63
  else if (process.env.SNOW_FLOW_ARTIFACTS_DIR) {
60
64
  this.baseDir = process.env.SNOW_FLOW_ARTIFACTS_DIR;
65
+ console.log(` šŸ“ Using environment variable directory: ${this.baseDir}`);
61
66
  }
62
67
  else {
63
68
  // Default to 'servicenow' folder in current working directory
64
69
  this.baseDir = path.join(process.cwd(), 'servicenow');
70
+ console.log(` šŸ“ Using project directory: ${this.baseDir}`);
65
71
  }
66
72
  // Create base directory if it doesn't exist
67
73
  if (!fs.existsSync(this.baseDir)) {
68
74
  fs.mkdirSync(this.baseDir, { recursive: true });
69
- console.log(`šŸ“ Created ServiceNow artifacts directory: ${this.baseDir}`);
75
+ console.log(` āœ… Created ServiceNow artifacts directory`);
70
76
  }
71
77
  else {
72
- console.log(`šŸ“ Using ServiceNow artifacts directory: ${this.baseDir}`);
78
+ console.log(` āœ… Directory exists`);
73
79
  }
74
80
  // Create .gitignore if it doesn't exist to optionally exclude from version control
75
81
  const gitignorePath = path.join(this.baseDir, '.gitignore');
@@ -121,22 +127,34 @@ class ArtifactLocalSync {
121
127
  throw new Error(`Unsupported artifact type: ${tableName}. Supported types: ${Object.keys(artifact_registry_1.ARTIFACT_REGISTRY).join(', ')}`);
122
128
  }
123
129
  console.log(`\nšŸ”„ Pulling ${config.displayName} (${sys_id}) to local files...`);
130
+ // Get timeout configuration
131
+ const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
124
132
  // Use smart fetcher for known types, otherwise direct query
125
133
  let artifactData;
126
- if (tableName === 'sp_widget') {
127
- artifactData = await this.smartFetcher.fetchWidget(sys_id);
128
- }
129
- else if (tableName === 'sys_hub_flow') {
130
- artifactData = await this.smartFetcher.fetchFlow(sys_id);
131
- }
132
- else if (tableName === 'sys_script') {
133
- artifactData = await this.smartFetcher.fetchBusinessRule(sys_id);
134
+ // Fetch with timeout protection
135
+ try {
136
+ if (tableName === 'sp_widget') {
137
+ artifactData = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.smartFetcher.fetchWidget(sys_id), timeoutConfig.pullToolTimeout, `Fetch widget ${sys_id}`);
138
+ }
139
+ else if (tableName === 'sys_hub_flow') {
140
+ artifactData = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.smartFetcher.fetchFlow(sys_id), timeoutConfig.pullToolTimeout, `Fetch flow ${sys_id}`);
141
+ }
142
+ else if (tableName === 'sys_script') {
143
+ artifactData = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.smartFetcher.fetchBusinessRule(sys_id), timeoutConfig.pullToolTimeout, `Fetch business rule ${sys_id}`);
144
+ }
145
+ else {
146
+ // Generic fetch for other types
147
+ const allFields = config.fieldMappings.map(fm => fm.serviceNowField);
148
+ const response = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords(tableName, `sys_id=${sys_id}`, 1), timeoutConfig.queryToolTimeout, `Query ${tableName} ${sys_id}`);
149
+ artifactData = response.result?.[0];
150
+ }
134
151
  }
135
- else {
136
- // Generic fetch for other types
137
- const allFields = config.fieldMappings.map(fm => fm.serviceNowField);
138
- const response = await this.client.searchRecords(tableName, `sys_id=${sys_id}`, 1);
139
- artifactData = response.result?.[0];
152
+ catch (error) {
153
+ if (error instanceof Error && error.message.includes('timed out')) {
154
+ console.error(`ā±ļø Fetch timed out - ServiceNow may be slow or artifact is very large`);
155
+ console.error(`šŸ’” Try using snow_debug_widget_fetch to diagnose the issue`);
156
+ }
157
+ throw error;
140
158
  }
141
159
  if (!artifactData) {
142
160
  throw new Error(`Artifact not found: ${tableName}/${sys_id}`);
@@ -296,10 +314,11 @@ class ArtifactLocalSync {
296
314
  console.log(`\nā“ Continue with deployment anyway? (Issues might cause runtime errors)`);
297
315
  // In real implementation, prompt for confirmation
298
316
  }
299
- // Update in ServiceNow
317
+ // Update in ServiceNow with timeout protection
300
318
  try {
301
319
  console.log(`\nšŸ“¤ Updating ${config.displayName} in ServiceNow...`);
302
- await this.client.updateRecord(config.tableName, sys_id, updates);
320
+ const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
321
+ await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.updateRecord(config.tableName, sys_id, updates), timeoutConfig.pushToolTimeout, `Update ${config.displayName} ${sys_id}`);
303
322
  artifact.syncStatus = 'synced';
304
323
  artifact.lastSyncedAt = new Date();
305
324
  console.log(`āœ… ${config.displayName} successfully updated in ServiceNow!`);
@@ -570,9 +589,12 @@ snow-flow sync status ${widget.sys_id}
570
589
  async pullArtifactBySysId(sys_id) {
571
590
  // Try to detect table by querying common tables
572
591
  const tables = Object.keys(artifact_registry_1.ARTIFACT_REGISTRY);
592
+ const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
573
593
  for (const table of tables) {
574
594
  try {
575
- const response = await this.client.searchRecords(table, `sys_id=${sys_id}`, 1);
595
+ // Quick query with short timeout
596
+ const response = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords(table, `sys_id=${sys_id}`, 1), 3000, // 3 second timeout for detection
597
+ `Detect table for ${sys_id}`);
576
598
  if (response.result?.[0]) {
577
599
  console.log(`šŸŽ† Found artifact in table: ${table}`);
578
600
  return this.pullArtifact(table, sys_id);
@@ -0,0 +1,39 @@
1
+ /**
2
+ * MCP Server Timeout Configuration
3
+ *
4
+ * Provides fast, reliable timeout settings for MCP server operations
5
+ * to prevent Claude from timing out during API calls.
6
+ */
7
+ export interface MCPTimeoutConfig {
8
+ serverInitTimeout: number;
9
+ defaultToolTimeout: number;
10
+ queryToolTimeout: number;
11
+ pullToolTimeout: number;
12
+ pushToolTimeout: number;
13
+ debugToolTimeout: number;
14
+ scriptToolTimeout: number;
15
+ apiCallTimeout: number;
16
+ apiRetryDelay: number;
17
+ apiMaxRetries: number;
18
+ healthCheckInterval: number;
19
+ healthCheckTimeout: number;
20
+ maxResponseSize: number;
21
+ chunkDelay: number;
22
+ }
23
+ /**
24
+ * Get MCP timeout configuration based on environment
25
+ */
26
+ export declare function getMCPTimeoutConfig(): MCPTimeoutConfig;
27
+ /**
28
+ * Wrap a promise with timeout protection
29
+ */
30
+ export declare function withMCPTimeout<T>(promise: Promise<T>, timeoutMs: number, operationName?: string): Promise<T>;
31
+ /**
32
+ * Execute with retry logic and timeout
33
+ */
34
+ export declare function withMCPRetry<T>(fn: () => Promise<T>, config?: Partial<MCPTimeoutConfig>, operationName?: string): Promise<T>;
35
+ /**
36
+ * Quick health check with timeout
37
+ */
38
+ export declare function quickHealthCheck(checkFn: () => Promise<boolean>, timeoutMs?: number): Promise<boolean>;
39
+ //# sourceMappingURL=mcp-timeout-config.d.ts.map
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ /**
3
+ * MCP Server Timeout Configuration
4
+ *
5
+ * Provides fast, reliable timeout settings for MCP server operations
6
+ * to prevent Claude from timing out during API calls.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getMCPTimeoutConfig = getMCPTimeoutConfig;
10
+ exports.withMCPTimeout = withMCPTimeout;
11
+ exports.withMCPRetry = withMCPRetry;
12
+ exports.quickHealthCheck = quickHealthCheck;
13
+ /**
14
+ * Get MCP timeout configuration based on environment
15
+ */
16
+ function getMCPTimeoutConfig() {
17
+ const isDebug = process.env.DEBUG === 'true';
18
+ const isProduction = process.env.NODE_ENV === 'production';
19
+ // Fast timeouts to prevent Claude from timing out
20
+ const config = {
21
+ // Server should start quickly (3s max)
22
+ serverInitTimeout: parseInt(process.env.MCP_SERVER_INIT_TIMEOUT || '3000'),
23
+ // Tool timeouts - keep them short to fail fast
24
+ defaultToolTimeout: parseInt(process.env.MCP_DEFAULT_TOOL_TIMEOUT || '8000'), // 8s default
25
+ queryToolTimeout: parseInt(process.env.MCP_QUERY_TIMEOUT || '5000'), // 5s for queries
26
+ pullToolTimeout: parseInt(process.env.MCP_PULL_TIMEOUT || '10000'), // 10s for pulls
27
+ pushToolTimeout: parseInt(process.env.MCP_PUSH_TIMEOUT || '10000'), // 10s for pushes
28
+ debugToolTimeout: parseInt(process.env.MCP_DEBUG_TIMEOUT || '15000'), // 15s for debug
29
+ scriptToolTimeout: parseInt(process.env.MCP_SCRIPT_TIMEOUT || '20000'), // 20s for scripts
30
+ // API calls should be fast
31
+ apiCallTimeout: parseInt(process.env.MCP_API_TIMEOUT || '5000'), // 5s per API call
32
+ apiRetryDelay: parseInt(process.env.MCP_API_RETRY_DELAY || '1000'), // 1s between retries
33
+ apiMaxRetries: parseInt(process.env.MCP_API_MAX_RETRIES || '2'), // Only 2 retries
34
+ // Health checks
35
+ healthCheckInterval: parseInt(process.env.MCP_HEALTH_INTERVAL || '30000'), // Every 30s
36
+ healthCheckTimeout: parseInt(process.env.MCP_HEALTH_TIMEOUT || '2000'), // 2s timeout
37
+ // Response optimization
38
+ maxResponseSize: parseInt(process.env.MCP_MAX_RESPONSE_SIZE || '100000'), // 100KB chunks
39
+ chunkDelay: parseInt(process.env.MCP_CHUNK_DELAY || '100'), // 100ms between chunks
40
+ };
41
+ // In debug mode, allow longer timeouts
42
+ if (isDebug) {
43
+ config.defaultToolTimeout *= 2;
44
+ config.queryToolTimeout *= 2;
45
+ config.pullToolTimeout *= 2;
46
+ config.pushToolTimeout *= 2;
47
+ config.debugToolTimeout *= 2;
48
+ config.scriptToolTimeout *= 2;
49
+ }
50
+ // In production, ensure fast responses
51
+ if (isProduction) {
52
+ config.apiMaxRetries = 1; // Only 1 retry in production
53
+ config.apiRetryDelay = 500; // Faster retry
54
+ }
55
+ return config;
56
+ }
57
+ /**
58
+ * Wrap a promise with timeout protection
59
+ */
60
+ async function withMCPTimeout(promise, timeoutMs, operationName = 'Operation') {
61
+ const timeoutPromise = new Promise((_, reject) => {
62
+ setTimeout(() => {
63
+ reject(new Error(`${operationName} timed out after ${timeoutMs / 1000}s`));
64
+ }, timeoutMs);
65
+ });
66
+ try {
67
+ return await Promise.race([promise, timeoutPromise]);
68
+ }
69
+ catch (error) {
70
+ if (error instanceof Error && error.message.includes('timed out')) {
71
+ // Log timeout for debugging
72
+ console.error(`ā±ļø MCP Timeout: ${operationName} exceeded ${timeoutMs}ms`);
73
+ // Add context about what to do
74
+ error.message += ' - Consider increasing timeout or optimizing the operation';
75
+ }
76
+ throw error;
77
+ }
78
+ }
79
+ /**
80
+ * Execute with retry logic and timeout
81
+ */
82
+ async function withMCPRetry(fn, config = {}, operationName = 'Operation') {
83
+ const fullConfig = { ...getMCPTimeoutConfig(), ...config };
84
+ let lastError = null;
85
+ for (let attempt = 0; attempt <= fullConfig.apiMaxRetries; attempt++) {
86
+ try {
87
+ // Try with timeout
88
+ const result = await withMCPTimeout(fn(), fullConfig.defaultToolTimeout, `${operationName} (attempt ${attempt + 1})`);
89
+ return result;
90
+ }
91
+ catch (error) {
92
+ lastError = error;
93
+ // Don't retry on non-retryable errors
94
+ if (error instanceof Error) {
95
+ if (error.message.includes('Authentication') ||
96
+ error.message.includes('Permission') ||
97
+ error.message.includes('Not found')) {
98
+ throw error; // Don't retry auth/permission errors
99
+ }
100
+ }
101
+ // If we have retries left, wait and try again
102
+ if (attempt < fullConfig.apiMaxRetries) {
103
+ console.log(`āš ļø ${operationName} failed, retrying in ${fullConfig.apiRetryDelay / 1000}s...`);
104
+ await new Promise(resolve => setTimeout(resolve, fullConfig.apiRetryDelay));
105
+ }
106
+ }
107
+ }
108
+ // All retries exhausted
109
+ throw lastError || new Error(`${operationName} failed after ${fullConfig.apiMaxRetries} retries`);
110
+ }
111
+ /**
112
+ * Quick health check with timeout
113
+ */
114
+ async function quickHealthCheck(checkFn, timeoutMs = 2000) {
115
+ try {
116
+ return await withMCPTimeout(checkFn(), timeoutMs, 'Health check');
117
+ }
118
+ catch (error) {
119
+ // Health check failed or timed out
120
+ return false;
121
+ }
122
+ }
123
+ //# sourceMappingURL=mcp-timeout-config.js.map
@@ -46,7 +46,7 @@ class ServiceNowClientWithTracking extends servicenow_client_js_1.ServiceNowClie
46
46
  // Estimate tokens based on fields requested
47
47
  const fieldCount = fields.length;
48
48
  const estimatedTokens = Math.min(fieldCount * 100, 1000); // Rough estimate
49
- this.mcpLogger.trackTokens(estimatedTokens, 0);
49
+ this.mcpLogger.addTokens(estimatedTokens, 0);
50
50
  if (result?.data?.result?.length > 0) {
51
51
  this.mcpLogger.info(`Found ${result.data.result.length} records with ${fieldCount} fields`);
52
52
  }
@@ -52,6 +52,10 @@ export declare class SmartFieldFetcher {
52
52
  * Search within fields using GlideRecord queries
53
53
  */
54
54
  searchInField(table: string, field: string, searchTerm: string, additionalQuery?: string): Promise<any[]>;
55
+ /**
56
+ * Debug method to directly test API calls
57
+ */
58
+ debugFetchWidget(sys_id: string): Promise<any>;
55
59
  /**
56
60
  * Get smart fetch strategy based on table - NOW USES ARTIFACT REGISTRY!
57
61
  */
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.SmartFieldFetcher = void 0;
10
10
  exports.createFetchStrategyHint = createFetchStrategyHint;
11
11
  const artifact_registry_js_1 = require("./artifact-sync/artifact-registry.js");
12
+ const mcp_timeout_config_js_1 = require("./mcp-timeout-config.js");
12
13
  // Widget field groups with relationship context - 20K per field for better context
13
14
  const WIDGET_FIELD_GROUPS = [
14
15
  {
@@ -160,11 +161,12 @@ class SmartFieldFetcher {
160
161
  };
161
162
  // Try to fetch all fields first, then fall back to individual fields if too large
162
163
  console.log(`šŸ“¦ Attempting to fetch complete widget data...`);
164
+ const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
163
165
  try {
164
- // Try to get all fields at once
165
- const response = await this.client.searchRecords('sp_widget', `sys_id=${sys_id}`, 1);
166
- if (response && response.result && response.result.length > 0) {
167
- const widgetData = response.result[0];
166
+ // Try to get all fields at once with timeout protection
167
+ const response = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords('sp_widget', `sys_id=${sys_id}`, 1), timeoutConfig.queryToolTimeout, `Fetch complete widget ${sys_id}`);
168
+ if (response && response.success && response.data && response.data.result && response.data.result.length > 0) {
169
+ const widgetData = response.data.result[0];
168
170
  console.log(`āœ… Successfully fetched complete widget`);
169
171
  // Organize into field groups for better context
170
172
  for (const group of WIDGET_FIELD_GROUPS) {
@@ -184,68 +186,73 @@ class SmartFieldFetcher {
184
186
  Object.assign(results, widgetData);
185
187
  }
186
188
  else {
187
- throw new Error('Widget not found');
189
+ throw new Error('Widget not found or no data returned');
188
190
  }
189
191
  }
190
192
  catch (error) {
191
193
  console.log(`āš ļø Complete fetch failed: ${error.message}`);
192
- console.log(`šŸ”„ Switching to field-by-field fetching...`);
193
- // Fall back to fetching fields per group or individually
194
- for (const group of WIDGET_FIELD_GROUPS) {
195
- console.log(`šŸ“¦ Fetching ${group.groupName}: ${group.description}`);
196
- try {
197
- // Try to fetch all fields in this group at once
198
- const groupResponse = await this.client.searchRecordsWithFields('sp_widget', `sys_id=${sys_id}`, group.fields, 1);
199
- if (groupResponse && groupResponse.success && groupResponse.data && groupResponse.data.result && groupResponse.data.result.length > 0) {
200
- const groupData = groupResponse.data.result[0];
201
- console.log(` āœ… Successfully fetched ${group.groupName}`);
194
+ console.log(`šŸ”„ Switching to more robust fetching approach...`);
195
+ // First, get the widget with minimal fields to ensure it exists
196
+ try {
197
+ const basicResponse = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords('sp_widget', `sys_id=${sys_id}`, 1), timeoutConfig.queryToolTimeout, `Basic widget fetch ${sys_id}`);
198
+ if (!basicResponse || !basicResponse.success || !basicResponse.data || !basicResponse.data.result || basicResponse.data.result.length === 0) {
199
+ throw new Error(`Widget with sys_id ${sys_id} not found`);
200
+ }
201
+ // Widget exists, now get it via getRecord which might handle large fields better
202
+ const widgetData = basicResponse.data.result[0];
203
+ // Store what we got
204
+ if (widgetData) {
205
+ // Add all available fields to results
206
+ Object.assign(results, widgetData);
207
+ // Organize into field groups
208
+ for (const group of WIDGET_FIELD_GROUPS) {
209
+ const groupData = {};
210
+ for (const fieldName of group.fields) {
211
+ if (widgetData[fieldName] !== undefined) {
212
+ groupData[fieldName] = widgetData[fieldName];
213
+ }
214
+ }
202
215
  results._field_groups[group.groupName] = {
203
216
  data: groupData,
204
217
  description: group.description,
205
218
  fields: group.fields
206
219
  };
207
- // Add to flat structure
208
- Object.assign(results, groupData);
209
- }
210
- else {
211
- throw new Error('No data returned for group');
212
220
  }
221
+ // Log what we got
222
+ console.log(`šŸ“Š Retrieved widget fields:`);
223
+ console.log(` - name: ${widgetData.name || 'N/A'}`);
224
+ console.log(` - template: ${widgetData.template ? widgetData.template.length + ' chars' : 'empty'}`);
225
+ console.log(` - script: ${widgetData.script ? widgetData.script.length + ' chars' : 'empty'}`);
226
+ console.log(` - client_script: ${widgetData.client_script ? widgetData.client_script.length + ' chars' : 'empty'}`);
227
+ console.log(` - css: ${widgetData.css ? widgetData.css.length + ' chars' : 'empty'}`);
213
228
  }
214
- catch (groupError) {
215
- console.log(` āš ļø Group ${group.groupName} failed, fetching fields individually...`);
216
- // If group fails, fetch fields one by one
217
- const groupData = {};
218
- for (const fieldName of group.fields) {
219
- console.log(` šŸ“„ Fetching field: ${fieldName}`);
220
- try {
221
- // Fetch just this single field
222
- const fieldResponse = await this.client.searchRecordsWithFields('sp_widget', `sys_id=${sys_id}`, [fieldName], 1);
223
- if (fieldResponse && fieldResponse.success && fieldResponse.data && fieldResponse.data.result && fieldResponse.data.result.length > 0) {
224
- const fieldValue = fieldResponse.data.result[0][fieldName];
225
- if (fieldValue !== undefined && fieldValue !== null) {
226
- groupData[fieldName] = fieldValue;
227
- results[fieldName] = fieldValue; // Also add to flat structure
228
- console.log(` āœ… Got ${fieldName} (${typeof fieldValue === 'string' ? fieldValue.length : 0} chars)`);
229
- }
230
- else {
231
- groupData[fieldName] = '';
232
- console.log(` āš ļø ${fieldName} is empty`);
233
- }
234
- }
235
- }
236
- catch (fieldError) {
237
- console.log(` āŒ Failed to fetch ${fieldName}: ${fieldError.message}`);
238
- groupData[fieldName] = ''; // Empty string for failed fields
239
- }
229
+ // If critical fields are missing, try alternative approach
230
+ if (!widgetData.script && !widgetData.client_script && !widgetData.template) {
231
+ console.log(`āš ļø Critical fields missing, trying direct API call...`);
232
+ // Try using getRecord instead of searchRecords with short timeout
233
+ const directResponse = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.getRecord('sp_widget', sys_id), 5000, // 5 second timeout for direct fetch
234
+ `Direct widget fetch ${sys_id}`);
235
+ if (directResponse && directResponse.success && directResponse.data && directResponse.data.result) {
236
+ const directData = directResponse.data.result;
237
+ // Merge any new data we got
238
+ Object.assign(results, directData);
239
+ console.log(`šŸ“Š Direct API retrieved:`);
240
+ console.log(` - template: ${directData.template ? directData.template.length + ' chars' : 'empty'}`);
241
+ console.log(` - script: ${directData.script ? directData.script.length + ' chars' : 'empty'}`);
242
+ console.log(` - client_script: ${directData.client_script ? directData.client_script.length + ' chars' : 'empty'}`);
240
243
  }
241
- results._field_groups[group.groupName] = {
242
- data: groupData,
243
- description: group.description,
244
- fields: group.fields,
245
- _fetched_individually: true
246
- };
247
244
  }
248
245
  }
246
+ catch (fallbackError) {
247
+ console.log(`āŒ All fetch attempts failed: ${fallbackError.message}`);
248
+ // At least return basic structure with sys_id
249
+ results.sys_id = sys_id;
250
+ results.name = 'Unknown Widget';
251
+ results.template = '';
252
+ results.script = '';
253
+ results.client_script = '';
254
+ results.css = '';
255
+ }
249
256
  // Make sure we have at least the sys_id
250
257
  if (!results.sys_id) {
251
258
  results.sys_id = sys_id;
@@ -350,6 +357,70 @@ class SmartFieldFetcher {
350
357
  return [];
351
358
  }
352
359
  }
360
+ /**
361
+ * Debug method to directly test API calls
362
+ */
363
+ async debugFetchWidget(sys_id) {
364
+ console.log(`\nšŸ” DEBUG: Testing different fetch approaches for widget ${sys_id}\n`);
365
+ const results = {};
366
+ const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
367
+ // Test 1: Basic searchRecords with timeout
368
+ try {
369
+ console.log(`Test 1: searchRecords (${timeoutConfig.queryToolTimeout / 1000}s timeout)...`);
370
+ const test1 = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecords('sp_widget', `sys_id=${sys_id}`, 1), timeoutConfig.queryToolTimeout, 'searchRecords test');
371
+ console.log(` - Response structure: success=${test1?.success}, has data=${!!test1?.data}, has result=${!!test1?.data?.result}`);
372
+ if (test1?.data?.result?.[0]) {
373
+ const widget = test1.data.result[0];
374
+ console.log(` - Fields received: ${Object.keys(widget).join(', ')}`);
375
+ console.log(` - Script length: ${widget.script?.length || 0}`);
376
+ console.log(` - Client script length: ${widget.client_script?.length || 0}`);
377
+ console.log(` - Template length: ${widget.template?.length || 0}`);
378
+ results.searchRecords = widget;
379
+ }
380
+ }
381
+ catch (e) {
382
+ console.log(` āŒ Error: ${e.message}`);
383
+ }
384
+ // Test 2: getRecord with timeout
385
+ try {
386
+ console.log(`Test 2: getRecord (${timeoutConfig.queryToolTimeout / 1000}s timeout)...`);
387
+ const test2 = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.getRecord('sp_widget', sys_id), timeoutConfig.queryToolTimeout, 'getRecord test');
388
+ console.log(` - Response structure: success=${test2?.success}, has data=${!!test2?.data}, has result=${!!test2?.data?.result}`);
389
+ if (test2?.data?.result) {
390
+ const widget = test2.data.result;
391
+ console.log(` - Fields received: ${Object.keys(widget).join(', ')}`);
392
+ console.log(` - Script length: ${widget.script?.length || 0}`);
393
+ console.log(` - Client script length: ${widget.client_script?.length || 0}`);
394
+ console.log(` - Template length: ${widget.template?.length || 0}`);
395
+ results.getRecord = widget;
396
+ }
397
+ }
398
+ catch (e) {
399
+ console.log(` āŒ Error: ${e.message}`);
400
+ }
401
+ // Test 3: searchRecordsWithFields with timeout
402
+ try {
403
+ console.log(`Test 3: searchRecordsWithFields (${timeoutConfig.queryToolTimeout / 1000}s timeout)...`);
404
+ const test3 = await (0, mcp_timeout_config_js_1.withMCPTimeout)(this.client.searchRecordsWithFields('sp_widget', `sys_id=${sys_id}`, ['name', 'script', 'client_script', 'template', 'css'], 1), timeoutConfig.queryToolTimeout, 'searchRecordsWithFields test');
405
+ console.log(` - Response structure: success=${test3?.success}, has data=${!!test3?.data}, has result=${!!test3?.data?.result}`);
406
+ if (test3?.data?.result?.[0]) {
407
+ const widget = test3.data.result[0];
408
+ console.log(` - Fields received: ${Object.keys(widget).join(', ')}`);
409
+ console.log(` - Script length: ${widget.script?.length || 0}`);
410
+ console.log(` - Client script length: ${widget.client_script?.length || 0}`);
411
+ console.log(` - Template length: ${widget.template?.length || 0}`);
412
+ results.searchRecordsWithFields = widget;
413
+ }
414
+ }
415
+ catch (e) {
416
+ console.log(` āŒ Error: ${e.message}`);
417
+ }
418
+ console.log(`\nšŸ“Š Debug Results Summary:`);
419
+ console.log(` - searchRecords got: ${results.searchRecords ? Object.keys(results.searchRecords).length + ' fields' : 'failed'}`);
420
+ console.log(` - getRecord got: ${results.getRecord ? Object.keys(results.getRecord).length + ' fields' : 'failed'}`);
421
+ console.log(` - searchRecordsWithFields got: ${results.searchRecordsWithFields ? Object.keys(results.searchRecordsWithFields).length + ' fields' : 'failed'}`);
422
+ return results;
423
+ }
353
424
  /**
354
425
  * Get smart fetch strategy based on table - NOW USES ARTIFACT REGISTRY!
355
426
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "3.5.9",
4
- "description": "ServiceNow development framework with MCP server integration. Executes background scripts using ES5 JavaScript only (ServiceNow Rhino engine requirement). Provides 18 MCP servers including local development sync for editing ServiceNow artifacts with Claude Code native tools, widget deployment with coherence validation, table operations, script execution, machine learning, advanced features, and comprehensive platform management.",
3
+ "version": "3.5.11",
4
+ "description": "ServiceNow development framework with MCP server integration and fast response times. Features timeout-protected MCP operations to prevent Claude API timeouts. Executes background scripts using ES5 JavaScript only (ServiceNow Rhino engine requirement). Provides 18 MCP servers including local development sync for editing ServiceNow artifacts with Claude Code native tools, widget deployment with coherence validation, table operations, script execution, machine learning, advanced features, and comprehensive platform management.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
7
7
  "bin": {