snow-flow 3.3.6 → 3.3.8

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.
@@ -131,6 +131,18 @@
131
131
  "SNOW_CLIENT_ID": "{{SNOW_CLIENT_ID}}",
132
132
  "SNOW_CLIENT_SECRET": "{{SNOW_CLIENT_SECRET}}"
133
133
  }
134
+ },
135
+ "servicenow-system-properties": {
136
+ "command": "node",
137
+ "args": [
138
+ "{{PROJECT_ROOT}}/dist/mcp/servicenow-system-properties-mcp.js"
139
+ ],
140
+ "description": "System property management via official ServiceNow APIs: get/set/list/delete properties, bulk operations, import/export JSON, validate values, search, categories, audit history - all using standard Table API on sys_properties",
141
+ "env": {
142
+ "SNOW_INSTANCE": "{{SNOW_INSTANCE}}",
143
+ "SNOW_CLIENT_ID": "{{SNOW_CLIENT_ID}}",
144
+ "SNOW_CLIENT_SECRET": "{{SNOW_CLIENT_SECRET}}"
145
+ }
134
146
  }
135
147
  }
136
148
  }
@@ -36,7 +36,7 @@ function getDynamicVersion() {
36
36
  console.warn('Warning: Could not read version from package.json:', error);
37
37
  }
38
38
  // Fallback to hardcoded version
39
- return '3.3.6';
39
+ return '3.3.8';
40
40
  }
41
41
  // Export a constant that uses the dynamic version
42
42
  exports.VERSION = getDynamicVersion();
@@ -1316,7 +1316,12 @@ ${is403Error ? '\n⚠️ **Possible False Negative**: Widget may have been crea
1316
1316
  const { updateSetId, updateSetName } = await this.ensureUpdateSet('Portal Page', args.page_id);
1317
1317
  // Validate portal page structure
1318
1318
  if (!args.page_id || !args.title) {
1319
- throw new Error('Portal page must have page_id and title');
1319
+ this.logger.error('Portal page validation failed', {
1320
+ args,
1321
+ hasPageId: !!args.page_id,
1322
+ hasTitle: !!args.title
1323
+ });
1324
+ throw new Error(`Portal page must have page_id and title. Received: page_id=${args.page_id}, title=${args.title}`);
1320
1325
  }
1321
1326
  // Find widget sys_id if widget name is provided
1322
1327
  let widgetSysId = args.widget_sys_id;
@@ -1337,18 +1342,22 @@ ${is403Error ? '\n⚠️ **Possible False Negative**: Widget may have been crea
1337
1342
  }
1338
1343
  }
1339
1344
  // Determine portal sys_id
