@eclipse-glsp/server-mcp 2.8.0-next.1 → 2.8.0-next.10

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 (120) hide show
  1. package/README.md +7 -7
  2. package/lib/browser/modules/browser-mcp-server-module.js +1 -1
  3. package/lib/browser/modules/browser-mcp-server-module.js.map +1 -1
  4. package/lib/common/modules/abstract-mcp-server-module.d.ts +1 -2
  5. package/lib/common/modules/abstract-mcp-server-module.d.ts.map +1 -1
  6. package/lib/common/modules/abstract-mcp-server-module.js.map +1 -1
  7. package/lib/common/prompts/handlers/describe-diagram-mcp-prompt-handler.js +3 -2
  8. package/lib/common/prompts/handlers/describe-diagram-mcp-prompt-handler.js.map +1 -1
  9. package/lib/common/prompts/handlers/suggest-improvements-mcp-prompt-handler.js +3 -2
  10. package/lib/common/prompts/handlers/suggest-improvements-mcp-prompt-handler.js.map +1 -1
  11. package/lib/common/resources/handlers/diagram-png-mcp-resource-handler.js +5 -4
  12. package/lib/common/resources/handlers/diagram-png-mcp-resource-handler.js.map +1 -1
  13. package/lib/common/resources/handlers/diagram-svg-mcp-resource-handler.js +4 -3
  14. package/lib/common/resources/handlers/diagram-svg-mcp-resource-handler.js.map +1 -1
  15. package/lib/common/resources/services/element-types-provider.d.ts.map +1 -1
  16. package/lib/common/resources/services/element-types-provider.js +1 -2
  17. package/lib/common/resources/services/element-types-provider.js.map +1 -1
  18. package/lib/common/resources/services/mcp-model-serializer.d.ts +1 -2
  19. package/lib/common/resources/services/mcp-model-serializer.d.ts.map +1 -1
  20. package/lib/common/resources/services/mcp-model-serializer.js +3 -5
  21. package/lib/common/resources/services/mcp-model-serializer.js.map +1 -1
  22. package/lib/common/server/abstract-mcp-server-launcher.d.ts +3 -4
  23. package/lib/common/server/abstract-mcp-server-launcher.d.ts.map +1 -1
  24. package/lib/common/server/abstract-mcp-server-launcher.js +9 -14
  25. package/lib/common/server/abstract-mcp-server-launcher.js.map +1 -1
  26. package/lib/common/server/glsp-mcp-server.d.ts.map +1 -1
  27. package/lib/common/server/glsp-mcp-server.js +2 -2
  28. package/lib/common/server/glsp-mcp-server.js.map +1 -1
  29. package/lib/common/server/lru-event-store.js +2 -3
  30. package/lib/common/server/lru-event-store.js.map +1 -1
  31. package/lib/common/server/mcp-diagram-handler-dispatcher.js +4 -8
  32. package/lib/common/server/mcp-diagram-handler-dispatcher.js.map +1 -1
  33. package/lib/common/server/mcp-handler-shared.d.ts +1 -2
  34. package/lib/common/server/mcp-handler-shared.d.ts.map +1 -1
  35. package/lib/common/server/mcp-handler-shared.js +1 -1
  36. package/lib/common/server/mcp-handler-shared.js.map +1 -1
  37. package/lib/common/server/mcp-id-alias-service.js +1 -2
  38. package/lib/common/server/mcp-id-alias-service.js.map +1 -1
  39. package/lib/common/server/mcp-log-level-registry.js +10 -10
  40. package/lib/common/server/mcp-log-level-registry.js.map +1 -1
  41. package/lib/common/server/mcp-mime-types.d.ts +1 -1
  42. package/lib/common/server/mcp-mime-types.d.ts.map +1 -1
  43. package/lib/common/server/mcp-options.d.ts +1 -1
  44. package/lib/common/server/mcp-options.d.ts.map +1 -1
  45. package/lib/common/server/mcp-progress-reporter.js +1 -2
  46. package/lib/common/server/mcp-progress-reporter.js.map +1 -1
  47. package/lib/common/server/mcp-resource-handler.js +5 -9
  48. package/lib/common/server/mcp-resource-handler.js.map +1 -1
  49. package/lib/common/server/mcp-tool-handler.js +1 -2
  50. package/lib/common/server/mcp-tool-handler.js.map +1 -1
  51. package/lib/common/tools/handlers/count-elements-mcp-tool-handler.js +4 -4
  52. package/lib/common/tools/handlers/count-elements-mcp-tool-handler.js.map +1 -1
  53. package/lib/common/tools/handlers/create-edges-mcp-tool-handler.js +4 -3
  54. package/lib/common/tools/handlers/create-edges-mcp-tool-handler.js.map +1 -1
  55. package/lib/common/tools/handlers/create-nodes-mcp-tool-handler.js +5 -5
  56. package/lib/common/tools/handlers/create-nodes-mcp-tool-handler.js.map +1 -1
  57. package/lib/common/tools/handlers/delete-elements-mcp-tool-handler.js +3 -2
  58. package/lib/common/tools/handlers/delete-elements-mcp-tool-handler.js.map +1 -1
  59. package/lib/common/tools/handlers/diagram-model-mcp-tool-handler.js +3 -2
  60. package/lib/common/tools/handlers/diagram-model-mcp-tool-handler.js.map +1 -1
  61. package/lib/common/tools/handlers/element-types-mcp-tool-handler.js +4 -3
  62. package/lib/common/tools/handlers/element-types-mcp-tool-handler.js.map +1 -1
  63. package/lib/common/tools/handlers/get-selection-mcp-tool-handler.js +3 -2
  64. package/lib/common/tools/handlers/get-selection-mcp-tool-handler.js.map +1 -1
  65. package/lib/common/tools/handlers/layout-mcp-tool-handler.js +3 -2
  66. package/lib/common/tools/handlers/layout-mcp-tool-handler.js.map +1 -1
  67. package/lib/common/tools/handlers/modify-edges-mcp-tool-handler.js +3 -2
  68. package/lib/common/tools/handlers/modify-edges-mcp-tool-handler.js.map +1 -1
  69. package/lib/common/tools/handlers/modify-nodes-mcp-tool-handler.js +6 -6
  70. package/lib/common/tools/handlers/modify-nodes-mcp-tool-handler.js.map +1 -1
  71. package/lib/common/tools/handlers/query-elements-mcp-tool-handler.js +7 -7
  72. package/lib/common/tools/handlers/query-elements-mcp-tool-handler.js.map +1 -1
  73. package/lib/common/tools/handlers/redo-mcp-tool-handler.js +3 -2
  74. package/lib/common/tools/handlers/redo-mcp-tool-handler.js.map +1 -1
  75. package/lib/common/tools/handlers/save-model-mcp-tool-handler.js +3 -2
  76. package/lib/common/tools/handlers/save-model-mcp-tool-handler.js.map +1 -1
  77. package/lib/common/tools/handlers/session-info-mcp-tool-handler.js +4 -4
  78. package/lib/common/tools/handlers/session-info-mcp-tool-handler.js.map +1 -1
  79. package/lib/common/tools/handlers/set-selection-mcp-tool-handler.js +5 -4
  80. package/lib/common/tools/handlers/set-selection-mcp-tool-handler.js.map +1 -1
  81. package/lib/common/tools/handlers/set-view-mcp-tool-handler.js +5 -4
  82. package/lib/common/tools/handlers/set-view-mcp-tool-handler.js.map +1 -1
  83. package/lib/common/tools/handlers/undo-mcp-tool-handler.js +3 -2
  84. package/lib/common/tools/handlers/undo-mcp-tool-handler.js.map +1 -1
  85. package/lib/common/tools/handlers/validate-diagram-mcp-tool-handler.js +0 -0
  86. package/lib/common/tools/handlers/validate-diagram-mcp-tool-handler.js.map +1 -1
  87. package/lib/common/util/markdown-util.js +1 -2
  88. package/lib/common/util/markdown-util.js.map +1 -1
  89. package/lib/node/modules/node-mcp-server-module.js +8 -8
  90. package/lib/node/modules/node-mcp-server-module.js.map +1 -1
  91. package/package.json +17 -17
  92. package/src/common/modules/abstract-mcp-server-module.ts +3 -3
  93. package/src/common/resources/services/element-types-provider.ts +2 -1
  94. package/src/common/resources/services/mcp-model-serializer.ts +1 -2
  95. package/src/common/server/abstract-mcp-server-launcher.ts +3 -3
  96. package/src/common/server/glsp-mcp-server.ts +2 -1
  97. package/src/common/server/mcp-handler-shared.ts +2 -4
  98. package/src/common/server/mcp-mime-types.ts +1 -1
  99. package/src/common/server/mcp-options.ts +1 -1
  100. package/src/common/server/mcp-resource-handler.ts +1 -1
  101. package/src/common/server/abstract-mcp-server-launcher.spec.ts +0 -458
  102. package/src/common/server/glsp-mcp-server.spec.ts +0 -73
  103. package/src/common/server/lru-event-store.spec.ts +0 -121
  104. package/src/common/server/mcp-diagram-handler-dispatcher.spec.ts +0 -231
  105. package/src/common/server/mcp-handler-shared.spec.ts +0 -53
  106. package/src/common/server/mcp-id-alias-service.spec.ts +0 -106
  107. package/src/common/server/mcp-log-level-registry.spec.ts +0 -75
  108. package/src/common/server/mcp-logger.spec.ts +0 -235
  109. package/src/common/server/mcp-progress-reporter.spec.ts +0 -103
  110. package/src/common/server/mcp-tool-handler.spec.ts +0 -182
  111. package/src/common/tools/handlers/count-elements-mcp-tool-handler.spec.ts +0 -101
  112. package/src/common/tools/handlers/create-edges-mcp-tool-handler.spec.ts +0 -198
  113. package/src/common/tools/handlers/create-nodes-mcp-tool-handler.spec.ts +0 -199
  114. package/src/common/tools/handlers/query-elements-mcp-tool-handler.spec.ts +0 -212
  115. package/src/common/tools/handlers/session-info-mcp-tool-handler.spec.ts +0 -152
  116. package/src/common/tools/handlers/set-selection-mcp-tool-handler.spec.ts +0 -120
  117. package/src/common/tools/tool-annotations.spec.ts +0 -141
  118. package/src/node/server/mcp-http-transport-e2e.spec.ts +0 -297
  119. package/src/node/server/node-mcp-server-launcher.spec.ts +0 -184
  120. package/src/node/server/raw-http.spec.ts +0 -59
