@eclipse-glsp/server-mcp 2.8.0-next.9 → 2.8.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.
- package/lib/common/modules/mcp-diagram-module.js +1 -1
- package/lib/common/modules/mcp-diagram-module.js.map +1 -1
- package/lib/common/server/mcp-diagram-handler-dispatcher.d.ts +20 -4
- package/lib/common/server/mcp-diagram-handler-dispatcher.d.ts.map +1 -1
- package/lib/common/server/mcp-diagram-handler-dispatcher.js +36 -5
- package/lib/common/server/mcp-diagram-handler-dispatcher.js.map +1 -1
- package/lib/common/server/mcp-input-schemas.d.ts +2 -0
- package/lib/common/server/mcp-input-schemas.d.ts.map +1 -1
- package/lib/common/server/mcp-input-schemas.js +3 -1
- package/lib/common/server/mcp-input-schemas.js.map +1 -1
- package/lib/common/server/mcp-resource-handler.d.ts +10 -1
- package/lib/common/server/mcp-resource-handler.d.ts.map +1 -1
- package/lib/common/server/mcp-resource-handler.js +12 -1
- package/lib/common/server/mcp-resource-handler.js.map +1 -1
- package/lib/common/server/mcp-tool-handler.d.ts +33 -16
- package/lib/common/server/mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/server/mcp-tool-handler.js +18 -1
- package/lib/common/server/mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/count-elements-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/count-elements-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/count-elements-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/create-edges-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/create-edges-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/create-edges-mcp-tool-handler.js +11 -6
- package/lib/common/tools/handlers/create-edges-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/create-nodes-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/create-nodes-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/create-nodes-mcp-tool-handler.js +14 -6
- package/lib/common/tools/handlers/create-nodes-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/delete-elements-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/delete-elements-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/delete-elements-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/diagram-model-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/diagram-model-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/diagram-model-mcp-tool-handler.js +4 -2
- package/lib/common/tools/handlers/diagram-model-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/element-types-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/element-types-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/element-types-mcp-tool-handler.js +4 -2
- package/lib/common/tools/handlers/element-types-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/get-selection-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/get-selection-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/get-selection-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/layout-mcp-tool-handler.d.ts +5 -2
- package/lib/common/tools/handlers/layout-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/layout-mcp-tool-handler.js +5 -1
- package/lib/common/tools/handlers/layout-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/modify-edges-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/modify-edges-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/modify-edges-mcp-tool-handler.js +10 -7
- package/lib/common/tools/handlers/modify-edges-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/modify-nodes-mcp-tool-handler.d.ts +4 -1
- package/lib/common/tools/handlers/modify-nodes-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/modify-nodes-mcp-tool-handler.js +54 -22
- package/lib/common/tools/handlers/modify-nodes-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/query-elements-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/query-elements-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/query-elements-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/redo-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/redo-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/redo-mcp-tool-handler.js +7 -3
- package/lib/common/tools/handlers/redo-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/save-model-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/save-model-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/save-model-mcp-tool-handler.js +3 -1
- package/lib/common/tools/handlers/save-model-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/session-info-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/session-info-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/session-info-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/set-selection-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/set-selection-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/set-selection-mcp-tool-handler.js +5 -3
- package/lib/common/tools/handlers/set-selection-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/set-view-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/set-view-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/set-view-mcp-tool-handler.js +1 -1
- package/lib/common/tools/handlers/set-view-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/undo-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/undo-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/undo-mcp-tool-handler.js +7 -3
- package/lib/common/tools/handlers/undo-mcp-tool-handler.js.map +1 -1
- package/lib/common/tools/handlers/validate-diagram-mcp-tool-handler.d.ts +2 -1
- package/lib/common/tools/handlers/validate-diagram-mcp-tool-handler.d.ts.map +1 -1
- package/lib/common/tools/handlers/validate-diagram-mcp-tool-handler.js +0 -0
- package/lib/common/tools/handlers/validate-diagram-mcp-tool-handler.js.map +1 -1
- package/package.json +2 -2
- package/src/common/modules/mcp-diagram-module.ts +1 -1
- package/src/common/server/mcp-diagram-handler-dispatcher.ts +56 -5
- package/src/common/server/mcp-input-schemas.ts +3 -0
- package/src/common/server/mcp-resource-handler.ts +13 -1
- package/src/common/server/mcp-tool-handler.ts +47 -19
- package/src/common/tools/handlers/count-elements-mcp-tool-handler.ts +2 -1
- package/src/common/tools/handlers/create-edges-mcp-tool-handler.ts +20 -8
- package/src/common/tools/handlers/create-nodes-mcp-tool-handler.ts +19 -8
- package/src/common/tools/handlers/delete-elements-mcp-tool-handler.ts +2 -1
- package/src/common/tools/handlers/diagram-model-mcp-tool-handler.ts +6 -3
- package/src/common/tools/handlers/element-types-mcp-tool-handler.ts +6 -3
- package/src/common/tools/handlers/get-selection-mcp-tool-handler.ts +2 -1
- package/src/common/tools/handlers/layout-mcp-tool-handler.ts +8 -2
- package/src/common/tools/handlers/modify-edges-mcp-tool-handler.ts +14 -8
- package/src/common/tools/handlers/modify-nodes-mcp-tool-handler.ts +63 -25
- package/src/common/tools/handlers/query-elements-mcp-tool-handler.ts +2 -1
- package/src/common/tools/handlers/redo-mcp-tool-handler.ts +9 -4
- package/src/common/tools/handlers/save-model-mcp-tool-handler.ts +5 -2
- package/src/common/tools/handlers/session-info-mcp-tool-handler.ts +2 -1
- package/src/common/tools/handlers/set-selection-mcp-tool-handler.ts +8 -5
- package/src/common/tools/handlers/set-view-mcp-tool-handler.ts +3 -2
- package/src/common/tools/handlers/undo-mcp-tool-handler.ts +9 -4
- package/src/common/tools/handlers/validate-diagram-mcp-tool-handler.ts +0 -0
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
16
|
|
|
17
|
-
import { ApplyLabelEditOperation, ChangeBoundsOperation, GEdge,
|
|
17
|
+
import { ApplyLabelEditOperation, ChangeBoundsOperation, GEdge, GModelRoot, GNode } from '@eclipse-glsp/server';
|
|
18
18
|
import { injectable } from 'inversify';
|
|
19
19
|
import * as z from 'zod/v4';
|
|
20
20
|
import { McpToolError, McpToolResult } from '../../server/mcp-handler-shared';
|
|
@@ -37,7 +37,13 @@ export const NodeSizeSchema = z.strictObject({
|
|
|
37
37
|
/** Single node-modification entry. Strict so an LLM-typoed field surfaces as a validation error instead of being silently dropped. */
|
|
38
38
|
export const ModifyNodeSpecSchema = z.strictObject({
|
|
39
39
|
elementId,
|
|
40
|
-
position: position
|
|
40
|
+
position: position
|
|
41
|
+
.optional()
|
|
42
|
+
.describe(
|
|
43
|
+
'Position where the node should be moved to, relative to the parent element ' +
|
|
44
|
+
'(identical to absolute diagram coordinates for direct children of the root, which is the common case). ' +
|
|
45
|
+
'Matches the `position` reported by `query-elements`. Note `create-nodes` takes absolute coordinates instead.'
|
|
46
|
+
),
|
|
41
47
|
size: NodeSizeSchema.optional().describe('New size of the node'),
|
|
42
48
|
text: z.string().optional().describe("Label text to use instead (given that the element's type allows for labels).")
|
|
43
49
|
});
|
|
@@ -55,15 +61,17 @@ export const ModifyNodesOutputSchema = z.object({
|
|
|
55
61
|
.array(ElementIdentitySchema)
|
|
56
62
|
.describe('Identity of each node whose change request was dispatched (post-modification labels).'),
|
|
57
63
|
dispatchedCommands,
|
|
64
|
+
errors: z.array(z.string()).describe('Per-input failure messages; absent or empty when every input succeeded.'),
|
|
58
65
|
warnings: z
|
|
59
66
|
.array(z.string())
|
|
60
67
|
.describe(
|
|
61
68
|
'Soft notices for inputs whose change applied with caveats (e.g. `text` supplied for a node whose type has no editable label).'
|
|
62
69
|
)
|
|
63
70
|
});
|
|
71
|
+
export type ModifyNodesOutput = z.infer<typeof ModifyNodesOutputSchema>;
|
|
64
72
|
|
|
65
73
|
@injectable()
|
|
66
|
-
export class ModifyNodesMcpToolHandler extends OperationMcpDiagramToolHandler<ModifyNodesInput> {
|
|
74
|
+
export class ModifyNodesMcpToolHandler extends OperationMcpDiagramToolHandler<ModifyNodesInput, ModifyNodesOutput> {
|
|
67
75
|
static readonly NAME = 'modify-nodes';
|
|
68
76
|
readonly name = ModifyNodesMcpToolHandler.NAME;
|
|
69
77
|
override readonly title = 'Modify Diagram Nodes';
|
|
@@ -88,53 +96,83 @@ export class ModifyNodesMcpToolHandler extends OperationMcpDiagramToolHandler<Mo
|
|
|
88
96
|
throw new McpToolError(`modify-nodes does not accept edges — got: ${wrongType.join(', ')}. Use modify-edges for edges.`);
|
|
89
97
|
}
|
|
90
98
|
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
99
|
+
// The root has no bounds of its own, and a `text` edit against it would rename whatever
|
|
100
|
+
// top-level `GLabel` the label provider happens to find first.
|
|
101
|
+
const roots = elements.filter(([, element]) => element instanceof GModelRoot).map(([change]) => `'${change.elementId}'`);
|
|
102
|
+
if (roots.length) {
|
|
103
|
+
throw new McpToolError(
|
|
104
|
+
`modify-nodes does not accept the diagram root — got: ${roots.join(', ')}. Target a node inside the diagram instead.`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Core's `GModelChangeBoundsOperationHandler` only applies bounds to a `GNode`
|
|
109
|
+
// (`findByClass`). Label-only edits stay open to every element kind.
|
|
110
|
+
const unmovable = elements
|
|
111
|
+
.filter(([change, element]) => (change.position || change.size) && !(element instanceof GNode))
|
|
112
|
+
.map(([change, element]) => `'${change.elementId}' (type '${element.type}')`);
|
|
113
|
+
if (unmovable.length) {
|
|
95
114
|
throw new McpToolError(
|
|
96
|
-
`modify-nodes only
|
|
97
|
-
'Use `query-elements` (inspect mode) to find
|
|
115
|
+
`modify-nodes can only change \`position\` / \`size\` of nodes — got: ${unmovable.join(', ')}. ` +
|
|
116
|
+
'Use `query-elements` (inspect mode) to find node ids, or target the parent node.'
|
|
98
117
|
);
|
|
99
118
|
}
|
|
100
119
|
|
|
101
|
-
// Modifications are independent of each other — dispatch in parallel.
|
|
102
|
-
|
|
120
|
+
// Modifications are independent of each other — dispatch in parallel. `allSettled` so one
|
|
121
|
+
// failed dispatch surfaces in `errors` instead of rejecting the whole call and losing the
|
|
122
|
+
// other outcomes, which have already mutated the model.
|
|
123
|
+
const dispatched: Array<{ promise: Promise<void>; inputId: string }> = [];
|
|
124
|
+
const errors: string[] = [];
|
|
103
125
|
const warnings: string[] = [];
|
|
104
126
|
elements.forEach(([change, element]) => {
|
|
105
|
-
// Guaranteed non-null and shape-typed by the missing-elements / nonShape checks above.
|
|
106
|
-
const resolved = element as GShapeElement;
|
|
107
127
|
const { size, position, text } = change;
|
|
108
128
|
const realId = this.aliasService.lookup(change.elementId);
|
|
109
129
|
|
|
110
|
-
if (size
|
|
111
|
-
|
|
112
|
-
|
|
130
|
+
if (!size && !position && !text) {
|
|
131
|
+
errors.push(`No change requested for node '${change.elementId}' — provide \`position\`, \`size\` or a non-empty \`text\`.`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if ((size || position) && element instanceof GNode) {
|
|
136
|
+
const newSize = size ?? element.size;
|
|
137
|
+
const newPosition = position ?? element.position;
|
|
113
138
|
|
|
114
139
|
const operation = ChangeBoundsOperation.create([{ elementId: realId, newSize, newPosition }]);
|
|
115
|
-
|
|
140
|
+
dispatched.push({ promise: this.actionDispatcher.dispatch(operation), inputId: change.elementId });
|
|
116
141
|
}
|
|
117
142
|
|
|
118
143
|
if (text) {
|
|
119
|
-
const labelId = this.labelProvider.getLabel(
|
|
144
|
+
const labelId = this.labelProvider.getLabel(element)?.id;
|
|
120
145
|
if (labelId) {
|
|
121
|
-
|
|
146
|
+
const operation = ApplyLabelEditOperation.create({ labelId, text });
|
|
147
|
+
dispatched.push({ promise: this.actionDispatcher.dispatch(operation), inputId: change.elementId });
|
|
122
148
|
} else {
|
|
123
149
|
warnings.push(
|
|
124
|
-
`Ignored \`text\` for '${change.elementId}' (type '${
|
|
150
|
+
`Ignored \`text\` for '${change.elementId}' (type '${element.type}') — this element has no editable label.`
|
|
125
151
|
);
|
|
126
152
|
}
|
|
127
153
|
}
|
|
128
154
|
});
|
|
129
155
|
|
|
130
|
-
await Promise.
|
|
156
|
+
const results = await Promise.allSettled(dispatched.map(entry => entry.promise));
|
|
157
|
+
const modifiedInputIds = new Set<string>();
|
|
158
|
+
results.forEach((result, index) => {
|
|
159
|
+
const { inputId } = dispatched[index];
|
|
160
|
+
if (result.status === 'rejected') {
|
|
161
|
+
const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
162
|
+
errors.push(`Failed to modify node '${inputId}': ${reason}`);
|
|
163
|
+
} else {
|
|
164
|
+
modifiedInputIds.add(inputId);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
131
167
|
|
|
132
|
-
const modifiedNodes =
|
|
133
|
-
.map(
|
|
168
|
+
const modifiedNodes = [...modifiedInputIds]
|
|
169
|
+
.map(inputId => this.describeElement(inputId))
|
|
134
170
|
.filter((entry): entry is NonNullable<typeof entry> => entry !== undefined);
|
|
135
171
|
return this.success(
|
|
136
|
-
`Successfully modified ${
|
|
137
|
-
|
|
172
|
+
`Successfully modified ${modifiedNodes.length} node(s) (in ${dispatched.length} commands)` +
|
|
173
|
+
formatNoticeList('errors', errors) +
|
|
174
|
+
formatNoticeList('warnings', warnings),
|
|
175
|
+
{ modifiedNodes, dispatchedCommands: dispatched.length, errors, warnings }
|
|
138
176
|
);
|
|
139
177
|
}
|
|
140
178
|
}
|
|
@@ -60,10 +60,11 @@ export const QueryElementsOutputSchema = z.object({
|
|
|
60
60
|
'The `elements` array then includes the container plus its descendants.'
|
|
61
61
|
)
|
|
62
62
|
});
|
|
63
|
+
export type QueryElementsOutput = z.infer<typeof QueryElementsOutputSchema>;
|
|
63
64
|
|
|
64
65
|
/** Two-mode element query — list/filter or inspect-by-id, discriminated by `elementIds` presence. */
|
|
65
66
|
@injectable()
|
|
66
|
-
export class QueryElementsMcpToolHandler extends AbstractMcpDiagramToolHandler<QueryElementsInput> {
|
|
67
|
+
export class QueryElementsMcpToolHandler extends AbstractMcpDiagramToolHandler<QueryElementsInput, QueryElementsOutput> {
|
|
67
68
|
static readonly NAME = 'query-elements';
|
|
68
69
|
readonly name = QueryElementsMcpToolHandler.NAME;
|
|
69
70
|
override readonly title = 'Query Diagram Elements';
|
|
@@ -22,19 +22,20 @@ import { McpDiagramScopedInputSchema } from '../../server/mcp-input-schemas';
|
|
|
22
22
|
import { OperationMcpDiagramToolHandler } from '../../server/mcp-tool-handler';
|
|
23
23
|
|
|
24
24
|
export const RedoInputSchema = McpDiagramScopedInputSchema.extend({
|
|
25
|
-
commandsToRedo: z.number().min(1).default(1).describe('Number of commands to redo. Defaults to 1 (most recent undone command).')
|
|
25
|
+
commandsToRedo: z.number().int().min(1).default(1).describe('Number of commands to redo. Defaults to 1 (most recent undone command).')
|
|
26
26
|
});
|
|
27
27
|
export type RedoInput = z.infer<typeof RedoInputSchema>;
|
|
28
28
|
|
|
29
29
|
export const RedoOutputSchema = z.object({
|
|
30
30
|
commandsRedone: z.number().int().describe('Number of previously-undone commands re-applied.')
|
|
31
31
|
});
|
|
32
|
+
export type RedoOutput = z.infer<typeof RedoOutputSchema>;
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Redo a given number of the most recent undone actions on the command stack.
|
|
35
36
|
*/
|
|
36
37
|
@injectable()
|
|
37
|
-
export class RedoMcpToolHandler extends OperationMcpDiagramToolHandler<RedoInput> {
|
|
38
|
+
export class RedoMcpToolHandler extends OperationMcpDiagramToolHandler<RedoInput, RedoOutput> {
|
|
38
39
|
static readonly NAME = 'redo';
|
|
39
40
|
readonly name = RedoMcpToolHandler.NAME;
|
|
40
41
|
override readonly title = 'Redo Diagram Commands';
|
|
@@ -55,10 +56,14 @@ export class RedoMcpToolHandler extends OperationMcpDiagramToolHandler<RedoInput
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
// The stack may hold fewer commands than requested.
|
|
60
|
+
let commandsRedone = 0;
|
|
61
|
+
while (commandsRedone < commandsToRedo && this.commandStack.canRedo()) {
|
|
59
62
|
await this.actionDispatcher.dispatch(RedoAction.create());
|
|
63
|
+
commandsRedone++;
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
const shortfall = commandsRedone < commandsToRedo ? ` (${commandsToRedo} requested; the redo stack held no more commands)` : '';
|
|
67
|
+
return this.success(`Redo successful: re-applied ${commandsRedone} command(s)${shortfall}`, { commandsRedone });
|
|
63
68
|
}
|
|
64
69
|
}
|
|
@@ -34,10 +34,11 @@ export const SaveModelOutputSchema = z.object({
|
|
|
34
34
|
.optional()
|
|
35
35
|
.describe('Destination file URI when explicitly provided; absent when saving to the original source location.')
|
|
36
36
|
});
|
|
37
|
+
export type SaveModelOutput = z.infer<typeof SaveModelOutputSchema>;
|
|
37
38
|
|
|
38
39
|
/** Doesn't extend `OperationMcpDiagramToolHandler` — saving a read-only model is a legitimate adopter scenario (e.g. "save as" to a writable location). */
|
|
39
40
|
@injectable()
|
|
40
|
-
export class SaveModelMcpToolHandler extends AbstractMcpDiagramToolHandler<SaveModelInput> {
|
|
41
|
+
export class SaveModelMcpToolHandler extends AbstractMcpDiagramToolHandler<SaveModelInput, SaveModelOutput> {
|
|
41
42
|
static readonly NAME = 'save-model';
|
|
42
43
|
readonly name = SaveModelMcpToolHandler.NAME;
|
|
43
44
|
override readonly title = 'Save Diagram Model';
|
|
@@ -62,7 +63,9 @@ export class SaveModelMcpToolHandler extends AbstractMcpDiagramToolHandler<SaveM
|
|
|
62
63
|
@inject(McpLogger) protected mcpLogger: McpLogger;
|
|
63
64
|
|
|
64
65
|
protected async createResult({ fileUri }: SaveModelInput): Promise<McpToolResult> {
|
|
65
|
-
|
|
66
|
+
// Only save-in-place can be short-circuited: a "save as" destination has no copy yet,
|
|
67
|
+
// dirty or not.
|
|
68
|
+
if (!fileUri && !this.commandStack.isDirty) {
|
|
66
69
|
this.mcpLogger.info('save-model: nothing to save');
|
|
67
70
|
return this.success('No changes to save', { saved: false, fileUri });
|
|
68
71
|
}
|
|
@@ -40,9 +40,10 @@ export type SessionInfoRow = z.infer<typeof SessionInfoRowSchema>;
|
|
|
40
40
|
export const SessionInfoOutputSchema = z.object({
|
|
41
41
|
sessions: z.array(SessionInfoRowSchema)
|
|
42
42
|
});
|
|
43
|
+
export type SessionInfoOutput = z.infer<typeof SessionInfoOutputSchema>;
|
|
43
44
|
|
|
44
45
|
@injectable()
|
|
45
|
-
export class SessionInfoMcpToolHandler extends AbstractMcpToolHandler<SessionInfoInput> {
|
|
46
|
+
export class SessionInfoMcpToolHandler extends AbstractMcpToolHandler<SessionInfoInput, SessionInfoOutput> {
|
|
46
47
|
@inject(ClientSessionManager) protected clientSessionManager: ClientSessionManager;
|
|
47
48
|
|
|
48
49
|
static readonly NAME = 'session-info';
|
|
@@ -18,14 +18,16 @@ import { ActionDispatcher, SelectAction, SelectAllAction } from '@eclipse-glsp/s
|
|
|
18
18
|
import { inject, injectable } from 'inversify';
|
|
19
19
|
import * as z from 'zod/v4';
|
|
20
20
|
import { McpToolResult } from '../../server/mcp-handler-shared';
|
|
21
|
-
import { McpDiagramScopedInputSchema,
|
|
21
|
+
import { McpDiagramScopedInputSchema, elementIdsAllowingEmpty } from '../../server/mcp-input-schemas';
|
|
22
22
|
import { AbstractMcpDiagramToolHandler } from '../../server/mcp-tool-handler';
|
|
23
23
|
|
|
24
24
|
export const SetSelectionInputSchema = McpDiagramScopedInputSchema.extend({
|
|
25
|
-
selectedElementIds:
|
|
25
|
+
selectedElementIds: elementIdsAllowingEmpty
|
|
26
26
|
.optional()
|
|
27
|
-
.describe('Element IDs to select. Pass an empty array (or omit
|
|
28
|
-
deselectedElementIds:
|
|
27
|
+
.describe('Element IDs to select. Pass an empty array (or omit the field) together with `clear: true` to clear the selection.'),
|
|
28
|
+
deselectedElementIds: elementIdsAllowingEmpty
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Element IDs to remove from the selection. Used to subtract without replacing.'),
|
|
29
31
|
clear: z
|
|
30
32
|
.boolean()
|
|
31
33
|
.optional()
|
|
@@ -38,6 +40,7 @@ export const SetSelectionOutputSchema = z.object({
|
|
|
38
40
|
deselectedElementIds: z.array(z.string()).describe('Aliased ids requested for deselection.'),
|
|
39
41
|
cleared: z.boolean().describe('Whether the existing selection was cleared before applying.')
|
|
40
42
|
});
|
|
43
|
+
export type SetSelectionOutput = z.infer<typeof SetSelectionOutputSchema>;
|
|
41
44
|
|
|
42
45
|
/**
|
|
43
46
|
* Pushes a selection change to the client. Counterpart to `get-selection` — useful for the
|
|
@@ -48,7 +51,7 @@ export const SetSelectionOutputSchema = z.object({
|
|
|
48
51
|
* even though the underlying GModel is unaffected.
|
|
49
52
|
*/
|
|
50
53
|
@injectable()
|
|
51
|
-
export class SetSelectionMcpToolHandler extends AbstractMcpDiagramToolHandler<SetSelectionInput> {
|
|
54
|
+
export class SetSelectionMcpToolHandler extends AbstractMcpDiagramToolHandler<SetSelectionInput, SetSelectionOutput> {
|
|
52
55
|
static readonly NAME = 'set-selection';
|
|
53
56
|
readonly name = SetSelectionMcpToolHandler.NAME;
|
|
54
57
|
override readonly title = 'Set Diagram Selection';
|
|
@@ -74,9 +74,10 @@ export const SetViewOutputSchema = z.object({
|
|
|
74
74
|
.optional()
|
|
75
75
|
.describe("Resolved final viewport when `action: 'set-viewport'` was applied (current values merged with the supplied overrides).")
|
|
76
76
|
});
|
|
77
|
+
export type SetViewOutput = z.infer<typeof SetViewOutputSchema>;
|
|
77
78
|
|
|
78
79
|
@injectable()
|
|
79
|
-
export class SetViewMcpToolHandler extends AbstractMcpDiagramToolHandler<SetViewInput> {
|
|
80
|
+
export class SetViewMcpToolHandler extends AbstractMcpDiagramToolHandler<SetViewInput, SetViewOutput> {
|
|
80
81
|
/** Timeout (in ms) for the `GetViewportAction` round-trip used by `set-viewport` partial updates. Override via subclass + rebind. */
|
|
81
82
|
protected readonly viewportQueryTimeoutMs: number = 5000;
|
|
82
83
|
|
|
@@ -102,7 +103,7 @@ export class SetViewMcpToolHandler extends AbstractMcpDiagramToolHandler<SetView
|
|
|
102
103
|
if (action === 'set-viewport') {
|
|
103
104
|
return this.applyExplicitViewport(zoom, scroll);
|
|
104
105
|
}
|
|
105
|
-
const resolvedIds = elementIds ?
|
|
106
|
+
const resolvedIds = elementIds ? this.resolveExistingIds(elementIds) : this.modelState.index.allIds();
|
|
106
107
|
const dispatchAction = this.buildIntentAction(action, resolvedIds);
|
|
107
108
|
await this.actionDispatcher.dispatch(dispatchAction);
|
|
108
109
|
return this.success('Viewport successfully changed', { action, targetIds: this.encodeIds(resolvedIds) });
|
|
@@ -22,19 +22,20 @@ import { McpDiagramScopedInputSchema } from '../../server/mcp-input-schemas';
|
|
|
22
22
|
import { OperationMcpDiagramToolHandler } from '../../server/mcp-tool-handler';
|
|
23
23
|
|
|
24
24
|
export const UndoInputSchema = McpDiagramScopedInputSchema.extend({
|
|
25
|
-
commandsToUndo: z.number().min(1).default(1).describe('Number of commands to undo. Defaults to 1 (most recent command).')
|
|
25
|
+
commandsToUndo: z.number().int().min(1).default(1).describe('Number of commands to undo. Defaults to 1 (most recent command).')
|
|
26
26
|
});
|
|
27
27
|
export type UndoInput = z.infer<typeof UndoInputSchema>;
|
|
28
28
|
|
|
29
29
|
export const UndoOutputSchema = z.object({
|
|
30
30
|
commandsUndone: z.number().int().describe('Number of commands actually reverted.')
|
|
31
31
|
});
|
|
32
|
+
export type UndoOutput = z.infer<typeof UndoOutputSchema>;
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Undo a given number of the most recent actions on the command stack.
|
|
35
36
|
*/
|
|
36
37
|
@injectable()
|
|
37
|
-
export class UndoMcpToolHandler extends OperationMcpDiagramToolHandler<UndoInput> {
|
|
38
|
+
export class UndoMcpToolHandler extends OperationMcpDiagramToolHandler<UndoInput, UndoOutput> {
|
|
38
39
|
static readonly NAME = 'undo';
|
|
39
40
|
readonly name = UndoMcpToolHandler.NAME;
|
|
40
41
|
override readonly title = 'Undo Diagram Commands';
|
|
@@ -54,10 +55,14 @@ export class UndoMcpToolHandler extends OperationMcpDiagramToolHandler<UndoInput
|
|
|
54
55
|
throw new McpToolError('Nothing to undo (undo stack is empty; the model is at its initial state for this session).');
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
// The stack may hold fewer commands than requested.
|
|
59
|
+
let commandsUndone = 0;
|
|
60
|
+
while (commandsUndone < commandsToUndo && this.commandStack.canUndo()) {
|
|
58
61
|
await this.actionDispatcher.dispatch(UndoAction.create());
|
|
62
|
+
commandsUndone++;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
const shortfall = commandsUndone < commandsToUndo ? ` (${commandsToUndo} requested; the undo stack held no more commands)` : '';
|
|
66
|
+
return this.success(`Undo successful: reverted ${commandsUndone} command(s)${shortfall}`, { commandsUndone });
|
|
62
67
|
}
|
|
63
68
|
}
|
|
Binary file
|