@mag.ni/process 1.0.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.
@@ -0,0 +1,702 @@
1
+ /**
2
+ * MCP Processes - Work Management Tools
3
+ * Tools for managing instances, actions, and diagram context
4
+ */
5
+ import { hasClaimedUser } from "./session.js";
6
+ // =============================================================================
7
+ // Tool Definitions
8
+ // =============================================================================
9
+ /**
10
+ * Tool: get_my_instances
11
+ * Get instances assigned to the claimed user
12
+ */
13
+ export const getMyInstancesTool = {
14
+ name: "get_my_instances",
15
+ description: "Get all process instances assigned to you. Each instance is an active run of a user-designed workflow — it tracks your current step, history, and status. Requires an active claim (call claim_user first).",
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ status: {
20
+ type: "string",
21
+ enum: ["active", "completed", "cancelled", "paused"],
22
+ description: "Optional filter by instance status. If not provided, returns all instances.",
23
+ },
24
+ },
25
+ required: [],
26
+ },
27
+ };
28
+ /**
29
+ * Tool: get_instance_details
30
+ * Get full context for a specific instance
31
+ */
32
+ export const getInstanceDetailsTool = {
33
+ name: "get_instance_details",
34
+ description: "Get the full context for a process instance: current step (with label, description preview, and diagram), step history, and metadata. The step description contains the user's instructions for what to do at this point. Use get_node_details if the description preview is truncated.",
35
+ inputSchema: {
36
+ type: "object",
37
+ properties: {
38
+ instanceId: {
39
+ type: "string",
40
+ description: "ID of the instance to get details for",
41
+ },
42
+ },
43
+ required: ["instanceId"],
44
+ },
45
+ };
46
+ /**
47
+ * Tool: get_available_actions
48
+ * Get actions available at the current step
49
+ */
50
+ export const getAvailableActionsTool = {
51
+ name: "get_available_actions",
52
+ description: "Get the actions available at your current step. At a decision point (diamond node), you choose one path. At a regular step, there is typically one action to proceed. At a parallel split, all paths start simultaneously. Always call this before take_action.",
53
+ inputSchema: {
54
+ type: "object",
55
+ properties: {
56
+ instanceId: {
57
+ type: "string",
58
+ description: "ID of the instance to get available actions for",
59
+ },
60
+ },
61
+ required: ["instanceId"],
62
+ },
63
+ };
64
+ /**
65
+ * Tool: take_action
66
+ * Execute an action on an instance
67
+ */
68
+ export const takeActionTool = {
69
+ name: "take_action",
70
+ description: "Execute an action to advance the process to its next step. Pass the action 'id' from get_available_actions (not the name). The process designer determines what each action leads to — it may advance to the next task, branch at a decision, or complete the workflow.",
71
+ inputSchema: {
72
+ type: "object",
73
+ properties: {
74
+ instanceId: {
75
+ type: "string",
76
+ description: "ID of the instance to take action on",
77
+ },
78
+ action: {
79
+ type: "string",
80
+ description: "ID of the action to execute (use the 'id' field from get_available_actions)",
81
+ },
82
+ data: {
83
+ type: "object",
84
+ description: "Optional data payload for the action, if required",
85
+ additionalProperties: true,
86
+ },
87
+ },
88
+ required: ["instanceId", "action"],
89
+ },
90
+ };
91
+ /**
92
+ * Tool: get_diagram_context
93
+ * Get the full process flow structure
94
+ */
95
+ export const getDiagramContextTool = {
96
+ name: "get_diagram_context",
97
+ description: "Get the complete structure of a process flow: all steps (nodes), connections (edges), and available actions at each step. Use this when you need to understand the full workflow map. Prefer get_node_details for individual steps to reduce context size.",
98
+ inputSchema: {
99
+ type: "object",
100
+ properties: {
101
+ diagramId: {
102
+ type: "string",
103
+ description: "ID of the diagram to get context for",
104
+ },
105
+ },
106
+ required: ["diagramId"],
107
+ },
108
+ };
109
+ /**
110
+ * Tool: get_instance_notes
111
+ * Get all notes for an instance
112
+ */
113
+ export const getInstanceNotesTool = {
114
+ name: "get_instance_notes",
115
+ description: "Get all notes on a process instance. Notes are observations, decisions, or status updates left by agents and users throughout the workflow.",
116
+ inputSchema: {
117
+ type: "object",
118
+ properties: {
119
+ instanceId: {
120
+ type: "string",
121
+ description: "ID of the instance to get notes for",
122
+ },
123
+ },
124
+ required: ["instanceId"],
125
+ },
126
+ };
127
+ /**
128
+ * Tool: add_instance_note
129
+ * Add a note to an instance
130
+ */
131
+ export const addInstanceNoteTool = {
132
+ name: "add_instance_note",
133
+ description: "Add a note to a process instance to report progress, document a decision, or flag an issue. Notes are visible to the user who designed the process and to other agents working on it.",
134
+ inputSchema: {
135
+ type: "object",
136
+ properties: {
137
+ instanceId: {
138
+ type: "string",
139
+ description: "ID of the instance to add the note to",
140
+ },
141
+ text: {
142
+ type: "string",
143
+ description: "The note text to add",
144
+ },
145
+ },
146
+ required: ["instanceId", "text"],
147
+ },
148
+ };
149
+ /**
150
+ * Tool: get_node_details
151
+ * Get details for specific nodes in a diagram
152
+ */
153
+ export const getNodeDetailsTool = {
154
+ name: "get_node_details",
155
+ description: "Get the full label and description for specific steps (nodes) in a process diagram. Step descriptions contain the user's detailed instructions for what the agent should do. Pass multiple nodeIds in a single call to batch lookups.",
156
+ inputSchema: {
157
+ type: "object",
158
+ properties: {
159
+ diagramId: {
160
+ type: "string",
161
+ description: "ID of the diagram containing the nodes",
162
+ },
163
+ nodeIds: {
164
+ type: "array",
165
+ items: { type: "string" },
166
+ description: "Array of node IDs to get details for",
167
+ },
168
+ },
169
+ required: ["diagramId", "nodeIds"],
170
+ },
171
+ };
172
+ // =============================================================================
173
+ // Constants
174
+ // =============================================================================
175
+ const DESCRIPTION_PREVIEW_LENGTH = 200;
176
+ // =============================================================================
177
+ // Tool Handlers
178
+ // =============================================================================
179
+ /**
180
+ * Truncate a description to preview length, indicating if truncated
181
+ */
182
+ function truncateDescription(description) {
183
+ if (!description) {
184
+ return { text: null, truncated: false };
185
+ }
186
+ if (description.length <= DESCRIPTION_PREVIEW_LENGTH) {
187
+ return { text: description, truncated: false };
188
+ }
189
+ return {
190
+ text: description.substring(0, DESCRIPTION_PREVIEW_LENGTH) + "...",
191
+ truncated: true,
192
+ };
193
+ }
194
+ /**
195
+ * Handler for get_my_instances tool
196
+ * Gets instances assigned to the claimed user
197
+ */
198
+ export async function handleGetMyInstances(client, params) {
199
+ if (!hasClaimedUser() || !client.hasClaimedUser()) {
200
+ return JSON.stringify({
201
+ success: false,
202
+ error: "No user claimed. Call claim_user first.",
203
+ }, null, 2);
204
+ }
205
+ const instances = await client.getMyInstances(params.status);
206
+ if (!Array.isArray(instances) || instances.length === 0) {
207
+ return JSON.stringify({
208
+ success: true,
209
+ message: "No instances found for the claimed user",
210
+ instances: [],
211
+ filter: params.status || "all",
212
+ }, null, 2);
213
+ }
214
+ // Format instances for display (using backend WorkflowInstanceDto fields)
215
+ const formattedInstances = instances.map((instance) => ({
216
+ id: instance.id,
217
+ customer: instance.customer,
218
+ currentStep: instance.currentStep,
219
+ currentDiagramId: instance.currentDiagramId,
220
+ status: instance.status,
221
+ startedAt: instance.startedAt,
222
+ stepEnteredAt: instance.stepEnteredAt,
223
+ }));
224
+ return JSON.stringify({
225
+ success: true,
226
+ message: `Found ${instances.length} instance(s)`,
227
+ instances: formattedInstances,
228
+ filter: params.status || "all",
229
+ }, null, 2);
230
+ }
231
+ /**
232
+ * Handler for get_instance_details tool
233
+ * Gets full context for a specific instance including node labels
234
+ */
235
+ export async function handleGetInstanceDetails(client, params) {
236
+ if (!hasClaimedUser() || !client.hasClaimedUser()) {
237
+ return JSON.stringify({
238
+ success: false,
239
+ error: "No user claimed. Call claim_user first.",
240
+ }, null, 2);
241
+ }
242
+ if (!params.instanceId || params.instanceId.trim().length === 0) {
243
+ return JSON.stringify({
244
+ success: false,
245
+ error: "instanceId is required",
246
+ }, null, 2);
247
+ }
248
+ // Get the instance details from backend (returns WorkflowInstanceDto)
249
+ const instance = await client.getInstanceDetails(params.instanceId.trim());
250
+ // Fetch diagram to get node labels and descriptions
251
+ let currentNodeLabel = null;
252
+ let currentNodeDescription = null;
253
+ let previousNodeLabel = null;
254
+ let diagramName = null;
255
+ const stepHistoryWithLabels = [];
256
+ try {
257
+ const diagramId = instance.currentDiagramId;
258
+ if (diagramId) {
259
+ const diagram = await client.getDiagram(diagramId);
260
+ diagramName = diagram.name;
261
+ // Build a map of node IDs to nodes for quick lookup
262
+ const nodeMap = new Map();
263
+ for (const node of diagram.nodes) {
264
+ nodeMap.set(node.id, node);
265
+ // Also map by originalId if present
266
+ if (node.originalId) {
267
+ nodeMap.set(node.originalId, node);
268
+ }
269
+ }
270
+ // Get current node info
271
+ const currentStep = instance.currentStep;
272
+ const currentNode = nodeMap.get(currentStep);
273
+ if (currentNode) {
274
+ currentNodeLabel = currentNode.label || null;
275
+ currentNodeDescription = currentNode.description || null;
276
+ }
277
+ // Get previous node label
278
+ const previousStep = instance.previousStep;
279
+ if (previousStep) {
280
+ const prevNode = nodeMap.get(previousStep);
281
+ if (prevNode) {
282
+ previousNodeLabel = prevNode.label || null;
283
+ }
284
+ }
285
+ // Build step history with labels
286
+ const stepHistory = instance.stepHistory || [];
287
+ for (const stepId of stepHistory) {
288
+ const stepNode = nodeMap.get(stepId);
289
+ stepHistoryWithLabels.push({
290
+ nodeId: stepId,
291
+ label: stepNode ? (stepNode.label || null) : null,
292
+ });
293
+ }
294
+ }
295
+ }
296
+ catch {
297
+ // If diagram fetch fails, continue without labels
298
+ }
299
+ // Truncate description if too long
300
+ const descriptionPreview = truncateDescription(currentNodeDescription);
301
+ // Return the instance data with node labels
302
+ return JSON.stringify({
303
+ success: true,
304
+ instanceId: params.instanceId,
305
+ instance: {
306
+ id: instance.id,
307
+ userId: instance.userId,
308
+ userName: instance.userName,
309
+ customer: instance.customer,
310
+ currentStep: {
311
+ nodeId: instance.currentStep,
312
+ label: currentNodeLabel,
313
+ descriptionPreview: descriptionPreview.text,
314
+ hasFullDescription: descriptionPreview.truncated,
315
+ },
316
+ previousStep: instance.previousStep ? {
317
+ nodeId: instance.previousStep,
318
+ label: previousNodeLabel,
319
+ } : null,
320
+ currentDiagram: {
321
+ id: instance.currentDiagramId,
322
+ name: diagramName,
323
+ },
324
+ status: instance.status,
325
+ startedAt: instance.startedAt,
326
+ stepEnteredAt: instance.stepEnteredAt,
327
+ stepHistory: stepHistoryWithLabels,
328
+ iterationCount: instance.iterationCount,
329
+ rootInstanceId: instance.rootInstanceId,
330
+ waitingAtNodeId: instance.waitingAtNodeId,
331
+ parentNodeId: instance.parentNodeId,
332
+ parentDiagramId: instance.parentDiagramId,
333
+ isShadow: instance.isShadow,
334
+ },
335
+ hint: descriptionPreview.truncated
336
+ ? "Use get_node_details to get the full description of the current node"
337
+ : undefined,
338
+ }, null, 2);
339
+ }
340
+ /**
341
+ * Handler for get_available_actions tool
342
+ * Gets actions available at the current step.
343
+ * Detects parallel splits (non-diamond nodes with multiple exits) and merges
344
+ * them into a single action, matching the web interface behavior.
345
+ */
346
+ export async function handleGetAvailableActions(client, params) {
347
+ if (!hasClaimedUser() || !client.hasClaimedUser()) {
348
+ return JSON.stringify({
349
+ success: false,
350
+ error: "No user claimed. Call claim_user first.",
351
+ }, null, 2);
352
+ }
353
+ if (!params.instanceId || params.instanceId.trim().length === 0) {
354
+ return JSON.stringify({
355
+ success: false,
356
+ error: "instanceId is required",
357
+ }, null, 2);
358
+ }
359
+ const instanceId = params.instanceId.trim();
360
+ // Fetch instance to check waiting status and get diagram context
361
+ let instance = null;
362
+ try {
363
+ instance = await client.getInstanceDetails(instanceId);
364
+ }
365
+ catch {
366
+ // Continue without instance details
367
+ }
368
+ // Check if instance is waiting at a waitForAll node
369
+ if (instance?.waitingAtNodeId) {
370
+ return JSON.stringify({
371
+ success: true,
372
+ instanceId,
373
+ currentNodeId: instance.currentStep,
374
+ message: "Instance is waiting for all parallel branches to arrive at this node. No actions available until all splits merge.",
375
+ actions: [],
376
+ waiting: true,
377
+ }, null, 2);
378
+ }
379
+ const response = await client.getAvailableActions(instanceId);
380
+ if (response.actions.length === 0) {
381
+ return JSON.stringify({
382
+ success: true,
383
+ instanceId,
384
+ currentNodeId: response.currentNodeId,
385
+ message: "No actions available at the current step (may be at an end node)",
386
+ actions: [],
387
+ }, null, 2);
388
+ }
389
+ // Detect parallel splits by checking the current node's shape in the diagram
390
+ let isParallelSplit = false;
391
+ if (response.actions.length > 1 && instance) {
392
+ try {
393
+ const diagramId = instance.currentDiagramId;
394
+ if (diagramId) {
395
+ const diagram = await client.getDiagram(diagramId);
396
+ const currentStep = instance.currentStep;
397
+ // Find the current node and check its shape
398
+ const nodes = diagram.nodes;
399
+ const currentNode = nodes.find((n) => n.id === currentStep || n.originalId === currentStep);
400
+ if (currentNode && currentNode.shape !== "diamond") {
401
+ isParallelSplit = true;
402
+ }
403
+ }
404
+ }
405
+ catch {
406
+ // If diagram fetch fails, show actions individually
407
+ }
408
+ }
409
+ if (isParallelSplit) {
410
+ // Merge parallel actions into a single action (matches web interface)
411
+ // The backend automatically creates split instances for all other paths
412
+ // when any single action is taken from a parallel split node.
413
+ const mergedName = response.actions.map((a) => a.name || "proceed").join(" / ");
414
+ const targetDescriptions = response.actions
415
+ .map((a) => a.description || a.name || "proceed")
416
+ .join(", ");
417
+ return JSON.stringify({
418
+ success: true,
419
+ instanceId,
420
+ currentNodeId: response.currentNodeId,
421
+ message: "1 action available (parallel split - all paths start simultaneously). IMPORTANT: pass the action 'id' (not 'name') to take_action.",
422
+ actions: [
423
+ {
424
+ id: response.actions[0].id,
425
+ name: mergedName,
426
+ description: `Parallel split: starts all paths simultaneously (${targetDescriptions})`,
427
+ },
428
+ ],
429
+ }, null, 2);
430
+ }
431
+ // Decision point (diamond) or single action - show individually
432
+ const formattedActions = response.actions.map((action) => ({
433
+ id: action.id,
434
+ name: action.name,
435
+ description: action.description,
436
+ targetNodeId: action.targetNodeId,
437
+ requiresData: action.requiresData,
438
+ dataSchema: action.dataSchema,
439
+ }));
440
+ return JSON.stringify({
441
+ success: true,
442
+ instanceId,
443
+ currentNodeId: response.currentNodeId,
444
+ message: response.actions.length > 1
445
+ ? `${response.actions.length} actions available (decision point - choose one)`
446
+ : "1 action available",
447
+ actions: formattedActions,
448
+ }, null, 2);
449
+ }
450
+ /**
451
+ * Handler for take_action tool
452
+ * Executes an action on an instance
453
+ */
454
+ export async function handleTakeAction(client, params) {
455
+ if (!hasClaimedUser() || !client.hasClaimedUser()) {
456
+ return JSON.stringify({
457
+ success: false,
458
+ error: "No user claimed. Call claim_user first.",
459
+ }, null, 2);
460
+ }
461
+ if (!params.instanceId || params.instanceId.trim().length === 0) {
462
+ return JSON.stringify({
463
+ success: false,
464
+ error: "instanceId is required",
465
+ }, null, 2);
466
+ }
467
+ if (!params.action || params.action.trim().length === 0) {
468
+ return JSON.stringify({
469
+ success: false,
470
+ error: "action is required",
471
+ }, null, 2);
472
+ }
473
+ const response = await client.takeAction(params.instanceId.trim(), {
474
+ action: params.action.trim(),
475
+ data: params.data,
476
+ });
477
+ return JSON.stringify({
478
+ success: response.success,
479
+ message: response.success
480
+ ? `Action '${params.action}' executed successfully`
481
+ : "Action failed",
482
+ result: {
483
+ instanceId: response.instanceId,
484
+ action: response.action,
485
+ previousNodeId: response.previousNodeId,
486
+ currentNodeId: response.currentNodeId,
487
+ timestamp: response.timestamp,
488
+ },
489
+ }, null, 2);
490
+ }
491
+ /**
492
+ * Handler for get_diagram_context tool
493
+ * Gets the full process flow structure
494
+ */
495
+ export async function handleGetDiagramContext(client, params) {
496
+ if (!params.diagramId || params.diagramId.trim().length === 0) {
497
+ return JSON.stringify({
498
+ success: false,
499
+ error: "diagramId is required",
500
+ }, null, 2);
501
+ }
502
+ const context = await client.getDiagramContext(params.diagramId.trim());
503
+ // Convert Map to plain object for JSON serialization
504
+ const nodeDetailsObj = {};
505
+ context.nodeDetails.forEach((value, key) => {
506
+ nodeDetailsObj[key] = {
507
+ node: {
508
+ id: value.node.id,
509
+ type: value.node.type,
510
+ label: value.node.label,
511
+ position: value.node.position,
512
+ },
513
+ incomingEdges: value.incomingEdges.map((e) => ({
514
+ id: e.id,
515
+ sourceNodeId: e.sourceNodeId,
516
+ label: e.label,
517
+ })),
518
+ outgoingEdges: value.outgoingEdges.map((e) => ({
519
+ id: e.id,
520
+ targetNodeId: e.targetNodeId,
521
+ label: e.label,
522
+ condition: e.condition,
523
+ })),
524
+ availableActions: value.availableActions.map((a) => ({
525
+ id: a.id,
526
+ name: a.name,
527
+ description: a.description,
528
+ targetNodeId: a.targetNodeId,
529
+ })),
530
+ };
531
+ });
532
+ const diagramNodes = context.diagram.nodes ?? [];
533
+ const diagramEdges = context.diagram.edges ?? [];
534
+ return JSON.stringify({
535
+ success: true,
536
+ diagram: {
537
+ id: context.diagram.id,
538
+ name: context.diagram.name,
539
+ description: context.diagram.description,
540
+ nodeCount: diagramNodes.length,
541
+ edgeCount: diagramEdges.length,
542
+ },
543
+ nodes: diagramNodes.map((n) => ({
544
+ id: n.id,
545
+ type: n.type,
546
+ label: n.label,
547
+ position: n.position,
548
+ })),
549
+ edges: diagramEdges.map((e) => ({
550
+ id: e.id,
551
+ sourceNodeId: e.sourceNodeId,
552
+ targetNodeId: e.targetNodeId,
553
+ label: e.label,
554
+ condition: e.condition,
555
+ })),
556
+ nodeDetails: nodeDetailsObj,
557
+ }, null, 2);
558
+ }
559
+ /**
560
+ * Handler for get_instance_notes tool
561
+ * Gets all notes for an instance
562
+ */
563
+ export async function handleGetInstanceNotes(client, params) {
564
+ if (!hasClaimedUser() || !client.hasClaimedUser()) {
565
+ return JSON.stringify({
566
+ success: false,
567
+ error: "No user claimed. Call claim_user first.",
568
+ }, null, 2);
569
+ }
570
+ if (!params.instanceId || params.instanceId.trim().length === 0) {
571
+ return JSON.stringify({
572
+ success: false,
573
+ error: "instanceId is required",
574
+ }, null, 2);
575
+ }
576
+ const response = await client.getInstanceNotes(params.instanceId.trim());
577
+ const notes = response.notes || [];
578
+ if (notes.length === 0) {
579
+ return JSON.stringify({
580
+ success: true,
581
+ instanceId: params.instanceId,
582
+ message: "No notes found for this instance",
583
+ notes: [],
584
+ }, null, 2);
585
+ }
586
+ // Format notes for display
587
+ const formattedNotes = notes.map((note) => ({
588
+ id: note.id,
589
+ text: note.text,
590
+ userId: note.userId,
591
+ userName: note.userName,
592
+ createdAt: note.createdAt,
593
+ }));
594
+ return JSON.stringify({
595
+ success: true,
596
+ instanceId: params.instanceId,
597
+ message: `Found ${notes.length} note(s)`,
598
+ notes: formattedNotes,
599
+ }, null, 2);
600
+ }
601
+ /**
602
+ * Handler for add_instance_note tool
603
+ * Adds a note to an instance
604
+ */
605
+ export async function handleAddInstanceNote(client, params) {
606
+ if (!hasClaimedUser() || !client.hasClaimedUser()) {
607
+ return JSON.stringify({
608
+ success: false,
609
+ error: "No user claimed. Call claim_user first.",
610
+ }, null, 2);
611
+ }
612
+ if (!params.instanceId || params.instanceId.trim().length === 0) {
613
+ return JSON.stringify({
614
+ success: false,
615
+ error: "instanceId is required",
616
+ }, null, 2);
617
+ }
618
+ if (!params.text || params.text.trim().length === 0) {
619
+ return JSON.stringify({
620
+ success: false,
621
+ error: "text is required",
622
+ }, null, 2);
623
+ }
624
+ const note = await client.addInstanceNote(params.instanceId.trim(), params.text.trim());
625
+ return JSON.stringify({
626
+ success: true,
627
+ message: "Note added successfully",
628
+ note: {
629
+ id: note.id,
630
+ text: note.text,
631
+ userId: note.userId,
632
+ userName: note.userName,
633
+ createdAt: note.createdAt,
634
+ },
635
+ }, null, 2);
636
+ }
637
+ /**
638
+ * Handler for get_node_details tool
639
+ * Gets details for specific nodes in a diagram
640
+ */
641
+ export async function handleGetNodeDetails(client, params) {
642
+ if (!params.diagramId || params.diagramId.trim().length === 0) {
643
+ return JSON.stringify({
644
+ success: false,
645
+ error: "diagramId is required",
646
+ }, null, 2);
647
+ }
648
+ if (!params.nodeIds || !Array.isArray(params.nodeIds) || params.nodeIds.length === 0) {
649
+ return JSON.stringify({
650
+ success: false,
651
+ error: "nodeIds is required and must be a non-empty array",
652
+ }, null, 2);
653
+ }
654
+ const diagram = await client.getDiagram(params.diagramId.trim());
655
+ // Build node map for quick lookup
656
+ const nodeMap = new Map();
657
+ for (const node of diagram.nodes) {
658
+ nodeMap.set(node.id, node);
659
+ if (node.originalId) {
660
+ nodeMap.set(node.originalId, node);
661
+ }
662
+ }
663
+ // Get details for requested nodes
664
+ const nodeDetails = params.nodeIds.map((nodeId) => {
665
+ const node = nodeMap.get(nodeId.trim());
666
+ if (!node) {
667
+ return {
668
+ nodeId,
669
+ found: false,
670
+ };
671
+ }
672
+ return {
673
+ nodeId,
674
+ found: true,
675
+ label: node.label || null,
676
+ description: node.description || null,
677
+ type: node.type || null,
678
+ };
679
+ });
680
+ const foundCount = nodeDetails.filter((n) => n.found).length;
681
+ return JSON.stringify({
682
+ success: true,
683
+ diagramId: params.diagramId,
684
+ diagramName: diagram.name,
685
+ message: `Found ${foundCount} of ${params.nodeIds.length} node(s)`,
686
+ nodes: nodeDetails,
687
+ }, null, 2);
688
+ }
689
+ // =============================================================================
690
+ // Exports
691
+ // =============================================================================
692
+ export const workTools = [
693
+ getMyInstancesTool,
694
+ getInstanceDetailsTool,
695
+ getAvailableActionsTool,
696
+ takeActionTool,
697
+ getDiagramContextTool,
698
+ getInstanceNotesTool,
699
+ addInstanceNoteTool,
700
+ getNodeDetailsTool,
701
+ ];
702
+ //# sourceMappingURL=work.js.map