callman-mcp 0.6.0 → 0.7.0

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.
Files changed (147) hide show
  1. package/CHANGELOG.md +36 -59
  2. package/README.md +18 -24
  3. package/dist/client/api.d.ts +18 -40
  4. package/dist/client/api.d.ts.map +1 -1
  5. package/dist/client/api.js +7 -54
  6. package/dist/client/api.js.map +1 -1
  7. package/dist/client/types.d.ts +19 -44
  8. package/dist/client/types.d.ts.map +1 -1
  9. package/dist/config.d.ts +4 -0
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +5 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/domain/diagram/build.d.ts +17 -17
  14. package/dist/domain/diagram/build.d.ts.map +1 -1
  15. package/dist/domain/diagram/build.js +3 -3
  16. package/dist/domain/diagram/build.js.map +1 -1
  17. package/dist/domain/diagram/layout.d.ts +1 -3
  18. package/dist/domain/diagram/layout.d.ts.map +1 -1
  19. package/dist/domain/diagram/layout.js +3 -9
  20. package/dist/domain/diagram/layout.js.map +1 -1
  21. package/dist/domain/diagram/patch.d.ts +22 -723
  22. package/dist/domain/diagram/patch.d.ts.map +1 -1
  23. package/dist/domain/diagram/patch.js +0 -53
  24. package/dist/domain/diagram/patch.js.map +1 -1
  25. package/dist/domain/diagram/summarize.d.ts +0 -1
  26. package/dist/domain/diagram/summarize.d.ts.map +1 -1
  27. package/dist/domain/diagram/summarize.js +0 -1
  28. package/dist/domain/diagram/summarize.js.map +1 -1
  29. package/dist/domain/requestIntegrations.d.ts +13 -468
  30. package/dist/domain/requestIntegrations.d.ts.map +1 -1
  31. package/dist/domain/requestIntegrations.js +4 -95
  32. package/dist/domain/requestIntegrations.js.map +1 -1
  33. package/dist/domain/requestShape.d.ts +37 -679
  34. package/dist/domain/requestShape.d.ts.map +1 -1
  35. package/dist/domain/requestShape.js +2 -2
  36. package/dist/domain/requestShape.js.map +1 -1
  37. package/dist/domain/scenario/build.d.ts +6 -6
  38. package/dist/domain/scenario/build.d.ts.map +1 -1
  39. package/dist/domain/scenario/build.js +17 -68
  40. package/dist/domain/scenario/build.js.map +1 -1
  41. package/dist/domain/scenario/nodeConfig.d.ts.map +1 -1
  42. package/dist/domain/scenario/nodeConfig.js +16 -0
  43. package/dist/domain/scenario/nodeConfig.js.map +1 -1
  44. package/dist/domain/scenario/summarize.d.ts +0 -1
  45. package/dist/domain/scenario/summarize.d.ts.map +1 -1
  46. package/dist/domain/scenario/summarize.js +6 -1
  47. package/dist/domain/scenario/summarize.js.map +1 -1
  48. package/dist/index.js +16 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/resources/docs/collectionScripts.d.ts +2 -0
  51. package/dist/resources/docs/collectionScripts.d.ts.map +1 -0
  52. package/dist/resources/docs/collectionScripts.js +51 -0
  53. package/dist/resources/docs/collectionScripts.js.map +1 -0
  54. package/dist/resources/docs/diagramSchema.d.ts +1 -2
  55. package/dist/resources/docs/diagramSchema.d.ts.map +1 -1
  56. package/dist/resources/docs/diagramSchema.js +4 -63
  57. package/dist/resources/docs/diagramSchema.js.map +1 -1
  58. package/dist/resources/docs/overview.d.ts +1 -1
  59. package/dist/resources/docs/overview.d.ts.map +1 -1
  60. package/dist/resources/docs/overview.js +13 -34
  61. package/dist/resources/docs/overview.js.map +1 -1
  62. package/dist/resources/docs/requestSchema.d.ts +1 -1
  63. package/dist/resources/docs/requestSchema.d.ts.map +1 -1
  64. package/dist/resources/docs/requestSchema.js +11 -16
  65. package/dist/resources/docs/requestSchema.js.map +1 -1
  66. package/dist/resources/generated/aiPacks.d.ts +1 -1
  67. package/dist/resources/generated/aiPacks.js +6 -6
  68. package/dist/resources/generated/aiPacks.js.map +1 -1
  69. package/dist/resources/index.d.ts.map +1 -1
  70. package/dist/resources/index.js +5 -3
  71. package/dist/resources/index.js.map +1 -1
  72. package/dist/run/executeCollection.d.ts.map +1 -1
  73. package/dist/run/executeCollection.js +19 -3
  74. package/dist/run/executeCollection.js.map +1 -1
  75. package/dist/run/loadBundle.d.ts.map +1 -1
  76. package/dist/run/loadBundle.js +28 -3
  77. package/dist/run/loadBundle.js.map +1 -1
  78. package/dist/run/summarize.d.ts +4 -4
  79. package/dist/run/types.d.ts +5 -1
  80. package/dist/run/types.d.ts.map +1 -1
  81. package/dist/server.d.ts +1 -1
  82. package/dist/server.d.ts.map +1 -1
  83. package/dist/server.js +2 -2
  84. package/dist/server.js.map +1 -1
  85. package/dist/tools/catalog.d.ts.map +1 -1
  86. package/dist/tools/catalog.js +3 -11
  87. package/dist/tools/catalog.js.map +1 -1
  88. package/dist/tools/collections.d.ts.map +1 -1
  89. package/dist/tools/collections.js +137 -16
  90. package/dist/tools/collections.js.map +1 -1
  91. package/dist/tools/contracts.js +2 -2
  92. package/dist/tools/contracts.js.map +1 -1
  93. package/dist/tools/diagrams.d.ts.map +1 -1
  94. package/dist/tools/diagrams.js +42 -331
  95. package/dist/tools/diagrams.js.map +1 -1
  96. package/dist/tools/index.d.ts.map +1 -1
  97. package/dist/tools/index.js +4 -3
  98. package/dist/tools/index.js.map +1 -1
  99. package/dist/tools/loadTest.d.ts +4 -0
  100. package/dist/tools/loadTest.d.ts.map +1 -0
  101. package/dist/tools/loadTest.js +299 -0
  102. package/dist/tools/loadTest.js.map +1 -0
  103. package/dist/tools/lookups.d.ts +1 -5
  104. package/dist/tools/lookups.d.ts.map +1 -1
  105. package/dist/tools/lookups.js +0 -41
  106. package/dist/tools/lookups.js.map +1 -1
  107. package/dist/tools/requests.js +3 -3
  108. package/dist/tools/requests.js.map +1 -1
  109. package/dist/tools/run.d.ts +18 -0
  110. package/dist/tools/run.d.ts.map +1 -1
  111. package/dist/tools/run.js +14 -5
  112. package/dist/tools/run.js.map +1 -1
  113. package/dist/tools/scenarios.d.ts +0 -4
  114. package/dist/tools/scenarios.d.ts.map +1 -1
  115. package/dist/tools/scenarios.js +17 -112
  116. package/dist/tools/scenarios.js.map +1 -1
  117. package/dist-cjs/client/api.js +7 -54
  118. package/dist-cjs/config.js +5 -0
  119. package/dist-cjs/domain/diagram/build.js +3 -3
  120. package/dist-cjs/domain/diagram/layout.js +3 -10
  121. package/dist-cjs/domain/diagram/patch.js +1 -54
  122. package/dist-cjs/domain/diagram/summarize.js +0 -1
  123. package/dist-cjs/domain/requestIntegrations.js +4 -95
  124. package/dist-cjs/domain/requestShape.js +2 -2
  125. package/dist-cjs/domain/scenario/build.js +17 -68
  126. package/dist-cjs/domain/scenario/nodeConfig.js +16 -0
  127. package/dist-cjs/domain/scenario/summarize.js +6 -1
  128. package/dist-cjs/resources/docs/collectionScripts.js +53 -0
  129. package/dist-cjs/resources/docs/diagramSchema.js +5 -65
  130. package/dist-cjs/resources/docs/overview.js +13 -34
  131. package/dist-cjs/resources/docs/requestSchema.js +11 -16
  132. package/dist-cjs/resources/generated/aiPacks.js +6 -6
  133. package/dist-cjs/resources/index.js +4 -2
  134. package/dist-cjs/run/executeCollection.js +18 -2
  135. package/dist-cjs/run/loadBundle.js +28 -3
  136. package/dist-cjs/server.js +2 -2
  137. package/dist-cjs/tools/catalog.js +3 -11
  138. package/dist-cjs/tools/collections.js +137 -16
  139. package/dist-cjs/tools/contracts.js +2 -2
  140. package/dist-cjs/tools/diagrams.js +37 -326
  141. package/dist-cjs/tools/index.js +4 -3
  142. package/dist-cjs/tools/loadTest.js +335 -0
  143. package/dist-cjs/tools/lookups.js +1 -46
  144. package/dist-cjs/tools/requests.js +3 -3
  145. package/dist-cjs/tools/run.js +17 -7
  146. package/dist-cjs/tools/scenarios.js +18 -114
  147. package/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerScenarioTools = exports.resolveGroup = void 0;
