snow-flow 1.1.68 → 1.1.70

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.
@@ -1825,12 +1825,40 @@ class ServiceNowIntelligentMCP {
1825
1825
  performance_metrics: {},
1826
1826
  test_data_used: test_data
1827
1827
  };
1828
- // Get flow details first
1829
- const flowDetails = await this.client.get(`/api/now/table/wf_workflow/${flow_sys_id}`, {
1830
- sysparm_fields: 'name,description,active,table'
1831
- });
1832
- if (!flowDetails.result) {
1833
- throw new Error(`Flow not found: ${flow_sys_id}`);
1828
+ // Try modern Flow Designer API first
1829
+ let flowDetails;
1830
+ let flowType = 'flow_designer';
1831
+ try {
1832
+ // First try sys_hub_flow (modern Flow Designer)
1833
+ flowDetails = await this.client.get(`/api/now/table/sys_hub_flow/${flow_sys_id}`, {
1834
+ sysparm_fields: 'name,description,active,type,status,sys_id,latest_snapshot'
1835
+ });
1836
+ if (!flowDetails.result) {
1837
+ throw new Error('Not found in sys_hub_flow');
1838
+ }
1839
+ }
1840
+ catch (error) {
1841
+ // Fallback to legacy workflow if not found
1842
+ try {
1843
+ flowDetails = await this.client.get(`/api/now/table/wf_workflow/${flow_sys_id}`, {
1844
+ sysparm_fields: 'name,description,active,table'
1845
+ });
1846
+ flowType = 'legacy_workflow';
1847
+ if (!flowDetails.result) {
1848
+ throw new Error('Not found in wf_workflow');
1849
+ }
1850
+ }
1851
+ catch (fallbackError) {
1852
+ // Try searching by name if sys_id fails
1853
+ const searchResults = await this.findFlowByNameOrSysId(flow_sys_id);
1854
+ if (searchResults) {
1855
+ flowDetails = { result: searchResults };
1856
+ flowType = searchResults.sys_class_name === 'sys_hub_flow' ? 'flow_designer' : 'legacy_workflow';
1857
+ }
1858
+ else {
1859
+ throw new Error(`Flow not found with identifier: ${flow_sys_id}`);
1860
+ }
1861
+ }
1834
1862
  }
1835
1863
  executionResults.flow_info = flowDetails.result;
1836
1864
  // For flows that can be triggered programmatically
@@ -1838,11 +1866,39 @@ class ServiceNowIntelligentMCP {
1838
1866
  try {
1839
1867
  // Attempt to trigger the flow (this depends on the flow type and trigger conditions)
1840
1868
  // For now, we'll simulate testing by checking flow structure and providing recommendations
1841
- const flowActivities = await this.client.get('/api/now/table/wf_activity', {
1842
- sysparm_query: `workflow=${flow_sys_id}`,
1843
- sysparm_fields: 'name,order,active,script',
1844
- sysparm_orderby: 'order'
1845
- });
1869
+ let flowActivities;
1870
+ if (flowType === 'flow_designer') {
1871
+ // For modern flows, get activities from flow definition
1872
+ const flowData = flowDetails.result;
1873
+ if (flowData.latest_snapshot) {
1874
+ try {
1875
+ const snapshot = JSON.parse(flowData.latest_snapshot);
1876
+ flowActivities = {
1877
+ result: (snapshot.activities || snapshot.steps || []).map((activity, index) => ({
1878
+ name: activity.name || activity.label || `Activity ${index + 1}`,
1879
+ order: index * 100,
1880
+ active: true,
1881
+ type: activity.activity_type || activity.type,
1882
+ script: activity.script || ''
1883
+ }))
1884
+ };
1885
+ }
1886
+ catch (parseError) {
1887
+ flowActivities = { result: [] };
1888
+ }
1889
+ }
1890
+ else {
1891
+ flowActivities = { result: [] };
1892
+ }
1893
+ }
1894
+ else {
1895
+ // Legacy workflow activities
1896
+ flowActivities = await this.client.get('/api/now/table/wf_activity', {
1897
+ sysparm_query: `workflow=${flow_sys_id}`,
1898
+ sysparm_fields: 'name,order,active,script',
1899
+ sysparm_orderby: 'order'
1900
+ });
1901
+ }
1846
1902
  executionResults.execution_steps = flowActivities.result || [];
1847
1903
  executionResults.total_steps = executionResults.execution_steps.length;
1848
1904
  // Simulated execution analysis
@@ -1852,12 +1908,28 @@ class ServiceNowIntelligentMCP {
1852
1908
  estimated_execution_time_ms: executionResults.total_steps * 500, // Rough estimate
1853
1909
  complexity_score: this.calculateFlowComplexity(executionResults.execution_steps)
1854
1910
  };
1855
- // Provide testing recommendations
1911
+ // Provide testing recommendations based on flow type
1856
1912
  executionResults.testing_recommendations = [
1857
- 'Create test records in the target table before execution',
1858
- 'Monitor the execution context for proper variable passing',
1859
- 'Verify all conditions and approval steps work as expected',
1860
- 'Test error handling and rollback scenarios'
1913
+ `Flow Type: ${flowType === 'flow_designer' ? 'Modern Flow Designer' : 'Legacy Workflow'}`,
1914
+ `Flow Sys ID: ${flowDetails.result.sys_id}`,
1915
+ `Flow Name: ${flowDetails.result.name}`,
1916
+ '',
1917
+ '📋 Testing Steps:',
1918
+ '1. Use snow_test_flow_with_mock for mock data testing (recommended)',
1919
+ '2. Create test records in the target table before execution',
1920
+ '3. Monitor execution via sys_flow_context (Flow Designer) or wf_context (Legacy)',
1921
+ '4. Verify all conditions and approval steps work as expected',
1922
+ '5. Test error handling and rollback scenarios',
1923
+ '',
1924
+ '💡 Alternative Testing Tools:',
1925
+ '- snow_test_flow_with_mock: Test with mock users and data (always works)',
1926
+ '- snow_comprehensive_flow_test: Advanced testing with edge cases',
1927
+ '- Direct execution: Trigger via actual record creation/update',
1928
+ '',
1929
+ '🔍 Sys ID Tracking:',
1930
+ `- Flow Sys ID: ${flowDetails.result.sys_id}`,
1931
+ `- Table: ${flowType === 'flow_designer' ? 'sys_hub_flow' : 'wf_workflow'}`,
1932
+ `- URL: ${process.env.SNOW_INSTANCE ? `https://${process.env.SNOW_INSTANCE.replace(/\/$/, '')}.service-now.com` : ''}/${flowType === 'flow_designer' ? 'nav_to.do?uri=sys_hub_flow.do?sys_id=' : 'workflow_ide.do?sysparm_nostack=true&sysparm_sys_id='}${flowDetails.result.sys_id}`
1861
1933
  ];
1862
1934
  if (monitor_execution) {
1863
1935
  executionResults.monitoring_notes = 'Full monitoring requires Flow Designer integration';
@@ -1870,10 +1942,25 @@ class ServiceNowIntelligentMCP {
1870
1942
  return { content: [{ type: 'text', text: JSON.stringify(executionResults, null, 2) }] };
1871
1943
  }
1872
1944
  catch (error) {
1873
- return { content: [{
1874
- type: 'text',
1875
- text: `❌ Flow Testing Failed: ${error instanceof Error ? error.message : 'Unknown error'}`
1876
- }] };
1945
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1946
+ let helpText = `❌ Flow Testing Failed: ${errorMessage}\n\n`;
1947
+ if (errorMessage.includes('not found')) {
1948
+ helpText += `🔍 Flow Discovery Tips:\n`;
1949
+ helpText += `- Provided identifier: "${flow_sys_id}"\n`;
1950
+ helpText += `- This could be a sys_id OR a flow name\n`;
1951
+ helpText += `- Checked tables: sys_hub_flow (modern), wf_workflow (legacy)\n\n`;
1952
+ helpText += `💡 Try these alternatives:\n`;
1953
+ helpText += `1. Use snow_find_artifact to search for the flow:\n`;
1954
+ helpText += ` snow_find_artifact({ query: "${flow_sys_id}", type: "flow" })\n\n`;
1955
+ helpText += `2. Use snow_test_flow_with_mock for testing without sys_id:\n`;
1956
+ helpText += ` snow_test_flow_with_mock({ flow_id: "${flow_sys_id}" })\n\n`;
1957
+ helpText += `3. Search by partial name:\n`;
1958
+ helpText += ` snow_discover_existing_flows({ flow_purpose: "${flow_sys_id}" })\n\n`;
1959
+ helpText += `4. Get exact sys_id from ServiceNow UI:\n`;
1960
+ helpText += ` - Flow Designer → Flows → Copy sys_id from list\n`;
1961
+ helpText += ` - Or right-click flow → Copy sys_id\n`;
1962
+ }
1963
+ return { content: [{ type: 'text', text: helpText }] };
1877
1964
  }
1878
1965
  }
1879
1966
  async batchDeploymentValidator(args) {
@@ -2136,58 +2223,111 @@ class ServiceNowIntelligentMCP {
2136
2223
  current_permissions: {},
2137
2224
  required_actions: []
2138
2225
  };
2139
- // Check current user permissions
2140
- const currentUser = await this.client.get('/api/now/table/sys_user', {
2141
- sysparm_query: 'user_name=current_user',
2142
- sysparm_fields: 'sys_id,user_name,roles'
2226
+ // Get current user info
2227
+ const whoAmI = await this.client.get('/api/now/table/sys_user', {
2228
+ sysparm_query: 'user_name=admin', // Try admin user first
2229
+ sysparm_fields: 'sys_id,user_name,name,email',
2230
+ sysparm_limit: 1
2143
2231
  });
2144
- if (currentUser.result?.[0]) {
2145
- // Get user roles
2146
- const userRoles = await this.client.get('/api/now/table/sys_user_has_role', {
2147
- sysparm_query: `user=${currentUser.result[0].sys_id}`,
2148
- sysparm_fields: 'role.name,role.sys_id'
2149
- });
2150
- escalationResults.current_permissions = {
2151
- user_id: currentUser.result[0].sys_id,
2152
- current_roles: userRoles.result?.map((r) => r.role?.name || r.role) || []
2153
- };
2154
- // Check which roles are missing
2155
- const currentRoleNames = escalationResults.current_permissions.current_roles;
2156
- const missingRoles = required_roles.filter((role) => !currentRoleNames.includes(role));
2157
- if (missingRoles.length === 0) {
2158
- escalationResults.escalation_status = 'not_needed';
2159
- escalationResults.message = 'User already has all required permissions';
2160
- }
2161
- else {
2162
- escalationResults.escalation_status = 'required';
2163
- escalationResults.missing_roles = missingRoles;
2164
- // Generate escalation recommendations
2165
- escalationResults.required_actions = [
2166
- `Contact ServiceNow administrator to temporarily grant these roles: ${missingRoles.join(', ')}`,
2167
- `Reason: ${reason}`,
2168
- `Duration: ${duration}`,
2169
- `Workflow context: ${workflow_context || 'Multi-agent development'}`
2170
- ];
2171
- if (duration === 'session') {
2172
- escalationResults.required_actions.push('Permissions can be revoked after current development session');
2173
- }
2174
- // Provide specific guidance for common roles
2175
- for (const role of missingRoles) {
2176
- switch (role) {
2177
- case 'admin':
2178
- escalationResults.required_actions.push('🔐 Admin role: Navigate to User Administration > Users, find your user, and add "admin" role');
2179
- break;
2180
- case 'app_creator':
2181
- escalationResults.required_actions.push('📱 App Creator role: Required for creating new applications and scoped artifacts');
2182
- break;
2183
- case 'system_administrator':
2184
- escalationResults.required_actions.push('⚙️ System Administrator: Full system access for advanced configuration');
2185
- break;
2186
- }
2187
- }
2232
+ const currentUser = whoAmI.result?.[0];
2233
+ if (!currentUser) {
2234
+ return { content: [{
2235
+ type: 'text',
2236
+ text: '❌ Could not identify current user. Please ensure you are logged in to ServiceNow.'
2237
+ }] };
2238
+ }
2239
+ // Get user roles
2240
+ const userRoles = await this.client.get('/api/now/table/sys_user_has_role', {
2241
+ sysparm_query: `user=${currentUser.sys_id}`,
2242
+ sysparm_fields: 'role.name,role.description,inherited'
2243
+ });
2244
+ const currentRoles = userRoles.result?.map((r) => r.role?.name).filter(Boolean) || [];
2245
+ const missingRoles = required_roles.filter((role) => !currentRoles.includes(role));
2246
+ // Get instance URL
2247
+ const instanceUrl = process.env.SNOW_INSTANCE ?
2248
+ `https://${process.env.SNOW_INSTANCE.replace(/\/$/, '')}.service-now.com` :
2249
+ 'https://your-instance.service-now.com';
2250
+ if (missingRoles.length === 0) {
2251
+ return { content: [{
2252
+ type: 'text',
2253
+ text: `✅ **Permission Check Passed**\n\nYou already have all required roles:\n${required_roles.map((r) => `- ✓ ${r}`).join('\n')}\n\nNo escalation needed!`
2254
+ }] };
2255
+ }
2256
+ // Build actionable response
2257
+ let response = `🔐 **Permission Escalation Required**\n\n`;
2258
+ response += `**Current User:** ${currentUser.name} (${currentUser.user_name})\n`;
2259
+ response += `**Current Roles:** ${currentRoles.length > 0 ? currentRoles.join(', ') : 'None'}\n`;
2260
+ response += `**Missing Roles:** ${missingRoles.join(', ')}\n`;
2261
+ response += `**Reason:** ${reason}\n`;
2262
+ response += `**Duration:** ${duration}\n\n`;
2263
+ response += `## 🎯 Required Actions:\n\n`;
2264
+ // Provide specific instructions for each missing role
2265
+ for (const role of missingRoles) {
2266
+ response += `### ${role} Role\n`;
2267
+ switch (role) {
2268
+ case 'admin':
2269
+ response += `The **admin** role provides:\n`;
2270
+ response += `- Global scope access for creating widgets, flows, and applications\n`;
2271
+ response += `- Ability to modify system tables and configurations\n`;
2272
+ response += `- Access to all ServiceNow modules and features\n\n`;
2273
+ response += `**How to obtain:**\n`;
2274
+ response += `1. Contact your ServiceNow administrator\n`;
2275
+ response += `2. Or if you have admin access: [Click here to manage user roles](${instanceUrl}/sys_user.do?sys_id=${currentUser.sys_id})\n`;
2276
+ response += `3. In the "Roles" related list, click "Edit" and add "admin"\n\n`;
2277
+ break;
2278
+ case 'app_creator':
2279
+ response += `The **app_creator** role provides:\n`;
2280
+ response += `- Create custom applications and scoped apps\n`;
2281
+ response += `- Design application modules and menus\n`;
2282
+ response += `- Manage application artifacts\n\n`;
2283
+ response += `**How to obtain:**\n`;
2284
+ response += `1. Request from ServiceNow administrator\n`;
2285
+ response += `2. Or navigate to: [User Administration > Users](${instanceUrl}/sys_user_list.do)\n`;
2286
+ response += `3. Find your user record and add "app_creator" role\n\n`;
2287
+ break;
2288
+ case 'system_administrator':
2289
+ response += `The **system_administrator** role provides:\n`;
2290
+ response += `- Full system access and configuration\n`;
2291
+ response += `- Advanced scripting and development capabilities\n`;
2292
+ response += `- Access to all system properties and settings\n\n`;
2293
+ response += `**How to obtain:**\n`;
2294
+ response += `1. This is a highly privileged role - contact system admin\n`;
2295
+ response += `2. Requires approval from ServiceNow instance owner\n\n`;
2296
+ break;
2297
+ case 'global_admin':
2298
+ response += `The **global_admin** role provides:\n`;
2299
+ response += `- Cross-scope application access\n`;
2300
+ response += `- Global artifact creation and management\n`;
2301
+ response += `- Override scope restrictions\n\n`;
2302
+ response += `**How to obtain:**\n`;
2303
+ response += `1. Contact ServiceNow administrator\n`;
2304
+ response += `2. May require business justification\n\n`;
2305
+ break;
2306
+ default:
2307
+ response += `The **${role}** role is required for this operation.\n\n`;
2308
+ response += `**How to obtain:**\n`;
2309
+ response += `1. Contact your ServiceNow administrator\n`;
2310
+ response += `2. Request temporary access for: "${reason}"\n\n`;
2188
2311
  }
2189
2312
  }
2190
- return { content: [{ type: 'text', text: JSON.stringify(escalationResults, null, 2) }] };
2313
+ response += `## 💡 Alternative Solutions:\n\n`;
2314
+ response += `1. **Use a development instance** where you have admin access\n`;
2315
+ response += `2. **Request a personal developer instance** from [developer.servicenow.com](https://developer.servicenow.com)\n`;
2316
+ response += `3. **Work with a team member** who has the required permissions\n`;
2317
+ response += `4. **Use Update Sets** to package changes for deployment by an admin\n\n`;
2318
+ response += `## 📋 Template Request for Admin:\n\n`;
2319
+ response += `\`\`\`\n`;
2320
+ response += `Subject: Temporary Permission Request - ${reason}\n\n`;
2321
+ response += `Hi Admin,\n\n`;
2322
+ response += `I need temporary access to the following roles for development:\n`;
2323
+ response += `- Roles needed: ${missingRoles.join(', ')}\n`;
2324
+ response += `- Reason: ${reason}\n`;
2325
+ response += `- Duration: ${duration}\n`;
2326
+ response += `- Context: ${workflow_context || 'ServiceNow multi-agent development'}\n\n`;
2327
+ response += `These permissions can be revoked after the ${duration === 'session' ? 'current session' : duration}.\n\n`;
2328
+ response += `Thank you!\n`;
2329
+ response += `\`\`\``;
2330
+ return { content: [{ type: 'text', text: response }] };
2191
2331
  }
2192
2332
  catch (error) {
2193
2333
  return { content: [{
@@ -2611,10 +2751,40 @@ class ServiceNowIntelligentMCP {
2611
2751
  overall_status: 'running',
2612
2752
  recommendations: []
2613
2753
  };
2614
- // Get flow details
2615
- const flowDetails = await this.client.get(`/api/now/table/wf_workflow/${flow_sys_id}`);
2616
- if (!flowDetails.result) {
2617
- throw new Error(`Flow not found: ${flow_sys_id}`);
2754
+ // Get flow details using improved discovery
2755
+ let flowDetails;
2756
+ let flowType = 'flow_designer';
2757
+ try {
2758
+ // First try sys_hub_flow (modern Flow Designer)
2759
+ flowDetails = await this.client.get(`/api/now/table/sys_hub_flow/${flow_sys_id}`, {
2760
+ sysparm_fields: 'name,description,active,type,status,sys_id,latest_snapshot'
2761
+ });
2762
+ if (!flowDetails.result) {
2763
+ throw new Error('Not found in sys_hub_flow');
2764
+ }
2765
+ }
2766
+ catch (error) {
2767
+ // Fallback to legacy workflow if not found
2768
+ try {
2769
+ flowDetails = await this.client.get(`/api/now/table/wf_workflow/${flow_sys_id}`, {
2770
+ sysparm_fields: 'name,description,active,table'
2771
+ });
2772
+ flowType = 'legacy_workflow';
2773
+ if (!flowDetails.result) {
2774
+ throw new Error('Not found in wf_workflow');
2775
+ }
2776
+ }
2777
+ catch (fallbackError) {
2778
+ // Try searching by name if sys_id fails
2779
+ const searchResults = await this.findFlowByNameOrSysId(flow_sys_id);
2780
+ if (searchResults) {
2781
+ flowDetails = { result: searchResults };
2782
+ flowType = searchResults.sys_class_name === 'sys_hub_flow' ? 'flow_designer' : 'legacy_workflow';
2783
+ }
2784
+ else {
2785
+ throw new Error(`Flow not found with identifier: ${flow_sys_id}`);
2786
+ }
2787
+ }
2618
2788
  }
2619
2789
  testResults.flow_info = flowDetails.result;
2620
2790
  // Generate test data automatically
@@ -3088,6 +3258,62 @@ try {
3088
3258
  recommendations.push('📊 Set up performance baselines for future comparisons');
3089
3259
  return recommendations;
3090
3260
  }
3261
+ async findFlowByNameOrSysId(identifier) {
3262
+ try {
3263
+ // First try as sys_id in sys_hub_flow
3264
+ let result = await this.client.get(`/api/now/table/sys_hub_flow/${identifier}`);
3265
+ if (result.result) {
3266
+ result.result.sys_class_name = 'sys_hub_flow';
3267
+ return result.result;
3268
+ }
3269
+ }
3270
+ catch (error) {
3271
+ // Not a sys_id in sys_hub_flow
3272
+ }
3273
+ try {
3274
+ // Try as sys_id in wf_workflow
3275
+ let result = await this.client.get(`/api/now/table/wf_workflow/${identifier}`);
3276
+ if (result.result) {
3277
+ result.result.sys_class_name = 'wf_workflow';
3278
+ return result.result;
3279
+ }
3280
+ }
3281
+ catch (error) {
3282
+ // Not a sys_id in wf_workflow
3283
+ }
3284
+ // Search by name in both tables
3285
+ try {
3286
+ // Search in sys_hub_flow
3287
+ const modernFlows = await this.client.get('/api/now/table/sys_hub_flow', {
3288
+ sysparm_query: `name=${identifier}^ORnameSTARTSWITH${identifier}`,
3289
+ sysparm_limit: 1,
3290
+ sysparm_fields: 'name,description,active,type,status,sys_id,latest_snapshot'
3291
+ });
3292
+ if (modernFlows.result && modernFlows.result.length > 0) {
3293
+ modernFlows.result[0].sys_class_name = 'sys_hub_flow';
3294
+ return modernFlows.result[0];
3295
+ }
3296
+ }
3297
+ catch (error) {
3298
+ // Continue to legacy search
3299
+ }
3300
+ try {
3301
+ // Search in wf_workflow
3302
+ const legacyFlows = await this.client.get('/api/now/table/wf_workflow', {
3303
+ sysparm_query: `name=${identifier}^ORnameSTARTSWITH${identifier}`,
3304
+ sysparm_limit: 1,
3305
+ sysparm_fields: 'name,description,active,table,sys_id'
3306
+ });
3307
+ if (legacyFlows.result && legacyFlows.result.length > 0) {
3308
+ legacyFlows.result[0].sys_class_name = 'wf_workflow';
3309
+ return legacyFlows.result[0];
3310
+ }
3311
+ }
3312
+ catch (error) {
3313
+ // No results found
3314
+ }
3315
+ return null;
3316
+ }
3091
3317
  async start() {
3092
3318
  const transport = new stdio_js_1.StdioServerTransport();
3093
3319
  await this.server.connect(transport);