snow-flow 10.0.1-dev.443 → 10.0.1-dev.445
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
|
@@ -311,6 +311,216 @@ async function addActionViaGraphQL(
|
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
// ── FLOW LOGIC (If/Else, For Each, etc.) ─────────────────────────────
|
|
315
|
+
|
|
316
|
+
async function addFlowLogicViaGraphQL(
|
|
317
|
+
client: any,
|
|
318
|
+
flowId: string,
|
|
319
|
+
logicType: string,
|
|
320
|
+
inputs?: Record<string, string>,
|
|
321
|
+
order?: number,
|
|
322
|
+
parentUiId?: string
|
|
323
|
+
): Promise<{ success: boolean; logicId?: string; steps?: any; error?: string }> {
|
|
324
|
+
const steps: any = {};
|
|
325
|
+
|
|
326
|
+
// Dynamically look up flow logic definition in sys_hub_flow_logic_definition
|
|
327
|
+
let defId: string | null = null;
|
|
328
|
+
let defName = '';
|
|
329
|
+
let defType = logicType;
|
|
330
|
+
// Try exact match on type (IF, FOR_EACH, DO_UNTIL, SWITCH), then name
|
|
331
|
+
for (const field of ['type', 'name']) {
|
|
332
|
+
if (defId) break;
|
|
333
|
+
try {
|
|
334
|
+
const resp = await client.get('/api/now/table/sys_hub_flow_logic_definition', {
|
|
335
|
+
params: { sysparm_query: field + '=' + logicType, sysparm_fields: 'sys_id,type,name,description', sysparm_limit: 1 }
|
|
336
|
+
});
|
|
337
|
+
const found = resp.data.result?.[0];
|
|
338
|
+
if (found?.sys_id) {
|
|
339
|
+
defId = found.sys_id;
|
|
340
|
+
defName = found.name || logicType;
|
|
341
|
+
defType = found.type || logicType;
|
|
342
|
+
steps.def_lookup = { id: found.sys_id, type: found.type, name: found.name, matched: field + '=' + logicType };
|
|
343
|
+
}
|
|
344
|
+
} catch (_) {}
|
|
345
|
+
}
|
|
346
|
+
// Fallback: LIKE search
|
|
347
|
+
if (!defId) {
|
|
348
|
+
try {
|
|
349
|
+
const resp = await client.get('/api/now/table/sys_hub_flow_logic_definition', {
|
|
350
|
+
params: {
|
|
351
|
+
sysparm_query: 'typeLIKE' + logicType + '^ORnameLIKE' + logicType,
|
|
352
|
+
sysparm_fields: 'sys_id,type,name,description', sysparm_limit: 5
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
const results = resp.data.result || [];
|
|
356
|
+
steps.def_lookup_fallback_candidates = results.map((r: any) => ({ sys_id: r.sys_id, type: r.type, name: r.name }));
|
|
357
|
+
if (results[0]?.sys_id) {
|
|
358
|
+
defId = results[0].sys_id;
|
|
359
|
+
defName = results[0].name || logicType;
|
|
360
|
+
defType = results[0].type || logicType;
|
|
361
|
+
steps.def_lookup = { id: results[0].sys_id, type: results[0].type, name: results[0].name, matched: 'LIKE ' + logicType };
|
|
362
|
+
}
|
|
363
|
+
} catch (_) {}
|
|
364
|
+
}
|
|
365
|
+
if (!defId) return { success: false, error: 'Flow logic definition not found for: ' + logicType, steps };
|
|
366
|
+
|
|
367
|
+
const uuid = generateUUID();
|
|
368
|
+
const logicResponseFields = 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
|
|
369
|
+
try {
|
|
370
|
+
const result = await executeFlowPatchMutation(client, {
|
|
371
|
+
flowId: flowId,
|
|
372
|
+
flowLogics: {
|
|
373
|
+
insert: [{
|
|
374
|
+
order: String(order || 1),
|
|
375
|
+
uiUniqueIdentifier: uuid,
|
|
376
|
+
parent: '',
|
|
377
|
+
metadata: '{"predicates":[]}',
|
|
378
|
+
flowSysId: flowId,
|
|
379
|
+
generationSource: '',
|
|
380
|
+
definitionId: defId,
|
|
381
|
+
type: 'flowlogic',
|
|
382
|
+
parentUiId: parentUiId || '',
|
|
383
|
+
inputs: []
|
|
384
|
+
}]
|
|
385
|
+
}
|
|
386
|
+
}, logicResponseFields);
|
|
387
|
+
|
|
388
|
+
const logicId = result?.flowLogics?.inserts?.[0]?.sysId;
|
|
389
|
+
steps.insert = { success: !!logicId, logicId, uuid };
|
|
390
|
+
if (!logicId) return { success: false, steps, error: 'GraphQL flow logic INSERT returned no ID' };
|
|
391
|
+
|
|
392
|
+
// Update with input values if provided
|
|
393
|
+
if (inputs && Object.keys(inputs).length > 0) {
|
|
394
|
+
const updateInputs = Object.entries(inputs).map(([name, value]) => ({
|
|
395
|
+
name,
|
|
396
|
+
value: { schemaless: false, schemalessValue: '', value: String(value) }
|
|
397
|
+
}));
|
|
398
|
+
try {
|
|
399
|
+
await executeFlowPatchMutation(client, {
|
|
400
|
+
flowId: flowId,
|
|
401
|
+
flowLogics: { update: [{ uiUniqueIdentifier: uuid, type: 'flowlogic', inputs: updateInputs }] }
|
|
402
|
+
}, logicResponseFields);
|
|
403
|
+
steps.value_update = { success: true, inputs: updateInputs.map(i => i.name) };
|
|
404
|
+
} catch (e: any) {
|
|
405
|
+
steps.value_update = { success: false, error: e.message };
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return { success: true, logicId, steps };
|
|
410
|
+
} catch (e: any) {
|
|
411
|
+
steps.insert = { success: false, error: e.message };
|
|
412
|
+
return { success: false, steps, error: 'GraphQL flow logic INSERT failed: ' + e.message };
|
|
413
|
+
}
|
|
414
|
+
}
|
|
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
|
+
|
|
314
524
|
async function createFlowViaProcessFlowAPI(
|
|
315
525
|
client: any,
|
|
316
526
|
params: {
|
|
@@ -419,8 +629,8 @@ export const toolDefinition: MCPToolDefinition = {
|
|
|
419
629
|
properties: {
|
|
420
630
|
action: {
|
|
421
631
|
type: 'string',
|
|
422
|
-
enum: ['create', 'create_subflow', 'list', 'get', 'update', 'activate', 'deactivate', 'delete', 'publish', 'add_trigger', 'update_trigger', 'add_action'],
|
|
423
|
-
description: 'Action to perform. Use add_trigger/add_action to add
|
|
632
|
+
enum: ['create', 'create_subflow', 'list', 'get', 'update', 'activate', 'deactivate', 'delete', 'publish', 'add_trigger', 'update_trigger', 'add_action', 'add_flow_logic', 'add_subflow'],
|
|
633
|
+
description: 'Action to perform. Use add_trigger/add_action/add_flow_logic/add_subflow to add elements, update_trigger to change an existing trigger. Flow logic types: IF, FOR_EACH, DO_UNTIL, SWITCH. add_subflow calls an existing subflow as a step.'
|
|
424
634
|
},
|
|
425
635
|
|
|
426
636
|
flow_id: {
|
|
@@ -506,6 +716,22 @@ export const toolDefinition: MCPToolDefinition = {
|
|
|
506
716
|
description: 'Activate flow after creation (default: true)',
|
|
507
717
|
default: true
|
|
508
718
|
},
|
|
719
|
+
logic_type: {
|
|
720
|
+
type: 'string',
|
|
721
|
+
description: 'Flow logic type for add_flow_logic. Looked up dynamically in sys_hub_flow_logic_definition. Common values: IF, FOR_EACH, DO_UNTIL, SWITCH'
|
|
722
|
+
},
|
|
723
|
+
logic_inputs: {
|
|
724
|
+
type: 'object',
|
|
725
|
+
description: 'Input values for the flow logic block (e.g. {condition: "expression", condition_name: "My Condition"})'
|
|
726
|
+
},
|
|
727
|
+
parent_ui_id: {
|
|
728
|
+
type: 'string',
|
|
729
|
+
description: 'Parent UI unique identifier for nesting elements inside flow logic blocks (e.g. placing actions/subflows inside an If block)'
|
|
730
|
+
},
|
|
731
|
+
subflow_id: {
|
|
732
|
+
type: 'string',
|
|
733
|
+
description: 'Subflow sys_id or name to call as a step (for add_subflow action). Looked up in sys_hub_flow where type=subflow.'
|
|
734
|
+
},
|
|
509
735
|
type: {
|
|
510
736
|
type: 'string',
|
|
511
737
|
enum: ['flow', 'subflow', 'all'],
|
|
@@ -1444,6 +1670,74 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
|
|
|
1444
1670
|
: createErrorResult(addActResult.error || 'Failed to add action');
|
|
1445
1671
|
}
|
|
1446
1672
|
|
|
1673
|
+
// ────────────────────────────────────────────────────────────────
|
|
1674
|
+
// ADD_FLOW_LOGIC — add If/Else, For Each, Do Until, Switch blocks
|
|
1675
|
+
// ────────────────────────────────────────────────────────────────
|
|
1676
|
+
case 'add_flow_logic': {
|
|
1677
|
+
if (!args.flow_id) {
|
|
1678
|
+
throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required for add_flow_logic');
|
|
1679
|
+
}
|
|
1680
|
+
if (!args.logic_type) {
|
|
1681
|
+
throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'logic_type is required for add_flow_logic (e.g. IF, FOR_EACH, DO_UNTIL, SWITCH)');
|
|
1682
|
+
}
|
|
1683
|
+
var addLogicFlowId = await resolveFlowId(client, args.flow_id);
|
|
1684
|
+
var addLogicType = args.logic_type;
|
|
1685
|
+
var addLogicInputs = args.logic_inputs || {};
|
|
1686
|
+
var addLogicOrder = args.order;
|
|
1687
|
+
var addLogicParentUiId = args.parent_ui_id || '';
|
|
1688
|
+
|
|
1689
|
+
var addLogicResult = await addFlowLogicViaGraphQL(client, addLogicFlowId, addLogicType, addLogicInputs, addLogicOrder, addLogicParentUiId);
|
|
1690
|
+
|
|
1691
|
+
var addLogicSummary = summary();
|
|
1692
|
+
if (addLogicResult.success) {
|
|
1693
|
+
addLogicSummary
|
|
1694
|
+
.success('Flow logic added via GraphQL')
|
|
1695
|
+
.field('Flow', addLogicFlowId)
|
|
1696
|
+
.field('Type', addLogicType)
|
|
1697
|
+
.field('Logic ID', addLogicResult.logicId || 'unknown');
|
|
1698
|
+
} else {
|
|
1699
|
+
addLogicSummary.error('Failed to add flow logic: ' + (addLogicResult.error || 'unknown'));
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
return addLogicResult.success
|
|
1703
|
+
? createSuccessResult({ action: 'add_flow_logic', ...addLogicResult }, {}, addLogicSummary.build())
|
|
1704
|
+
: createErrorResult(addLogicResult.error || 'Failed to add flow logic');
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
// ────────────────────────────────────────────────────────────────
|
|
1708
|
+
// ADD_SUBFLOW — call an existing subflow as a step in the flow
|
|
1709
|
+
// ────────────────────────────────────────────────────────────────
|
|
1710
|
+
case 'add_subflow': {
|
|
1711
|
+
if (!args.flow_id) {
|
|
1712
|
+
throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required for add_subflow');
|
|
1713
|
+
}
|
|
1714
|
+
if (!args.subflow_id) {
|
|
1715
|
+
throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'subflow_id is required for add_subflow (sys_id or name of the subflow to call)');
|
|
1716
|
+
}
|
|
1717
|
+
var addSubFlowId = await resolveFlowId(client, args.flow_id);
|
|
1718
|
+
var addSubSubflowId = args.subflow_id;
|
|
1719
|
+
var addSubInputs = args.action_inputs || args.inputs || {};
|
|
1720
|
+
var addSubOrder = args.order;
|
|
1721
|
+
var addSubParentUiId = args.parent_ui_id || '';
|
|
1722
|
+
|
|
1723
|
+
var addSubResult = await addSubflowCallViaGraphQL(client, addSubFlowId, addSubSubflowId, addSubInputs, addSubOrder, addSubParentUiId);
|
|
1724
|
+
|
|
1725
|
+
var addSubSummary = summary();
|
|
1726
|
+
if (addSubResult.success) {
|
|
1727
|
+
addSubSummary
|
|
1728
|
+
.success('Subflow call added via GraphQL')
|
|
1729
|
+
.field('Flow', addSubFlowId)
|
|
1730
|
+
.field('Subflow', addSubSubflowId)
|
|
1731
|
+
.field('Call ID', addSubResult.callId || 'unknown');
|
|
1732
|
+
} else {
|
|
1733
|
+
addSubSummary.error('Failed to add subflow call: ' + (addSubResult.error || 'unknown'));
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
return addSubResult.success
|
|
1737
|
+
? createSuccessResult({ action: 'add_subflow', ...addSubResult }, {}, addSubSummary.build())
|
|
1738
|
+
: createErrorResult(addSubResult.error || 'Failed to add subflow call');
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1447
1741
|
default:
|
|
1448
1742
|
throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'Unknown action: ' + action);
|
|
1449
1743
|
}
|