3
+ exports.registerScenarioTools = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const errors_js_1 = require("../client/errors.js");
6
6
  const resolve_js_1 = require("../domain/resolve.js");
@@ -43,67 +43,7 @@ const resolveGroup = async (ctx, ws, ref) => {
43
43
  return { id: null, note: "Scenario groups exist only on-prem — `group` was ignored." };
44
44
  return { id: (0, resolve_js_1.resolveRef)("scenario group", ref, items).item.id };
45
45
  };
46
- exports.resolveGroup = resolveGroup;
47
46
  const latestScenario = async (ctx, ws, id) => ctx.api.getScenario(ws, id);
48
- // The version of each scenario this session last READ or wrote. update_scenario
49
- // replaces the whole graph, so without an explicit expectedVersion it checks
50
- // against what the model actually saw — a newer save from the desktop (or a
51
- // teammate) then comes back as a conflict instead of being overwritten.
52
- const seenVersions = new WeakMap();
53
- const rememberVersion = (ctx, scenario) => {
54
- if (!scenario.id || typeof scenario.version !== "number")
55
- return;
56
- let map = seenVersions.get(ctx);
57
- if (!map) {
58
- map = new Map();
59
- seenVersions.set(ctx, map);
60
- }
61
- map.set(scenario.id, scenario.version);
62
- };
63
- const seenVersion = (ctx, id) => seenVersions.get(ctx)?.get(id);
64
- // scenario-ref nodes name the scenario to link (`config.scenario`: name or
65
- // id); the server copies it in when the snapshot is empty.
66
- const resolveScenarioLinks = async (ctx, ws, nodes) => {
67
- const out = [];
68
- for (const node of nodes) {
69
- const ref = node.type.trim() === "scenario-ref" && node.config ? node.config.scenario : undefined;
70
- if (typeof ref !== "string" || !ref.trim()) {
71
- out.push(node);
72
- continue;
73
- }
74
- const target = await (0, lookups_js_1.resolveScenario)(ctx, ws, ref);
75
- const { scenario: _ref, ...rest } = node.config;
76
- void _ref;
77
- out.push({ ...node, config: { ...rest, referencedScenarioId: target.item.id, nodes: [], edges: [] } });
78
- }
79
- return out;
80
- };
81
- // Labels must be unique across the whole graph; a linked scenario brings its
82
- // own nodes' labels, which this tool can only check after the server copied
83
- // them in.
84
- const labelClashWarnings = (nodes) => {
85
- const owners = new Map();
86
- const clashes = [];
87
- const walk = (list, container) => {
88
- for (const node of list) {
89
- const label = node.data?.label;
90
- if (typeof label === "string" && label) {
91
- const where = container ? `${node.id} (inside ${container})` : node.id;
92
- const first = owners.get(label);
93
- if (first)
94
- clashes.push(`"${label}" is used by ${first} and ${where}`);
95
- else
96
- owners.set(label, where);
97
- }
98
- const inner = node.data?.config?.nodes;
99
- if (Array.isArray(inner))
100
- walk(inner, container ?? node.id);
101
- }
102
- };
103
- walk(nodes, null);
104
- return clashes.length > 0 ? [`Label clash after linking — later nodes read the wrong output until you rename one: ${clashes.join("; ")}.`] : [];
105
- };
106
- const hasLink = (graph) => Boolean(graph?.nodes.some((node) => node.type === "scenario-ref" && Array.isArray(node.data.config.nodes) && node.data.config.nodes.length === 0));
107
47
  const registerScenarioTools = (server, ctx) => {
108
48
  server.registerTool("list_scenarios", {
109
49
  title: "List scenarios",
@@ -152,7 +92,6 @@ const registerScenarioTools = (server, ctx) => {
152
92
  const ws = await ctx.resolveWorkspaceOrNull(workspace);
153
93
  const resolved = await (0, lookups_js_1.resolveScenario)(ctx, ws?.id ?? null, scenario);
154
94
  const fresh = resolved.matched === "id" && !reveal ? resolved.item : await ctx.api.getScenario(ws?.id ?? null, resolved.item.id, reveal ?? false);
155
- rememberVersion(ctx, fresh);
156
95
  if (node) {
157
96
  const found = (0, summarize_js_1.scenarioNodeView)(fresh, node, { reveal });
158
97
  if (!found) {
@@ -165,7 +104,7 @@ const registerScenarioTools = (server, ctx) => {
165
104
  }));
166
105
  server.registerTool("get_scenario_report", {
167
106
  title: "Get a scenario run report",
168
- description: "The result of a scenario run: status, summary counts, an error outside any node (unexpectedError), a per-node timeline and every failing node expanded (failed tests, assertion checks with expected vs actual, failed contracts, script error with line, the input as resolved at run time, response, logs). `node` returns one node's result in that detail. Defaults to the latest run; `list: true` lists recent runs instead. Covers every run the server knows: server runs, schedules, CLI/pipeline runs, desktop-relay runs (run_scenario target \"desktop\") and runs started in the desktop app (saved there unless the user turned that off) — `triggerType` / `executor` say which.",
107
+ description: "The result of a scenario run: status, summary counts, an error outside any node (unexpectedError), a per-node timeline and every failing node expanded (failed tests, assertion checks with expected vs actual, failed contracts, script error with line, the input as resolved at run time, response, logs). `node` returns one node's result in that detail. Defaults to the latest run; `list: true` lists recent runs instead.",
169
108
  inputSchema: {
170
109
  workspace: shared_js_1.workspaceArg,
171
110
  scenario: (0, shared_js_1.refArg)("Scenario"),
@@ -186,7 +125,7 @@ const registerScenarioTools = (server, ctx) => {
186
125
  const runs = await ctx.api.listScenarioReports(ws?.id ?? null, resolved.item.id, 5);
187
126
  const latest = runs.items[0];
188
127
  if (!latest)
189
- return (0, shared_js_1.ok)({ scenario: { id: resolved.item.id, name: resolved.item.name }, report: null, note: "No saved runs yet. Use run_scenario (target \"desktop\" for what only the user's machine reaches)." });
128
+ return (0, shared_js_1.ok)({ scenario: { id: resolved.item.id, name: resolved.item.name }, report: null, note: "This scenario has not been run on the server yet. Use run_scenario." });
190
129
  reportId = latest.reportId;
191
130
  }
192
131
  const detail = await ctx.api.getScenarioReport(ws?.id ?? null, resolved.item.id, reportId);
@@ -200,7 +139,7 @@ const registerScenarioTools = (server, ctx) => {
200
139
  }));
201
140
  server.registerTool("create_scenario", {
202
141
  title: "Create a scenario",
203
- description: "Create a multi-step API test scenario from compact nodes and edges. READ callman://docs/scenario-nodes first: it lists every node type, its config fields, defaults and which are required. Configs are merged over the type's defaults (send what you set; arrays you send must be complete). Request nodes also accept the request tools' shapes (auth { type: \"bearer\", token }, a JSON body) and are converted. Labels are generated when omitted and must be unique. db/redis/kafka/notification/ai nodes need a connectionId (list_connections). Config keys the server does not know come back as warnings. dryRun: true returns the complete resolved nodes without saving. On-prem: new scenarios start as drafts — submit_scenario sends them for approval; personal-token runs use the approved version.",
142
+ description: "Create a multi-step API test scenario from compact nodes and edges. READ callman://docs/scenario-nodes first: it lists every node type, its config fields, defaults and which are required. Configs are merged over the type's defaults (send what you set; arrays you send must be complete). Request nodes also accept the request tools' shapes (auth { type: \"bearer\", token }, a JSON body) and are converted. Labels are generated when omitted and must be unique. db/redis/kafka/notification/ai/temporal nodes need a connectionId (list_connections). A temporal node runs one Temporal.io operation (start/signal/query/update/await/describe/history/cancel/terminate/list/count/describeTaskQueue) — set `operation` and its required fields; use `expect` rules to assert on the result. Config keys the server does not know come back as warnings. dryRun: true returns the complete resolved nodes without saving. On-prem: new scenarios start as drafts — submit_scenario sends them for approval; personal-token runs use the approved version.",
204
143
  inputSchema: {
205
144
  workspace: shared_js_1.workspaceArg,
206
145
  folder: zod_1.z.string().max(200).optional().describe("Scenario folder id or name"),
@@ -212,22 +151,14 @@ const registerScenarioTools = (server, ctx) => {
212
151
  }, (0, shared_js_1.guarded)(async ({ workspace, folder, group, dryRun, ...input }) => {
213
152
  const ws = await ctx.resolveWorkspace(workspace);
214
153
  const targetFolder = folder ? await (0, lookups_js_1.resolveScenarioFolder)(ctx, ws.id, folder) : null;
215
- const targetGroup = await (0, exports.resolveGroup)(ctx, ws.id, group);
216
- const linkedInput = { ...input, nodes: await resolveScenarioLinks(ctx, ws.id, input.nodes) };
217
- const { body, graph } = (0, build_js_1.buildScenarioCreateBody)(linkedInput, targetFolder?.item.id ?? null, targetGroup?.id === null ? undefined : targetGroup?.id);
154
+ const targetGroup = await resolveGroup(ctx, ws.id, group);
155
+ const { body, graph } = (0, build_js_1.buildScenarioCreateBody)(input, targetFolder?.item.id ?? null, targetGroup?.id === null ? undefined : targetGroup?.id);
218
156
  if (dryRun) {
219
157
  return (0, shared_js_1.ok)({ dryRun: true, scenario: { name: body.name, nodes: graph.nodes.map((node) => ({ id: node.id, type: node.type, label: node.data.label, config: node.data.config })), edges: graph.edges }, warnings: graph.warnings });
220
158
  }
221
159
  const created = await ctx.api.createScenario(ws.id, body);
222
160
  ctx.invalidate("scenarios:");
223
- rememberVersion(ctx, created);
224
- const warnings = [
225
- ...graph.warnings,
226
- ...droppedFieldWarnings(graph, created),
227
- ...(created.graph?.warnings ?? []),
228
- ...(targetGroup?.note ? [targetGroup.note] : []),
229
- ...(hasLink(graph) ? labelClashWarnings(created.nodes) : []),
230
- ];
161
+ const warnings = [...graph.warnings, ...droppedFieldWarnings(graph, created), ...(created.graph?.warnings ?? []), ...(targetGroup?.note ? [targetGroup.note] : [])];
231
162
  return (0, shared_js_1.ok)({
232
163
  scenario: (0, summarize_js_1.scenarioGraphView)(created),
233
164
  ...(warnings.length > 0 ? { warnings } : {}),
@@ -295,12 +226,12 @@ const registerScenarioTools = (server, ctx) => {
295
226
  }
296
227
  }
297
228
  }
298
- const graph = (0, build_js_1.buildScenarioGraph)(await resolveScenarioLinks(ctx, ws?.id ?? null, [...nodes.values()]), edges, { existing: current.nodes });
229
+ const graph = (0, build_js_1.buildScenarioGraph)([...nodes.values()], edges, { existing: current.nodes });
299
230
  return saveGraph(ctx, ws?.id ?? null, current, graph, { expectedVersion, annotation: resolved.annotation ? (0, resolve_js_1.resolvedSummary)(scenario, resolved) : undefined });
300
231
  }));
301
232
  server.registerTool("update_scenario", {
302
233
  title: "Update a scenario",
303
- description: "Update a scenario's name / description / priority / folder / environments / dataset / dbSettings and/or replace its graph (nodes AND edges together). A node whose id already exists MERGES into the stored node (only the config keys you send change, its position is kept); new ids merge over the type's defaults; nodes you leave out are removed. `environments` replaces the whole list (stored ids are kept by name; a masked value sent back unchanged keeps the stored one). For small edits prefer patch_scenario. Version-checked: a graph replace is checked against the `version` you last read (or expectedVersion) — if someone saved since (e.g. in the desktop app) you get a structured conflict instead of overwriting their work. On-prem: editing an approved scenario returns it to draft.",
234
+ description: "Update a scenario's name / description / priority / folder / environments / dataset / dbSettings and/or replace its graph (nodes AND edges together). A node whose id already exists MERGES into the stored node (only the config keys you send change, its position is kept); new ids merge over the type's defaults; nodes you leave out are removed. `environments` replaces the whole list (stored ids are kept by name; a masked value sent back unchanged keeps the stored one). For small edits prefer patch_scenario. Version-checked: pass the `version` you read (expectedVersion) or it is fetched first; a conflict comes back structured. On-prem: editing an approved scenario returns it to draft.",
304
235
  inputSchema: {
305
236
  workspace: shared_js_1.workspaceArg,
306
237
  scenario: (0, shared_js_1.refArg)("Scenario"),
@@ -325,7 +256,7 @@ const registerScenarioTools = (server, ctx) => {
325
256
  const resolved = await (0, lookups_js_1.resolveScenario)(ctx, ws?.id ?? null, scenario);
326
257
  const current = await latestScenario(ctx, ws?.id ?? null, resolved.item.id);
327
258
  const targetFolder = folder ? await (0, lookups_js_1.resolveScenarioFolder)(ctx, ws?.id ?? null, folder) : null;
328
- const graph = nodes && edges ? (0, build_js_1.buildScenarioGraph)(await resolveScenarioLinks(ctx, ws?.id ?? null, nodes), edges, { existing: current.nodes }) : null;
259
+ const graph = nodes && edges ? (0, build_js_1.buildScenarioGraph)(nodes, edges, { existing: current.nodes }) : null;
329
260
  const extra = {
330
261
  ...(name !== undefined ? { name } : {}),
331
262
  ...(description !== undefined ? { description } : {}),
@@ -337,9 +268,7 @@ const registerScenarioTools = (server, ctx) => {
337
268
  };
338
269
  if (!graph && Object.keys(extra).length === 0)
339
270
  throw new errors_js_1.ToolInputError("NOTHING_TO_UPDATE", "Pass at least one field to change.");
340
- // A whole-graph replace checks against the version this session read.
341
- const checkedVersion = expectedVersion ?? (graph ? seenVersion(ctx, current.id) : undefined);
342
- return saveGraph(ctx, ws?.id ?? null, current, graph, { expectedVersion: checkedVersion, force, extra, annotation: resolved.annotation ? (0, resolve_js_1.resolvedSummary)(scenario, resolved) : undefined });
271
+ return saveGraph(ctx, ws?.id ?? null, current, graph, { expectedVersion, force, extra, annotation: resolved.annotation ? (0, resolve_js_1.resolvedSummary)(scenario, resolved) : undefined });
343
272
  }));
344
273
  server.registerTool("submit_scenario", {
345
274
  title: "Submit a scenario for approval",
@@ -381,18 +310,17 @@ const registerScenarioTools = (server, ctx) => {
381
310
  return (0, shared_js_1.ok)({ deleted: { id: resolved.item.id, name: resolved.item.name }, resolved: [(0, resolve_js_1.resolvedSummary)(scenario, resolved)] });
382
311
  }));
383
312
  server.registerTool("run_scenario", {
384
- title: "Run a scenario",
385
- description: "Run a scenario (with one of its inline environments, by name or id; the first one when omitted) and wait for the result (default up to 5 minutes, max 30). Returns the report as in get_scenario_report. WHERE it runs matters: target \"server\" (default) runs on the Callman server, which reaches only what the server's network reaches — not the user's localhost, port-forwards, VPN-only hosts or internal databases. target \"desktop\" runs it on the user's own Callman desktop app (it must be open and signed in with Settings → AI tools → \"Let AI tools run scenarios on this computer\" on) — use it for local Kafka, local or internal databases and anything only that machine reaches. A personal token runs the scenario's APPROVED version on-prem (either target) — the result says when that differs from the version you edited. If it times out you get the reportId to check later.",
313
+ title: "Run a scenario on the server",
314
+ description: "Start a server-side run of a scenario (with one of its inline environments, by name or id; the first one when omitted) and wait for the result (default up to 5 minutes, max 30). Returns the report as in get_scenario_report. A personal token runs the scenario's APPROVED version on-prem — the result says when that differs from the version you edited. If it times out you get the reportId to check later.",
386
315
  inputSchema: {
387
316
  workspace: shared_js_1.workspaceArg,
388
317
  scenario: (0, shared_js_1.refArg)("Scenario"),
389
- target: zod_1.z.enum(["server", "desktop"]).default("server").describe('"server" (default) or "desktop" — the user\'s own desktop app, for what only their machine reaches'),
390
318
  environment: zod_1.z.string().max(200).optional().describe("One of the scenario's own environments (name or id)"),
391
319
  wait: zod_1.z.boolean().default(true),
392
320
  timeoutSec: zod_1.z.number().int().min(5).max(1800).default(300),
393
321
  },
394
322
  annotations: shared_js_1.RUN,
395
- }, (0, shared_js_1.guarded)(async ({ workspace, scenario, target, environment, wait, timeoutSec }) => {
323
+ }, (0, shared_js_1.guarded)(async ({ workspace, scenario, environment, wait, timeoutSec }) => {
396
324
  const ws = await ctx.resolveWorkspaceOrNull(workspace);
397
325
  const resolved = await (0, lookups_js_1.resolveScenario)(ctx, ws?.id ?? null, scenario);
398
326
  let environmentId;
@@ -405,19 +333,9 @@ const registerScenarioTools = (server, ctx) => {
405
333
  }
406
334
  environmentId = picked.id;
407
335
  }
408
- const runBody = environmentId ? { environmentId } : {};
409
- let trigger;
410
- try {
411
- trigger = target === "desktop" ? await ctx.api.runScenarioOnDesktop(ws?.id ?? null, resolved.item.id, runBody) : await ctx.api.runScenario(ws?.id ?? null, resolved.item.id, runBody);
412
- }
413
- catch (error) {
414
- if (target === "desktop" && error instanceof errors_js_1.CallmanApiError && error.status === 404 && error.code !== "SCENARIO_NOT_FOUND") {
415
- throw new errors_js_1.ToolInputError("SCENARIO_DESKTOP_UNSUPPORTED", "This Callman server cannot run scenarios on a desktop yet (update the backend), so only target \"server\" works.");
416
- }
417
- throw error;
418
- }
336
+ const trigger = await ctx.api.runScenario(ws?.id ?? null, resolved.item.id, environmentId ? { environmentId } : {});
419
337
  if (!wait)
420
- return (0, shared_js_1.ok)({ scenario: { id: resolved.item.id, name: resolved.item.name }, reportId: trigger.reportId, status: trigger.status, target, note: "Check with get_scenario_report." });
338
+ return (0, shared_js_1.ok)({ scenario: { id: resolved.item.id, name: resolved.item.name }, reportId: trigger.reportId, status: trigger.status, note: "Check with get_scenario_report." });
421
339
  const deadline = Date.now() + timeoutSec * 1000;
422
340
  let polls = 0;
423
341
  let last = null;
@@ -436,15 +354,7 @@ const registerScenarioTools = (server, ctx) => {
436
354
  break;
437
355
  }
438
356
  if (!last || !TERMINAL_STATUSES.has(last.status)) {
439
- return (0, shared_js_1.ok)({
440
- scenario: { id: resolved.item.id, name: resolved.item.name },
441
- reportId: trigger.reportId,
442
- status: last?.status ?? "running",
443
- timedOut: true,
444
- note: target === "desktop" && (last?.status ?? "queued") === "queued"
445
- ? `No desktop picked the run up within ${timeoutSec}s — the app must be open with "Let AI tools run scenarios on this computer" on. It is failed automatically after 10 minutes; check with get_scenario_report.`
446
- : `Still running after ${timeoutSec}s — call get_scenario_report with this reportId later.`,
447
- });
357
+ return (0, shared_js_1.ok)({ scenario: { id: resolved.item.id, name: resolved.item.name }, reportId: trigger.reportId, status: last?.status ?? "running", timedOut: true, note: `Still running after ${timeoutSec}s — call get_scenario_report with this reportId later.` });
448
358
  }
449
359
  const warnings = [];
450
360
  if (typeof last.scenarioVersion === "number" && last.scenarioVersion !== resolved.item.version) {
@@ -469,13 +379,7 @@ const saveGraph = async (ctx, ws, current, graph, options) => {
469
379
  try {
470
380
  const updated = await ctx.api.patchScenario(ws, current.id, body);
471
381
  ctx.invalidate("scenarios:");
472
- rememberVersion(ctx, updated);
473
- const warnings = [
474
- ...(graph?.warnings ?? []),
475
- ...(graph ? droppedFieldWarnings(graph, updated) : []),
476
- ...(updated.graph?.warnings ?? []),
477
- ...(hasLink(graph) ? labelClashWarnings(updated.nodes) : []),
478
- ];
382
+ const warnings = [...(graph?.warnings ?? []), ...(graph ? droppedFieldWarnings(graph, updated) : []), ...(updated.graph?.warnings ?? [])];
479
383
  const removed = graph ? current.nodes.filter((node) => !graph.nodes.some((entry) => entry.id === node.id)).map((node) => node.id) : [];
480
384
  return (0, shared_js_1.ok)({
481
385
  scenario: (0, summarize_js_1.scenarioGraphView)(updated),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "callman-mcp",
3
- "version": "0.6.0",
4
- "description": "MCP server for Callman: lets Claude Code, Claude Desktop, Cursor, Codex and Gemini CLI manage collections, requests, environments, test scenarios, UI tests (web, Android, iOS) and diagrams (system design, BPMN, Mermaid, database/ERD) in your Callman workspace — cloud or on-prem.",
3
+ "version": "0.7.0",
4
+ "description": "MCP server for Callman: lets Claude Code, Claude Desktop, Cursor, Codex and Gemini CLI manage collections, requests, environments, test scenarios, UI tests (web, Android, iOS) and system-design diagrams in your Callman workspace \u2014 cloud or on-prem.",
5
5
  "keywords": [
6
6
  "callman",
7
7
  "mcp",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@modelcontextprotocol/sdk": "^1.30.0",
65
- "callman-core": "^1.32.0",
65
+ "callman-core": "^1.37.0",
66
66
  "elkjs": "^0.12.0",
67
67
  "js-yaml": "^4.1.0",
68
68
  "zod": "^3.25.0"