snow-flow 2.7.4 ā 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -128,7 +128,56 @@ class ServiceNowOperationsMCP {
|
|
|
128
128
|
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
129
129
|
return {
|
|
130
130
|
tools: [
|
|
131
|
-
//
|
|
131
|
+
// šÆ UNIVERSAL TABLE QUERY - Works for ANY ServiceNow table!
|
|
132
|
+
{
|
|
133
|
+
name: 'snow_query_table',
|
|
134
|
+
description: 'š Universal high-performance query tool for ANY ServiceNow table - optimized for memory efficiency',
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
properties: {
|
|
138
|
+
table: {
|
|
139
|
+
type: 'string',
|
|
140
|
+
description: 'ServiceNow table name (e.g., incident, sc_request, problem, task, change_request, u_custom_table)',
|
|
141
|
+
examples: ['incident', 'sc_request', 'sc_req_item', 'problem', 'change_request', 'task', 'cmdb_ci']
|
|
142
|
+
},
|
|
143
|
+
query: {
|
|
144
|
+
type: 'string',
|
|
145
|
+
description: 'ServiceNow encoded query or natural language description'
|
|
146
|
+
},
|
|
147
|
+
limit: {
|
|
148
|
+
type: 'number',
|
|
149
|
+
description: 'Maximum number of results (default: 10)',
|
|
150
|
+
default: 10
|
|
151
|
+
},
|
|
152
|
+
include_content: {
|
|
153
|
+
type: 'boolean',
|
|
154
|
+
description: 'šÆ Include full record data (default: false for performance, only returns count)',
|
|
155
|
+
default: false
|
|
156
|
+
},
|
|
157
|
+
fields: {
|
|
158
|
+
type: 'array',
|
|
159
|
+
items: { type: 'string' },
|
|
160
|
+
description: 'Specific fields to return (automatically sets include_content=true). Examples: ["number", "short_description", "state"]'
|
|
161
|
+
},
|
|
162
|
+
include_display_values: {
|
|
163
|
+
type: 'boolean',
|
|
164
|
+
description: 'Include display values for reference fields (e.g., show user names instead of sys_ids)',
|
|
165
|
+
default: false
|
|
166
|
+
},
|
|
167
|
+
group_by: {
|
|
168
|
+
type: 'string',
|
|
169
|
+
description: 'Field to group results by (returns counts per group)'
|
|
170
|
+
},
|
|
171
|
+
order_by: {
|
|
172
|
+
type: 'string',
|
|
173
|
+
description: 'Field to sort by (prefix with - for descending)',
|
|
174
|
+
examples: ['created_on', '-priority', 'number']
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
required: ['table', 'query']
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
// Core Operational Queries (keeping for backwards compatibility)
|
|
132
181
|
{
|
|
133
182
|
name: 'snow_query_incidents',
|
|
134
183
|
description: 'Advanced incident querying with filters and _analysis - optimized for performance',
|
|
@@ -728,6 +777,8 @@ class ServiceNowOperationsMCP {
|
|
|
728
777
|
const { name, arguments: args } = request.params;
|
|
729
778
|
try {
|
|
730
779
|
switch (name) {
|
|
780
|
+
case 'snow_query_table':
|
|
781
|
+
return await this.handleUniversalQuery(args);
|
|
731
782
|
case 'snow_query_incidents':
|
|
732
783
|
return await this.handleQueryIncidents(args);
|
|
733
784
|
case 'snow_analyze_incident':
|
|
@@ -776,6 +827,117 @@ class ServiceNowOperationsMCP {
|
|
|
776
827
|
}
|
|
777
828
|
});
|
|
778
829
|
}
|
|
830
|
+
async handleUniversalQuery(args) {
|
|
831
|
+
const { table, query, limit = 10, include_content = false, fields, include_display_values = false, group_by, order_by } = args;
|
|
832
|
+
logger_js_1.logger.info(`Universal query on table '${table}' with: ${query} (include_content: ${include_content})`);
|
|
833
|
+
try {
|
|
834
|
+
// Convert natural language to ServiceNow query if needed
|
|
835
|
+
const processedQuery = this.processNaturalLanguageQuery(query, table);
|
|
836
|
+
// Build the query with order_by if specified
|
|
837
|
+
let finalQuery = processedQuery;
|
|
838
|
+
if (order_by) {
|
|
839
|
+
const orderDirection = order_by.startsWith('-') ? 'DESC' : '';
|
|
840
|
+
const orderField = order_by.replace(/^-/, '');
|
|
841
|
+
finalQuery += `^ORDERBY${orderDirection}${orderField}`;
|
|
842
|
+
}
|
|
843
|
+
// Query the table
|
|
844
|
+
const records = await this.client.searchRecords(table, finalQuery, limit);
|
|
845
|
+
let result = {
|
|
846
|
+
table: table,
|
|
847
|
+
total_results: records.success ? records.data.result.length : 0,
|
|
848
|
+
query_used: processedQuery
|
|
849
|
+
};
|
|
850
|
+
// Handle group_by aggregation
|
|
851
|
+
if (group_by && records.success) {
|
|
852
|
+
const grouped = {};
|
|
853
|
+
records.data.result.forEach((record) => {
|
|
854
|
+
const groupValue = record[group_by] || 'undefined';
|
|
855
|
+
grouped[groupValue] = (grouped[groupValue] || 0) + 1;
|
|
856
|
+
});
|
|
857
|
+
result.grouped_counts = grouped;
|
|
858
|
+
result.unique_values = Object.keys(grouped).length;
|
|
859
|
+
}
|
|
860
|
+
// šÆ SMART CONTENT DECISION: Only include full data if explicitly requested
|
|
861
|
+
if (include_content || (fields && fields.length > 0)) {
|
|
862
|
+
// Include full record data when specifically requested
|
|
863
|
+
result.records = records.success ? records.data.result : [];
|
|
864
|
+
// If specific fields requested, filter them
|
|
865
|
+
if (fields && fields.length > 0 && records.success) {
|
|
866
|
+
result.records = records.data.result.map((record) => {
|
|
867
|
+
const filtered = {};
|
|
868
|
+
// Always include sys_id and number/name if available
|
|
869
|
+
if (record.sys_id)
|
|
870
|
+
filtered.sys_id = record.sys_id;
|
|
871
|
+
if (record.number)
|
|
872
|
+
filtered.number = record.number;
|
|
873
|
+
if (record.name && !fields.includes('name'))
|
|
874
|
+
filtered.name = record.name;
|
|
875
|
+
// Add requested fields
|
|
876
|
+
fields.forEach((field) => {
|
|
877
|
+
if (record[field] !== undefined) {
|
|
878
|
+
filtered[field] = record[field];
|
|
879
|
+
// Add display value if requested and available
|
|
880
|
+
if (include_display_values && record[`${field}_display_value`]) {
|
|
881
|
+
filtered[`${field}_display`] = record[`${field}_display_value`];
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
return filtered;
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
// š PERFORMANCE MODE: Only return summary for large datasets
|
|
891
|
+
result.summary = {
|
|
892
|
+
count: records.success ? records.data.result.length : 0,
|
|
893
|
+
message: `Use include_content=true to retrieve full ${table} data`
|
|
894
|
+
};
|
|
895
|
+
// Provide intelligent sample based on table type
|
|
896
|
+
if (records.success && records.data.result.length > 0) {
|
|
897
|
+
const sampleSize = Math.min(5, records.data.result.length);
|
|
898
|
+
const sample = records.data.result.slice(0, sampleSize);
|
|
899
|
+
// Dynamic field detection for sample
|
|
900
|
+
const commonFields = this.detectCommonFields(table, sample);
|
|
901
|
+
result.summary.sample = {
|
|
902
|
+
record_identifiers: sample.map((r) => r.number || r.name || r.sys_id),
|
|
903
|
+
common_fields: commonFields
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return {
|
|
908
|
+
content: [
|
|
909
|
+
{
|
|
910
|
+
type: 'text',
|
|
911
|
+
text: `Found ${records.success ? records.data.result.length : 0} ${table} records matching query: "${query}"\n\n${JSON.stringify(result, null, 2)}`
|
|
912
|
+
}
|
|
913
|
+
]
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
catch (error) {
|
|
917
|
+
logger_js_1.logger.error(`Error querying ${table}:`, error);
|
|
918
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to query ${table}: ${error}`);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
detectCommonFields(table, records) {
|
|
922
|
+
const commonFields = {};
|
|
923
|
+
// Fields to check based on table type
|
|
924
|
+
const fieldsToCheck = ['state', 'priority', 'category', 'type', 'status', 'active', 'stage'];
|
|
925
|
+
records.forEach(record => {
|
|
926
|
+
fieldsToCheck.forEach(field => {
|
|
927
|
+
if (record[field] !== undefined && record[field] !== null) {
|
|
928
|
+
if (!commonFields[field])
|
|
929
|
+
commonFields[field] = new Set();
|
|
930
|
+
commonFields[field].add(record[field]);
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
});
|
|
934
|
+
// Convert sets to arrays for JSON serialization
|
|
935
|
+
const result = {};
|
|
936
|
+
Object.entries(commonFields).forEach(([field, values]) => {
|
|
937
|
+
result[field] = Array.from(values);
|
|
938
|
+
});
|
|
939
|
+
return result;
|
|
940
|
+
}
|
|
779
941
|
async handleQueryIncidents(args) {
|
|
780
942
|
const { query, limit = 10, include__analysis = false, fields, include_content = false // šÆ NEW: Explicit control over returning full incident data
|
|
781
943
|
} = args;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
// Test the universal query tool with different tables and options
|
|
6
|
+
async function testUniversalQuery() {
|
|
7
|
+
const logger = new logger_js_1.Logger('UniversalQueryTest');
|
|
8
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
9
|
+
console.log('šÆ Testing Universal Query Tool for ANY ServiceNow Table\n');
|
|
10
|
+
console.log('ā'.repeat(60));
|
|
11
|
+
try {
|
|
12
|
+
// Test 1: Count-only for incidents (super fast)
|
|
13
|
+
console.log('\n1ļøā£ INCIDENTS - Count only (default):');
|
|
14
|
+
console.log(' Query: state!=7, limit: 100');
|
|
15
|
+
const incidentCount = await client.searchRecords('incident', 'state!=7', 100);
|
|
16
|
+
console.log(` ā
Found: ${incidentCount.data.result.length} incidents`);
|
|
17
|
+
console.log(` š¾ Memory: ~${JSON.stringify({ count: incidentCount.data.result.length }).length} bytes\n`);
|
|
18
|
+
// Test 2: Specific fields from requests
|
|
19
|
+
console.log('2ļøā£ SERVICE REQUESTS - Specific fields:');
|
|
20
|
+
console.log(' Table: sc_request, Fields: [number, short_description, state]');
|
|
21
|
+
const requests = await client.searchRecords('sc_request', 'active=true', 5);
|
|
22
|
+
const requestsFiltered = requests.data.result.map((r) => ({
|
|
23
|
+
number: r.number,
|
|
24
|
+
short_description: r.short_description,
|
|
25
|
+
state: r.state
|
|
26
|
+
}));
|
|
27
|
+
console.log(` ā
Retrieved: ${requestsFiltered.length} requests`);
|
|
28
|
+
console.log(` š Sample:`, requestsFiltered[0] || 'No requests found');
|
|
29
|
+
console.log(` š¾ Memory: ~${JSON.stringify(requestsFiltered).length} bytes\n`);
|
|
30
|
+
// Test 3: Group by state for problems
|
|
31
|
+
console.log('3ļøā£ PROBLEMS - Group by state:');
|
|
32
|
+
const problems = await client.searchRecords('problem', '', 20);
|
|
33
|
+
const grouped = {};
|
|
34
|
+
problems.data.result.forEach((p) => {
|
|
35
|
+
const state = p.state || 'unknown';
|
|
36
|
+
grouped[state] = (grouped[state] || 0) + 1;
|
|
37
|
+
});
|
|
38
|
+
console.log(` ā
Grouped ${problems.data.result.length} problems by state:`);
|
|
39
|
+
Object.entries(grouped).forEach(([state, count]) => {
|
|
40
|
+
console.log(` State ${state}: ${count} problems`);
|
|
41
|
+
});
|
|
42
|
+
console.log(` š¾ Memory: ~${JSON.stringify(grouped).length} bytes\n`);
|
|
43
|
+
// Test 4: Custom table query
|
|
44
|
+
console.log('4ļøā£ CUSTOM TABLE - Universal support:');
|
|
45
|
+
console.log(' Any u_* table works automatically!');
|
|
46
|
+
try {
|
|
47
|
+
const customTable = await client.searchRecords('u_custom_data', '', 1);
|
|
48
|
+
console.log(` ā
Custom table query successful`);
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.log(` ā¹ļø No custom tables found (expected in demo environment)`);
|
|
52
|
+
}
|
|
53
|
+
console.log();
|
|
54
|
+
// Test 5: CMDB with relationships
|
|
55
|
+
console.log('5ļøā£ CMDB - Configuration items:');
|
|
56
|
+
const cmdbItems = await client.searchRecords('cmdb_ci', 'operational_status=1', 5);
|
|
57
|
+
console.log(` ā
Found: ${cmdbItems.data.result.length} operational CIs`);
|
|
58
|
+
if (cmdbItems.data.result.length > 0) {
|
|
59
|
+
const ciTypes = [...new Set(cmdbItems.data.result.map((ci) => ci.sys_class_name))];
|
|
60
|
+
console.log(` š¦ CI Types:`, ciTypes);
|
|
61
|
+
}
|
|
62
|
+
console.log();
|
|
63
|
+
// Show universal query examples
|
|
64
|
+
console.log('ā'.repeat(60));
|
|
65
|
+
console.log('\nš UNIVERSAL QUERY EXAMPLES:\n');
|
|
66
|
+
console.log('// Count only (minimal memory):');
|
|
67
|
+
console.log(`snow_query_table({
|
|
68
|
+
table: "incident",
|
|
69
|
+
query: "state!=7",
|
|
70
|
+
limit: 1000
|
|
71
|
+
})\n`);
|
|
72
|
+
console.log('// Specific fields (optimized):');
|
|
73
|
+
console.log(`snow_query_table({
|
|
74
|
+
table: "sc_request",
|
|
75
|
+
query: "active=true",
|
|
76
|
+
fields: ["number", "short_description", "requested_for"],
|
|
77
|
+
include_display_values: true
|
|
78
|
+
})\n`);
|
|
79
|
+
console.log('// Group by analysis:');
|
|
80
|
+
console.log(`snow_query_table({
|
|
81
|
+
table: "problem",
|
|
82
|
+
query: "active=true",
|
|
83
|
+
group_by: "category",
|
|
84
|
+
order_by: "-priority"
|
|
85
|
+
})\n`);
|
|
86
|
+
console.log('// Full content when needed:');
|
|
87
|
+
console.log(`snow_query_table({
|
|
88
|
+
table: "change_request",
|
|
89
|
+
query: "type=emergency",
|
|
90
|
+
include_content: true,
|
|
91
|
+
limit: 5
|
|
92
|
+
})\n`);
|
|
93
|
+
console.log('ā'.repeat(60));
|
|
94
|
+
console.log('\n⨠The LLM can now intelligently choose:');
|
|
95
|
+
console.log(' ⢠Count-only for ML training (saves 99.9% memory)');
|
|
96
|
+
console.log(' ⢠Specific fields for targeted analysis');
|
|
97
|
+
console.log(' ⢠Full content only when necessary');
|
|
98
|
+
console.log(' ⢠Works with ANY ServiceNow table automatically!');
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
logger.error('Test failed:', error);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Run the test
|
|
105
|
+
testUniversalQuery().catch(console.error);
|
|
106
|
+
//# sourceMappingURL=test-universal-query.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Snow-Flow: ServiceNow Advanced Intelligence Platform - 100+ real MCP tools with AI-powered swarm orchestration and neural networks. Dynamic task categorization using AI. Machine learning for incident classification, change risk prediction, and anomaly detection. Zero Mock Data, 100% Real API Integration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|