@@ -1,152 +0,0 @@
1
- /********************************************************************************
2
- * Copyright (c) 2026 EclipseSource and others.
3
- *
4
- * This program and the accompanying materials are made available under the
5
- * terms of the Eclipse Public License v. 2.0 which is available at
6
- * http://www.eclipse.org/legal/epl-2.0.
7
- *
8
- * This Source Code may also be made available under the following Secondary
9
- * Licenses when the conditions for such availability set forth in the Eclipse
10
- * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- * with the GNU Classpath Exception which is available at
12
- * https://www.gnu.org/software/classpath/license.html.
13
- *
14
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
- ********************************************************************************/
16
-
17
- import { ClientSession, ClientSessionManager, CommandStack, Logger, ModelState, NullLogger } from '@eclipse-glsp/server';
18
- import { expect } from 'chai';
19
- import { Container, ContainerModule } from 'inversify';
20
- import { McpToolResult } from '../../server/mcp-handler-shared';
21
- import { SessionInfoMcpToolHandler } from './session-info-mcp-tool-handler';
22
-
23
- function asText(result: McpToolResult): string {
24
- const block = result.content[0];
25
- if (!block || block.type !== 'text') throw new Error('expected text content');
26
- return block.text;
27
- }
28
-
29
- /** Build a {@link ClientSession} stub whose container resolves a fixed {@link ModelState} + {@link CommandStack}. */
30
- function makeSession(
31
- id: string,
32
- diagramType: string,
33
- sourceUri: string | undefined,
34
- isReadonly: boolean,
35
- isDirty: boolean = false
36
- ): ClientSession {
37
- const sessionContainer = new Container();
38
- const modelStateStub: Partial<ModelState> = { sourceUri, isReadonly };
39
- sessionContainer.bind(ModelState).toConstantValue(modelStateStub as ModelState);
40
- const commandStackStub: Partial<CommandStack> = { isDirty };
41
- sessionContainer.bind(CommandStack).toConstantValue(commandStackStub as CommandStack);
42
- return {
43
- id,
44
- diagramType,
45
- container: sessionContainer,
46
- // The handler does not exercise this dispatcher, but the interface requires it.
47
- actionDispatcher: {} as ClientSession['actionDispatcher'],
48
- dispose: () => {
49
- //
50
- }
51
- };
52
- }
53
-
54
- class StubClientSessionManager implements ClientSessionManager {
55
- constructor(private readonly sessions: ClientSession[]) {}
56
-
57
- getOrCreateClientSession(): ClientSession {
58
- throw new Error('not implemented');
59
- }
60
- getSession(sessionId?: string): ClientSession | undefined {
61
- return this.sessions.find(s => s.id === sessionId);
62
- }
63
- getSessions(): ClientSession[] {
64
- return this.sessions;
65
- }
66
- getSessionsByType(): ClientSession[] {
67
- return [];
68
- }
69
- disposeClientSession(): boolean {
70
- return true;
71
- }
72
- addListener(): boolean {
73
- return true;
74
- }
75
- removeListener(): boolean {
76
- return true;
77
- }
78
- removeListeners(): void {
79
- //
80
- }
81
- }
82
-
83
- function buildHandler(sessions: ClientSession[]): SessionInfoMcpToolHandler {
84
- const container = new Container();
85
- container.load(
86
- new ContainerModule(bind => {
87
- bind(Logger).toConstantValue(new NullLogger());
88
- bind(ClientSessionManager).toConstantValue(new StubClientSessionManager(sessions));
89
- bind(SessionInfoMcpToolHandler).toSelf().inSingletonScope();
90
- })
91
- );
92
- return container.get(SessionInfoMcpToolHandler);
93
- }
94
-
95
- interface SessionRow {
96
- sessionId: string;
97
- diagramType: string;
98
- sourceUri: string;
99
- readOnly: boolean;
100
- dirty: boolean;
101
- }
102
-
103
- describe('SessionInfoMcpToolHandler', () => {
104
- it('summarizes each session by id and diagram type when sessions exist', () => {
105
- const handler = buildHandler([
106
- makeSession('session-alpha', 'workflow-diagram', 'file:///alpha.wf', false),
107
- makeSession('session-beta', 'state-diagram', 'file:///beta.sd', true)
108
- ]);
109
-
110
- const result = handler['createResult']({});
111
- const text = asText(result);
112
- // Summary lists each session's id and diagram type — the load-bearing referenceable bits.
113
- expect(text).to.include('session-alpha');
114
- expect(text).to.include('session-beta');
115
- expect(text).to.include('workflow-diagram');
116
- expect(text).to.include('state-diagram');
117
- // Full per-session detail goes via structuredContent.
118
- const sessions = (result.structuredContent as { sessions: SessionRow[] }).sessions;
119
- expect(sessions.map(row => row.sessionId)).to.deep.equal(['session-alpha', 'session-beta']);
120
- });
121
-
122
- it("passes through `sourceUri` and `readOnly` from each session container's ModelState via structuredContent", () => {
123
- const handler = buildHandler([makeSession('s1', 'workflow-diagram', 'file:///only.wf', true, /* isDirty */ false)]);
124
-
125
- const result = handler['createResult']({});
126
- const sessions = (result.structuredContent as { sessions: SessionRow[] }).sessions;
127
- expect(sessions).to.have.lengthOf(1);
128
- expect(sessions[0].sourceUri).to.equal('file:///only.wf');
129
- expect(sessions[0].readOnly).to.equal(true);
130
- expect(sessions[0].dirty).to.equal(false);
131
- // Read-only state is also surfaced in the text summary so content-only clients can see it.
132
- expect(asText(result)).to.include('read-only');
133
- });
134
-
135
- it('filters to a single session when `sessionId` matches', () => {
136
- const handler = buildHandler([
137
- makeSession('session-alpha', 'workflow-diagram', 'file:///alpha.wf', false),
138
- makeSession('session-beta', 'state-diagram', 'file:///beta.sd', true)
139
- ]);
140
-
141
- const text = asText(handler['createResult']({ sessionId: 'session-beta' }));
142
-
143
- expect(text).to.include('session-beta');
144
- expect(text).to.not.include('session-alpha');
145
- });
146
-
147
- it('throws McpToolError when the requested `sessionId` does not exist', () => {
148
- const handler = buildHandler([makeSession('session-alpha', 'workflow-diagram', 'file:///alpha.wf', false)]);
149
-
150
- expect(() => handler['createResult']({ sessionId: 'unknown' })).to.throw(/Unknown sessionId: unknown/);
151
- });
152
- });
@@ -1,120 +0,0 @@
1
- /********************************************************************************
2
- * Copyright (c) 2026 EclipseSource and others.
3
- *
4
- * This program and the accompanying materials are made available under the
5
- * terms of the Eclipse Public License v. 2.0 which is available at
6
- * http://www.eclipse.org/legal/epl-2.0.
7
- *
8
- * This Source Code may also be made available under the following Secondary
9
- * Licenses when the conditions for such availability set forth in the Eclipse
10
- * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- * with the GNU Classpath Exception which is available at
12
- * https://www.gnu.org/software/classpath/license.html.
13
- *
14
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
- ********************************************************************************/
16
-
17
- import {
18
- Action,
19
- ActionDispatcher,
20
- ClientId,
21
- GModelElement,
22
- Logger,
23
- ModelState,
24
- NullLogger,
25
- SelectAction,
26
- SelectAllAction
27
- } from '@eclipse-glsp/server';
28
- import { expect } from 'chai';
29
- import { Container, ContainerModule } from 'inversify';
30
- import { McpElementsNotFoundError, McpToolResult } from '../../server/mcp-handler-shared';
31
- import { McpIdAliasService } from '../../server/mcp-id-alias-service';
32
- import { DefaultMcpLabelProvider, McpLabelProvider } from '../../server/mcp-label-provider';
33
- import { SetSelectionInput, SetSelectionMcpToolHandler } from './set-selection-mcp-tool-handler';
34
-
35
- function makeElement(id: string, type: string): GModelElement {
36
- return { id, type, children: [] } as unknown as GModelElement;
37
- }
38
-
39
- function makeModelState(elements: GModelElement[]): ModelState {
40
- const byId = new Map(elements.map(el => [el.id, el]));
41
- return {
42
- index: {
43
- allIds: () => [...byId.keys()],
44
- get: (id: string) => byId.get(id),
45
- find: (id: string) => byId.get(id)
46
- }
47
- } as unknown as ModelState;
48
- }
49
-
50
- function makeRecordingDispatcher(): { dispatcher: ActionDispatcher; dispatched: Action[] } {
51
- const dispatched: Action[] = [];
52
- const dispatcher = {
53
- dispatch: async (action: Action) => {
54
- dispatched.push(action);
55
- }
56
- } as unknown as ActionDispatcher;
57
- return { dispatcher, dispatched };
58
- }
59
-
60
- function buildHandler(elements: GModelElement[], dispatcher: ActionDispatcher): SetSelectionMcpToolHandler {
61
- const container = new Container();
62
- container.load(
63
- new ContainerModule(bind => {
64
- bind(Logger).toConstantValue(new NullLogger());
65
- bind(ClientId).toConstantValue('test-session');
66
- bind(ModelState).toConstantValue(makeModelState(elements));
67
- bind(McpIdAliasService).toConstantValue({
68
- lookup: (id: string) => id,
69
- alias: (id: string) => id
70
- } as McpIdAliasService);
71
- bind(ActionDispatcher).toConstantValue(dispatcher);
72
- bind(McpLabelProvider).to(DefaultMcpLabelProvider);
73
- bind(SetSelectionMcpToolHandler).toSelf();
74
- })
75
- );
76
- return container.get(SetSelectionMcpToolHandler);
77
- }
78
-
79
- function callCreateResult(handler: SetSelectionMcpToolHandler, params: SetSelectionInput): Promise<McpToolResult> {
80
- return (handler as unknown as { createResult: (p: SetSelectionInput) => Promise<McpToolResult> }).createResult(params);
81
- }
82
-
83
- describe('SetSelectionMcpToolHandler', () => {
84
- it('dispatches SelectAllAction(false) before SelectAction when `clear: true`', async () => {
85
- const { dispatcher, dispatched } = makeRecordingDispatcher();
86
- const handler = buildHandler([makeElement('n1', 'task'), makeElement('n2', 'task')], dispatcher);
87
-
88
- await callCreateResult(handler, { sessionId: 's', selectedElementIds: ['n1'], clear: true });
89
-
90
- expect(dispatched).to.have.lengthOf(2);
91
- expect(SelectAllAction.is(dispatched[0])).to.equal(true);
92
- expect((dispatched[0] as SelectAllAction).select).to.equal(false);
93
- expect(SelectAction.is(dispatched[1])).to.equal(true);
94
- });
95
-
96
- it('dispatches SelectAction with resolved selected and deselected ids', async () => {
97
- const { dispatcher, dispatched } = makeRecordingDispatcher();
98
- const handler = buildHandler([makeElement('a', 'task'), makeElement('b', 'task'), makeElement('c', 'task')], dispatcher);
99
-
100
- await callCreateResult(handler, { sessionId: 's', selectedElementIds: ['a', 'b'], deselectedElementIds: ['c'] });
101
-
102
- expect(dispatched).to.have.lengthOf(1);
103
- const action = dispatched[0] as SelectAction;
104
- expect(action.selectedElementsIDs).to.deep.equal(['a', 'b']);
105
- expect(action.deselectedElementsIDs).to.deep.equal(['c']);
106
- });
107
-
108
- it('throws McpElementsNotFoundError when an id is missing from the model', async () => {
109
- const { dispatcher } = makeRecordingDispatcher();
110
- const handler = buildHandler([makeElement('n1', 'task')], dispatcher);
111
-
112
- let error: unknown;
113
- try {
114
- await callCreateResult(handler, { sessionId: 's', selectedElementIds: ['n1', 'ghost'] });
115
- } catch (err: unknown) {
116
- error = err;
117
- }
118
- expect(error).to.be.instanceOf(McpElementsNotFoundError);
119
- });
120
- });
@@ -1,141 +0,0 @@
1
- /********************************************************************************
2
- * Copyright (c) 2026 EclipseSource and others.
3
- *
4
- * This program and the accompanying materials are made available under the
5
- * terms of the Eclipse Public License v. 2.0 which is available at
6
- * http://www.eclipse.org/legal/epl-2.0.
7
- *
8
- * This Source Code may also be made available under the following Secondary
9
- * Licenses when the conditions for such availability set forth in the Eclipse
10
- * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- * with the GNU Classpath Exception which is available at
12
- * https://www.gnu.org/software/classpath/license.html.
13
- *
14
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
- ********************************************************************************/
16
-
17
- import { expect } from 'chai';
18
- import { interfaces } from 'inversify';
19
- import { SetViewMcpToolHandler } from './handlers/set-view-mcp-tool-handler';
20
- import { CreateEdgesMcpToolHandler } from './handlers/create-edges-mcp-tool-handler';
21
- import { CreateNodesMcpToolHandler } from './handlers/create-nodes-mcp-tool-handler';
22
- import { DeleteElementsMcpToolHandler } from './handlers/delete-elements-mcp-tool-handler';
23
- import { QueryElementsMcpToolHandler } from './handlers/query-elements-mcp-tool-handler';
24
- import { GetSelectionMcpToolHandler } from './handlers/get-selection-mcp-tool-handler';
25
- import { ModifyEdgesMcpToolHandler } from './handlers/modify-edges-mcp-tool-handler';
26
- import { ModifyNodesMcpToolHandler } from './handlers/modify-nodes-mcp-tool-handler';
27
- import { RedoMcpToolHandler } from './handlers/redo-mcp-tool-handler';
28
- import { SaveModelMcpToolHandler } from './handlers/save-model-mcp-tool-handler';
29
- import { UndoMcpToolHandler } from './handlers/undo-mcp-tool-handler';
30
- import { ValidateDiagramMcpToolHandler } from './handlers/validate-diagram-mcp-tool-handler';
31
-
32
- interface AnnotatedHandler {
33
- readOnlyHint?: boolean;
34
- destructiveHint?: boolean;
35
- idempotentHint?: boolean;
36
- openWorldHint?: boolean;
37
- }
38
-
39
- /**
40
- * Reads the four annotation hint fields off a freshly `new`-ed handler. Mirrors how the launcher
41
- * reads metadata via `new Constructor()` at MCP-session-init (see
42
- * `mcp-server-launcher.ts#registerDiagramScopeTools`); no DI wiring needed because the fields
43
- * are plain class properties with literal initializers.
44
- */
45
- function hintsOf(Constructor: interfaces.Newable<AnnotatedHandler>): AnnotatedHandler {
46
- const handler = new Constructor();
47
- return {
48
- readOnlyHint: handler.readOnlyHint,
49
- destructiveHint: handler.destructiveHint,
50
- idempotentHint: handler.idempotentHint,
51
- openWorldHint: handler.openWorldHint
52
- };
53
- }
54
-
55
- const READ_DEFAULT: AnnotatedHandler = {
56
- readOnlyHint: true,
57
- destructiveHint: undefined,
58
- idempotentHint: undefined,
59
- openWorldHint: false
60
- };
61
-
62
- const OPERATION_DEFAULT: AnnotatedHandler = {
63
- readOnlyHint: false,
64
- destructiveHint: false,
65
- idempotentHint: false,
66
- openWorldHint: false
67
- };
68
-
69
- describe('Tool annotations · per-handler matrix', () => {
70
- describe('read-style handlers (inherit base default)', () => {
71
- const readStyleHandlers: Array<[string, interfaces.Newable<AnnotatedHandler>]> = [
72
- ['query-elements', QueryElementsMcpToolHandler],
73
- ['get-selection', GetSelectionMcpToolHandler],
74
- ['validate-diagram', ValidateDiagramMcpToolHandler]
75
- ];
76
- readStyleHandlers.forEach(([name, Constructor]) => {
77
- it(`${name} matches the read-style default`, () => {
78
- expect(hintsOf(Constructor)).to.deep.equal(READ_DEFAULT);
79
- });
80
- });
81
- });
82
-
83
- describe('operation-style handlers (override to write defaults)', () => {
84
- const operationStyleHandlers: Array<[string, interfaces.Newable<AnnotatedHandler>]> = [
85
- ['create-nodes', CreateNodesMcpToolHandler],
86
- ['create-edges', CreateEdgesMcpToolHandler],
87
- ['modify-nodes', ModifyNodesMcpToolHandler],
88
- ['modify-edges', ModifyEdgesMcpToolHandler],
89
- ['undo', UndoMcpToolHandler],
90
- ['redo', RedoMcpToolHandler]
91
- ];
92
- operationStyleHandlers.forEach(([name, Constructor]) => {
93
- it(`${name} matches the operation-style default`, () => {
94
- expect(hintsOf(Constructor)).to.deep.equal(OPERATION_DEFAULT);
95
- });
96
- });
97
- });
98
-
99
- describe('per-handler overrides', () => {
100
- it('delete-elements is destructiveHint:true (single-flag override)', () => {
101
- expect(hintsOf(DeleteElementsMcpToolHandler)).to.deep.equal({ ...OPERATION_DEFAULT, destructiveHint: true });
102
- });
103
-
104
- it('save-model is NOT readOnly (writes to disk) and NOT destructive (creative)', () => {
105
- // Spec: `readOnlyHint` means "does not modify its environment" — disk IS environment.
106
- // `destructiveHint` is for irreversible deletion / data loss; save is creative.
107
- // Save-model extends the read base (not the operation base) because it doesn't
108
- // dispatch a model-mutating Operation, but we override the flat fields explicitly.
109
- expect(hintsOf(SaveModelMcpToolHandler)).to.deep.equal({
110
- readOnlyHint: false,
111
- destructiveHint: false,
112
- idempotentHint: false,
113
- openWorldHint: false
114
- });
115
- });
116
-
117
- it('set-view drops the read-only claim (viewport IS environment)', () => {
118
- // Inherits from the read base, which would claim readOnlyHint:true. Override flips
119
- // it: dispatching a viewport action mutates client-side state, even though no
120
- // diagram model bytes change.
121
- expect(hintsOf(SetViewMcpToolHandler)).to.deep.equal({
122
- readOnlyHint: false,
123
- destructiveHint: undefined,
124
- idempotentHint: undefined,
125
- openWorldHint: false
126
- });
127
- });
128
- });
129
-
130
- describe('toRegistrationConfig() assembles the annotations object the SDK expects', () => {
131
- it('aggregates the flat fields into the SDK ToolAnnotations shape', () => {
132
- const config = new DeleteElementsMcpToolHandler().toRegistrationConfig();
133
- expect(config.annotations).to.deep.equal({
134
- readOnlyHint: false,
135
- destructiveHint: true,
136
- idempotentHint: false,
137
- openWorldHint: false
138
- });
139
- });
140
- });
141
- });