1340
- let portalSysId = '';
1341
- try {
1342
- // Default to Employee Service Portal if available, otherwise standard Service Portal
1343
- const portalQuery = args.portal === 'esc' ? 'url_suffix=esc' : 'url_suffix=sp';
1344
- const portalResult = await this.client.searchRecords('sp_portal', portalQuery, 1);
1345
- if (portalResult.success && portalResult.data?.length > 0) {
1346
- portalSysId = portalResult.data[0].sys_id;
1347
- this.logger.info('Found portal', { portal: args.portal, sys_id: portalSysId });
1345
+ let portalSysId = args.sp_portal || '';
1346
+ // If sp_portal is already a sys_id (32 char hex), use it directly
1347
+ if (!/^[a-f0-9]{32}$/.test(portalSysId)) {
1348
+ // Not a sys_id, try to look it up
1349
+ try {
1350
+ // Default to Employee Service Portal if available, otherwise standard Service Portal
1351
+ const portalQuery = args.portal === 'esc' ? 'url_suffix=esc' : 'url_suffix=sp';
1352
+ const portalResult = await this.client.searchRecords('sp_portal', portalQuery, 1);
1353
+ if (portalResult.success && portalResult.data?.result?.length > 0) {
1354
+ portalSysId = portalResult.data.result[0].sys_id;
1355
+ this.logger.info('Found portal', { portal: args.portal, sys_id: portalSysId });
1356
+ }
1357
+ }
1358
+ catch (error) {
1359
+ this.logger.warn('Failed to lookup portal, using provided value', { portal: args.portal, error });
1348
1360
  }
1349
- }
1350
- catch (error) {
1351
- this.logger.warn('Failed to lookup portal, using default', { portal: args.portal, error });
1352
1361
  }
1353
1362
  // Create portal page
1354
1363
  let pageResult;
@@ -1381,8 +1390,35 @@ ${is403Error ? '\n⚠️ **Possible False Negative**: Widget may have been crea
1381
1390
  const credentials = await this.oauth.loadCredentials();
1382
1391
  // Create widget instances on the page if widget is provided
1383
1392
  const widgetInstances = [];
1384
- if (widgetSysId && args.widgets && args.widgets.length > 0) {
1385
- for (const widgetConfig of args.widgets) {
1393
+ // Support both widgets array and single widget
1394
+ const widgetsToCreate = args.widgets || [];
1395
+ // If no widgets array but a single widget is specified, create one widget
1396
+ if (widgetsToCreate.length === 0 && widgetSysId) {
1397
+ widgetsToCreate.push({
1398
+ widget: args.widget_name || widgetSysId,
1399
+ width: 12,
1400
+ row: 1,
1401
+ column: 1,
1402
+ title: args.widget_title || args.title || '',
1403
+ options: args.widget_options || {}
1404
+ });
1405
+ }
1406
+ if (widgetsToCreate.length > 0) {
1407
+ for (const widgetConfig of widgetsToCreate) {
1408
+ // Determine widget sys_id for this widget
1409
+ let currentWidgetSysId = widgetSysId; // Default to the main widget
1410
+ // If widget config specifies a different widget, look it up
1411
+ if (widgetConfig.widget && widgetConfig.widget !== args.widget_name) {
1412
+ try {
1413
+ const widgetLookup = await this.client.searchRecords('sp_widget', `sys_id=${widgetConfig.widget}^ORname=${widgetConfig.widget}`, 1);
1414
+ if (widgetLookup.success && widgetLookup.data?.result?.length > 0) {
1415
+ currentWidgetSysId = widgetLookup.data.result[0].sys_id;
1416
+ }
1417
+ }
1418
+ catch (lookupError) {
1419
+ this.logger.warn('Could not find widget, using default', { widget: widgetConfig.widget });
1420
+ }
1421
+ }
1386
1422
  try {
1387
1423
  // Create container
1388
1424
  const containerResult = await this.client.createRecord('sp_container', {
@@ -1417,10 +1453,10 @@ ${is403Error ? '\n⚠️ **Possible False Negative**: Widget may have been crea
1417
1453
  // Create widget instance
1418
1454
  const instanceResult = await this.client.createRecord('sp_instance', {
1419
1455
  sp_column: columnResult.data.sys_id,
1420
- sp_widget: widgetSysId,
1456
+ sp_widget: currentWidgetSysId, // Use the current widget sys_id, not the main one
1421
1457
  order: widgetConfig.order || 100,
1422
1458
  title: widgetConfig.title || '',
1423
- options: JSON.stringify(widgetConfig.options || {}),
1459
+ options: JSON.stringify(widgetConfig.options || widgetConfig.widget_parameters || {}),
1424
1460
  class_name: widgetConfig.instance_class || '',
1425
1461
  color: widgetConfig.color || 'default',
1426
1462
  active: true,
@@ -7260,7 +7296,23 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
7260
7296
  case 'widget':
7261
7297
  return await this.deployWidget(scopedConfig);
7262
7298
  case 'portal_page':
7263
- return await this.deployPortalPage(scopedConfig);
7299
+ // Map config fields to expected parameter names for portal_page
7300
+ const portalPageArgs = {
7301
+ page_id: scopedConfig.id || scopedConfig.page_id,
7302
+ title: scopedConfig.title,
7303
+ widget_name: scopedConfig.widget_name,
7304
+ widget_sys_id: scopedConfig.widget_sys_id,
7305
+ description: scopedConfig.summary || scopedConfig.description,
7306
+ page_css: scopedConfig.css,
7307
+ portal: scopedConfig.portal || scopedConfig.sp_portal || 'sp',
7308
+ public: scopedConfig.public !== undefined ? scopedConfig.public : true,
7309
+ requires_authentication: scopedConfig.requires_authentication,
7310
+ draft: scopedConfig.draft || false,
7311
+ // Map containers to widgets format
7312
+ widgets: this.mapContainersToWidgets(scopedConfig.containers),
7313
+ ...scopedConfig // Include any other fields
7314
+ };
7315
+ return await this.deployPortalPage(portalPageArgs);
7264
7316
  case 'application':
7265
7317
  return await this.deployApplication(scopedConfig);
7266
7318
  case 'xml_update_set':
@@ -8851,6 +8903,36 @@ c.$onInit = function() {
8851
8903
  await this.server.connect(transport);
8852
8904
  this.logger.info('ServiceNow Deployment MCP Server started');
8853
8905
  }
8906
+ /**
8907
+ * Map container structure to widget structure for portal page deployment
8908
+ */
8909
+ mapContainersToWidgets(containers) {
8910
+ if (!containers || !Array.isArray(containers)) {
8911
+ return [];
8912
+ }
8913
+ const widgets = [];
8914
+ for (const container of containers) {
8915
+ if (container.widget_instance) {
8916
+ widgets.push({
8917
+ widget: container.widget_instance.widget,
8918
+ width: container.width || 12,
8919
+ row: container.row || 1,
8920
+ column: container.column || 1,
8921
+ title: container.widget_instance.title || '',
8922
+ options: container.widget_instance.widget_parameters || {},
8923
+ container_title: container.title || '',
8924
+ class_name: container.class_name || '',
8925
+ background_color: container.background_color || '',
8926
+ background_image: container.background_image || '',
8927
+ background_style: container.background_style || 'default',
8928
+ instance_class: container.widget_instance.class_name || '',
8929
+ color: container.widget_instance.color || 'default',
8930
+ order: container.widget_instance.order || 100
8931
+ });
8932
+ }
8933
+ }
8934
+ return widgets;
8935
+ }
8854
8936
  /**
8855
8937
  * Escape XML special characters
8856
8938
  */
@@ -0,0 +1,68 @@
1
+ /**
2
+ * ServiceNow System Properties MCP Server
3
+ *
4
+ * Provides comprehensive system property management through official ServiceNow APIs
5
+ * Uses the standard Table API on sys_properties table
6
+ */
7
+ /**
8
+ * ServiceNow System Properties MCP Server
9
+ * Manages system properties through official ServiceNow REST APIs
10
+ */
11
+ export declare class ServiceNowSystemPropertiesMCP {
12
+ private server;
13
+ private client;
14
+ private propertyCache;
15
+ constructor();
16
+ private setupHandlers;
17
+ private setupTools;
18
+ /**
19
+ * Get a system property value
20
+ */
21
+ private getProperty;
22
+ /**
23
+ * Set or create a system property
24
+ */
25
+ private setProperty;
26
+ /**
27
+ * List system properties
28
+ */
29
+ private listProperties;
30
+ /**
31
+ * Delete a system property
32
+ */
33
+ private deleteProperty;
34
+ /**
35
+ * Search properties
36
+ */
37
+ private searchProperties;
38
+ /**
39
+ * Bulk get properties
40
+ */
41
+ private bulkGetProperties;
42
+ /**
43
+ * Bulk set properties
44
+ */
45
+ private bulkSetProperties;
46
+ /**
47
+ * Export properties
48
+ */
49
+ private exportProperties;
50
+ /**
51
+ * Import properties
52
+ */
53
+ private importProperties;
54
+ /**
55
+ * Validate property value
56
+ */
57
+ private validateProperty;
58
+ /**
59
+ * Get property categories
60
+ */
61
+ private getCategories;
62
+ /**
63
+ * Get property audit history
64
+ */
65
+ private getPropertyHistory;
66
+ start(): Promise<void>;
67
+ }
68
+ //# sourceMappingURL=servicenow-system-properties-mcp.d.ts.map