snow-flow 1.3.22 → 1.3.24
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/base-mcp-server.js +183 -15
- package/dist/mcp/servicenow-deployment-mcp.js +258 -9
- package/dist/mcp/servicenow-flow-composer-mcp.js +218 -32
- package/dist/mcp/servicenow-intelligent-mcp.js +354 -25
- package/dist/mcp/servicenow-security-compliance-mcp-refactored.js +9 -9
- package/dist/memory/memory-client.js +30 -2
- package/dist/memory/memory-operations.js +63 -11
- package/dist/memory/memory-system.js +89 -17
- package/dist/utils/servicenow-client.js +178 -48
- package/dist/utils/xml-first-flow-generator.js +24 -2
- package/dist/version.js +1 -1
- package/npm-publish-summary.txt +35 -0
- package/package.json +1 -1
|
@@ -331,6 +331,7 @@ class ServiceNowFlowComposerMCP {
|
|
|
331
331
|
console.log('🧠 Generated flow definition:', JSON.stringify(flowDefinition, null, 2));
|
|
332
332
|
// 🧠 STEP 5: Deploy using XML-first approach for maximum reliability
|
|
333
333
|
let deploymentResult = null;
|
|
334
|
+
let xmlResult = null; // 🔴 FIX: Declare outside try block to avoid scope issues
|
|
334
335
|
if (args.deploy_immediately !== false) {
|
|
335
336
|
console.log('🚀 DEPLOYING flow using XML-first approach...');
|
|
336
337
|
try {
|
|
@@ -348,21 +349,30 @@ class ServiceNowFlowComposerMCP {
|
|
|
348
349
|
accessible_from: 'package_private'
|
|
349
350
|
};
|
|
350
351
|
// Generate production-ready XML
|
|
351
|
-
|
|
352
|
+
xmlResult = generateProductionFlowXML(xmlFlowDef);
|
|
352
353
|
console.log('✅ XML generated:', xmlResult.filePath);
|
|
353
|
-
// Auto-deploy with
|
|
354
|
+
// 🔴 CRITICAL FIX: Auto-deploy with INTEGRATED verification
|
|
355
|
+
// SNOW-001: Verification is now MANDATORY within each deployment strategy
|
|
354
356
|
const deployResult = await this.deployWithFallback(xmlResult.filePath, flowDefinition);
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
throw new Error('Flow deployment claimed success but flow not found in ServiceNow');
|
|
359
|
-
}
|
|
357
|
+
// 🔴 FIXED: No duplicate verification needed - it's integrated into deployment strategies
|
|
358
|
+
// deployResult.verification contains the comprehensive verification results
|
|
359
|
+
// Success with integrated verification
|
|
360
360
|
deploymentResult = {
|
|
361
361
|
success: true,
|
|
362
362
|
method: deployResult.strategy,
|
|
363
363
|
xml_file: xmlResult.filePath,
|
|
364
364
|
message: `✅ Flow deployed via ${deployResult.strategy} and verified in ServiceNow!`,
|
|
365
|
-
flow_sys_id:
|
|
365
|
+
flow_sys_id: deployResult.verification.sys_id,
|
|
366
|
+
flow_url: deployResult.verification.url,
|
|
367
|
+
verification_score: deployResult.verification.completeness_score,
|
|
368
|
+
verification_details: {
|
|
369
|
+
has_flow: true,
|
|
370
|
+
has_snapshot: deployResult.verification.has_snapshot,
|
|
371
|
+
has_trigger: deployResult.verification.has_trigger,
|
|
372
|
+
attempts_needed: deployResult.verification.verification_attempt,
|
|
373
|
+
deployment_verified: deployResult.deployment_verified
|
|
374
|
+
},
|
|
375
|
+
snow_001_fix: 'Deployment includes mandatory verification - no false positives possible'
|
|
366
376
|
};
|
|
367
377
|
}
|
|
368
378
|
catch (xmlError) {
|
|
@@ -382,7 +392,7 @@ class ServiceNowFlowComposerMCP {
|
|
|
382
392
|
deploymentResult = {
|
|
383
393
|
success: false,
|
|
384
394
|
error: errorDetails,
|
|
385
|
-
xml_generated:
|
|
395
|
+
xml_generated: xmlResult !== null, // 🔴 FIX: Check if XML was actually generated
|
|
386
396
|
xml_path: xmlResult?.filePath,
|
|
387
397
|
deployment_failed: true,
|
|
388
398
|
manual_steps: this.generateManualImportGuide(xmlResult?.filePath || ''),
|
|
@@ -1987,31 +1997,137 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
|
|
|
1987
1997
|
return mapping[activityType.toLowerCase()] || 'script';
|
|
1988
1998
|
}
|
|
1989
1999
|
/**
|
|
1990
|
-
*
|
|
2000
|
+
* Comprehensive flow verification with retry logic
|
|
1991
2001
|
*/
|
|
1992
2002
|
async verifyFlowInServiceNow(flowName) {
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
+
const maxRetries = 5;
|
|
2004
|
+
const baseDelay = 2000; // Start with 2 seconds
|
|
2005
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
2006
|
+
try {
|
|
2007
|
+
// Progressive delay - ServiceNow needs time to process
|
|
2008
|
+
if (attempt > 1) {
|
|
2009
|
+
const delay = baseDelay * attempt; // 2s, 4s, 6s, 8s, 10s
|
|
2010
|
+
this.logger.info(`Waiting ${delay}ms before verification attempt ${attempt}/${maxRetries}`);
|
|
2011
|
+
await this.sleep(delay);
|
|
2012
|
+
}
|
|
2013
|
+
const ServiceNowClient = (await Promise.resolve().then(() => __importStar(require('../utils/servicenow-client.js')))).ServiceNowClient;
|
|
2014
|
+
const client = new ServiceNowClient();
|
|
2015
|
+
// Multi-table verification for comprehensive check
|
|
2016
|
+
const verificationPromises = [
|
|
2017
|
+
// Check main flow table
|
|
2018
|
+
client.makeRequest({
|
|
2019
|
+
method: 'GET',
|
|
2020
|
+
url: '/api/now/table/sys_hub_flow',
|
|
2021
|
+
params: {
|
|
2022
|
+
sysparm_query: `name=${flowName}^ORsys_name=${flowName}`,
|
|
2023
|
+
sysparm_fields: 'sys_id,name,sys_name,active,table,description',
|
|
2024
|
+
sysparm_limit: 5
|
|
2025
|
+
}
|
|
2026
|
+
}),
|
|
2027
|
+
// Check flow snapshots
|
|
2028
|
+
client.makeRequest({
|
|
2029
|
+
method: 'GET',
|
|
2030
|
+
url: '/api/now/table/sys_hub_flow_snapshot',
|
|
2031
|
+
params: {
|
|
2032
|
+
sysparm_query: `flow.name=${flowName}^ORflow.sys_name=${flowName}`,
|
|
2033
|
+
sysparm_fields: 'sys_id,flow,name,active',
|
|
2034
|
+
sysparm_limit: 5
|
|
2035
|
+
}
|
|
2036
|
+
}),
|
|
2037
|
+
// Check trigger instances
|
|
2038
|
+
client.makeRequest({
|
|
2039
|
+
method: 'GET',
|
|
2040
|
+
url: '/api/now/table/sys_hub_trigger_instance',
|
|
2041
|
+
params: {
|
|
2042
|
+
sysparm_query: `flow.name=${flowName}^ORflow.sys_name=${flowName}`,
|
|
2043
|
+
sysparm_fields: 'sys_id,flow,trigger_type',
|
|
2044
|
+
sysparm_limit: 5
|
|
2045
|
+
}
|
|
2046
|
+
})
|
|
2047
|
+
];
|
|
2048
|
+
const [flowCheck, snapshotCheck, triggerCheck] = await Promise.allSettled(verificationPromises);
|
|
2049
|
+
// Analyze results
|
|
2050
|
+
const flowExists = flowCheck.status === 'fulfilled' &&
|
|
2051
|
+
flowCheck.value.result &&
|
|
2052
|
+
flowCheck.value.result.length > 0;
|
|
2053
|
+
const snapshotExists = snapshotCheck.status === 'fulfilled' &&
|
|
2054
|
+
snapshotCheck.value.result &&
|
|
2055
|
+
snapshotCheck.value.result.length > 0;
|
|
2056
|
+
const triggerExists = triggerCheck.status === 'fulfilled' &&
|
|
2057
|
+
triggerCheck.value.result &&
|
|
2058
|
+
triggerCheck.value.result.length > 0;
|
|
2059
|
+
// Comprehensive verification result
|
|
2060
|
+
if (flowExists) {
|
|
2061
|
+
const flowData = flowCheck.value.result[0];
|
|
2062
|
+
return {
|
|
2063
|
+
verified: true,
|
|
2064
|
+
sys_id: flowData.sys_id,
|
|
2065
|
+
name: flowData.name || flowData.sys_name,
|
|
2066
|
+
active: flowData.active,
|
|
2067
|
+
table: flowData.table,
|
|
2068
|
+
has_snapshot: snapshotExists,
|
|
2069
|
+
has_trigger: triggerExists,
|
|
2070
|
+
verification_attempt: attempt,
|
|
2071
|
+
verification_method: 'multi_table_comprehensive',
|
|
2072
|
+
completeness_score: (flowExists ? 1 : 0) + (snapshotExists ? 1 : 0) + (triggerExists ? 1 : 0),
|
|
2073
|
+
url: `https://${await this.getInstanceUrl()}/flow-designer/flow/${flowData.sys_id}`
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
// Log partial results for debugging
|
|
2077
|
+
this.logger.warn(`Verification attempt ${attempt}: Flow=${flowExists}, Snapshot=${snapshotExists}, Trigger=${triggerExists}`);
|
|
2078
|
+
// If this is the last attempt, return detailed failure info
|
|
2079
|
+
if (attempt === maxRetries) {
|
|
2080
|
+
return {
|
|
2081
|
+
verified: false,
|
|
2082
|
+
reason: 'Flow not found after comprehensive verification',
|
|
2083
|
+
attempts: maxRetries,
|
|
2084
|
+
partial_results: {
|
|
2085
|
+
flow_found: flowExists,
|
|
2086
|
+
snapshot_found: snapshotExists,
|
|
2087
|
+
trigger_found: triggerExists
|
|
2088
|
+
},
|
|
2089
|
+
search_query: `name=${flowName}`,
|
|
2090
|
+
recommendation: 'Check ServiceNow Flow Designer manually or verify deployment was successful'
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
catch (error) {
|
|
2095
|
+
this.logger.warn(`Verification attempt ${attempt} failed:`, error);
|
|
2096
|
+
// If this is the last attempt, include error details
|
|
2097
|
+
if (attempt === maxRetries) {
|
|
2098
|
+
return {
|
|
2099
|
+
verified: false,
|
|
2100
|
+
reason: 'Verification failed due to error',
|
|
2101
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2102
|
+
attempts: maxRetries
|
|
2103
|
+
};
|
|
2003
2104
|
}
|
|
2004
|
-
});
|
|
2005
|
-
if (flowCheck.result && flowCheck.result.length > 0) {
|
|
2006
|
-
return flowCheck.result[0];
|
|
2007
2105
|
}
|
|
2008
|
-
return null;
|
|
2009
2106
|
}
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2107
|
+
return {
|
|
2108
|
+
verified: false,
|
|
2109
|
+
reason: 'Max verification attempts exceeded',
|
|
2110
|
+
attempts: maxRetries
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Helper to get instance URL for flow links
|
|
2115
|
+
*/
|
|
2116
|
+
async getInstanceUrl() {
|
|
2117
|
+
try {
|
|
2118
|
+
const credentials = await this.oauth.loadCredentials();
|
|
2119
|
+
return credentials?.instance || 'your-instance.service-now.com';
|
|
2120
|
+
}
|
|
2121
|
+
catch {
|
|
2122
|
+
return 'your-instance.service-now.com';
|
|
2013
2123
|
}
|
|
2014
2124
|
}
|
|
2125
|
+
/**
|
|
2126
|
+
* Sleep utility
|
|
2127
|
+
*/
|
|
2128
|
+
sleep(ms) {
|
|
2129
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
2130
|
+
}
|
|
2015
2131
|
/**
|
|
2016
2132
|
* Generate manual import guide for failed deployments
|
|
2017
2133
|
*/
|
|
@@ -2039,17 +2155,19 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
|
|
|
2039
2155
|
- Check "My Flows" for your new flow`;
|
|
2040
2156
|
}
|
|
2041
2157
|
/**
|
|
2042
|
-
* Deploy with fallback strategies
|
|
2158
|
+
* Deploy with fallback strategies + MANDATORY VERIFICATION
|
|
2159
|
+
* 🔴 CRITICAL FIX: SNOW-001 Silent Deployment Failures
|
|
2160
|
+
* Each strategy now MUST verify that the flow actually exists before claiming success
|
|
2043
2161
|
*/
|
|
2044
2162
|
async deployWithFallback(xmlFilePath, flowDefinition) {
|
|
2045
2163
|
const strategies = [
|
|
2046
2164
|
{
|
|
2047
2165
|
name: 'XML Remote Update Set',
|
|
2048
|
-
fn: async () => await this.
|
|
2166
|
+
fn: async () => await this.deployXMLToServiceNowWithVerification(xmlFilePath, flowDefinition.name)
|
|
2049
2167
|
},
|
|
2050
2168
|
{
|
|
2051
2169
|
name: 'Direct Table API',
|
|
2052
|
-
fn: async () => await this.
|
|
2170
|
+
fn: async () => await this.deployViaTableAPIWithVerification(flowDefinition)
|
|
2053
2171
|
}
|
|
2054
2172
|
];
|
|
2055
2173
|
let lastError;
|
|
@@ -2057,7 +2175,17 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
|
|
|
2057
2175
|
try {
|
|
2058
2176
|
this.logger.info(`Trying deployment strategy: ${strategy.name}`);
|
|
2059
2177
|
const result = await strategy.fn();
|
|
2060
|
-
|
|
2178
|
+
// 🔴 CRITICAL: Strategy can only return if it includes verification proof
|
|
2179
|
+
if (!result.verification || !result.verification.verified) {
|
|
2180
|
+
throw new Error(`${strategy.name} completed but verification failed: ${result.verification?.reason || 'Unknown verification failure'}`);
|
|
2181
|
+
}
|
|
2182
|
+
return {
|
|
2183
|
+
success: true,
|
|
2184
|
+
strategy: strategy.name,
|
|
2185
|
+
result,
|
|
2186
|
+
verification: result.verification,
|
|
2187
|
+
deployment_verified: true
|
|
2188
|
+
};
|
|
2061
2189
|
}
|
|
2062
2190
|
catch (error) {
|
|
2063
2191
|
this.logger.warn(`Strategy ${strategy.name} failed:`, error);
|
|
@@ -2067,7 +2195,65 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
|
|
|
2067
2195
|
throw lastError || new Error('All deployment strategies failed');
|
|
2068
2196
|
}
|
|
2069
2197
|
/**
|
|
2070
|
-
* Deploy
|
|
2198
|
+
* 🔴 CRITICAL FIX: Deploy XML with MANDATORY verification
|
|
2199
|
+
* SNOW-001: Prevents false positive where XML import succeeds but flow doesn't exist
|
|
2200
|
+
*/
|
|
2201
|
+
async deployXMLToServiceNowWithVerification(xmlFilePath, flowName) {
|
|
2202
|
+
this.logger.info(`🔴 CRITICAL FIX: XML deployment with mandatory verification for: ${flowName}`);
|
|
2203
|
+
// Step 1: Deploy the XML (existing logic)
|
|
2204
|
+
await this.deployXMLToServiceNow(xmlFilePath);
|
|
2205
|
+
// Step 2: MANDATORY verification - wait for ServiceNow to process
|
|
2206
|
+
this.logger.info('🔍 Starting mandatory post-deployment verification...');
|
|
2207
|
+
const verification = await this.verifyFlowInServiceNow(flowName);
|
|
2208
|
+
if (!verification.verified) {
|
|
2209
|
+
// 🔴 CRITICAL: XML deployment succeeded but flow doesn't exist
|
|
2210
|
+
const errorMsg = `🔴 CRITICAL: XML deployment appeared to succeed but flow verification failed: ${verification.reason}`;
|
|
2211
|
+
this.logger.error('SNOW-001 detected: Silent deployment failure', {
|
|
2212
|
+
xmlFilePath,
|
|
2213
|
+
flowName,
|
|
2214
|
+
verificationResult: verification,
|
|
2215
|
+
issue: 'XML import/commit succeeded but no flow created'
|
|
2216
|
+
});
|
|
2217
|
+
throw new Error(errorMsg);
|
|
2218
|
+
}
|
|
2219
|
+
this.logger.info(`✅ XML deployment verified successfully: ${flowName} found with sys_id ${verification.sys_id}`);
|
|
2220
|
+
return {
|
|
2221
|
+
deployment_method: 'XML Remote Update Set',
|
|
2222
|
+
xml_file: xmlFilePath,
|
|
2223
|
+
verification: verification,
|
|
2224
|
+
success_message: `XML deployment completed and verified: Flow ${flowName} is live in ServiceNow`
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
/**
|
|
2228
|
+
* 🔴 CRITICAL FIX: Deploy via Table API with MANDATORY verification
|
|
2229
|
+
* SNOW-001: Prevents false positive where API call succeeds but flow doesn't exist
|
|
2230
|
+
*/
|
|
2231
|
+
async deployViaTableAPIWithVerification(flowDefinition) {
|
|
2232
|
+
this.logger.info(`🔴 CRITICAL FIX: Table API deployment with mandatory verification for: ${flowDefinition.name}`);
|
|
2233
|
+
// Step 1: Deploy via Table API (existing logic)
|
|
2234
|
+
await this.deployViaTableAPI(flowDefinition);
|
|
2235
|
+
// Step 2: MANDATORY verification
|
|
2236
|
+
this.logger.info('🔍 Starting mandatory post-deployment verification...');
|
|
2237
|
+
const verification = await this.verifyFlowInServiceNow(flowDefinition.name);
|
|
2238
|
+
if (!verification.verified) {
|
|
2239
|
+
// 🔴 CRITICAL: Table API deployment succeeded but flow doesn't exist
|
|
2240
|
+
const errorMsg = `🔴 CRITICAL: Table API deployment appeared to succeed but flow verification failed: ${verification.reason}`;
|
|
2241
|
+
this.logger.error('SNOW-001 detected: Silent deployment failure', {
|
|
2242
|
+
flowName: flowDefinition.name,
|
|
2243
|
+
verificationResult: verification,
|
|
2244
|
+
issue: 'Table API call succeeded but no flow created'
|
|
2245
|
+
});
|
|
2246
|
+
throw new Error(errorMsg);
|
|
2247
|
+
}
|
|
2248
|
+
this.logger.info(`✅ Table API deployment verified successfully: ${flowDefinition.name} found with sys_id ${verification.sys_id}`);
|
|
2249
|
+
return {
|
|
2250
|
+
deployment_method: 'Direct Table API',
|
|
2251
|
+
verification: verification,
|
|
2252
|
+
success_message: `Table API deployment completed and verified: Flow ${flowDefinition.name} is live in ServiceNow`
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Deploy via direct table API (LEGACY METHOD - used by new verified method)
|
|
2071
2257
|
*/
|
|
2072
2258
|
async deployViaTableAPI(flowDefinition) {
|
|
2073
2259
|
const ServiceNowClient = (await Promise.resolve().then(() => __importStar(require('../utils/servicenow-client.js')))).ServiceNowClient;
|