@tenonhq/dovetail-servicenow 0.0.14 → 0.0.16
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/README.md +17 -0
- package/dist/cli.js +70 -0
- package/dist/flowDesigner/editActionType.d.ts +70 -0
- package/dist/flowDesigner/editActionType.js +196 -0
- package/dist/flowDesigner/index.d.ts +2 -0
- package/dist/flowDesigner/index.js +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/mcp/registry.d.ts +2 -0
- package/dist/mcp/registry.js +27 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -195,6 +195,12 @@ npx dove-sn test-flow --sys-id <sys_id> --execute --confirm --inputs '{...}' #
|
|
|
195
195
|
echo '{"rename":{"name":"New Name"},"patchStepInputs":[{"step":"Calculate SMS Send At","input":"send_rate","value":"5"}]}' > ops.json
|
|
196
196
|
npx dove-sn edit-flow --sys-id <sys_id> --from-json ops.json # dry-run (diff)
|
|
197
197
|
npx dove-sn edit-flow --sys-id <sys_id> --from-json ops.json --apply --update-set <id> # persist
|
|
198
|
+
|
|
199
|
+
# Edit a Custom Action Type's script and/or output variables, then republish (headless)
|
|
200
|
+
npx dove-sn edit-action --sys-id <action_type_sys_id> --scope <scope_sys_id> \
|
|
201
|
+
--patch-script "grabHashData::grabRecipients" # dry-run (diff)
|
|
202
|
+
npx dove-sn edit-action --sys-id <id> --scope <scope> --set-script ./script.js \
|
|
203
|
+
--merge-outputs ./output-var.json --apply --update-set <id> # persist + publish
|
|
198
204
|
```
|
|
199
205
|
|
|
200
206
|
`copy-flow` calls the Designer's own `POST /processflow/flow/{id}/copy` — a
|
|
@@ -228,6 +234,17 @@ Step-input persistence via the snapshot POST is verified for action types but is
|
|
|
228
234
|
and **warns** if a value didn't actually persist, so a no-op never reports as a
|
|
229
235
|
silent success.
|
|
230
236
|
|
|
237
|
+
`edit-action` edits a **published Custom Action Type** headlessly: it GETs the
|
|
238
|
+
model, fetches the steps from `.../{id}/step_instances` (the model GET returns
|
|
239
|
+
`steps:null`), patches the script step input (`--patch-script "<find>::<replace>"`
|
|
240
|
+
or `--set-script <file>`) and/or merges output-variable definitions
|
|
241
|
+
(`--merge-outputs <json>`, matched by `name`), grafts the steps back, and POSTs
|
|
242
|
+
`/snapshot` to recompile — the same snapshot POST `publishActionType` uses, which
|
|
243
|
+
persists step input values back to `sys_variable_value`. It deliberately avoids
|
|
244
|
+
the Designer's model PUT, whose client-side step transform is unsafe to
|
|
245
|
+
hand-reconstruct. Dry-run (diff) by default; `--apply` republishes, and
|
|
246
|
+
`--update-set <id>` pins the capture into a chosen update set.
|
|
247
|
+
|
|
231
248
|
`view-flow` reads `GET /api/now/processflow/flow/{id}` — the Designer's own model
|
|
232
249
|
endpoint — and prints the ordered, nesting-aware action + flow-logic step graph
|
|
233
250
|
plus the flow variables. This works for the integration user with plain basic
|
package/dist/cli.js
CHANGED
|
@@ -75,6 +75,7 @@ const publishFlow_1 = require("./flowDesigner/publishFlow");
|
|
|
75
75
|
const copyFlow_1 = require("./flowDesigner/copyFlow");
|
|
76
76
|
const createFlow_1 = require("./flowDesigner/createFlow");
|
|
77
77
|
const editFlow_1 = require("./flowDesigner/editFlow");
|
|
78
|
+
const editActionType_1 = require("./flowDesigner/editActionType");
|
|
78
79
|
const testFlow_1 = require("./flowDesigner/testFlow");
|
|
79
80
|
const flowDesigner_formatter_2 = require("./flowDesigner-formatter");
|
|
80
81
|
function parseArgs(argv) {
|
|
@@ -595,6 +596,72 @@ async function runEditFlow(flags) {
|
|
|
595
596
|
}
|
|
596
597
|
return 0;
|
|
597
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* dove-sn edit-action:
|
|
601
|
+
* --sys-id <sys_id> Required. sys_hub_action_type_definition sys_id.
|
|
602
|
+
* --scope <sys_id> Required. sysparm_transaction_scope (app scope sys_id).
|
|
603
|
+
* --patch-script "<find>::<replace>" Optional. Find/replace in the script step value.
|
|
604
|
+
* --set-script <path> Optional. Replace the script step value from a file.
|
|
605
|
+
* --merge-outputs <path> Optional. JSON file: an output-variable object/array to merge by name.
|
|
606
|
+
* --script-input <name> Optional. Input name holding the script (default: auto-detect).
|
|
607
|
+
* --update-set <sys_id> Optional. Capture the republish into this update set.
|
|
608
|
+
* --apply Optional. Republish (POST /snapshot). Omit for dry-run.
|
|
609
|
+
* --json Optional. Emit the structured EditActionTypeResult.
|
|
610
|
+
*
|
|
611
|
+
* Edits a published Custom Action Type's script and/or output variables and
|
|
612
|
+
* republishes through the snapshot POST. Dry-run (read-only) by default; --apply writes.
|
|
613
|
+
*/
|
|
614
|
+
async function runEditAction(flags) {
|
|
615
|
+
var sysId = flags["sys-id"] || flags.sysId;
|
|
616
|
+
var scope = flags.scope || flags.scopeSysId;
|
|
617
|
+
if (!sysId || !scope) {
|
|
618
|
+
process.stderr.write("edit-action: --sys-id <sys_id> and --scope <sys_id> are required\n");
|
|
619
|
+
return 1;
|
|
620
|
+
}
|
|
621
|
+
var ops = {};
|
|
622
|
+
if (flags["patch-script"]) {
|
|
623
|
+
var parts = String(flags["patch-script"]).split("::");
|
|
624
|
+
if (parts.length !== 2) {
|
|
625
|
+
process.stderr.write("edit-action: --patch-script must be \"<find>::<replace>\"\n");
|
|
626
|
+
return 1;
|
|
627
|
+
}
|
|
628
|
+
ops.patchScript = { find: parts[0], replace: parts[1] };
|
|
629
|
+
}
|
|
630
|
+
if (flags["set-script"]) {
|
|
631
|
+
ops.setScript = fs.readFileSync(flags["set-script"], "utf8");
|
|
632
|
+
}
|
|
633
|
+
if (flags["merge-outputs"]) {
|
|
634
|
+
var parsedOutputs = JSON.parse(fs.readFileSync(flags["merge-outputs"], "utf8"));
|
|
635
|
+
ops.mergeOutputs = Array.isArray(parsedOutputs) ? parsedOutputs : [parsedOutputs];
|
|
636
|
+
}
|
|
637
|
+
if (flags["script-input"]) {
|
|
638
|
+
ops.scriptInputName = flags["script-input"];
|
|
639
|
+
}
|
|
640
|
+
var result = await (0, editActionType_1.editActionType)({
|
|
641
|
+
client: (0, client_1.createClient)({}),
|
|
642
|
+
sysId: sysId,
|
|
643
|
+
scopeSysId: scope,
|
|
644
|
+
ops: ops,
|
|
645
|
+
apply: flags.apply === "true",
|
|
646
|
+
updateSetSysId: flags["update-set"] || flags.updateSetSysId
|
|
647
|
+
});
|
|
648
|
+
if (flags.json === "true") {
|
|
649
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
650
|
+
return 0;
|
|
651
|
+
}
|
|
652
|
+
process.stdout.write("[" + result.status + "] " + result.changes.length + " change(s)"
|
|
653
|
+
+ (result.snapshotSysId ? " — snapshot " + result.snapshotSysId : "") + "\n");
|
|
654
|
+
for (var ci = 0; ci < result.changes.length; ci += 1) {
|
|
655
|
+
process.stdout.write(" + " + result.changes[ci] + "\n");
|
|
656
|
+
}
|
|
657
|
+
for (var wi = 0; wi < result.warnings.length; wi += 1) {
|
|
658
|
+
process.stdout.write(" ! " + result.warnings[wi] + "\n");
|
|
659
|
+
}
|
|
660
|
+
if (result.status === "preview" && result.scriptAfter !== undefined && result.scriptAfter !== result.scriptBefore) {
|
|
661
|
+
process.stdout.write("\n--- script after ---\n" + result.scriptAfter + "\n");
|
|
662
|
+
}
|
|
663
|
+
return 0;
|
|
664
|
+
}
|
|
598
665
|
async function runMcp(flags) {
|
|
599
666
|
if (flags.smoke === "true") {
|
|
600
667
|
await (0, server_1.runSmoke)();
|
|
@@ -675,6 +742,9 @@ async function main() {
|
|
|
675
742
|
if (parsed.command === "edit-flow") {
|
|
676
743
|
return await runEditFlow(parsed.flags);
|
|
677
744
|
}
|
|
745
|
+
if (parsed.command === "edit-action") {
|
|
746
|
+
return await runEditAction(parsed.flags);
|
|
747
|
+
}
|
|
678
748
|
if (parsed.command === "create-view") {
|
|
679
749
|
await runCreateView(parsed.flags);
|
|
680
750
|
return 0;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit a published Custom Action Type's script and/or output variables, then
|
|
3
|
+
* republish — entirely headless, basic auth, no Designer UI.
|
|
4
|
+
*
|
|
5
|
+
* Reuses the validated publish contract (see publishActionType):
|
|
6
|
+
* GET /api/now/processflow/action/action_types/{id} -> model (outputs[], steps:null)
|
|
7
|
+
* GET /api/now/processflow/action/action_types/{id}/step_instances -> { steps:[...] } (carries the script)
|
|
8
|
+
* POST /api/now/processflow/action/action_types/{id}/snapshot -> 201; compiles the snapshot AND
|
|
9
|
+
* persists step input values back to
|
|
10
|
+
* sys_variable_value
|
|
11
|
+
*
|
|
12
|
+
* The GET model returns `steps: null` (the Designer assembles them client-side),
|
|
13
|
+
* so we fetch them from `/step_instances`, patch the script input there,
|
|
14
|
+
* optionally merge output-variable definitions into `model.outputs`, graft the
|
|
15
|
+
* steps back onto the model, and POST `/snapshot`. Because the snapshot POST
|
|
16
|
+
* persists step input values, the patched script lands without a separate write.
|
|
17
|
+
*
|
|
18
|
+
* Why route through `/snapshot` rather than the Designer's model PUT: the PUT
|
|
19
|
+
* applies a client-side model transform (it strips ~14 step fields and reshapes
|
|
20
|
+
* inputs) that is unsafe to hand-reconstruct and risks corrupting the action.
|
|
21
|
+
* The snapshot POST is the path publishActionType already proved end to end.
|
|
22
|
+
*
|
|
23
|
+
* Full write-up: docs/servicenow-flow-designer-headless-authoring.md.
|
|
24
|
+
*/
|
|
25
|
+
import type { ServiceNowClient } from "../client";
|
|
26
|
+
export interface EditActionTypeOps {
|
|
27
|
+
/** Replace every occurrence of `find` with `replace` inside the script step value. */
|
|
28
|
+
patchScript?: {
|
|
29
|
+
find: string;
|
|
30
|
+
replace: string;
|
|
31
|
+
};
|
|
32
|
+
/** Replace the script step value outright (wins over patchScript). */
|
|
33
|
+
setScript?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Output-variable definition objects to merge into `model.outputs`, matched by
|
|
36
|
+
* `name` (replaced in place, else appended). Supply the modeled output JSON —
|
|
37
|
+
* e.g. an `array.object` with `children` — typically lifted from a Designer
|
|
38
|
+
* capture of the same shape.
|
|
39
|
+
*/
|
|
40
|
+
mergeOutputs?: Array<Record<string, any>>;
|
|
41
|
+
/**
|
|
42
|
+
* The input `name` holding the script. Defaults to auto-detect: the first step
|
|
43
|
+
* input whose value reads like an action script.
|
|
44
|
+
*/
|
|
45
|
+
scriptInputName?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface EditActionTypeParams {
|
|
48
|
+
client: ServiceNowClient;
|
|
49
|
+
/** sys_id of the sys_hub_action_type_definition to edit. */
|
|
50
|
+
sysId: string;
|
|
51
|
+
/** Application scope sys_id, passed as sysparm_transaction_scope. */
|
|
52
|
+
scopeSysId: string;
|
|
53
|
+
ops: EditActionTypeOps;
|
|
54
|
+
/** When true, republish. When false/omitted, dry-run (no write). */
|
|
55
|
+
apply?: boolean;
|
|
56
|
+
/** Update set to capture the publish into (pins the REST session's active set first). */
|
|
57
|
+
updateSetSysId?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface EditActionTypeResult {
|
|
60
|
+
status: "preview" | "published";
|
|
61
|
+
changes: Array<string>;
|
|
62
|
+
warnings: Array<string>;
|
|
63
|
+
scriptBefore?: string;
|
|
64
|
+
scriptAfter?: string;
|
|
65
|
+
outputsMerged: Array<string>;
|
|
66
|
+
/** HTTP status of the snapshot POST (201 on success); only set when applied. */
|
|
67
|
+
httpStatus?: number;
|
|
68
|
+
snapshotSysId?: string;
|
|
69
|
+
}
|
|
70
|
+
export declare function editActionType(params: EditActionTypeParams): Promise<EditActionTypeResult>;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Edit a published Custom Action Type's script and/or output variables, then
|
|
4
|
+
* republish — entirely headless, basic auth, no Designer UI.
|
|
5
|
+
*
|
|
6
|
+
* Reuses the validated publish contract (see publishActionType):
|
|
7
|
+
* GET /api/now/processflow/action/action_types/{id} -> model (outputs[], steps:null)
|
|
8
|
+
* GET /api/now/processflow/action/action_types/{id}/step_instances -> { steps:[...] } (carries the script)
|
|
9
|
+
* POST /api/now/processflow/action/action_types/{id}/snapshot -> 201; compiles the snapshot AND
|
|
10
|
+
* persists step input values back to
|
|
11
|
+
* sys_variable_value
|
|
12
|
+
*
|
|
13
|
+
* The GET model returns `steps: null` (the Designer assembles them client-side),
|
|
14
|
+
* so we fetch them from `/step_instances`, patch the script input there,
|
|
15
|
+
* optionally merge output-variable definitions into `model.outputs`, graft the
|
|
16
|
+
* steps back onto the model, and POST `/snapshot`. Because the snapshot POST
|
|
17
|
+
* persists step input values, the patched script lands without a separate write.
|
|
18
|
+
*
|
|
19
|
+
* Why route through `/snapshot` rather than the Designer's model PUT: the PUT
|
|
20
|
+
* applies a client-side model transform (it strips ~14 step fields and reshapes
|
|
21
|
+
* inputs) that is unsafe to hand-reconstruct and risks corrupting the action.
|
|
22
|
+
* The snapshot POST is the path publishActionType already proved end to end.
|
|
23
|
+
*
|
|
24
|
+
* Full write-up: docs/servicenow-flow-designer-headless-authoring.md.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.editActionType = editActionType;
|
|
28
|
+
function actionTypePath(sysId, scopeSysId, suffix) {
|
|
29
|
+
return "/api/now/processflow/action/action_types/" + encodeURIComponent(sysId)
|
|
30
|
+
+ suffix
|
|
31
|
+
+ "?sysparm_transaction_scope=" + encodeURIComponent(scopeSysId);
|
|
32
|
+
}
|
|
33
|
+
/** Normalize the `{ result: ... }` envelope the processflow endpoints sometimes use. */
|
|
34
|
+
function unwrap(data) {
|
|
35
|
+
if (data && typeof data === "object" && data.result && typeof data.result === "object") {
|
|
36
|
+
return data.result;
|
|
37
|
+
}
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
var SCRIPT_SIGNATURE = /function execute|inputs\.|outputs\.|new\s+[A-Za-z_$]/;
|
|
41
|
+
/** Locate the step input holding the action script (by name, else by signature). */
|
|
42
|
+
function findScriptInput(steps, inputName) {
|
|
43
|
+
for (var s = 0; s < steps.length; s += 1) {
|
|
44
|
+
var inputs = (steps[s] && steps[s].inputs) || [];
|
|
45
|
+
for (var i = 0; i < inputs.length; i += 1) {
|
|
46
|
+
var inp = inputs[i];
|
|
47
|
+
if (!inp) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (inputName) {
|
|
51
|
+
if (inp.name === inputName) {
|
|
52
|
+
return { input: inp };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (typeof inp.value === "string" && inp.value.length > 30 && SCRIPT_SIGNATURE.test(inp.value)) {
|
|
56
|
+
return { input: inp };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
async function editActionType(params) {
|
|
63
|
+
var client = params.client;
|
|
64
|
+
var sysId = params.sysId;
|
|
65
|
+
var scopeSysId = params.scopeSysId;
|
|
66
|
+
var ops = params.ops || {};
|
|
67
|
+
if (!sysId) {
|
|
68
|
+
throw new Error("editActionType: sysId is required.");
|
|
69
|
+
}
|
|
70
|
+
if (!scopeSysId) {
|
|
71
|
+
throw new Error("editActionType: scopeSysId is required (sysparm_transaction_scope).");
|
|
72
|
+
}
|
|
73
|
+
if (!ops.patchScript && !ops.setScript && !(ops.mergeOutputs && ops.mergeOutputs.length > 0)) {
|
|
74
|
+
throw new Error("editActionType: no ops — supply patchScript, setScript, and/or mergeOutputs.");
|
|
75
|
+
}
|
|
76
|
+
var changes = [];
|
|
77
|
+
var warnings = [];
|
|
78
|
+
var outputsMerged = [];
|
|
79
|
+
// 1. GET the model (outputs[], steps:null).
|
|
80
|
+
var model = unwrap(await client.now.get(actionTypePath(sysId, scopeSysId, "")));
|
|
81
|
+
if (!model || typeof model !== "object") {
|
|
82
|
+
throw new Error("editActionType: unexpected GET model response for action type " + sysId);
|
|
83
|
+
}
|
|
84
|
+
// 2. GET the steps — the model's `steps` come back null.
|
|
85
|
+
var stepsResp = unwrap(await client.now.get(actionTypePath(sysId, scopeSysId, "/step_instances")));
|
|
86
|
+
var steps = stepsResp && Array.isArray(stepsResp.steps) ? stepsResp.steps : [];
|
|
87
|
+
if (steps.length === 0) {
|
|
88
|
+
throw new Error("editActionType: /step_instances returned no steps for action type " + sysId);
|
|
89
|
+
}
|
|
90
|
+
// 3. Patch the script step input.
|
|
91
|
+
var scriptBefore;
|
|
92
|
+
var scriptAfter;
|
|
93
|
+
if (ops.patchScript || ops.setScript) {
|
|
94
|
+
var hit = findScriptInput(steps, ops.scriptInputName);
|
|
95
|
+
if (!hit) {
|
|
96
|
+
warnings.push("script input not found" + (ops.scriptInputName ? " (name=" + ops.scriptInputName + ")" : " (auto-detect)"));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
scriptBefore = String(hit.input.value);
|
|
100
|
+
if (ops.setScript) {
|
|
101
|
+
scriptAfter = ops.setScript;
|
|
102
|
+
}
|
|
103
|
+
else if (ops.patchScript) {
|
|
104
|
+
if (scriptBefore.indexOf(ops.patchScript.find) === -1) {
|
|
105
|
+
warnings.push("patchScript.find not present in script: " + ops.patchScript.find);
|
|
106
|
+
scriptAfter = scriptBefore;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
scriptAfter = scriptBefore.split(ops.patchScript.find).join(ops.patchScript.replace);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (scriptAfter !== undefined && scriptAfter !== scriptBefore) {
|
|
113
|
+
hit.input.value = scriptAfter;
|
|
114
|
+
changes.push("script input '" + hit.input.name + "' patched");
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
warnings.push("script unchanged");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// 4. Merge output-variable definitions by name.
|
|
122
|
+
if (ops.mergeOutputs && ops.mergeOutputs.length > 0) {
|
|
123
|
+
if (!Array.isArray(model.outputs)) {
|
|
124
|
+
model.outputs = [];
|
|
125
|
+
}
|
|
126
|
+
for (var od = 0; od < ops.mergeOutputs.length; od += 1) {
|
|
127
|
+
var outDef = ops.mergeOutputs[od];
|
|
128
|
+
var outName = outDef && outDef.name;
|
|
129
|
+
if (!outName) {
|
|
130
|
+
warnings.push("mergeOutputs entry missing name — skipped");
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
var replaced = false;
|
|
134
|
+
for (var m = 0; m < model.outputs.length; m += 1) {
|
|
135
|
+
if (model.outputs[m] && model.outputs[m].name === outName) {
|
|
136
|
+
model.outputs[m] = outDef;
|
|
137
|
+
replaced = true;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!replaced) {
|
|
142
|
+
model.outputs.push(outDef);
|
|
143
|
+
}
|
|
144
|
+
outputsMerged.push(outName);
|
|
145
|
+
changes.push("output '" + outName + "' " + (replaced ? "replaced" : "added"));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// 5. Graft steps onto the model (remap each step's `action` to this sysId, per
|
|
149
|
+
// the publish contract).
|
|
150
|
+
model.steps = steps.map(function (step) {
|
|
151
|
+
var copy = {};
|
|
152
|
+
for (var key in step) {
|
|
153
|
+
if (Object.prototype.hasOwnProperty.call(step, key)) {
|
|
154
|
+
copy[key] = step[key];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
copy.action = sysId;
|
|
158
|
+
return copy;
|
|
159
|
+
});
|
|
160
|
+
if (!params.apply) {
|
|
161
|
+
return {
|
|
162
|
+
status: "preview",
|
|
163
|
+
changes: changes,
|
|
164
|
+
warnings: warnings,
|
|
165
|
+
scriptBefore: scriptBefore,
|
|
166
|
+
scriptAfter: scriptAfter,
|
|
167
|
+
outputsMerged: outputsMerged
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// 6. Apply: optionally pin the update set, then POST /snapshot (compiles the
|
|
171
|
+
// snapshot and persists step input values).
|
|
172
|
+
if (params.updateSetSysId) {
|
|
173
|
+
await client.claude.changeUpdateSet({ sysId: params.updateSetSysId });
|
|
174
|
+
}
|
|
175
|
+
var snapResp = unwrap(await client.now.post(actionTypePath(sysId, scopeSysId, "/snapshot"), model));
|
|
176
|
+
var snapshotSysId;
|
|
177
|
+
if (snapResp && typeof snapResp === "object") {
|
|
178
|
+
var snap = snapResp.latest_snapshot || snapResp.master_snapshot || snapResp.snapshot;
|
|
179
|
+
if (snap && typeof snap === "object") {
|
|
180
|
+
snapshotSysId = typeof snap.sys_id === "string" ? snap.sys_id : undefined;
|
|
181
|
+
}
|
|
182
|
+
else if (typeof snap === "string") {
|
|
183
|
+
snapshotSysId = snap;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
status: "published",
|
|
188
|
+
changes: changes,
|
|
189
|
+
warnings: warnings,
|
|
190
|
+
scriptBefore: scriptBefore,
|
|
191
|
+
scriptAfter: scriptAfter,
|
|
192
|
+
outputsMerged: outputsMerged,
|
|
193
|
+
httpStatus: 201,
|
|
194
|
+
snapshotSysId: snapshotSysId
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -16,6 +16,8 @@ export { triggerPublication } from "./triggerPublication";
|
|
|
16
16
|
export type { TriggerPublicationParams, TriggerPublicationResult } from "./triggerPublication";
|
|
17
17
|
export { publishActionType } from "./publishActionType";
|
|
18
18
|
export type { PublishActionTypeParams, PublishActionTypeResult } from "./publishActionType";
|
|
19
|
+
export { editActionType } from "./editActionType";
|
|
20
|
+
export type { EditActionTypeParams, EditActionTypeResult, EditActionTypeOps } from "./editActionType";
|
|
19
21
|
export { readFlow } from "./readFlow";
|
|
20
22
|
export type { ReadFlowParams, ReadFlowResult, FlowStep, FlowVariable } from "./readFlow";
|
|
21
23
|
export { readActionType } from "./readActionType";
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* functions land in Phase 1.C/D.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.SYSTEM_FIELDS_TO_STRIP = exports.assertSysId = exports.applyScope = exports.stripSystemFields = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = exports.editFlow = exports.buildPublishModel = exports.createFlow = exports.copyFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.publishActionType = exports.triggerPublication = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = void 0;
|
|
9
|
+
exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.SYSTEM_FIELDS_TO_STRIP = exports.assertSysId = exports.applyScope = exports.stripSystemFields = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = exports.editFlow = exports.buildPublishModel = exports.createFlow = exports.copyFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.editActionType = exports.publishActionType = exports.triggerPublication = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = void 0;
|
|
10
10
|
var listTemplates_1 = require("./listTemplates");
|
|
11
11
|
Object.defineProperty(exports, "listTemplates", { enumerable: true, get: function () { return listTemplates_1.listTemplates; } });
|
|
12
12
|
var verifyArtifact_1 = require("./verifyArtifact");
|
|
@@ -19,6 +19,8 @@ var triggerPublication_1 = require("./triggerPublication");
|
|
|
19
19
|
Object.defineProperty(exports, "triggerPublication", { enumerable: true, get: function () { return triggerPublication_1.triggerPublication; } });
|
|
20
20
|
var publishActionType_1 = require("./publishActionType");
|
|
21
21
|
Object.defineProperty(exports, "publishActionType", { enumerable: true, get: function () { return publishActionType_1.publishActionType; } });
|
|
22
|
+
var editActionType_1 = require("./editActionType");
|
|
23
|
+
Object.defineProperty(exports, "editActionType", { enumerable: true, get: function () { return editActionType_1.editActionType; } });
|
|
22
24
|
var readFlow_1 = require("./readFlow");
|
|
23
25
|
Object.defineProperty(exports, "readFlow", { enumerable: true, get: function () { return readFlow_1.readFlow; } });
|
|
24
26
|
var readActionType_1 = require("./readActionType");
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,6 @@ export { setFormLayout } from "./layout/formLayout";
|
|
|
14
14
|
export { setRelatedLists } from "./layout/relatedLists";
|
|
15
15
|
export { formatLayoutResult, formatCreateViewResult } from "./layout/formatter";
|
|
16
16
|
export { sincPlugin } from "./plugin";
|
|
17
|
-
export { listTemplates, verifyArtifact, cloneSubflow, cloneActionType, triggerPublication, publishActionType, readFlow, readActionType, publishFlow, copyFlow, createFlow, buildPublishModel, editFlow, testFlow, DEFAULT_RUN_FLOW_PATH, generateSysId, topoSort, executeWritePlan, WriteOrderError } from "./flowDesigner";
|
|
18
|
-
export type { TemplateRef, ListTemplatesParams, FlowKind, VerifyExpect, VerifyFound, VerifyFailure, VerifyReport, VerifyArtifactParams, CloneSubflowParams, CloneSubflowResult, CloneActionTypeParams, CloneActionTypeResult, TriggerPublicationParams, TriggerPublicationResult, PublishActionTypeParams, PublishActionTypeResult, ReadFlowParams, ReadFlowResult, FlowStep, FlowVariable, ReadActionTypeParams, ReadActionTypeResult, ActionIo, PublishFlowParams, PublishFlowResult, CopyFlowParams, CopyFlowResult, CreateFlowParams, CreateFlowResult, EditFlowParams, EditFlowResult, EditFlowOps, StepInputPatch, TestFlowParams, TestFlowResult, WriteOp, WriteOpResult } from "./flowDesigner";
|
|
17
|
+
export { listTemplates, verifyArtifact, cloneSubflow, cloneActionType, triggerPublication, publishActionType, editActionType, readFlow, readActionType, publishFlow, copyFlow, createFlow, buildPublishModel, editFlow, testFlow, DEFAULT_RUN_FLOW_PATH, generateSysId, topoSort, executeWritePlan, WriteOrderError } from "./flowDesigner";
|
|
18
|
+
export type { TemplateRef, ListTemplatesParams, FlowKind, VerifyExpect, VerifyFound, VerifyFailure, VerifyReport, VerifyArtifactParams, CloneSubflowParams, CloneSubflowResult, CloneActionTypeParams, CloneActionTypeResult, TriggerPublicationParams, TriggerPublicationResult, PublishActionTypeParams, PublishActionTypeResult, EditActionTypeParams, EditActionTypeResult, EditActionTypeOps, ReadFlowParams, ReadFlowResult, FlowStep, FlowVariable, ReadActionTypeParams, ReadActionTypeResult, ActionIo, PublishFlowParams, PublishFlowResult, CopyFlowParams, CopyFlowResult, CreateFlowParams, CreateFlowResult, EditFlowParams, EditFlowResult, EditFlowOps, StepInputPatch, TestFlowParams, TestFlowResult, WriteOp, WriteOpResult } from "./flowDesigner";
|
|
19
19
|
export type { ServiceNowClientConfig, ChoiceValue, ChoiceType, AddChoicesParams, AddChoicesResult, ChoiceActionResult, DictionaryRecord, UpdateSetRecord, LayoutAction, LayoutRecordResult, LayoutResult, CreateViewParams, CreateViewResult, FormSectionSpec, SetFormLayoutParams, SetListLayoutParams, SetRelatedListsParams } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* REST API so every change lands in the target update set and scope.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = exports.editFlow = exports.buildPublishModel = exports.createFlow = exports.copyFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.publishActionType = exports.triggerPublication = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = exports.sincPlugin = exports.formatCreateViewResult = exports.formatLayoutResult = exports.setRelatedLists = exports.setFormLayout = exports.setListLayout = exports.createView = exports.formatAddChoicesResult = exports.addChoicesToField = exports.createClient = void 0;
|
|
9
|
+
exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = exports.editFlow = exports.buildPublishModel = exports.createFlow = exports.copyFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.editActionType = exports.publishActionType = exports.triggerPublication = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = exports.sincPlugin = exports.formatCreateViewResult = exports.formatLayoutResult = exports.setRelatedLists = exports.setFormLayout = exports.setListLayout = exports.createView = exports.formatAddChoicesResult = exports.addChoicesToField = exports.createClient = void 0;
|
|
10
10
|
var client_1 = require("./client");
|
|
11
11
|
Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return client_1.createClient; } });
|
|
12
12
|
var choices_1 = require("./choices");
|
|
@@ -33,6 +33,7 @@ Object.defineProperty(exports, "cloneSubflow", { enumerable: true, get: function
|
|
|
33
33
|
Object.defineProperty(exports, "cloneActionType", { enumerable: true, get: function () { return flowDesigner_1.cloneActionType; } });
|
|
34
34
|
Object.defineProperty(exports, "triggerPublication", { enumerable: true, get: function () { return flowDesigner_1.triggerPublication; } });
|
|
35
35
|
Object.defineProperty(exports, "publishActionType", { enumerable: true, get: function () { return flowDesigner_1.publishActionType; } });
|
|
36
|
+
Object.defineProperty(exports, "editActionType", { enumerable: true, get: function () { return flowDesigner_1.editActionType; } });
|
|
36
37
|
Object.defineProperty(exports, "readFlow", { enumerable: true, get: function () { return flowDesigner_1.readFlow; } });
|
|
37
38
|
Object.defineProperty(exports, "readActionType", { enumerable: true, get: function () { return flowDesigner_1.readActionType; } });
|
|
38
39
|
Object.defineProperty(exports, "publishFlow", { enumerable: true, get: function () { return flowDesigner_1.publishFlow; } });
|
package/dist/mcp/registry.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* a client is injected via RegistryDeps (used by tests).
|
|
8
8
|
*/
|
|
9
9
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
10
11
|
import { z } from "zod";
|
|
11
12
|
import type { ServiceNowClient } from "../client";
|
|
12
13
|
export declare var TOOL_NAMES: readonly ["create_view", "set_list_layout", "set_form_layout", "set_related_lists", "add_choices_to_field", "flow_view", "action_view", "flow_publish", "flow_copy", "flow_create", "flow_test", "flow_edit"];
|
|
@@ -19,6 +20,7 @@ export interface ToolDescriptor {
|
|
|
19
20
|
name: ToolName;
|
|
20
21
|
description: string;
|
|
21
22
|
shape: z.ZodRawShape;
|
|
23
|
+
annotations: ToolAnnotations;
|
|
22
24
|
handler: (args: any) => Promise<any>;
|
|
23
25
|
}
|
|
24
26
|
export declare function buildDescriptors(deps?: RegistryDeps): Array<ToolDescriptor>;
|
package/dist/mcp/registry.js
CHANGED
|
@@ -39,6 +39,20 @@ exports.TOOL_NAMES = [
|
|
|
39
39
|
"flow_test",
|
|
40
40
|
"flow_edit"
|
|
41
41
|
];
|
|
42
|
+
// MCP tool annotations (spec 2025-11-25) — untrusted behavioural hints that let a
|
|
43
|
+
// host parallelize reads and gate writes; never a security boundary. Five presets
|
|
44
|
+
// cover every tool's safety profile:
|
|
45
|
+
// READ_ONLY — no writes; safe to auto-approve and run concurrently.
|
|
46
|
+
// WRITE_ADDITIVE_IDEMPOTENT — additive write, safe to repeat (idempotent upsert/create).
|
|
47
|
+
// WRITE_CREATE — additive write, NOT idempotent (each call mints a new record).
|
|
48
|
+
// WRITE_OVERWRITE — destructive but idempotent (prune/overwrite: same end state).
|
|
49
|
+
// WRITE_EXECUTE — destructive AND non-idempotent (runs a flow; repeating repeats the effect).
|
|
50
|
+
// openWorldHint is left at its spec default (true) — every tool reaches a ServiceNow instance.
|
|
51
|
+
var READ_ONLY = { readOnlyHint: true };
|
|
52
|
+
var WRITE_ADDITIVE_IDEMPOTENT = { readOnlyHint: false, destructiveHint: false, idempotentHint: true };
|
|
53
|
+
var WRITE_CREATE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false };
|
|
54
|
+
var WRITE_OVERWRITE = { readOnlyHint: false, destructiveHint: true, idempotentHint: true };
|
|
55
|
+
var WRITE_EXECUTE = { readOnlyHint: false, destructiveHint: true, idempotentHint: false };
|
|
42
56
|
function buildDescriptors(deps = {}) {
|
|
43
57
|
function client() {
|
|
44
58
|
return deps.client || (0, client_1.createClient)({});
|
|
@@ -46,6 +60,7 @@ function buildDescriptors(deps = {}) {
|
|
|
46
60
|
return [
|
|
47
61
|
{
|
|
48
62
|
name: "create_view",
|
|
63
|
+
annotations: WRITE_ADDITIVE_IDEMPOTENT,
|
|
49
64
|
description: "Create a ServiceNow custom view (sys_ui_view). Idempotent — an existing view of the "
|
|
50
65
|
+ "same name is returned unchanged. Every write is captured in the supplied update set.",
|
|
51
66
|
shape: schemas_1.createViewSchema.shape,
|
|
@@ -55,6 +70,7 @@ function buildDescriptors(deps = {}) {
|
|
|
55
70
|
},
|
|
56
71
|
{
|
|
57
72
|
name: "set_list_layout",
|
|
73
|
+
annotations: WRITE_OVERWRITE,
|
|
58
74
|
description: "Declaratively set a ServiceNow list layout — which columns appear in a list, and their "
|
|
59
75
|
+ "order — for a table + view. Idempotent; prune (default true) removes columns not in "
|
|
60
76
|
+ "the spec; dryRun previews without writing. Writes are captured in the update set.",
|
|
@@ -65,6 +81,7 @@ function buildDescriptors(deps = {}) {
|
|
|
65
81
|
},
|
|
66
82
|
{
|
|
67
83
|
name: "set_form_layout",
|
|
84
|
+
annotations: WRITE_OVERWRITE,
|
|
68
85
|
description: "Declaratively set a ServiceNow form layout — sections and the fields within them — for "
|
|
69
86
|
+ "a table + view. The first section is the primary section (omit its caption). "
|
|
70
87
|
+ "Idempotent; prune (default true) removes sections/fields not in the spec; dryRun "
|
|
@@ -76,6 +93,7 @@ function buildDescriptors(deps = {}) {
|
|
|
76
93
|
},
|
|
77
94
|
{
|
|
78
95
|
name: "set_related_lists",
|
|
96
|
+
annotations: WRITE_OVERWRITE,
|
|
79
97
|
description: "Declaratively set which related lists appear on a ServiceNow form for a table + view. "
|
|
80
98
|
+ "Related-list ids are \"<table>.<field>\" or \"REL:<sys_relationship>\". Idempotent; "
|
|
81
99
|
+ "prune (default true); dryRun previews. Writes are captured in the update set.",
|
|
@@ -86,6 +104,7 @@ function buildDescriptors(deps = {}) {
|
|
|
86
104
|
},
|
|
87
105
|
{
|
|
88
106
|
name: "add_choices_to_field",
|
|
107
|
+
annotations: WRITE_ADDITIVE_IDEMPOTENT,
|
|
89
108
|
description: "Upsert sys_choice values for a ServiceNow table.column and (optionally) flip "
|
|
90
109
|
+ "sys_dictionary.choice so the field renders as a dropdown. Idempotent. Writes are "
|
|
91
110
|
+ "captured in the supplied update set.",
|
|
@@ -96,6 +115,7 @@ function buildDescriptors(deps = {}) {
|
|
|
96
115
|
},
|
|
97
116
|
{
|
|
98
117
|
name: "flow_view",
|
|
118
|
+
annotations: READ_ONLY,
|
|
99
119
|
description: "Read a ServiceNow Flow Designer flow or subflow's compiled step graph, headless. "
|
|
100
120
|
+ "Returns the ordered, nesting-aware list of action + flow-logic steps plus the flow "
|
|
101
121
|
+ "variables, via GET /api/now/processflow/flow/{sysId}. Read-only. Pass raw:true to "
|
|
@@ -108,6 +128,7 @@ function buildDescriptors(deps = {}) {
|
|
|
108
128
|
},
|
|
109
129
|
{
|
|
110
130
|
name: "action_view",
|
|
131
|
+
annotations: READ_ONLY,
|
|
111
132
|
description: "Read a ServiceNow Custom Action Type's compiled model (identity, inputs, outputs), "
|
|
112
133
|
+ "headless, via GET /api/now/processflow/action/action_types/{sysId}. Read-only. "
|
|
113
134
|
+ "sysId is the sys_hub_action_type_definition sys_id; scopeSysId is the application "
|
|
@@ -120,6 +141,7 @@ function buildDescriptors(deps = {}) {
|
|
|
120
141
|
},
|
|
121
142
|
{
|
|
122
143
|
name: "flow_publish",
|
|
144
|
+
annotations: WRITE_OVERWRITE,
|
|
123
145
|
description: "Publish (compile the snapshot of) a ServiceNow Flow Designer flow or subflow via "
|
|
124
146
|
+ "POST /api/now/processflow/flow/{sysId}/snapshot. This is a WRITE that recompiles the "
|
|
125
147
|
+ "flow's current design — use after editing in the Designer. sysId is the sys_hub_flow "
|
|
@@ -132,6 +154,7 @@ function buildDescriptors(deps = {}) {
|
|
|
132
154
|
},
|
|
133
155
|
{
|
|
134
156
|
name: "flow_copy",
|
|
157
|
+
annotations: WRITE_CREATE,
|
|
135
158
|
description: "Copy a ServiceNow flow/subflow via the Designer's Copy endpoint — a complete, faithful "
|
|
136
159
|
+ "clone created as an INACTIVE DRAFT in the target scope. sourceSysId is the sys_hub_flow "
|
|
137
160
|
+ "to copy; newName is the copy's name; scopeSysId defaults to the source's scope. Publish "
|
|
@@ -145,6 +168,7 @@ function buildDescriptors(deps = {}) {
|
|
|
145
168
|
},
|
|
146
169
|
{
|
|
147
170
|
name: "flow_create",
|
|
171
|
+
annotations: WRITE_CREATE,
|
|
148
172
|
description: "Create a NEW ServiceNow Flow Designer flow (sys_hub_flow, type=flow) from scratch and "
|
|
149
173
|
+ "PUBLISH it, headless. Mints a fresh flow via POST /processflow/flow, grafts the trigger + "
|
|
150
174
|
+ "action graph from an existing published template flow (templateSysId), then compiles the "
|
|
@@ -174,6 +198,7 @@ function buildDescriptors(deps = {}) {
|
|
|
174
198
|
},
|
|
175
199
|
{
|
|
176
200
|
name: "flow_test",
|
|
201
|
+
annotations: WRITE_EXECUTE,
|
|
177
202
|
description: "Test or run a ServiceNow flow/subflow. mode='validate' (default) is a safe, read-only "
|
|
178
203
|
+ "pre-flight — checks the flow is published and that supplied inputs match its declared "
|
|
179
204
|
+ "variables; it never runs the flow. mode='execute' actually runs it via the server-side "
|
|
@@ -194,6 +219,7 @@ function buildDescriptors(deps = {}) {
|
|
|
194
219
|
},
|
|
195
220
|
{
|
|
196
221
|
name: "flow_edit",
|
|
222
|
+
annotations: WRITE_OVERWRITE,
|
|
197
223
|
description: "Edit a ServiceNow flow/subflow in place. Supports rename (name/internalName), description, "
|
|
198
224
|
+ "and patchStepInputs (set named input values on steps by uiId or label). apply=false "
|
|
199
225
|
+ "(default) is a dry-run that returns the diff; apply=true persists the edit (a write). "
|
|
@@ -221,7 +247,7 @@ function registerAllTools(server, deps = {}) {
|
|
|
221
247
|
}
|
|
222
248
|
}
|
|
223
249
|
function registerOne(server, desc) {
|
|
224
|
-
server.registerTool(desc.name, { description: desc.description, inputSchema: desc.shape }, async function (args) {
|
|
250
|
+
server.registerTool(desc.name, { description: desc.description, inputSchema: desc.shape, annotations: desc.annotations }, async function (args) {
|
|
225
251
|
try {
|
|
226
252
|
var result = await desc.handler(args);
|
|
227
253
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|