snow-flow 3.4.13 → 3.4.14
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/cli.js +23 -51
- package/dist/mcp/servicenow-automation-mcp.js +65 -29
- package/dist/mcp/servicenow-change-virtualagent-pa-mcp.js +51 -21
- package/dist/mcp/servicenow-cmdb-event-hr-csm-devops-mcp.js +59 -25
- package/dist/mcp/servicenow-deployment-mcp.js +38 -16
- package/dist/mcp/servicenow-development-assistant-mcp.js +67 -31
- package/dist/mcp/servicenow-flow-workspace-mobile-mcp.js +53 -22
- package/dist/mcp/servicenow-integration-mcp.js +48 -12
- package/dist/mcp/servicenow-knowledge-catalog-mcp.js +42 -16
- package/dist/mcp/servicenow-machine-learning-mcp.js +44 -17
- package/dist/mcp/servicenow-operations-mcp.d.ts +1 -0
- package/dist/mcp/servicenow-operations-mcp.js +108 -79
- package/dist/mcp/servicenow-platform-development-mcp.js +42 -11
- package/dist/mcp/servicenow-reporting-analytics-mcp.js +36 -13
- package/dist/mcp/servicenow-security-compliance-mcp.js +35 -13
- package/dist/mcp/servicenow-system-properties-mcp.d.ts +1 -0
- package/dist/mcp/servicenow-system-properties-mcp.js +66 -42
- package/dist/mcp/servicenow-update-set-mcp.js +35 -11
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
|
18
18
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
19
19
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
20
20
|
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
|
|
21
|
-
const
|
|
21
|
+
const mcp_logger_js_1 = require("./shared/mcp-logger.js");
|
|
22
22
|
// Operational table mappings for ServiceNow
|
|
23
23
|
const operationalTableMapping = {
|
|
24
24
|
// ITIL Core Processes
|
|
@@ -122,6 +122,7 @@ class ServiceNowOperationsMCP {
|
|
|
122
122
|
},
|
|
123
123
|
});
|
|
124
124
|
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
125
|
+
this.logger = new mcp_logger_js_1.MCPLogger('ServiceNowOperationsMCP');
|
|
125
126
|
this.setupToolHandlers();
|
|
126
127
|
}
|
|
127
128
|
setupToolHandlers() {
|
|
@@ -777,53 +778,79 @@ class ServiceNowOperationsMCP {
|
|
|
777
778
|
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
778
779
|
const { name, arguments: args } = request.params;
|
|
779
780
|
try {
|
|
781
|
+
// Start operation with token tracking
|
|
782
|
+
this.logger.operationStart(name, args);
|
|
783
|
+
let result;
|
|
780
784
|
switch (name) {
|
|
781
785
|
case 'snow_query_table':
|
|
782
|
-
|
|
786
|
+
result = await this.handleUniversalQuery(args);
|
|
787
|
+
break;
|
|
783
788
|
case 'snow_query_incidents':
|
|
784
|
-
|
|
789
|
+
result = await this.handleQueryIncidents(args);
|
|
790
|
+
break;
|
|
785
791
|
case 'snow_analyze_incident':
|
|
786
|
-
|
|
792
|
+
result = await this.handleAnalyzeIncident(args);
|
|
793
|
+
break;
|
|
787
794
|
case 'snow_auto_resolve_incident':
|
|
788
|
-
|
|
795
|
+
result = await this.handleAutoResolveIncident(args);
|
|
796
|
+
break;
|
|
789
797
|
case 'snow_query_requests':
|
|
790
|
-
|
|
798
|
+
result = await this.handleQueryRequests(args);
|
|
799
|
+
break;
|
|
791
800
|
case 'snow_query_problems':
|
|
792
|
-
|
|
801
|
+
result = await this.handleQueryProblems(args);
|
|
802
|
+
break;
|
|
793
803
|
case 'snow_cmdb_search':
|
|
794
|
-
|
|
804
|
+
result = await this.handleCMDBSearch(args);
|
|
805
|
+
break;
|
|
795
806
|
case 'snow_user_lookup':
|
|
796
|
-
|
|
807
|
+
result = await this.handleUserLookup(args);
|
|
808
|
+
break;
|
|
797
809
|
case 'snow_operational_metrics':
|
|
798
|
-
|
|
810
|
+
result = await this.handleOperationalMetrics(args);
|
|
811
|
+
break;
|
|
799
812
|
case 'snow_pattern__analysis':
|
|
800
|
-
|
|
813
|
+
result = await this.handlePatternAnalysis(args);
|
|
814
|
+
break;
|
|
801
815
|
case 'snow_knowledge_search':
|
|
802
|
-
|
|
816
|
+
result = await this.handleKnowledgeSearch(args);
|
|
817
|
+
break;
|
|
803
818
|
case 'snow_predictive__analysis':
|
|
804
|
-
|
|
819
|
+
result = await this.handlePredictiveAnalysis(args);
|
|
820
|
+
break;
|
|
805
821
|
case 'snow_catalog_item_manager':
|
|
806
|
-
|
|
822
|
+
result = await this.handleCatalogItemManager(args);
|
|
823
|
+
break;
|
|
807
824
|
case 'snow_catalog_item_search':
|
|
808
|
-
|
|
825
|
+
result = await this.handleCatalogItemSearch(args);
|
|
826
|
+
break;
|
|
809
827
|
case 'snow_cleanup_test_artifacts':
|
|
810
|
-
|
|
828
|
+
result = await this.handleCleanupTestArtifacts(args);
|
|
829
|
+
break;
|
|
811
830
|
case 'snow_create_user_group':
|
|
812
|
-
|
|
831
|
+
result = await this.handleCreateUserGroup(args);
|
|
832
|
+
break;
|
|
813
833
|
case 'snow_create_user':
|
|
814
|
-
|
|
834
|
+
result = await this.handleCreateUser(args);
|
|
835
|
+
break;
|
|
815
836
|
case 'snow_assign_user_to_group':
|
|
816
|
-
|
|
837
|
+
result = await this.handleAssignUserToGroup(args);
|
|
838
|
+
break;
|
|
817
839
|
case 'snow_remove_user_from_group':
|
|
818
|
-
|
|
840
|
+
result = await this.handleRemoveUserFromGroup(args);
|
|
841
|
+
break;
|
|
819
842
|
case 'snow_list_group_members':
|
|
820
|
-
|
|
843
|
+
result = await this.handleListGroupMembers(args);
|
|
844
|
+
break;
|
|
821
845
|
default:
|
|
822
846
|
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Tool ${name} not found`);
|
|
823
847
|
}
|
|
848
|
+
// Complete operation with token tracking
|
|
849
|
+
this.logger.operationComplete(name, result);
|
|
850
|
+
return result;
|
|
824
851
|
}
|
|
825
852
|
catch (error) {
|
|
826
|
-
|
|
853
|
+
this.logger.error(`Error executing tool ${name}:`, error);
|
|
827
854
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to execute ${name}: ${error}`);
|
|
828
855
|
}
|
|
829
856
|
});
|
|
@@ -843,7 +870,7 @@ class ServiceNowOperationsMCP {
|
|
|
843
870
|
query?.toLowerCase().includes('onboard') ||
|
|
844
871
|
(fields && fields.length <= 2); // Minimal fields = analytics
|
|
845
872
|
if (isAnalyticsContext) {
|
|
846
|
-
|
|
873
|
+
logger.info(`📊 Analytics context detected - NO LIMIT applied for complete analysis`);
|
|
847
874
|
return undefined; // No limit - get ALL records
|
|
848
875
|
}
|
|
849
876
|
// 🤖 ML Training context
|
|
@@ -851,21 +878,21 @@ class ServiceNowOperationsMCP {
|
|
|
851
878
|
query?.toLowerCase().includes('ml') ||
|
|
852
879
|
table?.toLowerCase().includes('train');
|
|
853
880
|
if (isMLContext) {
|
|
854
|
-
|
|
881
|
+
logger.info(`🧠 ML context detected - using ML-optimized limit: 5000`);
|
|
855
882
|
return 5000; // ML training needs substantial data
|
|
856
883
|
}
|
|
857
884
|
// 📈 Count-only queries - efficient
|
|
858
885
|
if (!includeContent) {
|
|
859
|
-
|
|
886
|
+
logger.info(`📈 Count-only query - can handle large datasets efficiently`);
|
|
860
887
|
return 10000; // Count queries are very memory-efficient
|
|
861
888
|
}
|
|
862
889
|
// 🖥️ Display context - limited data needed
|
|
863
890
|
if (includeContent && fields && fields.length > 5) {
|
|
864
|
-
|
|
891
|
+
logger.info(`🖥️ Display context detected - limiting to viewable records`);
|
|
865
892
|
return 100; // Display queries need less data
|
|
866
893
|
}
|
|
867
894
|
// Default: moderate limit
|
|
868
|
-
|
|
895
|
+
logger.info(`⚠️ No specific context detected - using conservative limit. Consider specifying limit for your use case!`);
|
|
869
896
|
return 500; // Conservative default
|
|
870
897
|
};
|
|
871
898
|
const { table, query = '', include_content = false, fields, include_display_values = false, group_by, order_by, offset = 0 // ✅ NEW: Support pagination!
|
|
@@ -883,9 +910,9 @@ class ServiceNowOperationsMCP {
|
|
|
883
910
|
query?.toLowerCase().includes('ml') ||
|
|
884
911
|
args.limit !== undefined && args.limit < 1000;
|
|
885
912
|
if (isMLTrainingContext && limit < 1000) {
|
|
886
|
-
|
|
913
|
+
logger.warn(`⚠️ ML Training detected with low limit (${limit}). Consider setting limit=5000+ for better training data!`);
|
|
887
914
|
}
|
|
888
|
-
|
|
915
|
+
logger.info(`Universal query on table '${table}' with: ${query} (limit: ${limit === undefined ? 'UNLIMITED' : limit}, offset: ${offset}, include_content: ${include_content})`);
|
|
889
916
|
try {
|
|
890
917
|
// Convert natural language to ServiceNow query if needed
|
|
891
918
|
const processedQuery = this.processNaturalLanguageQuery(query || '', table);
|
|
@@ -898,6 +925,7 @@ class ServiceNowOperationsMCP {
|
|
|
898
925
|
finalQuery += `^ORDERBY${orderDirection ? orderDirection : ''}${orderField}`;
|
|
899
926
|
}
|
|
900
927
|
// ✅ NEW: Use searchRecordsWithOffset when offset is provided
|
|
928
|
+
this.logger.trackAPICall('SEARCH', table, effectiveLimit);
|
|
901
929
|
const records = offset > 0
|
|
902
930
|
? await this.client.searchRecordsWithOffset(table, finalQuery, effectiveLimit, offset)
|
|
903
931
|
: await this.client.searchRecords(table, finalQuery, effectiveLimit);
|
|
@@ -929,7 +957,7 @@ class ServiceNowOperationsMCP {
|
|
|
929
957
|
const exists = sampleRecord.hasOwnProperty(field) ||
|
|
930
958
|
sampleRecord.hasOwnProperty(`${field}_display_value`);
|
|
931
959
|
if (!exists) {
|
|
932
|
-
|
|
960
|
+
logger.warn(`⚠️ Field '${field}' not found in table '${table}'`);
|
|
933
961
|
}
|
|
934
962
|
return exists;
|
|
935
963
|
});
|
|
@@ -1012,7 +1040,7 @@ class ServiceNowOperationsMCP {
|
|
|
1012
1040
|
}
|
|
1013
1041
|
catch (error) {
|
|
1014
1042
|
// ✅ IMPROVED: Better error messages
|
|
1015
|
-
|
|
1043
|
+
logger.error(`Error querying ${table}:`, error);
|
|
1016
1044
|
if (error.code === 'ECONNREFUSED') {
|
|
1017
1045
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Cannot connect to ServiceNow. Please check your instance URL and network connection.`);
|
|
1018
1046
|
}
|
|
@@ -1051,11 +1079,12 @@ class ServiceNowOperationsMCP {
|
|
|
1051
1079
|
async handleQueryIncidents(args) {
|
|
1052
1080
|
const { query, limit = 10, include__analysis = false, fields, include_content = false // 🎯 NEW: Explicit control over returning full incident data
|
|
1053
1081
|
} = args;
|
|
1054
|
-
|
|
1082
|
+
logger.info(`Querying incidents with: ${query} (include_content: ${include_content})`);
|
|
1055
1083
|
try {
|
|
1056
1084
|
// Convert natural language to ServiceNow query if needed
|
|
1057
1085
|
const processedQuery = this.processNaturalLanguageQuery(query, 'incident');
|
|
1058
1086
|
// Query incidents
|
|
1087
|
+
this.logger.trackAPICall('SEARCH', 'incident', limit);
|
|
1059
1088
|
const incidents = await this.client.searchRecords('incident', processedQuery, limit);
|
|
1060
1089
|
let result = {
|
|
1061
1090
|
total_results: incidents.success ? incidents.data.result.length : 0,
|
|
@@ -1108,13 +1137,13 @@ class ServiceNowOperationsMCP {
|
|
|
1108
1137
|
};
|
|
1109
1138
|
}
|
|
1110
1139
|
catch (error) {
|
|
1111
|
-
|
|
1140
|
+
logger.error('Error querying incidents:', error);
|
|
1112
1141
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to query incidents: ${error}`);
|
|
1113
1142
|
}
|
|
1114
1143
|
}
|
|
1115
1144
|
async handleAnalyzeIncident(args) {
|
|
1116
1145
|
const { incident_id, include_similar = true, suggest_resolution = true } = args;
|
|
1117
|
-
|
|
1146
|
+
logger.info(`Analyzing incident: ${incident_id}`);
|
|
1118
1147
|
try {
|
|
1119
1148
|
// Get incident details
|
|
1120
1149
|
const incident = await this.getIncidentDetails(incident_id);
|
|
@@ -1133,13 +1162,13 @@ class ServiceNowOperationsMCP {
|
|
|
1133
1162
|
};
|
|
1134
1163
|
}
|
|
1135
1164
|
catch (error) {
|
|
1136
|
-
|
|
1165
|
+
logger.error('Error analyzing incident:', error);
|
|
1137
1166
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to analyze incident: ${error}`);
|
|
1138
1167
|
}
|
|
1139
1168
|
}
|
|
1140
1169
|
async handleAutoResolveIncident(args) {
|
|
1141
1170
|
const { incident_id, dry_run = true } = args;
|
|
1142
|
-
|
|
1171
|
+
logger.info(`Auto-resolving incident: ${incident_id} (dry_run: ${dry_run})`);
|
|
1143
1172
|
try {
|
|
1144
1173
|
// Get incident details
|
|
1145
1174
|
const incident = await this.getIncidentDetails(incident_id);
|
|
@@ -1171,13 +1200,13 @@ class ServiceNowOperationsMCP {
|
|
|
1171
1200
|
};
|
|
1172
1201
|
}
|
|
1173
1202
|
catch (error) {
|
|
1174
|
-
|
|
1203
|
+
logger.error('Error auto-resolving incident:', error);
|
|
1175
1204
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to auto-resolve incident: ${error}`);
|
|
1176
1205
|
}
|
|
1177
1206
|
}
|
|
1178
1207
|
async handleQueryRequests(args) {
|
|
1179
1208
|
const { query, limit = 10, include_items = false } = args;
|
|
1180
|
-
|
|
1209
|
+
logger.info(`Querying requests with: ${query}`);
|
|
1181
1210
|
try {
|
|
1182
1211
|
const processedQuery = this.processNaturalLanguageQuery(query, 'request');
|
|
1183
1212
|
const requests = await this.client.searchRecords('sc_request', processedQuery, limit);
|
|
@@ -1200,13 +1229,13 @@ class ServiceNowOperationsMCP {
|
|
|
1200
1229
|
};
|
|
1201
1230
|
}
|
|
1202
1231
|
catch (error) {
|
|
1203
|
-
|
|
1232
|
+
logger.error('Error querying requests:', error);
|
|
1204
1233
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to query requests: ${error}`);
|
|
1205
1234
|
}
|
|
1206
1235
|
}
|
|
1207
1236
|
async handleQueryProblems(args) {
|
|
1208
1237
|
const { query, limit = 10, include_incidents = false } = args;
|
|
1209
|
-
|
|
1238
|
+
logger.info(`Querying problems with: ${query}`);
|
|
1210
1239
|
try {
|
|
1211
1240
|
const processedQuery = this.processNaturalLanguageQuery(query, 'problem');
|
|
1212
1241
|
const problems = await this.client.searchRecords('problem', processedQuery, limit);
|
|
@@ -1229,13 +1258,13 @@ class ServiceNowOperationsMCP {
|
|
|
1229
1258
|
};
|
|
1230
1259
|
}
|
|
1231
1260
|
catch (error) {
|
|
1232
|
-
|
|
1261
|
+
logger.error('Error querying problems:', error);
|
|
1233
1262
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to query problems: ${error}`);
|
|
1234
1263
|
}
|
|
1235
1264
|
}
|
|
1236
1265
|
async handleCMDBSearch(args) {
|
|
1237
1266
|
const { query, ci_type = 'any', limit = 10, include_relationships = false } = args;
|
|
1238
|
-
|
|
1267
|
+
logger.info(`Searching CMDB with: ${query}, type: ${ci_type}`);
|
|
1239
1268
|
try {
|
|
1240
1269
|
const ciTable = ci_type === 'any' ? 'cmdb_ci' : operationalTableMapping[ci_type] || 'cmdb_ci';
|
|
1241
1270
|
const processedQuery = this.processNaturalLanguageQuery(query, 'cmdb');
|
|
@@ -1260,13 +1289,13 @@ class ServiceNowOperationsMCP {
|
|
|
1260
1289
|
};
|
|
1261
1290
|
}
|
|
1262
1291
|
catch (error) {
|
|
1263
|
-
|
|
1292
|
+
logger.error('Error searching CMDB:', error);
|
|
1264
1293
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search CMDB: ${error}`);
|
|
1265
1294
|
}
|
|
1266
1295
|
}
|
|
1267
1296
|
async handleUserLookup(args) {
|
|
1268
1297
|
const { identifier, include_roles = true, include_groups = true } = args;
|
|
1269
|
-
|
|
1298
|
+
logger.info(`Looking up user: ${identifier}`);
|
|
1270
1299
|
try {
|
|
1271
1300
|
// Try different user lookup strategies
|
|
1272
1301
|
let userQuery = '';
|
|
@@ -1309,13 +1338,13 @@ class ServiceNowOperationsMCP {
|
|
|
1309
1338
|
};
|
|
1310
1339
|
}
|
|
1311
1340
|
catch (error) {
|
|
1312
|
-
|
|
1341
|
+
logger.error('Error looking up user:', error);
|
|
1313
1342
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to lookup user: ${error}`);
|
|
1314
1343
|
}
|
|
1315
1344
|
}
|
|
1316
1345
|
async handleOperationalMetrics(args) {
|
|
1317
1346
|
const { timeframe = 'week', metric_types = [] } = args;
|
|
1318
|
-
|
|
1347
|
+
logger.info(`Getting operational metrics for timeframe: ${timeframe}`);
|
|
1319
1348
|
try {
|
|
1320
1349
|
const metrics = await this.calculateOperationalMetrics(timeframe, metric_types);
|
|
1321
1350
|
return {
|
|
@@ -1328,13 +1357,13 @@ class ServiceNowOperationsMCP {
|
|
|
1328
1357
|
};
|
|
1329
1358
|
}
|
|
1330
1359
|
catch (error) {
|
|
1331
|
-
|
|
1360
|
+
logger.error('Error getting operational metrics:', error);
|
|
1332
1361
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get operational metrics: ${error}`);
|
|
1333
1362
|
}
|
|
1334
1363
|
}
|
|
1335
1364
|
async handlePatternAnalysis(args) {
|
|
1336
1365
|
const { analysis_type, timeframe = 'week' } = args;
|
|
1337
|
-
|
|
1366
|
+
logger.info(`Performing pattern _analysis: ${analysis_type} for ${timeframe}`);
|
|
1338
1367
|
try {
|
|
1339
1368
|
const patterns = await this.analyzePatterns(analysis_type, timeframe);
|
|
1340
1369
|
return {
|
|
@@ -1347,13 +1376,13 @@ class ServiceNowOperationsMCP {
|
|
|
1347
1376
|
};
|
|
1348
1377
|
}
|
|
1349
1378
|
catch (error) {
|
|
1350
|
-
|
|
1379
|
+
logger.error('Error analyzing patterns:', error);
|
|
1351
1380
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to analyze patterns: ${error}`);
|
|
1352
1381
|
}
|
|
1353
1382
|
}
|
|
1354
1383
|
async handleKnowledgeSearch(args) {
|
|
1355
1384
|
const { query, match_incident, limit = 5 } = args;
|
|
1356
|
-
|
|
1385
|
+
logger.info(`Searching knowledge base with: ${query}`);
|
|
1357
1386
|
try {
|
|
1358
1387
|
const processedQuery = this.processNaturalLanguageQuery(query, 'knowledge');
|
|
1359
1388
|
const articles = await this.client.searchRecords('kb_knowledge', processedQuery, limit);
|
|
@@ -1379,13 +1408,13 @@ class ServiceNowOperationsMCP {
|
|
|
1379
1408
|
};
|
|
1380
1409
|
}
|
|
1381
1410
|
catch (error) {
|
|
1382
|
-
|
|
1411
|
+
logger.error('Error searching knowledge base:', error);
|
|
1383
1412
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search knowledge base: ${error}`);
|
|
1384
1413
|
}
|
|
1385
1414
|
}
|
|
1386
1415
|
async handlePredictiveAnalysis(args) {
|
|
1387
1416
|
const { prediction_type, timeframe = 'week' } = args;
|
|
1388
|
-
|
|
1417
|
+
logger.info(`Performing predictive _analysis: ${prediction_type} for ${timeframe}`);
|
|
1389
1418
|
try {
|
|
1390
1419
|
const predictions = await this.performPredictiveAnalysis(prediction_type, timeframe);
|
|
1391
1420
|
return {
|
|
@@ -1398,7 +1427,7 @@ class ServiceNowOperationsMCP {
|
|
|
1398
1427
|
};
|
|
1399
1428
|
}
|
|
1400
1429
|
catch (error) {
|
|
1401
|
-
|
|
1430
|
+
logger.error('Error performing predictive _analysis:', error);
|
|
1402
1431
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to perform predictive _analysis: ${error}`);
|
|
1403
1432
|
}
|
|
1404
1433
|
}
|
|
@@ -1408,7 +1437,7 @@ class ServiceNowOperationsMCP {
|
|
|
1408
1437
|
const lowerQuery = query.toLowerCase();
|
|
1409
1438
|
// If already a ServiceNow encoded query, return as-is
|
|
1410
1439
|
if (query.includes('=') || query.includes('!=') || query.includes('^') || query.includes('LIKE')) {
|
|
1411
|
-
|
|
1440
|
+
logger.info(`Using raw ServiceNow query: ${query}`);
|
|
1412
1441
|
return query;
|
|
1413
1442
|
}
|
|
1414
1443
|
// Common ServiceNow query patterns
|
|
@@ -1581,7 +1610,7 @@ class ServiceNowOperationsMCP {
|
|
|
1581
1610
|
for (const action of actions) {
|
|
1582
1611
|
try {
|
|
1583
1612
|
// This would integrate with actual automation systems
|
|
1584
|
-
|
|
1613
|
+
logger.info(`Executing action: ${action} for incident ${incident_id}`);
|
|
1585
1614
|
executed.push(`✅ ${action}`);
|
|
1586
1615
|
}
|
|
1587
1616
|
catch (error) {
|
|
@@ -1622,7 +1651,7 @@ class ServiceNowOperationsMCP {
|
|
|
1622
1651
|
});
|
|
1623
1652
|
}
|
|
1624
1653
|
catch (error) {
|
|
1625
|
-
|
|
1654
|
+
logger.error(`Error getting relationships for CI ${ci.sys_id}:`, error);
|
|
1626
1655
|
}
|
|
1627
1656
|
}
|
|
1628
1657
|
return relationships;
|
|
@@ -1671,7 +1700,7 @@ class ServiceNowOperationsMCP {
|
|
|
1671
1700
|
.map(([category, count]) => `${category}: ${count}`);
|
|
1672
1701
|
}
|
|
1673
1702
|
catch (error) {
|
|
1674
|
-
|
|
1703
|
+
logger.error('Error calculating operational metrics:', error);
|
|
1675
1704
|
}
|
|
1676
1705
|
return metrics;
|
|
1677
1706
|
}
|
|
@@ -2080,7 +2109,7 @@ class ServiceNowOperationsMCP {
|
|
|
2080
2109
|
}
|
|
2081
2110
|
async handleCatalogItemManager(args) {
|
|
2082
2111
|
const { action, item_id, ...params } = args;
|
|
2083
|
-
|
|
2112
|
+
logger.info(`Catalog item manager action: ${action}`, { item_id, params });
|
|
2084
2113
|
try {
|
|
2085
2114
|
switch (action) {
|
|
2086
2115
|
case 'create': {
|
|
@@ -2092,13 +2121,13 @@ class ServiceNowOperationsMCP {
|
|
|
2092
2121
|
const defaultCatalogResult = await this.client.searchRecords('sc_catalog', 'active=true^ORDERBYsys_created_on', 1);
|
|
2093
2122
|
if (defaultCatalogResult.success && defaultCatalogResult.data?.result?.length > 0) {
|
|
2094
2123
|
catalogId = defaultCatalogResult.data.result[0].sys_id;
|
|
2095
|
-
|
|
2124
|
+
logger.info('Using default catalog:', {
|
|
2096
2125
|
catalogId,
|
|
2097
2126
|
catalogName: defaultCatalogResult.data.result[0].title
|
|
2098
2127
|
});
|
|
2099
2128
|
}
|
|
2100
2129
|
else {
|
|
2101
|
-
|
|
2130
|
+
logger.warn('No catalogs found, catalog item may not be visible');
|
|
2102
2131
|
}
|
|
2103
2132
|
}
|
|
2104
2133
|
if (!categoryId) {
|
|
@@ -2106,7 +2135,7 @@ class ServiceNowOperationsMCP {
|
|
|
2106
2135
|
const defaultCategoryResult = await this.client.searchRecords('sc_category', 'active=true^titleLIKEHardware^ORtitleLIKEGeneral^ORtitleLIKEIT^ORDERBYsys_created_on', 1);
|
|
2107
2136
|
if (defaultCategoryResult.success && defaultCategoryResult.data?.result?.length > 0) {
|
|
2108
2137
|
categoryId = defaultCategoryResult.data.result[0].sys_id;
|
|
2109
|
-
|
|
2138
|
+
logger.info('Using default category:', {
|
|
2110
2139
|
categoryId,
|
|
2111
2140
|
categoryName: defaultCategoryResult.data.result[0].title
|
|
2112
2141
|
});
|
|
@@ -2294,7 +2323,7 @@ class ServiceNowOperationsMCP {
|
|
|
2294
2323
|
}
|
|
2295
2324
|
}
|
|
2296
2325
|
catch (error) {
|
|
2297
|
-
|
|
2326
|
+
logger.error('Error in catalog item manager:', error);
|
|
2298
2327
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Catalog item manager error: ${error}`);
|
|
2299
2328
|
}
|
|
2300
2329
|
}
|
|
@@ -2329,7 +2358,7 @@ class ServiceNowOperationsMCP {
|
|
|
2329
2358
|
}
|
|
2330
2359
|
async handleCatalogItemSearch(args) {
|
|
2331
2360
|
const { query, include_inactive = false, fuzzy_match = true, category_filter, limit = 50, include_variables = false } = args;
|
|
2332
|
-
|
|
2361
|
+
logger.info(`Searching catalog items for: ${query}`, { fuzzy_match, category_filter, limit });
|
|
2333
2362
|
try {
|
|
2334
2363
|
// Build search queries
|
|
2335
2364
|
const searchQueries = [];
|
|
@@ -2468,7 +2497,7 @@ class ServiceNowOperationsMCP {
|
|
|
2468
2497
|
};
|
|
2469
2498
|
}
|
|
2470
2499
|
catch (error) {
|
|
2471
|
-
|
|
2500
|
+
logger.error('Error searching catalog items:', error);
|
|
2472
2501
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Catalog search error: ${error}`);
|
|
2473
2502
|
}
|
|
2474
2503
|
}
|
|
@@ -2599,7 +2628,7 @@ class ServiceNowOperationsMCP {
|
|
|
2599
2628
|
}
|
|
2600
2629
|
async handleCleanupTestArtifacts(args) {
|
|
2601
2630
|
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;
|
|
2602
|
-
|
|
2631
|
+
logger.info('Starting test artifact cleanup', {
|
|
2603
2632
|
artifact_types,
|
|
2604
2633
|
test_patterns,
|
|
2605
2634
|
max_age_hours,
|
|
@@ -2708,7 +2737,7 @@ class ServiceNowOperationsMCP {
|
|
|
2708
2737
|
};
|
|
2709
2738
|
}
|
|
2710
2739
|
catch (error) {
|
|
2711
|
-
|
|
2740
|
+
logger.error('Error during test artifact cleanup:', error);
|
|
2712
2741
|
return {
|
|
2713
2742
|
content: [{
|
|
2714
2743
|
type: 'text',
|
|
@@ -2839,7 +2868,7 @@ class ServiceNowOperationsMCP {
|
|
|
2839
2868
|
}
|
|
2840
2869
|
async handleCreateUserGroup(args) {
|
|
2841
2870
|
const { name, description, email, manager, parent, type, active = true } = args;
|
|
2842
|
-
|
|
2871
|
+
logger.info(`Creating user group: ${name}`);
|
|
2843
2872
|
try {
|
|
2844
2873
|
// Check if group already exists
|
|
2845
2874
|
const existingGroup = await this.client.get('/api/now/table/sys_user_group', {
|
|
@@ -2872,7 +2901,7 @@ class ServiceNowOperationsMCP {
|
|
|
2872
2901
|
groupData.manager = managerUser.sys_id;
|
|
2873
2902
|
}
|
|
2874
2903
|
else {
|
|
2875
|
-
|
|
2904
|
+
logger.warn(`Manager "${manager}" not found, creating group without manager`);
|
|
2876
2905
|
}
|
|
2877
2906
|
}
|
|
2878
2907
|
// Handle parent group lookup
|
|
@@ -2882,7 +2911,7 @@ class ServiceNowOperationsMCP {
|
|
|
2882
2911
|
groupData.parent = parentGroup.sys_id;
|
|
2883
2912
|
}
|
|
2884
2913
|
else {
|
|
2885
|
-
|
|
2914
|
+
logger.warn(`Parent group "${parent}" not found, creating group without parent`);
|
|
2886
2915
|
}
|
|
2887
2916
|
}
|
|
2888
2917
|
// Create the group
|
|
@@ -2911,7 +2940,7 @@ class ServiceNowOperationsMCP {
|
|
|
2911
2940
|
}
|
|
2912
2941
|
async handleCreateUser(args) {
|
|
2913
2942
|
const { user_name, first_name, last_name, email, department, manager, phone, title, location, active = true, password } = args;
|
|
2914
|
-
|
|
2943
|
+
logger.info(`Creating user: ${user_name}`);
|
|
2915
2944
|
try {
|
|
2916
2945
|
// Check if user already exists
|
|
2917
2946
|
const existingUser = await this.client.get('/api/now/table/sys_user', {
|
|
@@ -2947,7 +2976,7 @@ class ServiceNowOperationsMCP {
|
|
|
2947
2976
|
userData.manager = managerUser.sys_id;
|
|
2948
2977
|
}
|
|
2949
2978
|
else {
|
|
2950
|
-
|
|
2979
|
+
logger.warn(`Manager "${manager}" not found, creating user without manager`);
|
|
2951
2980
|
}
|
|
2952
2981
|
}
|
|
2953
2982
|
// Handle department lookup
|
|
@@ -2957,7 +2986,7 @@ class ServiceNowOperationsMCP {
|
|
|
2957
2986
|
userData.department = dept.sys_id;
|
|
2958
2987
|
}
|
|
2959
2988
|
else {
|
|
2960
|
-
|
|
2989
|
+
logger.warn(`Department "${department}" not found, creating user without department`);
|
|
2961
2990
|
}
|
|
2962
2991
|
}
|
|
2963
2992
|
// Handle location lookup
|
|
@@ -2967,7 +2996,7 @@ class ServiceNowOperationsMCP {
|
|
|
2967
2996
|
userData.location = loc.sys_id;
|
|
2968
2997
|
}
|
|
2969
2998
|
else {
|
|
2970
|
-
|
|
2999
|
+
logger.warn(`Location "${location}" not found, creating user without location`);
|
|
2971
3000
|
}
|
|
2972
3001
|
}
|
|
2973
3002
|
// Create the user
|
|
@@ -2996,7 +3025,7 @@ class ServiceNowOperationsMCP {
|
|
|
2996
3025
|
}
|
|
2997
3026
|
async handleAssignUserToGroup(args) {
|
|
2998
3027
|
const { user, group } = args;
|
|
2999
|
-
|
|
3028
|
+
logger.info(`Assigning user ${user} to group ${group}`);
|
|
3000
3029
|
try {
|
|
3001
3030
|
// Find the user
|
|
3002
3031
|
const userRecord = await this.findUserBySysIdOrUsername(user);
|
|
@@ -3059,7 +3088,7 @@ class ServiceNowOperationsMCP {
|
|
|
3059
3088
|
}
|
|
3060
3089
|
async handleRemoveUserFromGroup(args) {
|
|
3061
3090
|
const { user, group } = args;
|
|
3062
|
-
|
|
3091
|
+
logger.info(`Removing user ${user} from group ${group}`);
|
|
3063
3092
|
try {
|
|
3064
3093
|
// Find the user
|
|
3065
3094
|
const userRecord = await this.findUserBySysIdOrUsername(user);
|
|
@@ -3114,7 +3143,7 @@ class ServiceNowOperationsMCP {
|
|
|
3114
3143
|
}
|
|
3115
3144
|
async handleListGroupMembers(args) {
|
|
3116
3145
|
const { group, active_only = true } = args;
|
|
3117
|
-
|
|
3146
|
+
logger.info(`Listing members of group: ${group}`);
|
|
3118
3147
|
try {
|
|
3119
3148
|
// Find the group
|
|
3120
3149
|
const groupRecord = await this.findGroupBySysIdOrName(group);
|
|
@@ -3151,7 +3180,7 @@ class ServiceNowOperationsMCP {
|
|
|
3151
3180
|
const u = member.user;
|
|
3152
3181
|
// Defensive programming: check if user object exists and has required fields
|
|
3153
3182
|
if (!u || !u.user_name) {
|
|
3154
|
-
|
|
3183
|
+
logger.warn(`Group member ${index + 1} has invalid user data:`, member);
|
|
3155
3184
|
response += `${index + 1}. **Invalid User Data** - ${member.sys_id || 'Unknown ID'}\n\n`;
|
|
3156
3185
|
return;
|
|
3157
3186
|
}
|
|
@@ -3247,7 +3276,7 @@ class ServiceNowOperationsMCP {
|
|
|
3247
3276
|
async run() {
|
|
3248
3277
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
3249
3278
|
await this.server.connect(transport);
|
|
3250
|
-
|
|
3279
|
+
logger.info('ServiceNow Operations MCP Server started');
|
|
3251
3280
|
}
|
|
3252
3281
|
}
|
|
3253
3282
|
exports.ServiceNowOperationsMCP = ServiceNowOperationsMCP;
|
|
@@ -3255,7 +3284,7 @@ exports.ServiceNowOperationsMCP = ServiceNowOperationsMCP;
|
|
|
3255
3284
|
if (require.main === module) {
|
|
3256
3285
|
const server = new ServiceNowOperationsMCP();
|
|
3257
3286
|
server.run().catch((error) => {
|
|
3258
|
-
|
|
3287
|
+
logger.error('Failed to start ServiceNow Operations MCP server:', error);
|
|
3259
3288
|
process.exit(1);
|
|
3260
3289
|
});
|
|
3261
3290
|
}
|