snow-flow 3.4.19 ā 3.4.21
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.
|
@@ -516,6 +516,8 @@ class ServiceNowAutomationMCP {
|
|
|
516
516
|
}
|
|
517
517
|
// Complete operation with token tracking
|
|
518
518
|
this.logger.operationComplete(name, result);
|
|
519
|
+
// Add token usage to response
|
|
520
|
+
result = this.logger.addTokenUsageToResponse(result);
|
|
519
521
|
return result;
|
|
520
522
|
}
|
|
521
523
|
catch (error) {
|
|
@@ -847,6 +847,8 @@ class ServiceNowOperationsMCP {
|
|
|
847
847
|
}
|
|
848
848
|
// Complete operation with token tracking
|
|
849
849
|
this.logger.operationComplete(name, result);
|
|
850
|
+
// Add token usage to response
|
|
851
|
+
result = this.logger.addTokenUsageToResponse(result);
|
|
850
852
|
return result;
|
|
851
853
|
}
|
|
852
854
|
catch (error) {
|
|
@@ -870,7 +872,7 @@ class ServiceNowOperationsMCP {
|
|
|
870
872
|
query?.toLowerCase().includes('onboard') ||
|
|
871
873
|
(fields && fields.length <= 2); // Minimal fields = analytics
|
|
872
874
|
if (isAnalyticsContext) {
|
|
873
|
-
logger.info(`š Analytics context detected - NO LIMIT applied for complete analysis`);
|
|
875
|
+
this.logger.info(`š Analytics context detected - NO LIMIT applied for complete analysis`);
|
|
874
876
|
return undefined; // No limit - get ALL records
|
|
875
877
|
}
|
|
876
878
|
// š¤ ML Training context
|
|
@@ -878,21 +880,21 @@ class ServiceNowOperationsMCP {
|
|
|
878
880
|
query?.toLowerCase().includes('ml') ||
|
|
879
881
|
table?.toLowerCase().includes('train');
|
|
880
882
|
if (isMLContext) {
|
|
881
|
-
logger.info(`š§ ML context detected - using ML-optimized limit: 5000`);
|
|
883
|
+
this.logger.info(`š§ ML context detected - using ML-optimized limit: 5000`);
|
|
882
884
|
return 5000; // ML training needs substantial data
|
|
883
885
|
}
|
|
884
886
|
// š Count-only queries - efficient
|
|
885
887
|
if (!includeContent) {
|
|
886
|
-
logger.info(`š Count-only query - can handle large datasets efficiently`);
|
|
888
|
+
this.logger.info(`š Count-only query - can handle large datasets efficiently`);
|
|
887
889
|
return 10000; // Count queries are very memory-efficient
|
|
888
890
|
}
|
|
889
891
|
// š„ļø Display context - limited data needed
|
|
890
892
|
if (includeContent && fields && fields.length > 5) {
|
|
891
|
-
logger.info(`š„ļø Display context detected - limiting to viewable records`);
|
|
893
|
+
this.logger.info(`š„ļø Display context detected - limiting to viewable records`);
|
|
892
894
|
return 100; // Display queries need less data
|
|
893
895
|
}
|
|
894
896
|
// Default: moderate limit
|
|
895
|
-
logger.info(`ā ļø No specific context detected - using conservative limit. Consider specifying limit for your use case!`);
|
|
897
|
+
this.logger.info(`ā ļø No specific context detected - using conservative limit. Consider specifying limit for your use case!`);
|
|
896
898
|
return 500; // Conservative default
|
|
897
899
|
};
|
|
898
900
|
const { table, query = '', include_content = false, fields, include_display_values = false, group_by, order_by, offset = 0 // ā
NEW: Support pagination!
|
|
@@ -910,9 +912,9 @@ class ServiceNowOperationsMCP {
|
|
|
910
912
|
query?.toLowerCase().includes('ml') ||
|
|
911
913
|
args.limit !== undefined && args.limit < 1000;
|
|
912
914
|
if (isMLTrainingContext && limit < 1000) {
|
|
913
|
-
logger.warn(`ā ļø ML Training detected with low limit (${limit}). Consider setting limit=5000+ for better training data!`);
|
|
915
|
+
this.logger.warn(`ā ļø ML Training detected with low limit (${limit}). Consider setting limit=5000+ for better training data!`);
|
|
914
916
|
}
|
|
915
|
-
logger.info(`Universal query on table '${table}' with: ${query} (limit: ${limit === undefined ? 'UNLIMITED' : limit}, offset: ${offset}, include_content: ${include_content})`);
|
|
917
|
+
this.logger.info(`Universal query on table '${table}' with: ${query} (limit: ${limit === undefined ? 'UNLIMITED' : limit}, offset: ${offset}, include_content: ${include_content})`);
|
|
916
918
|
try {
|
|
917
919
|
// Convert natural language to ServiceNow query if needed
|
|
918
920
|
const processedQuery = this.processNaturalLanguageQuery(query || '', table);
|
|
@@ -957,7 +959,7 @@ class ServiceNowOperationsMCP {
|
|
|
957
959
|
const exists = sampleRecord.hasOwnProperty(field) ||
|
|
958
960
|
sampleRecord.hasOwnProperty(`${field}_display_value`);
|
|
959
961
|
if (!exists) {
|
|
960
|
-
logger.warn(`ā ļø Field '${field}' not found in table '${table}'`);
|
|
962
|
+
this.logger.warn(`ā ļø Field '${field}' not found in table '${table}'`);
|
|
961
963
|
}
|
|
962
964
|
return exists;
|
|
963
965
|
});
|
|
@@ -1040,7 +1042,7 @@ class ServiceNowOperationsMCP {
|
|
|
1040
1042
|
}
|
|
1041
1043
|
catch (error) {
|
|
1042
1044
|
// ā
IMPROVED: Better error messages
|
|
1043
|
-
logger.error(`Error querying ${table}:`, error);
|
|
1045
|
+
this.logger.error(`Error querying ${table}:`, error);
|
|
1044
1046
|
if (error.code === 'ECONNREFUSED') {
|
|
1045
1047
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Cannot connect to ServiceNow. Please check your instance URL and network connection.`);
|
|
1046
1048
|
}
|
|
@@ -1079,7 +1081,7 @@ class ServiceNowOperationsMCP {
|
|
|
1079
1081
|
async handleQueryIncidents(args) {
|
|
1080
1082
|
const { query, limit = 10, include__analysis = false, fields, include_content = false // šÆ NEW: Explicit control over returning full incident data
|
|
1081
1083
|
} = args;
|
|
1082
|
-
logger.info(`Querying incidents with: ${query} (include_content: ${include_content})`);
|
|
1084
|
+
this.logger.info(`Querying incidents with: ${query} (include_content: ${include_content})`);
|
|
1083
1085
|
try {
|
|
1084
1086
|
// Convert natural language to ServiceNow query if needed
|
|
1085
1087
|
const processedQuery = this.processNaturalLanguageQuery(query, 'incident');
|
|
@@ -1137,13 +1139,13 @@ class ServiceNowOperationsMCP {
|
|
|
1137
1139
|
};
|
|
1138
1140
|
}
|
|
1139
1141
|
catch (error) {
|
|
1140
|
-
logger.error('Error querying incidents:', error);
|
|
1142
|
+
this.logger.error('Error querying incidents:', error);
|
|
1141
1143
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to query incidents: ${error}`);
|
|
1142
1144
|
}
|
|
1143
1145
|
}
|
|
1144
1146
|
async handleAnalyzeIncident(args) {
|
|
1145
1147
|
const { incident_id, include_similar = true, suggest_resolution = true } = args;
|
|
1146
|
-
logger.info(`Analyzing incident: ${incident_id}`);
|
|
1148
|
+
this.logger.info(`Analyzing incident: ${incident_id}`);
|
|
1147
1149
|
try {
|
|
1148
1150
|
// Get incident details
|
|
1149
1151
|
const incident = await this.getIncidentDetails(incident_id);
|
|
@@ -1162,13 +1164,13 @@ class ServiceNowOperationsMCP {
|
|
|
1162
1164
|
};
|
|
1163
1165
|
}
|
|
1164
1166
|
catch (error) {
|
|
1165
|
-
logger.error('Error analyzing incident:', error);
|
|
1167
|
+
this.logger.error('Error analyzing incident:', error);
|
|
1166
1168
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to analyze incident: ${error}`);
|
|
1167
1169
|
}
|
|
1168
1170
|
}
|
|
1169
1171
|
async handleAutoResolveIncident(args) {
|
|
1170
1172
|
const { incident_id, dry_run = true } = args;
|
|
1171
|
-
logger.info(`Auto-resolving incident: ${incident_id} (dry_run: ${dry_run})`);
|
|
1173
|
+
this.logger.info(`Auto-resolving incident: ${incident_id} (dry_run: ${dry_run})`);
|
|
1172
1174
|
try {
|
|
1173
1175
|
// Get incident details
|
|
1174
1176
|
const incident = await this.getIncidentDetails(incident_id);
|
|
@@ -1200,13 +1202,13 @@ class ServiceNowOperationsMCP {
|
|
|
1200
1202
|
};
|
|
1201
1203
|
}
|
|
1202
1204
|
catch (error) {
|
|
1203
|
-
logger.error('Error auto-resolving incident:', error);
|
|
1205
|
+
this.logger.error('Error auto-resolving incident:', error);
|
|
1204
1206
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to auto-resolve incident: ${error}`);
|
|
1205
1207
|
}
|
|
1206
1208
|
}
|
|
1207
1209
|
async handleQueryRequests(args) {
|
|
1208
1210
|
const { query, limit = 10, include_items = false } = args;
|
|
1209
|
-
logger.info(`Querying requests with: ${query}`);
|
|
1211
|
+
this.logger.info(`Querying requests with: ${query}`);
|
|
1210
1212
|
try {
|
|
1211
1213
|
const processedQuery = this.processNaturalLanguageQuery(query, 'request');
|
|
1212
1214
|
const requests = await this.client.searchRecords('sc_request', processedQuery, limit);
|
|
@@ -1229,13 +1231,13 @@ class ServiceNowOperationsMCP {
|
|
|
1229
1231
|
};
|
|
1230
1232
|
}
|
|
1231
1233
|
catch (error) {
|
|
1232
|
-
logger.error('Error querying requests:', error);
|
|
1234
|
+
this.logger.error('Error querying requests:', error);
|
|
1233
1235
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to query requests: ${error}`);
|
|
1234
1236
|
}
|
|
1235
1237
|
}
|
|
1236
1238
|
async handleQueryProblems(args) {
|
|
1237
1239
|
const { query, limit = 10, include_incidents = false } = args;
|
|
1238
|
-
logger.info(`Querying problems with: ${query}`);
|
|
1240
|
+
this.logger.info(`Querying problems with: ${query}`);
|
|
1239
1241
|
try {
|
|
1240
1242
|
const processedQuery = this.processNaturalLanguageQuery(query, 'problem');
|
|
1241
1243
|
const problems = await this.client.searchRecords('problem', processedQuery, limit);
|
|
@@ -1258,13 +1260,13 @@ class ServiceNowOperationsMCP {
|
|
|
1258
1260
|
};
|
|
1259
1261
|
}
|
|
1260
1262
|
catch (error) {
|
|
1261
|
-
logger.error('Error querying problems:', error);
|
|
1263
|
+
this.logger.error('Error querying problems:', error);
|
|
1262
1264
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to query problems: ${error}`);
|
|
1263
1265
|
}
|
|
1264
1266
|
}
|
|
1265
1267
|
async handleCMDBSearch(args) {
|
|
1266
1268
|
const { query, ci_type = 'any', limit = 10, include_relationships = false } = args;
|
|
1267
|
-
logger.info(`Searching CMDB with: ${query}, type: ${ci_type}`);
|
|
1269
|
+
this.logger.info(`Searching CMDB with: ${query}, type: ${ci_type}`);
|
|
1268
1270
|
try {
|
|
1269
1271
|
const ciTable = ci_type === 'any' ? 'cmdb_ci' : operationalTableMapping[ci_type] || 'cmdb_ci';
|
|
1270
1272
|
const processedQuery = this.processNaturalLanguageQuery(query, 'cmdb');
|
|
@@ -1289,13 +1291,13 @@ class ServiceNowOperationsMCP {
|
|
|
1289
1291
|
};
|
|
1290
1292
|
}
|
|
1291
1293
|
catch (error) {
|
|
1292
|
-
logger.error('Error searching CMDB:', error);
|
|
1294
|
+
this.logger.error('Error searching CMDB:', error);
|
|
1293
1295
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search CMDB: ${error}`);
|
|
1294
1296
|
}
|
|
1295
1297
|
}
|
|
1296
1298
|
async handleUserLookup(args) {
|
|
1297
1299
|
const { identifier, include_roles = true, include_groups = true } = args;
|
|
1298
|
-
logger.info(`Looking up user: ${identifier}`);
|
|
1300
|
+
this.logger.info(`Looking up user: ${identifier}`);
|
|
1299
1301
|
try {
|
|
1300
1302
|
// Try different user lookup strategies
|
|
1301
1303
|
let userQuery = '';
|
|
@@ -1338,13 +1340,13 @@ class ServiceNowOperationsMCP {
|
|
|
1338
1340
|
};
|
|
1339
1341
|
}
|
|
1340
1342
|
catch (error) {
|
|
1341
|
-
logger.error('Error looking up user:', error);
|
|
1343
|
+
this.logger.error('Error looking up user:', error);
|
|
1342
1344
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to lookup user: ${error}`);
|
|
1343
1345
|
}
|
|
1344
1346
|
}
|
|
1345
1347
|
async handleOperationalMetrics(args) {
|
|
1346
1348
|
const { timeframe = 'week', metric_types = [] } = args;
|
|
1347
|
-
logger.info(`Getting operational metrics for timeframe: ${timeframe}`);
|
|
1349
|
+
this.logger.info(`Getting operational metrics for timeframe: ${timeframe}`);
|
|
1348
1350
|
try {
|
|
1349
1351
|
const metrics = await this.calculateOperationalMetrics(timeframe, metric_types);
|
|
1350
1352
|
return {
|
|
@@ -1357,13 +1359,13 @@ class ServiceNowOperationsMCP {
|
|
|
1357
1359
|
};
|
|
1358
1360
|
}
|
|
1359
1361
|
catch (error) {
|
|
1360
|
-
logger.error('Error getting operational metrics:', error);
|
|
1362
|
+
this.logger.error('Error getting operational metrics:', error);
|
|
1361
1363
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get operational metrics: ${error}`);
|
|
1362
1364
|
}
|
|
1363
1365
|
}
|
|
1364
1366
|
async handlePatternAnalysis(args) {
|
|
1365
1367
|
const { analysis_type, timeframe = 'week' } = args;
|
|
1366
|
-
logger.info(`Performing pattern _analysis: ${analysis_type} for ${timeframe}`);
|
|
1368
|
+
this.logger.info(`Performing pattern _analysis: ${analysis_type} for ${timeframe}`);
|
|
1367
1369
|
try {
|
|
1368
1370
|
const patterns = await this.analyzePatterns(analysis_type, timeframe);
|
|
1369
1371
|
return {
|
|
@@ -1376,13 +1378,13 @@ class ServiceNowOperationsMCP {
|
|
|
1376
1378
|
};
|
|
1377
1379
|
}
|
|
1378
1380
|
catch (error) {
|
|
1379
|
-
logger.error('Error analyzing patterns:', error);
|
|
1381
|
+
this.logger.error('Error analyzing patterns:', error);
|
|
1380
1382
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to analyze patterns: ${error}`);
|
|
1381
1383
|
}
|
|
1382
1384
|
}
|
|
1383
1385
|
async handleKnowledgeSearch(args) {
|
|
1384
1386
|
const { query, match_incident, limit = 5 } = args;
|
|
1385
|
-
logger.info(`Searching knowledge base with: ${query}`);
|
|
1387
|
+
this.logger.info(`Searching knowledge base with: ${query}`);
|
|
1386
1388
|
try {
|
|
1387
1389
|
const processedQuery = this.processNaturalLanguageQuery(query, 'knowledge');
|
|
1388
1390
|
const articles = await this.client.searchRecords('kb_knowledge', processedQuery, limit);
|
|
@@ -1408,13 +1410,13 @@ class ServiceNowOperationsMCP {
|
|
|
1408
1410
|
};
|
|
1409
1411
|
}
|
|
1410
1412
|
catch (error) {
|
|
1411
|
-
logger.error('Error searching knowledge base:', error);
|
|
1413
|
+
this.logger.error('Error searching knowledge base:', error);
|
|
1412
1414
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search knowledge base: ${error}`);
|
|
1413
1415
|
}
|
|
1414
1416
|
}
|
|
1415
1417
|
async handlePredictiveAnalysis(args) {
|
|
1416
1418
|
const { prediction_type, timeframe = 'week' } = args;
|
|
1417
|
-
logger.info(`Performing predictive _analysis: ${prediction_type} for ${timeframe}`);
|
|
1419
|
+
this.logger.info(`Performing predictive _analysis: ${prediction_type} for ${timeframe}`);
|
|
1418
1420
|
try {
|
|
1419
1421
|
const predictions = await this.performPredictiveAnalysis(prediction_type, timeframe);
|
|
1420
1422
|
return {
|
|
@@ -1427,7 +1429,7 @@ class ServiceNowOperationsMCP {
|
|
|
1427
1429
|
};
|
|
1428
1430
|
}
|
|
1429
1431
|
catch (error) {
|
|
1430
|
-
logger.error('Error performing predictive _analysis:', error);
|
|
1432
|
+
this.logger.error('Error performing predictive _analysis:', error);
|
|
1431
1433
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to perform predictive _analysis: ${error}`);
|
|
1432
1434
|
}
|
|
1433
1435
|
}
|
|
@@ -1437,7 +1439,7 @@ class ServiceNowOperationsMCP {
|
|
|
1437
1439
|
const lowerQuery = query.toLowerCase();
|
|
1438
1440
|
// If already a ServiceNow encoded query, return as-is
|
|
1439
1441
|
if (query.includes('=') || query.includes('!=') || query.includes('^') || query.includes('LIKE')) {
|
|
1440
|
-
logger.info(`Using raw ServiceNow query: ${query}`);
|
|
1442
|
+
this.logger.info(`Using raw ServiceNow query: ${query}`);
|
|
1441
1443
|
return query;
|
|
1442
1444
|
}
|
|
1443
1445
|
// Common ServiceNow query patterns
|
|
@@ -1610,7 +1612,7 @@ class ServiceNowOperationsMCP {
|
|
|
1610
1612
|
for (const action of actions) {
|
|
1611
1613
|
try {
|
|
1612
1614
|
// This would integrate with actual automation systems
|
|
1613
|
-
logger.info(`Executing action: ${action} for incident ${incident_id}`);
|
|
1615
|
+
this.logger.info(`Executing action: ${action} for incident ${incident_id}`);
|
|
1614
1616
|
executed.push(`ā
${action}`);
|
|
1615
1617
|
}
|
|
1616
1618
|
catch (error) {
|
|
@@ -1651,7 +1653,7 @@ class ServiceNowOperationsMCP {
|
|
|
1651
1653
|
});
|
|
1652
1654
|
}
|
|
1653
1655
|
catch (error) {
|
|
1654
|
-
logger.error(`Error getting relationships for CI ${ci.sys_id}:`, error);
|
|
1656
|
+
this.logger.error(`Error getting relationships for CI ${ci.sys_id}:`, error);
|
|
1655
1657
|
}
|
|
1656
1658
|
}
|
|
1657
1659
|
return relationships;
|
|
@@ -1700,7 +1702,7 @@ class ServiceNowOperationsMCP {
|
|
|
1700
1702
|
.map(([category, count]) => `${category}: ${count}`);
|
|
1701
1703
|
}
|
|
1702
1704
|
catch (error) {
|
|
1703
|
-
logger.error('Error calculating operational metrics:', error);
|
|
1705
|
+
this.logger.error('Error calculating operational metrics:', error);
|
|
1704
1706
|
}
|
|
1705
1707
|
return metrics;
|
|
1706
1708
|
}
|
|
@@ -2109,7 +2111,7 @@ class ServiceNowOperationsMCP {
|
|
|
2109
2111
|
}
|
|
2110
2112
|
async handleCatalogItemManager(args) {
|
|
2111
2113
|
const { action, item_id, ...params } = args;
|
|
2112
|
-
logger.info(`Catalog item manager action: ${action}`, { item_id, params });
|
|
2114
|
+
this.logger.info(`Catalog item manager action: ${action}`, { item_id, params });
|
|
2113
2115
|
try {
|
|
2114
2116
|
switch (action) {
|
|
2115
2117
|
case 'create': {
|
|
@@ -2121,13 +2123,13 @@ class ServiceNowOperationsMCP {
|
|
|
2121
2123
|
const defaultCatalogResult = await this.client.searchRecords('sc_catalog', 'active=true^ORDERBYsys_created_on', 1);
|
|
2122
2124
|
if (defaultCatalogResult.success && defaultCatalogResult.data?.result?.length > 0) {
|
|
2123
2125
|
catalogId = defaultCatalogResult.data.result[0].sys_id;
|
|
2124
|
-
logger.info('Using default catalog:', {
|
|
2126
|
+
this.logger.info('Using default catalog:', {
|
|
2125
2127
|
catalogId,
|
|
2126
2128
|
catalogName: defaultCatalogResult.data.result[0].title
|
|
2127
2129
|
});
|
|
2128
2130
|
}
|
|
2129
2131
|
else {
|
|
2130
|
-
logger.warn('No catalogs found, catalog item may not be visible');
|
|
2132
|
+
this.logger.warn('No catalogs found, catalog item may not be visible');
|
|
2131
2133
|
}
|
|
2132
2134
|
}
|
|
2133
2135
|
if (!categoryId) {
|
|
@@ -2135,7 +2137,7 @@ class ServiceNowOperationsMCP {
|
|
|
2135
2137
|
const defaultCategoryResult = await this.client.searchRecords('sc_category', 'active=true^titleLIKEHardware^ORtitleLIKEGeneral^ORtitleLIKEIT^ORDERBYsys_created_on', 1);
|
|
2136
2138
|
if (defaultCategoryResult.success && defaultCategoryResult.data?.result?.length > 0) {
|
|
2137
2139
|
categoryId = defaultCategoryResult.data.result[0].sys_id;
|
|
2138
|
-
logger.info('Using default category:', {
|
|
2140
|
+
this.logger.info('Using default category:', {
|
|
2139
2141
|
categoryId,
|
|
2140
2142
|
categoryName: defaultCategoryResult.data.result[0].title
|
|
2141
2143
|
});
|
|
@@ -2323,7 +2325,7 @@ class ServiceNowOperationsMCP {
|
|
|
2323
2325
|
}
|
|
2324
2326
|
}
|
|
2325
2327
|
catch (error) {
|
|
2326
|
-
logger.error('Error in catalog item manager:', error);
|
|
2328
|
+
this.logger.error('Error in catalog item manager:', error);
|
|
2327
2329
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Catalog item manager error: ${error}`);
|
|
2328
2330
|
}
|
|
2329
2331
|
}
|
|
@@ -2358,7 +2360,7 @@ class ServiceNowOperationsMCP {
|
|
|
2358
2360
|
}
|
|
2359
2361
|
async handleCatalogItemSearch(args) {
|
|
2360
2362
|
const { query, include_inactive = false, fuzzy_match = true, category_filter, limit = 50, include_variables = false } = args;
|
|
2361
|
-
logger.info(`Searching catalog items for: ${query}`, { fuzzy_match, category_filter, limit });
|
|
2363
|
+
this.logger.info(`Searching catalog items for: ${query}`, { fuzzy_match, category_filter, limit });
|
|
2362
2364
|
try {
|
|
2363
2365
|
// Build search queries
|
|
2364
2366
|
const searchQueries = [];
|
|
@@ -2497,7 +2499,7 @@ class ServiceNowOperationsMCP {
|
|
|
2497
2499
|
};
|
|
2498
2500
|
}
|
|
2499
2501
|
catch (error) {
|
|
2500
|
-
logger.error('Error searching catalog items:', error);
|
|
2502
|
+
this.logger.error('Error searching catalog items:', error);
|
|
2501
2503
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Catalog search error: ${error}`);
|
|
2502
2504
|
}
|
|
2503
2505
|
}
|
|
@@ -2628,7 +2630,7 @@ class ServiceNowOperationsMCP {
|
|
|
2628
2630
|
}
|
|
2629
2631
|
async handleCleanupTestArtifacts(args) {
|
|
2630
2632
|
const { artifact_types = ['catalog_items', 'flows', 'users'], test_patterns = ['Test%', 'Mock%', 'Demo%', '%_test_%', '%test', '%mock'], max_age_hours = 1, dry_run = false, preserve_update_set_entries = true, update_set_filter } = args;
|
|
2631
|
-
logger.info('Starting test artifact cleanup', {
|
|
2633
|
+
this.logger.info('Starting test artifact cleanup', {
|
|
2632
2634
|
artifact_types,
|
|
2633
2635
|
test_patterns,
|
|
2634
2636
|
max_age_hours,
|
|
@@ -2737,7 +2739,7 @@ class ServiceNowOperationsMCP {
|
|
|
2737
2739
|
};
|
|
2738
2740
|
}
|
|
2739
2741
|
catch (error) {
|
|
2740
|
-
logger.error('Error during test artifact cleanup:', error);
|
|
2742
|
+
this.logger.error('Error during test artifact cleanup:', error);
|
|
2741
2743
|
return {
|
|
2742
2744
|
content: [{
|
|
2743
2745
|
type: 'text',
|
|
@@ -2868,7 +2870,7 @@ class ServiceNowOperationsMCP {
|
|
|
2868
2870
|
}
|
|
2869
2871
|
async handleCreateUserGroup(args) {
|
|
2870
2872
|
const { name, description, email, manager, parent, type, active = true } = args;
|
|
2871
|
-
logger.info(`Creating user group: ${name}`);
|
|
2873
|
+
this.logger.info(`Creating user group: ${name}`);
|
|
2872
2874
|
try {
|
|
2873
2875
|
// Check if group already exists
|
|
2874
2876
|
const existingGroup = await this.client.get('/api/now/table/sys_user_group', {
|
|
@@ -2901,7 +2903,7 @@ class ServiceNowOperationsMCP {
|
|
|
2901
2903
|
groupData.manager = managerUser.sys_id;
|
|
2902
2904
|
}
|
|
2903
2905
|
else {
|
|
2904
|
-
logger.warn(`Manager "${manager}" not found, creating group without manager`);
|
|
2906
|
+
this.logger.warn(`Manager "${manager}" not found, creating group without manager`);
|
|
2905
2907
|
}
|
|
2906
2908
|
}
|
|
2907
2909
|
// Handle parent group lookup
|
|
@@ -2911,7 +2913,7 @@ class ServiceNowOperationsMCP {
|
|
|
2911
2913
|
groupData.parent = parentGroup.sys_id;
|
|
2912
2914
|
}
|
|
2913
2915
|
else {
|
|
2914
|
-
logger.warn(`Parent group "${parent}" not found, creating group without parent`);
|
|
2916
|
+
this.logger.warn(`Parent group "${parent}" not found, creating group without parent`);
|
|
2915
2917
|
}
|
|
2916
2918
|
}
|
|
2917
2919
|
// Create the group
|
|
@@ -2940,7 +2942,7 @@ class ServiceNowOperationsMCP {
|
|
|
2940
2942
|
}
|
|
2941
2943
|
async handleCreateUser(args) {
|
|
2942
2944
|
const { user_name, first_name, last_name, email, department, manager, phone, title, location, active = true, password } = args;
|
|
2943
|
-
logger.info(`Creating user: ${user_name}`);
|
|
2945
|
+
this.logger.info(`Creating user: ${user_name}`);
|
|
2944
2946
|
try {
|
|
2945
2947
|
// Check if user already exists
|
|
2946
2948
|
const existingUser = await this.client.get('/api/now/table/sys_user', {
|
|
@@ -2976,7 +2978,7 @@ class ServiceNowOperationsMCP {
|
|
|
2976
2978
|
userData.manager = managerUser.sys_id;
|
|
2977
2979
|
}
|
|
2978
2980
|
else {
|
|
2979
|
-
logger.warn(`Manager "${manager}" not found, creating user without manager`);
|
|
2981
|
+
this.logger.warn(`Manager "${manager}" not found, creating user without manager`);
|
|
2980
2982
|
}
|
|
2981
2983
|
}
|
|
2982
2984
|
// Handle department lookup
|
|
@@ -2986,7 +2988,7 @@ class ServiceNowOperationsMCP {
|
|
|
2986
2988
|
userData.department = dept.sys_id;
|
|
2987
2989
|
}
|
|
2988
2990
|
else {
|
|
2989
|
-
logger.warn(`Department "${department}" not found, creating user without department`);
|
|
2991
|
+
this.logger.warn(`Department "${department}" not found, creating user without department`);
|
|
2990
2992
|
}
|
|
2991
2993
|
}
|
|
2992
2994
|
// Handle location lookup
|
|
@@ -2996,7 +2998,7 @@ class ServiceNowOperationsMCP {
|
|
|
2996
2998
|
userData.location = loc.sys_id;
|
|
2997
2999
|
}
|
|
2998
3000
|
else {
|
|
2999
|
-
logger.warn(`Location "${location}" not found, creating user without location`);
|
|
3001
|
+
this.logger.warn(`Location "${location}" not found, creating user without location`);
|
|
3000
3002
|
}
|
|
3001
3003
|
}
|
|
3002
3004
|
// Create the user
|
|
@@ -3025,7 +3027,7 @@ class ServiceNowOperationsMCP {
|
|
|
3025
3027
|
}
|
|
3026
3028
|
async handleAssignUserToGroup(args) {
|
|
3027
3029
|
const { user, group } = args;
|
|
3028
|
-
logger.info(`Assigning user ${user} to group ${group}`);
|
|
3030
|
+
this.logger.info(`Assigning user ${user} to group ${group}`);
|
|
3029
3031
|
try {
|
|
3030
3032
|
// Find the user
|
|
3031
3033
|
const userRecord = await this.findUserBySysIdOrUsername(user);
|
|
@@ -3088,7 +3090,7 @@ class ServiceNowOperationsMCP {
|
|
|
3088
3090
|
}
|
|
3089
3091
|
async handleRemoveUserFromGroup(args) {
|
|
3090
3092
|
const { user, group } = args;
|
|
3091
|
-
logger.info(`Removing user ${user} from group ${group}`);
|
|
3093
|
+
this.logger.info(`Removing user ${user} from group ${group}`);
|
|
3092
3094
|
try {
|
|
3093
3095
|
// Find the user
|
|
3094
3096
|
const userRecord = await this.findUserBySysIdOrUsername(user);
|
|
@@ -3143,7 +3145,7 @@ class ServiceNowOperationsMCP {
|
|
|
3143
3145
|
}
|
|
3144
3146
|
async handleListGroupMembers(args) {
|
|
3145
3147
|
const { group, active_only = true } = args;
|
|
3146
|
-
logger.info(`Listing members of group: ${group}`);
|
|
3148
|
+
this.logger.info(`Listing members of group: ${group}`);
|
|
3147
3149
|
try {
|
|
3148
3150
|
// Find the group
|
|
3149
3151
|
const groupRecord = await this.findGroupBySysIdOrName(group);
|
|
@@ -3180,7 +3182,7 @@ class ServiceNowOperationsMCP {
|
|
|
3180
3182
|
const u = member.user;
|
|
3181
3183
|
// Defensive programming: check if user object exists and has required fields
|
|
3182
3184
|
if (!u || !u.user_name) {
|
|
3183
|
-
logger.warn(`Group member ${index + 1} has invalid user data:`, member);
|
|
3185
|
+
this.logger.warn(`Group member ${index + 1} has invalid user data:`, member);
|
|
3184
3186
|
response += `${index + 1}. **Invalid User Data** - ${member.sys_id || 'Unknown ID'}\n\n`;
|
|
3185
3187
|
return;
|
|
3186
3188
|
}
|
|
@@ -3276,7 +3278,7 @@ class ServiceNowOperationsMCP {
|
|
|
3276
3278
|
async run() {
|
|
3277
3279
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
3278
3280
|
await this.server.connect(transport);
|
|
3279
|
-
logger.info('ServiceNow Operations MCP Server started');
|
|
3281
|
+
this.logger.info('ServiceNow Operations MCP Server started');
|
|
3280
3282
|
}
|
|
3281
3283
|
}
|
|
3282
3284
|
exports.ServiceNowOperationsMCP = ServiceNowOperationsMCP;
|
|
@@ -29,7 +29,7 @@ class ServiceNowSystemPropertiesMCP {
|
|
|
29
29
|
},
|
|
30
30
|
});
|
|
31
31
|
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
32
|
-
this.oauth = new snow_oauth_js_1.
|
|
32
|
+
this.oauth = new snow_oauth_js_1.ServiceNowOAuth();
|
|
33
33
|
this.logger = new mcp_logger_js_1.MCPLogger('ServiceNowSystemProperties');
|
|
34
34
|
this.setupHandlers();
|
|
35
35
|
this.setupTools();
|
|
@@ -369,6 +369,8 @@ class ServiceNowSystemPropertiesMCP {
|
|
|
369
369
|
}
|
|
370
370
|
// Complete operation with token tracking
|
|
371
371
|
this.logger.operationComplete(name, result);
|
|
372
|
+
// Add token usage to response
|
|
373
|
+
result = this.logger.addTokenUsageToResponse(result);
|
|
372
374
|
return result;
|
|
373
375
|
}
|
|
374
376
|
catch (error) {
|
|
@@ -66,6 +66,11 @@ export declare class MCPLogger {
|
|
|
66
66
|
* Get token usage
|
|
67
67
|
*/
|
|
68
68
|
getTokenUsage(): TokenUsage;
|
|
69
|
+
/**
|
|
70
|
+
* Add token usage to MCP response
|
|
71
|
+
* Helper method to append token usage to tool response
|
|
72
|
+
*/
|
|
73
|
+
addTokenUsageToResponse(result: any): any;
|
|
69
74
|
/**
|
|
70
75
|
* Reset token usage
|
|
71
76
|
*/
|
|
@@ -131,7 +131,8 @@ class MCPLogger {
|
|
|
131
131
|
*/
|
|
132
132
|
operationStart(operation, params) {
|
|
133
133
|
this.startTime = Date.now();
|
|
134
|
-
this.
|
|
134
|
+
this.resetTokens(); // Actually reset tokens when starting new operation!
|
|
135
|
+
this.info(`š Starting: ${operation}`, params);
|
|
135
136
|
}
|
|
136
137
|
/**
|
|
137
138
|
* Log operation complete
|
|
@@ -161,6 +162,21 @@ class MCPLogger {
|
|
|
161
162
|
getTokenUsage() {
|
|
162
163
|
return { ...this.tokenUsage };
|
|
163
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Add token usage to MCP response
|
|
167
|
+
* Helper method to append token usage to tool response
|
|
168
|
+
*/
|
|
169
|
+
addTokenUsageToResponse(result) {
|
|
170
|
+
const tokenUsage = this.getTokenUsage();
|
|
171
|
+
if (result && result.content && Array.isArray(result.content) && tokenUsage.total > 0) {
|
|
172
|
+
// Append token usage info to the last content item
|
|
173
|
+
const lastContent = result.content[result.content.length - 1];
|
|
174
|
+
if (lastContent && lastContent.type === 'text') {
|
|
175
|
+
lastContent.text += `\n\nš Token Usage: ${tokenUsage.total} (Input: ${tokenUsage.input}, Output: ${tokenUsage.output})`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
164
180
|
/**
|
|
165
181
|
* Reset token usage
|
|
166
182
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.21",
|
|
4
4
|
"description": "ServiceNow development framework with MCP server integration. Executes background scripts using ES5 JavaScript only (ServiceNow Rhino engine requirement). Provides 17 MCP servers for complete ServiceNow operations including 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",
|