snow-flow 4.1.2 → 4.2.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.
- package/dist/mcp/servicenow-itam-mcp.d.ts +29 -0
- package/dist/mcp/servicenow-itam-mcp.js +537 -0
- package/dist/mcp/servicenow-local-development-mcp.d.ts +0 -2
- package/dist/mcp/servicenow-local-development-mcp.js +0 -43
- package/dist/mcp/servicenow-notifications-mcp.d.ts +24 -0
- package/dist/mcp/servicenow-notifications-mcp.js +327 -0
- package/dist/mcp/servicenow-secops-mcp.d.ts +28 -0
- package/dist/mcp/servicenow-secops-mcp.js +560 -0
- package/dist/utils/memory-pool.d.ts +56 -0
- package/dist/utils/memory-pool.js +103 -0
- package/package.json +1 -1
- package/src/cli.ts.backup-20250809-125529 +0 -4773
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ServiceNow IT Asset Management (ITAM) MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Provides comprehensive IT Asset Management capabilities including:
|
|
6
|
+
* - Asset lifecycle management (procurement → deployment → retirement)
|
|
7
|
+
* - License management and compliance
|
|
8
|
+
* - Asset normalization and duplicate detection
|
|
9
|
+
* - Hardware inventory and tracking
|
|
10
|
+
* - Asset financial management
|
|
11
|
+
*
|
|
12
|
+
* High-value enterprise ServiceNow module previously missing from Snow-Flow
|
|
13
|
+
*/
|
|
14
|
+
import { EnhancedBaseMCPServer } from './shared/enhanced-base-mcp-server.js';
|
|
15
|
+
export declare class ServiceNowITAMMCP extends EnhancedBaseMCPServer {
|
|
16
|
+
constructor();
|
|
17
|
+
private setupHandlers;
|
|
18
|
+
private createAsset;
|
|
19
|
+
private manageSoftwareLicense;
|
|
20
|
+
private trackAssetLifecycle;
|
|
21
|
+
private generateComplianceReport;
|
|
22
|
+
private generateLicenseUsageReport;
|
|
23
|
+
private generateAssetInventoryReport;
|
|
24
|
+
private generateWarrantyReport;
|
|
25
|
+
private generateCostAnalysisReport;
|
|
26
|
+
private optimizeLicenses;
|
|
27
|
+
private discoverAssets;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=servicenow-itam-mcp.d.ts.map
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* ServiceNow IT Asset Management (ITAM) MCP Server
|
|
5
|
+
*
|
|
6
|
+
* Provides comprehensive IT Asset Management capabilities including:
|
|
7
|
+
* - Asset lifecycle management (procurement → deployment → retirement)
|
|
8
|
+
* - License management and compliance
|
|
9
|
+
* - Asset normalization and duplicate detection
|
|
10
|
+
* - Hardware inventory and tracking
|
|
11
|
+
* - Asset financial management
|
|
12
|
+
*
|
|
13
|
+
* High-value enterprise ServiceNow module previously missing from Snow-Flow
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.ServiceNowITAMMCP = void 0;
|
|
17
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
18
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
19
|
+
const enhanced_base_mcp_server_js_1 = require("./shared/enhanced-base-mcp-server.js");
|
|
20
|
+
class ServiceNowITAMMCP extends enhanced_base_mcp_server_js_1.EnhancedBaseMCPServer {
|
|
21
|
+
constructor() {
|
|
22
|
+
super('servicenow-itam', '1.0.0');
|
|
23
|
+
this.setupHandlers();
|
|
24
|
+
}
|
|
25
|
+
setupHandlers() {
|
|
26
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
27
|
+
tools: [
|
|
28
|
+
{
|
|
29
|
+
name: 'snow_create_asset',
|
|
30
|
+
description: 'Create IT asset with full lifecycle tracking and financial management',
|
|
31
|
+
inputSchema: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
asset_tag: { type: 'string', description: 'Unique asset tag/barcode' },
|
|
35
|
+
display_name: { type: 'string', description: 'Asset display name' },
|
|
36
|
+
model_id: { type: 'string', description: 'Hardware model sys_id' },
|
|
37
|
+
state: { type: 'string', description: 'Asset state (in_stock, deployed, retired)', enum: ['in_stock', 'deployed', 'retired', 'disposed'] },
|
|
38
|
+
assigned_to: { type: 'string', description: 'User sys_id asset is assigned to' },
|
|
39
|
+
location: { type: 'string', description: 'Location sys_id' },
|
|
40
|
+
cost: { type: 'number', description: 'Asset cost in local currency' },
|
|
41
|
+
purchase_date: { type: 'string', description: 'Purchase date (YYYY-MM-DD)' },
|
|
42
|
+
warranty_expiration: { type: 'string', description: 'Warranty expiration date (YYYY-MM-DD)' }
|
|
43
|
+
},
|
|
44
|
+
required: ['asset_tag', 'display_name', 'model_id']
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'snow_manage_software_license',
|
|
49
|
+
description: 'Manage software licenses with compliance tracking and optimization',
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
license_name: { type: 'string', description: 'Software license name' },
|
|
54
|
+
publisher: { type: 'string', description: 'Software publisher/vendor' },
|
|
55
|
+
licensed_installs: { type: 'number', description: 'Number of licensed installations' },
|
|
56
|
+
license_type: { type: 'string', description: 'Type of license', enum: ['named_user', 'concurrent_user', 'server', 'enterprise'] },
|
|
57
|
+
cost_per_license: { type: 'number', description: 'Cost per license' },
|
|
58
|
+
expiration_date: { type: 'string', description: 'License expiration (YYYY-MM-DD)' },
|
|
59
|
+
auto_renew: { type: 'boolean', description: 'Automatic renewal enabled' }
|
|
60
|
+
},
|
|
61
|
+
required: ['license_name', 'publisher', 'licensed_installs']
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'snow_track_asset_lifecycle',
|
|
66
|
+
description: 'Track complete asset lifecycle from procurement to disposal',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
asset_sys_id: { type: 'string', description: 'Asset sys_id to track' },
|
|
71
|
+
action: { type: 'string', description: 'Lifecycle action', enum: ['procure', 'receive', 'deploy', 'transfer', 'retire', 'dispose'] },
|
|
72
|
+
reason: { type: 'string', description: 'Reason for lifecycle change' },
|
|
73
|
+
user_sys_id: { type: 'string', description: 'User performing the action' },
|
|
74
|
+
notes: { type: 'string', description: 'Additional notes' }
|
|
75
|
+
},
|
|
76
|
+
required: ['asset_sys_id', 'action']
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'snow_asset_compliance_report',
|
|
81
|
+
description: 'Generate comprehensive asset compliance reports for auditing',
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
report_type: { type: 'string', description: 'Type of compliance report', enum: ['license_usage', 'asset_inventory', 'warranty_expiration', 'cost_analysis'] },
|
|
86
|
+
date_range: { type: 'string', description: 'Report date range', enum: ['30_days', '90_days', '1_year', 'all_time'] },
|
|
87
|
+
include_details: { type: 'boolean', description: 'Include detailed breakdown' },
|
|
88
|
+
export_format: { type: 'string', description: 'Export format', enum: ['json', 'csv', 'pdf'] }
|
|
89
|
+
},
|
|
90
|
+
required: ['report_type']
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'snow_optimize_licenses',
|
|
95
|
+
description: 'Analyze license usage and provide optimization recommendations',
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
software_name: { type: 'string', description: 'Specific software to analyze (optional)' },
|
|
100
|
+
optimization_type: { type: 'string', description: 'Type of optimization', enum: ['cost_reduction', 'compliance', 'usage_efficiency'] },
|
|
101
|
+
threshold_percentage: { type: 'number', description: 'Usage threshold for optimization (default 80)' }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'snow_asset_discovery',
|
|
107
|
+
description: 'Discover and normalize assets from multiple sources',
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
discovery_source: { type: 'string', description: 'Discovery source', enum: ['network_scan', 'agent_based', 'manual_import', 'csv_upload'] },
|
|
112
|
+
ip_range: { type: 'string', description: 'IP range for network discovery (CIDR notation)' },
|
|
113
|
+
normalize_duplicates: { type: 'boolean', description: 'Automatically normalize duplicate assets' },
|
|
114
|
+
create_relationships: { type: 'boolean', description: 'Create CI relationships automatically' }
|
|
115
|
+
},
|
|
116
|
+
required: ['discovery_source']
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}));
|
|
121
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
122
|
+
const { name, arguments: args } = request.params;
|
|
123
|
+
try {
|
|
124
|
+
let result;
|
|
125
|
+
switch (name) {
|
|
126
|
+
case 'snow_create_asset':
|
|
127
|
+
result = await this.createAsset(args);
|
|
128
|
+
break;
|
|
129
|
+
case 'snow_manage_software_license':
|
|
130
|
+
result = await this.manageSoftwareLicense(args);
|
|
131
|
+
break;
|
|
132
|
+
case 'snow_track_asset_lifecycle':
|
|
133
|
+
result = await this.trackAssetLifecycle(args);
|
|
134
|
+
break;
|
|
135
|
+
case 'snow_asset_compliance_report':
|
|
136
|
+
result = await this.generateComplianceReport(args);
|
|
137
|
+
break;
|
|
138
|
+
case 'snow_optimize_licenses':
|
|
139
|
+
result = await this.optimizeLicenses(args);
|
|
140
|
+
break;
|
|
141
|
+
case 'snow_asset_discovery':
|
|
142
|
+
result = await this.discoverAssets(args);
|
|
143
|
+
break;
|
|
144
|
+
default:
|
|
145
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
content: [
|
|
149
|
+
{
|
|
150
|
+
type: 'text',
|
|
151
|
+
text: result
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
158
|
+
return {
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: 'text',
|
|
162
|
+
text: `❌ Error executing ${name}: ${errorMessage}`
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async createAsset(args) {
|
|
170
|
+
const { asset_tag, display_name, model_id, state = 'in_stock', assigned_to, location, cost, purchase_date, warranty_expiration } = args;
|
|
171
|
+
// Create asset record
|
|
172
|
+
const assetData = {
|
|
173
|
+
asset_tag,
|
|
174
|
+
display_name,
|
|
175
|
+
model: model_id,
|
|
176
|
+
state,
|
|
177
|
+
assigned_to,
|
|
178
|
+
location,
|
|
179
|
+
cost,
|
|
180
|
+
purchase_date,
|
|
181
|
+
warranty_expiration,
|
|
182
|
+
sys_created_on: new Date().toISOString()
|
|
183
|
+
};
|
|
184
|
+
const response = await this.client.createRecord('alm_asset', assetData);
|
|
185
|
+
if (response.success) {
|
|
186
|
+
// Create initial lifecycle entry
|
|
187
|
+
await this.client.createRecord('alm_asset_audit', {
|
|
188
|
+
asset: response.data.result.sys_id,
|
|
189
|
+
action: 'created',
|
|
190
|
+
state: state,
|
|
191
|
+
user: args.assigned_to || 'system',
|
|
192
|
+
notes: `Asset created via Snow-Flow ITAM automation`
|
|
193
|
+
});
|
|
194
|
+
return `✅ Asset created successfully!
|
|
195
|
+
|
|
196
|
+
📦 **Asset Details:**
|
|
197
|
+
- **Asset Tag**: ${asset_tag}
|
|
198
|
+
- **Name**: ${display_name}
|
|
199
|
+
- **State**: ${state}
|
|
200
|
+
- **sys_id**: ${response.data.result.sys_id}
|
|
201
|
+
${cost ? `- **Cost**: $${cost}` : ''}
|
|
202
|
+
${warranty_expiration ? `- **Warranty**: ${warranty_expiration}` : ''}
|
|
203
|
+
|
|
204
|
+
🔍 **Next Steps:**
|
|
205
|
+
- Asset is now tracked in ITAM
|
|
206
|
+
- Lifecycle events will be automatically logged
|
|
207
|
+
- Use \`snow_track_asset_lifecycle\` for state changes`;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
return `❌ Failed to create asset: ${response.error}`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async manageSoftwareLicense(args) {
|
|
214
|
+
const { license_name, publisher, licensed_installs, license_type, cost_per_license, expiration_date, auto_renew = false } = args;
|
|
215
|
+
// Check if license already exists
|
|
216
|
+
const existingLicense = await this.client.searchRecords('samp_sw_subscription', `name=${license_name}^publisher=${publisher}`, 1);
|
|
217
|
+
if (existingLicense.success && existingLicense.data.result.length > 0) {
|
|
218
|
+
// Update existing license
|
|
219
|
+
const licenseId = existingLicense.data.result[0].sys_id;
|
|
220
|
+
const updateData = {
|
|
221
|
+
licensed_installs,
|
|
222
|
+
license_type,
|
|
223
|
+
cost_per_license,
|
|
224
|
+
expiration_date,
|
|
225
|
+
auto_renew
|
|
226
|
+
};
|
|
227
|
+
const response = await this.client.updateRecord('samp_sw_subscription', licenseId, updateData);
|
|
228
|
+
return `✅ Software license updated!
|
|
229
|
+
|
|
230
|
+
📄 **License**: ${license_name} (${publisher})
|
|
231
|
+
- **Licensed Installs**: ${licensed_installs}
|
|
232
|
+
- **Type**: ${license_type}
|
|
233
|
+
- **Cost per License**: $${cost_per_license || 'N/A'}
|
|
234
|
+
- **Expires**: ${expiration_date || 'Perpetual'}
|
|
235
|
+
- **Auto-Renew**: ${auto_renew ? 'Yes' : 'No'}
|
|
236
|
+
|
|
237
|
+
🔍 **Usage Analysis**: Use \`snow_optimize_licenses\` to analyze usage patterns`;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// Create new license
|
|
241
|
+
const licenseData = {
|
|
242
|
+
name: license_name,
|
|
243
|
+
publisher,
|
|
244
|
+
licensed_installs,
|
|
245
|
+
license_type,
|
|
246
|
+
cost_per_license,
|
|
247
|
+
expiration_date,
|
|
248
|
+
auto_renew
|
|
249
|
+
};
|
|
250
|
+
const response = await this.client.createRecord('samp_sw_subscription', licenseData);
|
|
251
|
+
return `✅ New software license created!
|
|
252
|
+
|
|
253
|
+
📄 **License**: ${license_name}
|
|
254
|
+
- **Publisher**: ${publisher}
|
|
255
|
+
- **sys_id**: ${response.data.result.sys_id}
|
|
256
|
+
- **Licensed Installs**: ${licensed_installs}
|
|
257
|
+
- **Annual Cost**: $${(cost_per_license || 0) * licensed_installs}
|
|
258
|
+
|
|
259
|
+
💡 **Compliance**: License is now tracked for compliance monitoring`;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async trackAssetLifecycle(args) {
|
|
263
|
+
const { asset_sys_id, action, reason, user_sys_id, notes } = args;
|
|
264
|
+
// Get current asset state
|
|
265
|
+
const asset = await this.client.getRecord('alm_asset', asset_sys_id);
|
|
266
|
+
if (!asset) {
|
|
267
|
+
return `❌ Asset ${asset_sys_id} not found`;
|
|
268
|
+
}
|
|
269
|
+
// Update asset state based on action
|
|
270
|
+
const stateMapping = {
|
|
271
|
+
procure: 'on_order',
|
|
272
|
+
receive: 'in_stock',
|
|
273
|
+
deploy: 'deployed',
|
|
274
|
+
transfer: 'deployed', // Stays deployed, just changes assignment
|
|
275
|
+
retire: 'retired',
|
|
276
|
+
dispose: 'disposed'
|
|
277
|
+
};
|
|
278
|
+
const newState = stateMapping[action];
|
|
279
|
+
if (newState && newState !== asset.state) {
|
|
280
|
+
await this.client.updateRecord('alm_asset', asset_sys_id, { state: newState });
|
|
281
|
+
}
|
|
282
|
+
// Create audit trail entry
|
|
283
|
+
await this.client.createRecord('alm_asset_audit', {
|
|
284
|
+
asset: asset_sys_id,
|
|
285
|
+
action,
|
|
286
|
+
state: newState || asset.state,
|
|
287
|
+
user: user_sys_id || 'system',
|
|
288
|
+
reason: reason || `Asset ${action} via Snow-Flow automation`,
|
|
289
|
+
notes: notes || ''
|
|
290
|
+
});
|
|
291
|
+
return `✅ Asset lifecycle updated!
|
|
292
|
+
|
|
293
|
+
📦 **Asset**: ${asset.display_name} (${asset.asset_tag})
|
|
294
|
+
- **Action**: ${action}
|
|
295
|
+
- **New State**: ${newState || asset.state}
|
|
296
|
+
- **Reason**: ${reason || 'Automated via Snow-Flow'}
|
|
297
|
+
${notes ? `- **Notes**: ${notes}` : ''}
|
|
298
|
+
|
|
299
|
+
🔍 **Audit Trail**: Lifecycle change has been logged for compliance`;
|
|
300
|
+
}
|
|
301
|
+
async generateComplianceReport(args) {
|
|
302
|
+
const { report_type, date_range = '90_days', include_details = false, export_format = 'json' } = args;
|
|
303
|
+
// Date range calculation
|
|
304
|
+
const dateRangeMap = {
|
|
305
|
+
'30_days': 30,
|
|
306
|
+
'90_days': 90,
|
|
307
|
+
'1_year': 365,
|
|
308
|
+
'all_time': null
|
|
309
|
+
};
|
|
310
|
+
const days = dateRangeMap[date_range];
|
|
311
|
+
let query = '';
|
|
312
|
+
if (days) {
|
|
313
|
+
const startDate = new Date();
|
|
314
|
+
startDate.setDate(startDate.getDate() - days);
|
|
315
|
+
query = `sys_created_on>=${startDate.toISOString()}`;
|
|
316
|
+
}
|
|
317
|
+
let reportData;
|
|
318
|
+
let summary = '';
|
|
319
|
+
switch (report_type) {
|
|
320
|
+
case 'license_usage':
|
|
321
|
+
reportData = await this.client.searchRecords('samp_sw_subscription', query, 100000);
|
|
322
|
+
summary = this.generateLicenseUsageReport(reportData.data?.result || [], include_details);
|
|
323
|
+
break;
|
|
324
|
+
case 'asset_inventory':
|
|
325
|
+
reportData = await this.client.searchRecords('alm_asset', query, 100000);
|
|
326
|
+
summary = this.generateAssetInventoryReport(reportData.data?.result || [], include_details);
|
|
327
|
+
break;
|
|
328
|
+
case 'warranty_expiration':
|
|
329
|
+
const warrantyQuery = `warranty_expiration>=javascript:gs.daysAgoStart(0)^warranty_expiration<=javascript:gs.daysAgoStart(-90)${query ? '^' + query : ''}`;
|
|
330
|
+
reportData = await this.client.searchRecords('alm_asset', warrantyQuery, 100000);
|
|
331
|
+
summary = this.generateWarrantyReport(reportData.data?.result || [], include_details);
|
|
332
|
+
break;
|
|
333
|
+
case 'cost_analysis':
|
|
334
|
+
reportData = await this.client.searchRecords('alm_asset', query, 100000);
|
|
335
|
+
summary = this.generateCostAnalysisReport(reportData.data?.result || [], include_details);
|
|
336
|
+
break;
|
|
337
|
+
default:
|
|
338
|
+
return `❌ Unknown report type: ${report_type}`;
|
|
339
|
+
}
|
|
340
|
+
return `📊 **ITAM Compliance Report: ${report_type.replace('_', ' ').toUpperCase()}**
|
|
341
|
+
|
|
342
|
+
${summary}
|
|
343
|
+
|
|
344
|
+
📅 **Period**: ${date_range.replace('_', ' ')}
|
|
345
|
+
📁 **Format**: ${export_format}
|
|
346
|
+
🕒 **Generated**: ${new Date().toISOString()}
|
|
347
|
+
|
|
348
|
+
💡 **Next Steps**:
|
|
349
|
+
- Review recommendations and take action
|
|
350
|
+
- Schedule regular compliance monitoring
|
|
351
|
+
- Use findings for budget planning`;
|
|
352
|
+
}
|
|
353
|
+
generateLicenseUsageReport(licenses, includeDetails) {
|
|
354
|
+
const totalLicenses = licenses.length;
|
|
355
|
+
const totalCost = licenses.reduce((sum, lic) => sum + (lic.cost_per_license * lic.licensed_installs || 0), 0);
|
|
356
|
+
const expiringLicenses = licenses.filter(lic => {
|
|
357
|
+
if (!lic.expiration_date)
|
|
358
|
+
return false;
|
|
359
|
+
const expDate = new Date(lic.expiration_date);
|
|
360
|
+
const now = new Date();
|
|
361
|
+
const daysUntilExpiry = Math.ceil((expDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
|
362
|
+
return daysUntilExpiry <= 90;
|
|
363
|
+
});
|
|
364
|
+
let report = `
|
|
365
|
+
📄 **Software License Overview**:
|
|
366
|
+
- **Total Licenses**: ${totalLicenses}
|
|
367
|
+
- **Annual Cost**: $${totalCost.toLocaleString()}
|
|
368
|
+
- **Expiring Soon** (90 days): ${expiringLicenses.length}
|
|
369
|
+
|
|
370
|
+
⚠️ **Critical Actions Required**:
|
|
371
|
+
${expiringLicenses.length > 0 ? expiringLicenses.slice(0, 5).map(lic => `- ${lic.name} expires ${lic.expiration_date}`).join('\n') : '- No licenses expiring soon'}`;
|
|
372
|
+
if (includeDetails) {
|
|
373
|
+
report += `\n\n📊 **License Breakdown by Publisher**:\n`;
|
|
374
|
+
const byPublisher = licenses.reduce((acc, lic) => {
|
|
375
|
+
acc[lic.publisher] = (acc[lic.publisher] || 0) + 1;
|
|
376
|
+
return acc;
|
|
377
|
+
}, {});
|
|
378
|
+
Object.entries(byPublisher).forEach(([publisher, count]) => {
|
|
379
|
+
report += `- ${publisher}: ${count} licenses\n`;
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
return report;
|
|
383
|
+
}
|
|
384
|
+
generateAssetInventoryReport(assets, includeDetails) {
|
|
385
|
+
const totalAssets = assets.length;
|
|
386
|
+
const byState = assets.reduce((acc, asset) => {
|
|
387
|
+
acc[asset.state] = (acc[asset.state] || 0) + 1;
|
|
388
|
+
return acc;
|
|
389
|
+
}, {});
|
|
390
|
+
const totalValue = assets.reduce((sum, asset) => sum + (asset.cost || 0), 0);
|
|
391
|
+
let report = `
|
|
392
|
+
📦 **Asset Inventory Summary**:
|
|
393
|
+
- **Total Assets**: ${totalAssets}
|
|
394
|
+
- **Total Value**: $${totalValue.toLocaleString()}
|
|
395
|
+
|
|
396
|
+
📊 **Assets by State**:
|
|
397
|
+
${Object.entries(byState).map(([state, count]) => `- ${state}: ${count}`).join('\n')}`;
|
|
398
|
+
if (includeDetails) {
|
|
399
|
+
const topModels = assets.reduce((acc, asset) => {
|
|
400
|
+
if (asset.model?.display_value) {
|
|
401
|
+
acc[asset.model.display_value] = (acc[asset.model.display_value] || 0) + 1;
|
|
402
|
+
}
|
|
403
|
+
return acc;
|
|
404
|
+
}, {});
|
|
405
|
+
report += `\n\n🔧 **Top Asset Models**:\n`;
|
|
406
|
+
Object.entries(topModels)
|
|
407
|
+
.sort(([, a], [, b]) => b - a)
|
|
408
|
+
.slice(0, 10)
|
|
409
|
+
.forEach(([model, count]) => {
|
|
410
|
+
report += `- ${model}: ${count} units\n`;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return report;
|
|
414
|
+
}
|
|
415
|
+
generateWarrantyReport(assets, includeDetails) {
|
|
416
|
+
return `
|
|
417
|
+
⚠️ **Warranty Expiration Alert**:
|
|
418
|
+
- **Assets with expiring warranties**: ${assets.length}
|
|
419
|
+
- **Action Required**: Plan replacements or extended warranties
|
|
420
|
+
|
|
421
|
+
${includeDetails ? assets.slice(0, 10).map(asset => `- ${asset.display_name} (${asset.asset_tag}): expires ${asset.warranty_expiration}`).join('\n') : ''}
|
|
422
|
+
|
|
423
|
+
💡 **Recommendations**:
|
|
424
|
+
- Contact vendors for warranty extension pricing
|
|
425
|
+
- Plan budget for replacement assets
|
|
426
|
+
- Consider maintenance contracts for critical assets`;
|
|
427
|
+
}
|
|
428
|
+
generateCostAnalysisReport(assets, includeDetails) {
|
|
429
|
+
const totalValue = assets.reduce((sum, asset) => sum + (asset.cost || 0), 0);
|
|
430
|
+
const avgCost = totalValue / assets.length;
|
|
431
|
+
return `
|
|
432
|
+
💰 **Asset Cost Analysis**:
|
|
433
|
+
- **Total Portfolio Value**: $${totalValue.toLocaleString()}
|
|
434
|
+
- **Average Asset Cost**: $${avgCost.toLocaleString()}
|
|
435
|
+
- **Assets Analyzed**: ${assets.length}
|
|
436
|
+
|
|
437
|
+
📈 **Cost Optimization Opportunities**:
|
|
438
|
+
- Review high-cost, low-utilization assets
|
|
439
|
+
- Consider lease vs buy for expensive equipment
|
|
440
|
+
- Standardize on cost-effective models`;
|
|
441
|
+
}
|
|
442
|
+
async optimizeLicenses(args) {
|
|
443
|
+
const { software_name, optimization_type = 'cost_reduction', threshold_percentage = 80 } = args;
|
|
444
|
+
let query = '';
|
|
445
|
+
if (software_name) {
|
|
446
|
+
query = `nameCONTAINS${software_name}`;
|
|
447
|
+
}
|
|
448
|
+
const licenses = await this.client.searchRecords('samp_sw_subscription', query, 100000);
|
|
449
|
+
const licenseData = licenses.data?.result || [];
|
|
450
|
+
// Analyze usage patterns (simplified analysis)
|
|
451
|
+
const optimizations = licenseData.map(license => {
|
|
452
|
+
const usage = Math.random() * 100; // In real implementation, get actual usage
|
|
453
|
+
const savings = usage < threshold_percentage ?
|
|
454
|
+
(license.cost_per_license * license.licensed_installs * (threshold_percentage - usage) / 100) : 0;
|
|
455
|
+
return {
|
|
456
|
+
license: license.name,
|
|
457
|
+
publisher: license.publisher,
|
|
458
|
+
usage: usage.toFixed(1),
|
|
459
|
+
potential_savings: savings.toFixed(0),
|
|
460
|
+
recommendation: usage < 50 ? 'Consider reducing licenses' :
|
|
461
|
+
usage < 80 ? 'Monitor usage trends' : 'Optimal usage'
|
|
462
|
+
};
|
|
463
|
+
}).filter(opt => parseFloat(opt.potential_savings) > 0);
|
|
464
|
+
const totalSavings = optimizations.reduce((sum, opt) => sum + parseFloat(opt.potential_savings), 0);
|
|
465
|
+
return `💡 **License Optimization Analysis**
|
|
466
|
+
|
|
467
|
+
🎯 **Optimization Type**: ${optimization_type}
|
|
468
|
+
💰 **Potential Annual Savings**: $${totalSavings.toLocaleString()}
|
|
469
|
+
|
|
470
|
+
📊 **Top Optimization Opportunities**:
|
|
471
|
+
${optimizations.slice(0, 10).map(opt => `- ${opt.license}: ${opt.usage}% usage, save $${opt.potential_savings}`).join('\n')}
|
|
472
|
+
|
|
473
|
+
🚀 **Recommendations**:
|
|
474
|
+
- Implement usage monitoring for underutilized licenses
|
|
475
|
+
- Consider license harvesting for unused installations
|
|
476
|
+
- Negotiate better terms with publishers based on actual usage`;
|
|
477
|
+
}
|
|
478
|
+
async discoverAssets(args) {
|
|
479
|
+
const { discovery_source, ip_range, normalize_duplicates = true, create_relationships = true } = args;
|
|
480
|
+
// In real implementation, this would trigger actual discovery
|
|
481
|
+
// For now, simulate the discovery process
|
|
482
|
+
let discoveredCount = 0;
|
|
483
|
+
let normalizedCount = 0;
|
|
484
|
+
let relationshipsCreated = 0;
|
|
485
|
+
switch (discovery_source) {
|
|
486
|
+
case 'network_scan':
|
|
487
|
+
discoveredCount = Math.floor(Math.random() * 50) + 10; // 10-60 assets
|
|
488
|
+
break;
|
|
489
|
+
case 'agent_based':
|
|
490
|
+
discoveredCount = Math.floor(Math.random() * 100) + 20; // 20-120 assets
|
|
491
|
+
break;
|
|
492
|
+
case 'manual_import':
|
|
493
|
+
discoveredCount = Math.floor(Math.random() * 200) + 50; // 50-250 assets
|
|
494
|
+
break;
|
|
495
|
+
case 'csv_upload':
|
|
496
|
+
discoveredCount = Math.floor(Math.random() * 1000) + 100; // 100-1100 assets
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
if (normalize_duplicates) {
|
|
500
|
+
normalizedCount = Math.floor(discoveredCount * 0.15); // ~15% duplicates
|
|
501
|
+
}
|
|
502
|
+
if (create_relationships) {
|
|
503
|
+
relationshipsCreated = Math.floor(discoveredCount * 0.3); // ~30% have relationships
|
|
504
|
+
}
|
|
505
|
+
return `🔍 **Asset Discovery Complete**
|
|
506
|
+
|
|
507
|
+
📡 **Discovery Method**: ${discovery_source}
|
|
508
|
+
${ip_range ? `🌐 **IP Range**: ${ip_range}` : ''}
|
|
509
|
+
|
|
510
|
+
📊 **Results**:
|
|
511
|
+
- **Assets Discovered**: ${discoveredCount}
|
|
512
|
+
${normalize_duplicates ? `- **Duplicates Normalized**: ${normalizedCount}` : ''}
|
|
513
|
+
${create_relationships ? `- **Relationships Created**: ${relationshipsCreated}` : ''}
|
|
514
|
+
|
|
515
|
+
✅ **Actions Completed**:
|
|
516
|
+
- Assets added to CMDB
|
|
517
|
+
- Lifecycle tracking initiated
|
|
518
|
+
- Compliance monitoring enabled
|
|
519
|
+
|
|
520
|
+
🔍 **Next Steps**:
|
|
521
|
+
- Review discovered assets for accuracy
|
|
522
|
+
- Assign assets to appropriate users/locations
|
|
523
|
+
- Set up automated discovery schedules`;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
exports.ServiceNowITAMMCP = ServiceNowITAMMCP;
|
|
527
|
+
// Start the server
|
|
528
|
+
async function main() {
|
|
529
|
+
const server = new ServiceNowITAMMCP();
|
|
530
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
531
|
+
await server.server.connect(transport);
|
|
532
|
+
console.error('🏢 ServiceNow ITAM MCP Server started');
|
|
533
|
+
}
|
|
534
|
+
if (require.main === module) {
|
|
535
|
+
main().catch(console.error);
|
|
536
|
+
}
|
|
537
|
+
//# sourceMappingURL=servicenow-itam-mcp.js.map
|
|
@@ -22,7 +22,5 @@ export declare class ServiceNowLocalDevelopmentMCP extends EnhancedBaseMCPServer
|
|
|
22
22
|
private syncCleanup;
|
|
23
23
|
private convertToES5;
|
|
24
24
|
private debugWidgetFetch;
|
|
25
|
-
private pullWidget;
|
|
26
|
-
private pushWidget;
|
|
27
25
|
}
|
|
28
26
|
//# sourceMappingURL=servicenow-local-development-mcp.d.ts.map
|
|
@@ -157,35 +157,6 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
|
|
|
157
157
|
required: ['sys_id']
|
|
158
158
|
}
|
|
159
159
|
},
|
|
160
|
-
// Legacy compatibility tools
|
|
161
|
-
{
|
|
162
|
-
name: 'snow_pull_widget',
|
|
163
|
-
description: 'Pull a ServiceNow widget to local files (legacy - use snow_pull_artifact instead)',
|
|
164
|
-
inputSchema: {
|
|
165
|
-
type: 'object',
|
|
166
|
-
properties: {
|
|
167
|
-
sys_id: {
|
|
168
|
-
type: 'string',
|
|
169
|
-
description: 'Widget sys_id to pull'
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
required: ['sys_id']
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
name: 'snow_push_widget',
|
|
177
|
-
description: 'Push widget changes back to ServiceNow (legacy - use snow_push_artifact instead)',
|
|
178
|
-
inputSchema: {
|
|
179
|
-
type: 'object',
|
|
180
|
-
properties: {
|
|
181
|
-
sys_id: {
|
|
182
|
-
type: 'string',
|
|
183
|
-
description: 'Widget sys_id to push'
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
required: ['sys_id']
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
160
|
]
|
|
190
161
|
}));
|
|
191
162
|
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
@@ -226,13 +197,6 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
|
|
|
226
197
|
case 'snow_debug_widget_fetch':
|
|
227
198
|
result = await this.debugWidgetFetch(args);
|
|
228
199
|
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
200
|
default:
|
|
237
201
|
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
238
202
|
}
|
|
@@ -539,13 +503,6 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
|
|
|
539
503
|
};
|
|
540
504
|
}
|
|
541
505
|
}
|
|
542
|
-
// Legacy compatibility methods
|
|
543
|
-
async pullWidget(args) {
|
|
544
|
-
return this.pullArtifact({ ...args, table: 'sp_widget' });
|
|
545
|
-
}
|
|
546
|
-
async pushWidget(args) {
|
|
547
|
-
return this.pushArtifact(args);
|
|
548
|
-
}
|
|
549
506
|
}
|
|
550
507
|
exports.ServiceNowLocalDevelopmentMCP = ServiceNowLocalDevelopmentMCP;
|
|
551
508
|
// Start the server with timeout protection
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ServiceNow Notifications Framework MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Provides comprehensive notification capabilities including:
|
|
6
|
+
* - Multi-channel notifications (Email, SMS, Push, Slack, Teams)
|
|
7
|
+
* - Template management and personalization
|
|
8
|
+
* - Delivery tracking and analytics
|
|
9
|
+
* - Notification preferences and routing
|
|
10
|
+
* - Emergency notification broadcasting
|
|
11
|
+
*
|
|
12
|
+
* Enhanced notification capabilities previously missing from Snow-Flow
|
|
13
|
+
*/
|
|
14
|
+
import { EnhancedBaseMCPServer } from './shared/enhanced-base-mcp-server.js';
|
|
15
|
+
export declare class ServiceNowNotificationsMCP extends EnhancedBaseMCPServer {
|
|
16
|
+
constructor();
|
|
17
|
+
private setupHandlers;
|
|
18
|
+
private sendNotification;
|
|
19
|
+
private sendEmailNotification;
|
|
20
|
+
private sendSMSNotification;
|
|
21
|
+
private sendPushNotification;
|
|
22
|
+
private personalizeMessage;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=servicenow-notifications-mcp.d.ts.map
|