snow-flow-test 10.0.1-test.159 → 10.0.1-test.161
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/package.json
CHANGED
|
@@ -413,6 +413,169 @@ async function addFlowLogicViaGraphQL(
|
|
|
413
413
|
}
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
+
// ── SUBFLOW CALL (invoke a subflow as a step) ────────────────────────
|
|
417
|
+
|
|
418
|
+
async function addSubflowCallViaGraphQL(
|
|
419
|
+
client: any,
|
|
420
|
+
flowId: string,
|
|
421
|
+
subflowId: string,
|
|
422
|
+
inputs?: Record<string, string>,
|
|
423
|
+
order?: number,
|
|
424
|
+
parentUiId?: string
|
|
425
|
+
): Promise<{ success: boolean; callId?: string; steps?: any; error?: string }> {
|
|
426
|
+
const steps: any = {};
|
|
427
|
+
|
|
428
|
+
// Resolve subflow: look up by sys_id, name, or internal_name in sys_hub_flow
|
|
429
|
+
let subflowSysId = isSysId(subflowId) ? subflowId : null;
|
|
430
|
+
let subflowName = '';
|
|
431
|
+
if (!subflowSysId) {
|
|
432
|
+
for (const field of ['name', 'internal_name']) {
|
|
433
|
+
if (subflowSysId) break;
|
|
434
|
+
try {
|
|
435
|
+
const resp = await client.get('/api/now/table/sys_hub_flow', {
|
|
436
|
+
params: {
|
|
437
|
+
sysparm_query: field + '=' + subflowId + '^type=subflow',
|
|
438
|
+
sysparm_fields: 'sys_id,name,internal_name',
|
|
439
|
+
sysparm_limit: 1
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
const found = resp.data.result?.[0];
|
|
443
|
+
if (found?.sys_id) {
|
|
444
|
+
subflowSysId = found.sys_id;
|
|
445
|
+
subflowName = found.name || subflowId;
|
|
446
|
+
steps.subflow_lookup = { id: found.sys_id, name: found.name, internal_name: found.internal_name, matched: field + '=' + subflowId };
|
|
447
|
+
}
|
|
448
|
+
} catch (_) {}
|
|
449
|
+
}
|
|
450
|
+
// LIKE fallback
|
|
451
|
+
if (!subflowSysId) {
|
|
452
|
+
try {
|
|
453
|
+
const resp = await client.get('/api/now/table/sys_hub_flow', {
|
|
454
|
+
params: {
|
|
455
|
+
sysparm_query: 'nameLIKE' + subflowId + '^type=subflow',
|
|
456
|
+
sysparm_fields: 'sys_id,name,internal_name',
|
|
457
|
+
sysparm_limit: 5
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
const results = resp.data.result || [];
|
|
461
|
+
steps.subflow_lookup_candidates = results.map((r: any) => ({ sys_id: r.sys_id, name: r.name, internal_name: r.internal_name }));
|
|
462
|
+
if (results[0]?.sys_id) {
|
|
463
|
+
subflowSysId = results[0].sys_id;
|
|
464
|
+
subflowName = results[0].name || subflowId;
|
|
465
|
+
steps.subflow_lookup = { id: results[0].sys_id, name: results[0].name, matched: 'LIKE ' + subflowId };
|
|
466
|
+
}
|
|
467
|
+
} catch (_) {}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (!subflowSysId) return { success: false, error: 'Subflow not found: ' + subflowId, steps };
|
|
471
|
+
|
|
472
|
+
if (!subflowName) subflowName = subflowId;
|
|
473
|
+
|
|
474
|
+
const uuid = generateUUID();
|
|
475
|
+
const subflowResponseFields = 'subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
476
|
+
try {
|
|
477
|
+
const result = await executeFlowPatchMutation(client, {
|
|
478
|
+
flowId: flowId,
|
|
479
|
+
subflows: {
|
|
480
|
+
insert: [{
|
|
481
|
+
metadata: '{"predicates":[]}',
|
|
482
|
+
flowSysId: flowId,
|
|
483
|
+
generationSource: '',
|
|
484
|
+
name: subflowName,
|
|
485
|
+
order: String(order || 1),
|
|
486
|
+
parent: parentUiId || '',
|
|
487
|
+
subflowSysId: subflowSysId,
|
|
488
|
+
uiUniqueIdentifier: uuid,
|
|
489
|
+
type: 'subflow',
|
|
490
|
+
parentUiId: parentUiId || '',
|
|
491
|
+
inputs: []
|
|
492
|
+
}]
|
|
493
|
+
}
|
|
494
|
+
}, subflowResponseFields);
|
|
495
|
+
|
|
496
|
+
const callId = result?.subflows?.inserts?.[0]?.sysId;
|
|
497
|
+
steps.insert = { success: !!callId, callId, uuid };
|
|
498
|
+
if (!callId) return { success: false, steps, error: 'GraphQL subflow INSERT returned no ID' };
|
|
499
|
+
|
|
500
|
+
// Update with input values if provided
|
|
501
|
+
if (inputs && Object.keys(inputs).length > 0) {
|
|
502
|
+
const updateInputs = Object.entries(inputs).map(([name, value]) => ({
|
|
503
|
+
name,
|
|
504
|
+
value: { schemaless: false, schemalessValue: '', value: String(value) }
|
|
505
|
+
}));
|
|
506
|
+
try {
|
|
507
|
+
await executeFlowPatchMutation(client, {
|
|
508
|
+
flowId: flowId,
|
|
509
|
+
subflows: { update: [{ uiUniqueIdentifier: uuid, type: 'subflow', inputs: updateInputs }] }
|
|
510
|
+
}, subflowResponseFields);
|
|
511
|
+
steps.value_update = { success: true, inputs: updateInputs.map(i => i.name) };
|
|
512
|
+
} catch (e: any) {
|
|
513
|
+
steps.value_update = { success: false, error: e.message };
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return { success: true, callId, steps };
|
|
518
|
+
} catch (e: any) {
|
|
519
|
+
steps.insert = { success: false, error: e.message };
|
|
520
|
+
return { success: false, steps, error: 'GraphQL subflow INSERT failed: ' + e.message };
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// ── GENERIC UPDATE/DELETE for any flow element ───────────────────────
|
|
525
|
+
|
|
526
|
+
const elementGraphQLMap: Record<string, { key: string; type: string; responseFields: string }> = {
|
|
527
|
+
action: { key: 'actions', type: 'action', responseFields: 'actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' },
|
|
528
|
+
trigger: { key: 'triggerInstances', type: 'trigger', responseFields: 'triggerInstances { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' },
|
|
529
|
+
flowlogic: { key: 'flowLogics', type: 'flowlogic', responseFields: 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' },
|
|
530
|
+
subflow: { key: 'subflows', type: 'subflow', responseFields: 'subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' },
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
async function updateElementViaGraphQL(
|
|
534
|
+
client: any,
|
|
535
|
+
flowId: string,
|
|
536
|
+
elementType: string,
|
|
537
|
+
elementId: string,
|
|
538
|
+
inputs: Record<string, string>
|
|
539
|
+
): Promise<{ success: boolean; steps?: any; error?: string }> {
|
|
540
|
+
const config = elementGraphQLMap[elementType];
|
|
541
|
+
if (!config) return { success: false, error: 'Unknown element type: ' + elementType };
|
|
542
|
+
|
|
543
|
+
const updateInputs = Object.entries(inputs).map(([name, value]) => ({
|
|
544
|
+
name,
|
|
545
|
+
value: { schemaless: false, schemalessValue: '', value: String(value) }
|
|
546
|
+
}));
|
|
547
|
+
|
|
548
|
+
try {
|
|
549
|
+
await executeFlowPatchMutation(client, {
|
|
550
|
+
flowId,
|
|
551
|
+
[config.key]: { update: [{ uiUniqueIdentifier: elementId, type: config.type, inputs: updateInputs }] }
|
|
552
|
+
}, config.responseFields);
|
|
553
|
+
return { success: true, steps: { element: elementId, type: elementType, inputs: updateInputs.map(i => i.name) } };
|
|
554
|
+
} catch (e: any) {
|
|
555
|
+
return { success: false, error: e.message, steps: { element: elementId, type: elementType } };
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
async function deleteElementViaGraphQL(
|
|
560
|
+
client: any,
|
|
561
|
+
flowId: string,
|
|
562
|
+
elementType: string,
|
|
563
|
+
elementIds: string[]
|
|
564
|
+
): Promise<{ success: boolean; steps?: any; error?: string }> {
|
|
565
|
+
const config = elementGraphQLMap[elementType];
|
|
566
|
+
if (!config) return { success: false, error: 'Unknown element type: ' + elementType };
|
|
567
|
+
|
|
568
|
+
try {
|
|
569
|
+
await executeFlowPatchMutation(client, {
|
|
570
|
+
flowId,
|
|
571
|
+
[config.key]: { delete: elementIds }
|
|
572
|
+
}, config.responseFields);
|
|
573
|
+
return { success: true, steps: { deleted: elementIds, type: elementType } };
|
|
574
|
+
} catch (e: any) {
|
|
575
|
+
return { success: false, error: e.message, steps: { elementIds, type: elementType } };
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
416
579
|
async function createFlowViaProcessFlowAPI(
|
|
417
580
|
client: any,
|
|
418
581
|
params: {
|
|
@@ -521,8 +684,14 @@ export const toolDefinition: MCPToolDefinition = {
|
|
|
521
684
|
properties: {
|
|
522
685
|
action: {
|
|
523
686
|
type: 'string',
|
|
524
|
-
enum: [
|
|
525
|
-
|
|
687
|
+
enum: [
|
|
688
|
+
'create', 'create_subflow', 'list', 'get', 'update', 'activate', 'deactivate', 'delete', 'publish',
|
|
689
|
+
'add_trigger', 'update_trigger', 'delete_trigger',
|
|
690
|
+
'add_action', 'update_action', 'delete_action',
|
|
691
|
+
'add_flow_logic', 'update_flow_logic', 'delete_flow_logic',
|
|
692
|
+
'add_subflow', 'update_subflow', 'delete_subflow'
|
|
693
|
+
],
|
|
694
|
+
description: 'Action to perform. add_*/update_*/delete_* for triggers, actions, flow_logic, subflows. update_trigger replaces the trigger type. update_action/update_flow_logic/update_subflow change input values. delete_* removes elements by element_id.'
|
|
526
695
|
},
|
|
527
696
|
|
|
528
697
|
flow_id: {
|
|
@@ -618,7 +787,15 @@ export const toolDefinition: MCPToolDefinition = {
|
|
|
618
787
|
},
|
|
619
788
|
parent_ui_id: {
|
|
620
789
|
type: 'string',
|
|
621
|
-
description: 'Parent UI unique identifier for nesting flow logic blocks (e.g. placing actions inside an If block)'
|
|
790
|
+
description: 'Parent UI unique identifier for nesting elements inside flow logic blocks (e.g. placing actions/subflows inside an If block)'
|
|
791
|
+
},
|
|
792
|
+
subflow_id: {
|
|
793
|
+
type: 'string',
|
|
794
|
+
description: 'Subflow sys_id or name to call as a step (for add_subflow action). Looked up in sys_hub_flow where type=subflow.'
|
|
795
|
+
},
|
|
796
|
+
element_id: {
|
|
797
|
+
type: 'string',
|
|
798
|
+
description: 'Element sys_id or uiUniqueIdentifier for update_*/delete_* actions. For delete_* this can also be a comma-separated list of IDs.'
|
|
622
799
|
},
|
|
623
800
|
type: {
|
|
624
801
|
type: 'string',
|
|
@@ -1592,6 +1769,96 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1592
1769
|
: createErrorResult(addLogicResult.error || 'Failed to add flow logic');
|
|
1593
1770
|
}
|
|
1594
1771
|
|
|
1772
|
+
// ────────────────────────────────────────────────────────────────
|
|
1773
|
+
// ADD_SUBFLOW — call an existing subflow as a step in the flow
|
|
1774
|
+
// ────────────────────────────────────────────────────────────────
|
|
1775
|
+
case 'add_subflow': {
|
|
1776
|
+
if (!args.flow_id) {
|
|
1777
|
+
throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required for add_subflow');
|
|
1778
|
+
}
|
|
1779
|
+
if (!args.subflow_id) {
|
|
1780
|
+
throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'subflow_id is required for add_subflow (sys_id or name of the subflow to call)');
|
|
1781
|
+
}
|
|
1782
|
+
var addSubFlowId = await resolveFlowId(client, args.flow_id);
|
|
1783
|
+
var addSubSubflowId = args.subflow_id;
|
|
1784
|
+
var addSubInputs = args.action_inputs || args.inputs || {};
|
|
1785
|
+
var addSubOrder = args.order;
|
|
1786
|
+
var addSubParentUiId = args.parent_ui_id || '';
|
|
1787
|
+
|
|
1788
|
+
var addSubResult = await addSubflowCallViaGraphQL(client, addSubFlowId, addSubSubflowId, addSubInputs, addSubOrder, addSubParentUiId);
|
|
1789
|
+
|
|
1790
|
+
var addSubSummary = summary();
|
|
1791
|
+
if (addSubResult.success) {
|
|
1792
|
+
addSubSummary
|
|
1793
|
+
.success('Subflow call added via GraphQL')
|
|
1794
|
+
.field('Flow', addSubFlowId)
|
|
1795
|
+
.field('Subflow', addSubSubflowId)
|
|
1796
|
+
.field('Call ID', addSubResult.callId || 'unknown');
|
|
1797
|
+
} else {
|
|
1798
|
+
addSubSummary.error('Failed to add subflow call: ' + (addSubResult.error || 'unknown'));
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
return addSubResult.success
|
|
1802
|
+
? createSuccessResult({ action: 'add_subflow', ...addSubResult }, {}, addSubSummary.build())
|
|
1803
|
+
: createErrorResult(addSubResult.error || 'Failed to add subflow call');
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
// ────────────────────────────────────────────────────────────────
|
|
1807
|
+
// UPDATE_ACTION / UPDATE_FLOW_LOGIC / UPDATE_SUBFLOW
|
|
1808
|
+
// ────────────────────────────────────────────────────────────────
|
|
1809
|
+
case 'update_action':
|
|
1810
|
+
case 'update_flow_logic':
|
|
1811
|
+
case 'update_subflow': {
|
|
1812
|
+
if (!args.flow_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required');
|
|
1813
|
+
if (!args.element_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'element_id is required (sys_id or uiUniqueIdentifier of the element)');
|
|
1814
|
+
var updElemFlowId = await resolveFlowId(client, args.flow_id);
|
|
1815
|
+
var updElemType = action === 'update_action' ? 'action' : action === 'update_flow_logic' ? 'flowlogic' : 'subflow';
|
|
1816
|
+
var updElemInputs = args.action_inputs || args.logic_inputs || args.inputs || {};
|
|
1817
|
+
|
|
1818
|
+
var updElemResult = await updateElementViaGraphQL(client, updElemFlowId, updElemType, args.element_id, updElemInputs);
|
|
1819
|
+
|
|
1820
|
+
var updElemSummary = summary();
|
|
1821
|
+
if (updElemResult.success) {
|
|
1822
|
+
updElemSummary.success('Element updated').field('Type', updElemType).field('Element', args.element_id);
|
|
1823
|
+
} else {
|
|
1824
|
+
updElemSummary.error('Failed to update element: ' + (updElemResult.error || 'unknown'));
|
|
1825
|
+
}
|
|
1826
|
+
return updElemResult.success
|
|
1827
|
+
? createSuccessResult({ action, ...updElemResult }, {}, updElemSummary.build())
|
|
1828
|
+
: createErrorResult(updElemResult.error || 'Failed to update element');
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// ────────────────────────────────────────────────────────────────
|
|
1832
|
+
// DELETE_ACTION / DELETE_FLOW_LOGIC / DELETE_SUBFLOW / DELETE_TRIGGER
|
|
1833
|
+
// ────────────────────────────────────────────────────────────────
|
|
1834
|
+
case 'delete_action':
|
|
1835
|
+
case 'delete_flow_logic':
|
|
1836
|
+
case 'delete_subflow':
|
|
1837
|
+
case 'delete_trigger': {
|
|
1838
|
+
if (!args.flow_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required');
|
|
1839
|
+
if (!args.element_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'element_id is required (sys_id(s) to delete, comma-separated for multiple)');
|
|
1840
|
+
var delElemFlowId = await resolveFlowId(client, args.flow_id);
|
|
1841
|
+
var delElemType = action === 'delete_action' ? 'action'
|
|
1842
|
+
: action === 'delete_flow_logic' ? 'flowlogic'
|
|
1843
|
+
: action === 'delete_subflow' ? 'subflow'
|
|
1844
|
+
: 'trigger';
|
|
1845
|
+
var delElemIds = String(args.element_id).split(',').map((id: string) => id.trim());
|
|
1846
|
+
|
|
1847
|
+
// Map 'trigger' to the correct GraphQL key
|
|
1848
|
+
var delGraphQLType = delElemType === 'trigger' ? 'trigger' : delElemType;
|
|
1849
|
+
var delResult = await deleteElementViaGraphQL(client, delElemFlowId, delGraphQLType, delElemIds);
|
|
1850
|
+
|
|
1851
|
+
var delSummary = summary();
|
|
1852
|
+
if (delResult.success) {
|
|
1853
|
+
delSummary.success('Element(s) deleted').field('Type', delElemType).field('Deleted', delElemIds.join(', '));
|
|
1854
|
+
} else {
|
|
1855
|
+
delSummary.error('Failed to delete element: ' + (delResult.error || 'unknown'));
|
|
1856
|
+
}
|
|
1857
|
+
return delResult.success
|
|
1858
|
+
? createSuccessResult({ action, ...delResult }, {}, delSummary.build())
|
|
1859
|
+
: createErrorResult(delResult.error || 'Failed to delete element');
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1595
1862
|
default:
|
|
1596
1863
|
throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'Unknown action: ' + action);
|
|
1597
1864
|
}
|