@eclipse-glsp/server-mcp 2.7.0-next.9

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 (303) hide show
  1. package/LICENSE +642 -0
  2. package/README.md +57 -0
  3. package/lib/index.d.ts +23 -0
  4. package/lib/index.d.ts.map +1 -0
  5. package/lib/index.js +41 -0
  6. package/lib/index.js.map +1 -0
  7. package/lib/prompts/handlers/describe-diagram-mcp-prompt-handler.d.ts +43 -0
  8. package/lib/prompts/handlers/describe-diagram-mcp-prompt-handler.d.ts.map +1 -0
  9. package/lib/prompts/handlers/describe-diagram-mcp-prompt-handler.js +96 -0
  10. package/lib/prompts/handlers/describe-diagram-mcp-prompt-handler.js.map +1 -0
  11. package/lib/prompts/handlers/suggest-improvements-mcp-prompt-handler.d.ts +43 -0
  12. package/lib/prompts/handlers/suggest-improvements-mcp-prompt-handler.d.ts.map +1 -0
  13. package/lib/prompts/handlers/suggest-improvements-mcp-prompt-handler.js +95 -0
  14. package/lib/prompts/handlers/suggest-improvements-mcp-prompt-handler.js.map +1 -0
  15. package/lib/prompts/index.d.ts +18 -0
  16. package/lib/prompts/index.d.ts.map +1 -0
  17. package/lib/prompts/index.js +34 -0
  18. package/lib/prompts/index.js.map +1 -0
  19. package/lib/resources/handlers/diagram-png-mcp-resource-handler.d.ts +81 -0
  20. package/lib/resources/handlers/diagram-png-mcp-resource-handler.d.ts.map +1 -0
  21. package/lib/resources/handlers/diagram-png-mcp-resource-handler.js +174 -0
  22. package/lib/resources/handlers/diagram-png-mcp-resource-handler.js.map +1 -0
  23. package/lib/resources/handlers/diagram-svg-mcp-resource-handler.d.ts +52 -0
  24. package/lib/resources/handlers/diagram-svg-mcp-resource-handler.d.ts.map +1 -0
  25. package/lib/resources/handlers/diagram-svg-mcp-resource-handler.js +96 -0
  26. package/lib/resources/handlers/diagram-svg-mcp-resource-handler.js.map +1 -0
  27. package/lib/resources/index.d.ts +20 -0
  28. package/lib/resources/index.d.ts.map +1 -0
  29. package/lib/resources/index.js +36 -0
  30. package/lib/resources/index.js.map +1 -0
  31. package/lib/resources/services/element-types-provider.d.ts +65 -0
  32. package/lib/resources/services/element-types-provider.d.ts.map +1 -0
  33. package/lib/resources/services/element-types-provider.js +81 -0
  34. package/lib/resources/services/element-types-provider.js.map +1 -0
  35. package/lib/resources/services/mcp-model-serializer.d.ts +78 -0
  36. package/lib/resources/services/mcp-model-serializer.d.ts.map +1 -0
  37. package/lib/resources/services/mcp-model-serializer.js +188 -0
  38. package/lib/resources/services/mcp-model-serializer.js.map +1 -0
  39. package/lib/server/glsp-mcp-server.d.ts +82 -0
  40. package/lib/server/glsp-mcp-server.d.ts.map +1 -0
  41. package/lib/server/glsp-mcp-server.js +140 -0
  42. package/lib/server/glsp-mcp-server.js.map +1 -0
  43. package/lib/server/index.d.ts +37 -0
  44. package/lib/server/index.d.ts.map +1 -0
  45. package/lib/server/index.js +57 -0
  46. package/lib/server/index.js.map +1 -0
  47. package/lib/server/lru-event-store.d.ts +53 -0
  48. package/lib/server/lru-event-store.d.ts.map +1 -0
  49. package/lib/server/lru-event-store.js +100 -0
  50. package/lib/server/lru-event-store.js.map +1 -0
  51. package/lib/server/mcp-diagram-handler-dispatcher.d.ts +144 -0
  52. package/lib/server/mcp-diagram-handler-dispatcher.d.ts.map +1 -0
  53. package/lib/server/mcp-diagram-handler-dispatcher.js +382 -0
  54. package/lib/server/mcp-diagram-handler-dispatcher.js.map +1 -0
  55. package/lib/server/mcp-diagram-module.d.ts +123 -0
  56. package/lib/server/mcp-diagram-module.d.ts.map +1 -0
  57. package/lib/server/mcp-diagram-module.js +186 -0
  58. package/lib/server/mcp-diagram-module.js.map +1 -0
  59. package/lib/server/mcp-diagram-prompt-handler-registry.d.ts +33 -0
  60. package/lib/server/mcp-diagram-prompt-handler-registry.d.ts.map +1 -0
  61. package/lib/server/mcp-diagram-prompt-handler-registry.js +76 -0
  62. package/lib/server/mcp-diagram-prompt-handler-registry.js.map +1 -0
  63. package/lib/server/mcp-diagram-resource-handler-registry.d.ts +35 -0
  64. package/lib/server/mcp-diagram-resource-handler-registry.d.ts.map +1 -0
  65. package/lib/server/mcp-diagram-resource-handler-registry.js +94 -0
  66. package/lib/server/mcp-diagram-resource-handler-registry.js.map +1 -0
  67. package/lib/server/mcp-diagram-tool-handler-registry.d.ts +57 -0
  68. package/lib/server/mcp-diagram-tool-handler-registry.d.ts.map +1 -0
  69. package/lib/server/mcp-diagram-tool-handler-registry.js +111 -0
  70. package/lib/server/mcp-diagram-tool-handler-registry.js.map +1 -0
  71. package/lib/server/mcp-handler-shared.d.ts +142 -0
  72. package/lib/server/mcp-handler-shared.d.ts.map +1 -0
  73. package/lib/server/mcp-handler-shared.js +199 -0
  74. package/lib/server/mcp-handler-shared.js.map +1 -0
  75. package/lib/server/mcp-http-transport.d.ts +93 -0
  76. package/lib/server/mcp-http-transport.d.ts.map +1 -0
  77. package/lib/server/mcp-http-transport.js +350 -0
  78. package/lib/server/mcp-http-transport.js.map +1 -0
  79. package/lib/server/mcp-id-alias-service.d.ts +70 -0
  80. package/lib/server/mcp-id-alias-service.d.ts.map +1 -0
  81. package/lib/server/mcp-id-alias-service.js +85 -0
  82. package/lib/server/mcp-id-alias-service.js.map +1 -0
  83. package/lib/server/mcp-input-schemas.d.ts +73 -0
  84. package/lib/server/mcp-input-schemas.d.ts.map +1 -0
  85. package/lib/server/mcp-input-schemas.js +67 -0
  86. package/lib/server/mcp-input-schemas.js.map +1 -0
  87. package/lib/server/mcp-label-provider.d.ts +45 -0
  88. package/lib/server/mcp-label-provider.d.ts.map +1 -0
  89. package/lib/server/mcp-label-provider.js +42 -0
  90. package/lib/server/mcp-label-provider.js.map +1 -0
  91. package/lib/server/mcp-log-level-registry.d.ts +54 -0
  92. package/lib/server/mcp-log-level-registry.d.ts.map +1 -0
  93. package/lib/server/mcp-log-level-registry.js +80 -0
  94. package/lib/server/mcp-log-level-registry.js.map +1 -0
  95. package/lib/server/mcp-logger.d.ts +59 -0
  96. package/lib/server/mcp-logger.d.ts.map +1 -0
  97. package/lib/server/mcp-logger.js +104 -0
  98. package/lib/server/mcp-logger.js.map +1 -0
  99. package/lib/server/mcp-mime-types.d.ts +28 -0
  100. package/lib/server/mcp-mime-types.d.ts.map +1 -0
  101. package/lib/server/mcp-mime-types.js +18 -0
  102. package/lib/server/mcp-mime-types.js.map +1 -0
  103. package/lib/server/mcp-options.d.ts +39 -0
  104. package/lib/server/mcp-options.d.ts.map +1 -0
  105. package/lib/server/mcp-options.js +53 -0
  106. package/lib/server/mcp-options.js.map +1 -0
  107. package/lib/server/mcp-progress-reporter.d.ts +48 -0
  108. package/lib/server/mcp-progress-reporter.d.ts.map +1 -0
  109. package/lib/server/mcp-progress-reporter.js +66 -0
  110. package/lib/server/mcp-progress-reporter.js.map +1 -0
  111. package/lib/server/mcp-prompt-handler.d.ts +120 -0
  112. package/lib/server/mcp-prompt-handler.d.ts.map +1 -0
  113. package/lib/server/mcp-prompt-handler.js +131 -0
  114. package/lib/server/mcp-prompt-handler.js.map +1 -0
  115. package/lib/server/mcp-request-context.d.ts +37 -0
  116. package/lib/server/mcp-request-context.d.ts.map +1 -0
  117. package/lib/server/mcp-request-context.js +37 -0
  118. package/lib/server/mcp-request-context.js.map +1 -0
  119. package/lib/server/mcp-resource-handler.d.ts +212 -0
  120. package/lib/server/mcp-resource-handler.d.ts.map +1 -0
  121. package/lib/server/mcp-resource-handler.js +298 -0
  122. package/lib/server/mcp-resource-handler.js.map +1 -0
  123. package/lib/server/mcp-server-launcher.d.ts +143 -0
  124. package/lib/server/mcp-server-launcher.d.ts.map +1 -0
  125. package/lib/server/mcp-server-launcher.js +355 -0
  126. package/lib/server/mcp-server-launcher.js.map +1 -0
  127. package/lib/server/mcp-server-module.d.ts +143 -0
  128. package/lib/server/mcp-server-module.d.ts.map +1 -0
  129. package/lib/server/mcp-server-module.js +249 -0
  130. package/lib/server/mcp-server-module.js.map +1 -0
  131. package/lib/server/mcp-session.d.ts +44 -0
  132. package/lib/server/mcp-session.d.ts.map +1 -0
  133. package/lib/server/mcp-session.js +18 -0
  134. package/lib/server/mcp-session.js.map +1 -0
  135. package/lib/server/mcp-tool-handler.d.ts +259 -0
  136. package/lib/server/mcp-tool-handler.d.ts.map +1 -0
  137. package/lib/server/mcp-tool-handler.js +355 -0
  138. package/lib/server/mcp-tool-handler.js.map +1 -0
  139. package/lib/tools/handlers/count-elements-mcp-tool-handler.d.ts +46 -0
  140. package/lib/tools/handlers/count-elements-mcp-tool-handler.d.ts.map +1 -0
  141. package/lib/tools/handlers/count-elements-mcp-tool-handler.js +76 -0
  142. package/lib/tools/handlers/count-elements-mcp-tool-handler.js.map +1 -0
  143. package/lib/tools/handlers/create-edges-mcp-tool-handler.d.ts +112 -0
  144. package/lib/tools/handlers/create-edges-mcp-tool-handler.d.ts.map +1 -0
  145. package/lib/tools/handlers/create-edges-mcp-tool-handler.js +190 -0
  146. package/lib/tools/handlers/create-edges-mcp-tool-handler.js.map +1 -0
  147. package/lib/tools/handlers/create-nodes-mcp-tool-handler.d.ts +81 -0
  148. package/lib/tools/handlers/create-nodes-mcp-tool-handler.d.ts.map +1 -0
  149. package/lib/tools/handlers/create-nodes-mcp-tool-handler.js +123 -0
  150. package/lib/tools/handlers/create-nodes-mcp-tool-handler.js.map +1 -0
  151. package/lib/tools/handlers/delete-elements-mcp-tool-handler.d.ts +52 -0
  152. package/lib/tools/handlers/delete-elements-mcp-tool-handler.d.ts.map +1 -0
  153. package/lib/tools/handlers/delete-elements-mcp-tool-handler.js +73 -0
  154. package/lib/tools/handlers/delete-elements-mcp-tool-handler.js.map +1 -0
  155. package/lib/tools/handlers/diagram-model-mcp-tool-handler.d.ts +59 -0
  156. package/lib/tools/handlers/diagram-model-mcp-tool-handler.d.ts.map +1 -0
  157. package/lib/tools/handlers/diagram-model-mcp-tool-handler.js +78 -0
  158. package/lib/tools/handlers/diagram-model-mcp-tool-handler.js.map +1 -0
  159. package/lib/tools/handlers/element-types-mcp-tool-handler.d.ts +97 -0
  160. package/lib/tools/handlers/element-types-mcp-tool-handler.d.ts.map +1 -0
  161. package/lib/tools/handlers/element-types-mcp-tool-handler.js +155 -0
  162. package/lib/tools/handlers/element-types-mcp-tool-handler.js.map +1 -0
  163. package/lib/tools/handlers/get-selection-mcp-tool-handler.d.ts +43 -0
  164. package/lib/tools/handlers/get-selection-mcp-tool-handler.d.ts.map +1 -0
  165. package/lib/tools/handlers/get-selection-mcp-tool-handler.js +68 -0
  166. package/lib/tools/handlers/get-selection-mcp-tool-handler.js.map +1 -0
  167. package/lib/tools/handlers/layout-mcp-tool-handler.d.ts +43 -0
  168. package/lib/tools/handlers/layout-mcp-tool-handler.d.ts.map +1 -0
  169. package/lib/tools/handlers/layout-mcp-tool-handler.js +71 -0
  170. package/lib/tools/handlers/layout-mcp-tool-handler.js.map +1 -0
  171. package/lib/tools/handlers/modify-edges-mcp-tool-handler.d.ts +78 -0
  172. package/lib/tools/handlers/modify-edges-mcp-tool-handler.d.ts.map +1 -0
  173. package/lib/tools/handlers/modify-edges-mcp-tool-handler.js +136 -0
  174. package/lib/tools/handlers/modify-edges-mcp-tool-handler.js.map +1 -0
  175. package/lib/tools/handlers/modify-nodes-mcp-tool-handler.d.ts +92 -0
  176. package/lib/tools/handlers/modify-nodes-mcp-tool-handler.d.ts.map +1 -0
  177. package/lib/tools/handlers/modify-nodes-mcp-tool-handler.js +125 -0
  178. package/lib/tools/handlers/modify-nodes-mcp-tool-handler.js.map +1 -0
  179. package/lib/tools/handlers/query-elements-mcp-tool-handler.d.ts +102 -0
  180. package/lib/tools/handlers/query-elements-mcp-tool-handler.d.ts.map +1 -0
  181. package/lib/tools/handlers/query-elements-mcp-tool-handler.js +158 -0
  182. package/lib/tools/handlers/query-elements-mcp-tool-handler.js.map +1 -0
  183. package/lib/tools/handlers/redo-mcp-tool-handler.d.ts +45 -0
  184. package/lib/tools/handlers/redo-mcp-tool-handler.d.ts.map +1 -0
  185. package/lib/tools/handlers/redo-mcp-tool-handler.js +73 -0
  186. package/lib/tools/handlers/redo-mcp-tool-handler.js.map +1 -0
  187. package/lib/tools/handlers/save-model-mcp-tool-handler.d.ts +55 -0
  188. package/lib/tools/handlers/save-model-mcp-tool-handler.d.ts.map +1 -0
  189. package/lib/tools/handlers/save-model-mcp-tool-handler.js +91 -0
  190. package/lib/tools/handlers/save-model-mcp-tool-handler.js.map +1 -0
  191. package/lib/tools/handlers/session-info-mcp-tool-handler.d.ts +65 -0
  192. package/lib/tools/handlers/session-info-mcp-tool-handler.d.ts.map +1 -0
  193. package/lib/tools/handlers/session-info-mcp-tool-handler.js +108 -0
  194. package/lib/tools/handlers/session-info-mcp-tool-handler.js.map +1 -0
  195. package/lib/tools/handlers/set-selection-mcp-tool-handler.d.ts +60 -0
  196. package/lib/tools/handlers/set-selection-mcp-tool-handler.d.ts.map +1 -0
  197. package/lib/tools/handlers/set-selection-mcp-tool-handler.js +103 -0
  198. package/lib/tools/handlers/set-selection-mcp-tool-handler.js.map +1 -0
  199. package/lib/tools/handlers/set-view-mcp-tool-handler.d.ts +110 -0
  200. package/lib/tools/handlers/set-view-mcp-tool-handler.d.ts.map +1 -0
  201. package/lib/tools/handlers/set-view-mcp-tool-handler.js +142 -0
  202. package/lib/tools/handlers/set-view-mcp-tool-handler.js.map +1 -0
  203. package/lib/tools/handlers/undo-mcp-tool-handler.d.ts +45 -0
  204. package/lib/tools/handlers/undo-mcp-tool-handler.d.ts.map +1 -0
  205. package/lib/tools/handlers/undo-mcp-tool-handler.js +74 -0
  206. package/lib/tools/handlers/undo-mcp-tool-handler.js.map +1 -0
  207. package/lib/tools/handlers/validate-diagram-mcp-tool-handler.d.ts +66 -0
  208. package/lib/tools/handlers/validate-diagram-mcp-tool-handler.d.ts.map +1 -0
  209. package/lib/tools/handlers/validate-diagram-mcp-tool-handler.js +0 -0
  210. package/lib/tools/handlers/validate-diagram-mcp-tool-handler.js.map +1 -0
  211. package/lib/tools/index.d.ts +34 -0
  212. package/lib/tools/index.d.ts.map +1 -0
  213. package/lib/tools/index.js +50 -0
  214. package/lib/tools/index.js.map +1 -0
  215. package/lib/util/index.d.ts +18 -0
  216. package/lib/util/index.d.ts.map +1 -0
  217. package/lib/util/index.js +34 -0
  218. package/lib/util/index.js.map +1 -0
  219. package/lib/util/markdown-util.d.ts +20 -0
  220. package/lib/util/markdown-util.d.ts.map +1 -0
  221. package/lib/util/markdown-util.js +45 -0
  222. package/lib/util/markdown-util.js.map +1 -0
  223. package/lib/util/mcp-util.d.ts +22 -0
  224. package/lib/util/mcp-util.d.ts.map +1 -0
  225. package/lib/util/mcp-util.js +29 -0
  226. package/lib/util/mcp-util.js.map +1 -0
  227. package/package.json +63 -0
  228. package/src/index.ts +24 -0
  229. package/src/prompts/handlers/describe-diagram-mcp-prompt-handler.ts +89 -0
  230. package/src/prompts/handlers/suggest-improvements-mcp-prompt-handler.ts +86 -0
  231. package/src/prompts/index.ts +18 -0
  232. package/src/resources/handlers/diagram-png-mcp-resource-handler.ts +181 -0
  233. package/src/resources/handlers/diagram-svg-mcp-resource-handler.ts +89 -0
  234. package/src/resources/index.ts +20 -0
  235. package/src/resources/services/element-types-provider.ts +105 -0
  236. package/src/resources/services/mcp-model-serializer.ts +211 -0
  237. package/src/server/glsp-mcp-server.spec.ts +73 -0
  238. package/src/server/glsp-mcp-server.ts +196 -0
  239. package/src/server/index.ts +42 -0
  240. package/src/server/lru-event-store.spec.ts +121 -0
  241. package/src/server/lru-event-store.ts +112 -0
  242. package/src/server/mcp-diagram-handler-dispatcher.spec.ts +231 -0
  243. package/src/server/mcp-diagram-handler-dispatcher.ts +459 -0
  244. package/src/server/mcp-diagram-module.ts +248 -0
  245. package/src/server/mcp-diagram-prompt-handler-registry.ts +59 -0
  246. package/src/server/mcp-diagram-resource-handler-registry.ts +73 -0
  247. package/src/server/mcp-diagram-tool-handler-registry.ts +97 -0
  248. package/src/server/mcp-handler-shared.spec.ts +53 -0
  249. package/src/server/mcp-handler-shared.ts +247 -0
  250. package/src/server/mcp-http-transport-e2e.spec.ts +151 -0
  251. package/src/server/mcp-http-transport.spec.ts +385 -0
  252. package/src/server/mcp-http-transport.ts +368 -0
  253. package/src/server/mcp-id-alias-service.spec.ts +106 -0
  254. package/src/server/mcp-id-alias-service.ts +104 -0
  255. package/src/server/mcp-input-schemas.ts +82 -0
  256. package/src/server/mcp-label-provider.ts +52 -0
  257. package/src/server/mcp-log-level-registry.spec.ts +75 -0
  258. package/src/server/mcp-log-level-registry.ts +90 -0
  259. package/src/server/mcp-logger.spec.ts +227 -0
  260. package/src/server/mcp-logger.ts +91 -0
  261. package/src/server/mcp-mime-types.ts +31 -0
  262. package/src/server/mcp-options.ts +43 -0
  263. package/src/server/mcp-progress-reporter.spec.ts +93 -0
  264. package/src/server/mcp-progress-reporter.ts +67 -0
  265. package/src/server/mcp-prompt-handler.ts +157 -0
  266. package/src/server/mcp-request-context.ts +39 -0
  267. package/src/server/mcp-resource-handler.ts +389 -0
  268. package/src/server/mcp-server-launcher.spec.ts +173 -0
  269. package/src/server/mcp-server-launcher.ts +369 -0
  270. package/src/server/mcp-server-module.ts +287 -0
  271. package/src/server/mcp-session.ts +45 -0
  272. package/src/server/mcp-tool-handler.spec.ts +182 -0
  273. package/src/server/mcp-tool-handler.ts +431 -0
  274. package/src/server/raw-http.spec.ts +59 -0
  275. package/src/tools/handlers/count-elements-mcp-tool-handler.spec.ts +99 -0
  276. package/src/tools/handlers/count-elements-mcp-tool-handler.ts +66 -0
  277. package/src/tools/handlers/create-edges-mcp-tool-handler.spec.ts +196 -0
  278. package/src/tools/handlers/create-edges-mcp-tool-handler.ts +205 -0
  279. package/src/tools/handlers/create-nodes-mcp-tool-handler.spec.ts +197 -0
  280. package/src/tools/handlers/create-nodes-mcp-tool-handler.ts +131 -0
  281. package/src/tools/handlers/delete-elements-mcp-tool-handler.ts +73 -0
  282. package/src/tools/handlers/diagram-model-mcp-tool-handler.ts +66 -0
  283. package/src/tools/handlers/element-types-mcp-tool-handler.ts +151 -0
  284. package/src/tools/handlers/get-selection-mcp-tool-handler.ts +54 -0
  285. package/src/tools/handlers/layout-mcp-tool-handler.ts +56 -0
  286. package/src/tools/handlers/modify-edges-mcp-tool-handler.ts +148 -0
  287. package/src/tools/handlers/modify-nodes-mcp-tool-handler.ts +140 -0
  288. package/src/tools/handlers/query-elements-mcp-tool-handler.spec.ts +210 -0
  289. package/src/tools/handlers/query-elements-mcp-tool-handler.ts +161 -0
  290. package/src/tools/handlers/redo-mcp-tool-handler.ts +62 -0
  291. package/src/tools/handlers/save-model-mcp-tool-handler.ts +71 -0
  292. package/src/tools/handlers/session-info-mcp-tool-handler.spec.ts +152 -0
  293. package/src/tools/handlers/session-info-mcp-tool-handler.ts +97 -0
  294. package/src/tools/handlers/set-selection-mcp-tool-handler.spec.ts +118 -0
  295. package/src/tools/handlers/set-selection-mcp-tool-handler.ts +90 -0
  296. package/src/tools/handlers/set-view-mcp-tool-handler.ts +162 -0
  297. package/src/tools/handlers/undo-mcp-tool-handler.ts +61 -0
  298. package/src/tools/handlers/validate-diagram-mcp-tool-handler.ts +0 -0
  299. package/src/tools/index.ts +34 -0
  300. package/src/tools/tool-annotations.spec.ts +141 -0
  301. package/src/util/index.ts +18 -0
  302. package/src/util/markdown-util.ts +44 -0
  303. package/src/util/mcp-util.ts +25 -0
@@ -0,0 +1,161 @@
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 { GModelElement } from '@eclipse-glsp/server';
18
+ import { inject, injectable } from 'inversify';
19
+ import * as z from 'zod/v4';
20
+ import { McpModelSerializer } from '../../resources/services/mcp-model-serializer';
21
+ import {
22
+ McpDiagramScopedInputSchema,
23
+ AbstractMcpDiagramToolHandler,
24
+ McpElementsNotFoundError,
25
+ McpToolResult,
26
+ elementIds as elementIdsSchema
27
+ } from '../../server';
28
+
29
+ export const QueryElementsInputSchema = McpDiagramScopedInputSchema.extend({
30
+ elementIds: elementIdsSchema
31
+ .optional()
32
+ .describe('Inspect-mode trigger. When set, returns rich per-element data and ignores the filter fields. Throws on unknown id.'),
33
+ types: z.array(z.string()).optional().describe('List-mode type filter. Empty/omitted = any. Ignored when `elementIds` is set.'),
34
+ labelMatch: z.string().optional().describe('List-mode case-insensitive substring on direct-child `GLabel.text`.'),
35
+ limit: z.number().int().min(1).max(1000).optional().describe('List-mode cap, 1–1000. Defaults to 100.')
36
+ });
37
+ export type QueryElementsInput = z.infer<typeof QueryElementsInputSchema>;
38
+
39
+ export const QueryElementMatchSchema = z.object({
40
+ id: z.string(),
41
+ type: z.string(),
42
+ label: z.string().optional()
43
+ });
44
+
45
+ /** `.loose()` lets adopter element types attach extra fields (position, size, …) without widening the schema each time. */
46
+ export const DiagramElementSchema = z
47
+ .object({
48
+ id: z.string(),
49
+ type: z.string(),
50
+ parentId: z.string().optional()
51
+ })
52
+ .loose();
53
+
54
+ export const QueryElementsOutputSchema = z.object({
55
+ mode: z.enum(['list', 'inspect']).describe('Echoes which mode the call ran in.'),
56
+ matches: z.array(QueryElementMatchSchema).optional().describe('Present in `list` mode: slim id/type/label entries.'),
57
+ elements: z.array(DiagramElementSchema).optional().describe('Present in `inspect` mode: rich per-element detail.'),
58
+ truncated: z.boolean().optional().describe('Present in `list` mode: true when more elements matched than `limit`.'),
59
+ expandedFromContainers: z
60
+ .array(z.string())
61
+ .optional()
62
+ .describe(
63
+ 'Present in `inspect` mode when one or more requested ids referred to containers — lists those container ids. ' +
64
+ 'The `elements` array then includes the container plus its descendants.'
65
+ )
66
+ });
67
+
68
+ /** Two-mode element query — list/filter or inspect-by-id, discriminated by `elementIds` presence. */
69
+ @injectable()
70
+ export class QueryElementsMcpToolHandler extends AbstractMcpDiagramToolHandler<QueryElementsInput> {
71
+ static readonly NAME = 'query-elements';
72
+ readonly name = QueryElementsMcpToolHandler.NAME;
73
+ override readonly title = 'Query Diagram Elements';
74
+ readonly description =
75
+ 'Find or inspect elements in the session diagram. Pass `elementIds` to inspect specific ' +
76
+ 'elements in detail (rich per-element data). Pass `types` and/or `labelMatch` to search by ' +
77
+ 'filter (slim id/type/label summaries with truncation). Useful as a precursor to the ' +
78
+ 'create/modify/delete tools, and a cheaper alternative to `diagram-model` on large diagrams.';
79
+ readonly inputSchema = QueryElementsInputSchema;
80
+ override readonly outputSchema = QueryElementsOutputSchema;
81
+
82
+ @inject(McpModelSerializer) protected serializer: McpModelSerializer;
83
+
84
+ /** List-mode result cap when the call doesn't override `limit`. Override via subclass. */
85
+ protected readonly defaultLimit: number = 100;
86
+
87
+ protected async createResult(params: QueryElementsInput): Promise<McpToolResult> {
88
+ return params.elementIds && params.elementIds.length > 0 ? this.inspect(params.elementIds) : this.list(params);
89
+ }
90
+
91
+ protected inspect(inputIds: string[]): McpToolResult {
92
+ const { realIds, missingIds } = this.resolveIds(inputIds);
93
+ if (missingIds.length > 0) {
94
+ throw new McpElementsNotFoundError(missingIds);
95
+ }
96
+ const elements: GModelElement[] = realIds.map(id => this.modelState.index.get(id)!);
97
+ // Per-input serialize lets us identify which inputs caused expansion: the serializer
98
+ // walks descendants for containers, so a 1-input call may produce N output entries.
99
+ // The structural-children check the previous version used overstated this — a leaf node
100
+ // with a label child counts as having `children.length > 0` but doesn't produce extra
101
+ // entries because the serializer's adjuster drops labels.
102
+ const expandedFromContainers = elements
103
+ .filter(element => {
104
+ // `serializeStructuredArray` returns an open `Record<string, unknown>` per the
105
+ // `McpStructuredContent` contract; narrow before reading `.elements.length`.
106
+ const entries = this.serializer.serializeStructuredArray([element]).elements;
107
+ return Array.isArray(entries) && entries.length > 1;
108
+ })
109
+ .map(element => this.aliasService.alias(element.id));
110
+ // Serializer's `serializeStructuredArray` already returns `{ elements: [...] }`; spread to keep one source of truth.
111
+ return this.success(this.summarizeInspect(elements, expandedFromContainers), {
112
+ mode: 'inspect',
113
+ ...this.serializer.serializeStructuredArray(elements),
114
+ ...(expandedFromContainers.length > 0 ? { expandedFromContainers } : {})
115
+ });
116
+ }
117
+
118
+ /** Builds the LLM-facing summary for inspect mode. Override to customize per-adopter wording. */
119
+ protected summarizeInspect(elements: GModelElement[], expandedFromContainers: string[]): string {
120
+ const lines = elements.map(element => `- ${this.aliasService.alias(element.id)} (${element.type})`);
121
+ const expansionNote =
122
+ expandedFromContainers.length > 0 ? `\nContainer(s) ${expandedFromContainers.join(', ')} expanded to include descendants.` : '';
123
+ return `Inspected ${elements.length} element(s); full data in structuredContent.\n${lines.join('\n')}${expansionNote}`;
124
+ }
125
+
126
+ protected list({ types, labelMatch, limit }: QueryElementsInput): McpToolResult {
127
+ const cap = limit ?? this.defaultLimit;
128
+ const typeFilter = types && types.length > 0 ? new Set(types) : undefined;
129
+ const needle = labelMatch?.toLowerCase();
130
+
131
+ const matches: { id: string; type: string; label?: string }[] = [];
132
+ let truncated = false;
133
+ for (const id of this.modelState.index.allIds()) {
134
+ const element = this.modelState.index.get(id);
135
+ if (!element) {
136
+ continue;
137
+ }
138
+ if (typeFilter && !typeFilter.has(element.type)) {
139
+ continue;
140
+ }
141
+ const label = this.labelProvider.getLabel(element)?.text;
142
+ if (needle !== undefined && !label?.toLowerCase().includes(needle)) {
143
+ continue;
144
+ }
145
+ if (matches.length >= cap) {
146
+ truncated = true;
147
+ break;
148
+ }
149
+ matches.push({ id: this.aliasService.alias(element.id), type: element.type, ...(label !== undefined ? { label } : {}) });
150
+ }
151
+
152
+ const summary = matches.length === 0 ? 'No elements matched the query.' : this.renderMarkdown(matches, truncated);
153
+ return this.success(summary, { mode: 'list', matches, truncated });
154
+ }
155
+
156
+ protected renderMarkdown(matches: { id: string; type: string; label?: string }[], truncated: boolean): string {
157
+ const rows = matches.map(match => `- ${match.id} (${match.type})${match.label ? ` — "${match.label}"` : ''}`).join('\n');
158
+ const tail = truncated ? '\n\n_(truncated — increase `limit` or refine filters to see more)_' : '';
159
+ return `Matched ${matches.length} element${matches.length === 1 ? '' : 's'}:\n${rows}${tail}`;
160
+ }
161
+ }
@@ -0,0 +1,62 @@
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 { CommandStack, RedoAction } from '@eclipse-glsp/server';
18
+ import { inject, injectable } from 'inversify';
19
+ import * as z from 'zod/v4';
20
+ import { McpDiagramScopedInputSchema, McpToolError, McpToolResult, OperationMcpDiagramToolHandler } from '../../server';
21
+
22
+ export const RedoInputSchema = McpDiagramScopedInputSchema.extend({
23
+ commandsToRedo: z.number().min(1).default(1).describe('Number of commands to redo. Defaults to 1 (most recent undone command).')
24
+ });
25
+ export type RedoInput = z.infer<typeof RedoInputSchema>;
26
+
27
+ export const RedoOutputSchema = z.object({
28
+ commandsRedone: z.number().int().describe('Number of previously-undone commands re-applied.')
29
+ });
30
+
31
+ /**
32
+ * Redo a given number of the most recent undone actions on the command stack.
33
+ */
34
+ @injectable()
35
+ export class RedoMcpToolHandler extends OperationMcpDiagramToolHandler<RedoInput> {
36
+ static readonly NAME = 'redo';
37
+ readonly name = RedoMcpToolHandler.NAME;
38
+ override readonly title = 'Redo Diagram Commands';
39
+ readonly description =
40
+ 'Re-apply commands that were previously undone. Defaults to redoing one command — the most recent undo — when ' +
41
+ '`commandsToRedo` is omitted. Use the same count you passed to `undo` to revert a complete undo cycle. ' +
42
+ "Throws when there's nothing on the redo stack (e.g. nothing has been undone, or the redo stack was cleared by a " +
43
+ 'subsequent edit). Only do this on an explicit user request.';
44
+ readonly inputSchema = RedoInputSchema;
45
+ override readonly outputSchema = RedoOutputSchema;
46
+
47
+ @inject(CommandStack) protected commandStack: CommandStack;
48
+
49
+ protected async createResult({ commandsToRedo }: RedoInput): Promise<McpToolResult> {
50
+ if (!this.commandStack.canRedo()) {
51
+ throw new McpToolError(
52
+ 'Nothing to redo (redo stack is empty; perform an undo first or accept that the redo history was cleared by a subsequent edit).'
53
+ );
54
+ }
55
+
56
+ for (let i = 0; i < commandsToRedo; i++) {
57
+ await this.actionDispatcher.dispatch(RedoAction.create());
58
+ }
59
+
60
+ return this.success('Redo successful', { commandsRedone: commandsToRedo });
61
+ }
62
+ }
@@ -0,0 +1,71 @@
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 { ActionDispatcher, CommandStack, SaveModelAction } from '@eclipse-glsp/server';
18
+ import { inject, injectable } from 'inversify';
19
+ import * as z from 'zod/v4';
20
+ import { McpDiagramScopedInputSchema, AbstractMcpDiagramToolHandler, McpLogger, McpToolResult } from '../../server';
21
+
22
+ export const SaveModelInputSchema = McpDiagramScopedInputSchema.extend({
23
+ fileUri: z.string().optional().describe('Optional destination file URI. If not provided, saves to the original source model location.')
24
+ });
25
+ export type SaveModelInput = z.infer<typeof SaveModelInputSchema>;
26
+
27
+ export const SaveModelOutputSchema = z.object({
28
+ saved: z.boolean().describe('True when a save operation was dispatched; false when the model was clean and nothing was written.'),
29
+ fileUri: z
30
+ .string()
31
+ .optional()
32
+ .describe('Destination file URI when explicitly provided; absent when saving to the original source location.')
33
+ });
34
+
35
+ /** Doesn't extend `OperationMcpDiagramToolHandler` — saving a read-only model is a legitimate adopter scenario (e.g. "save as" to a writable location). */
36
+ @injectable()
37
+ export class SaveModelMcpToolHandler extends AbstractMcpDiagramToolHandler<SaveModelInput> {
38
+ static readonly NAME = 'save-model';
39
+ readonly name = SaveModelMcpToolHandler.NAME;
40
+ override readonly title = 'Save Diagram Model';
41
+ readonly description =
42
+ 'Save the current diagram model to persistent storage. ' +
43
+ 'This operation persists all changes back to the source model. ' +
44
+ 'Only do this on an explicit user request and not as part of other tasks. ' +
45
+ 'Optionally specify a new fileUri to save to a different location.';
46
+ readonly inputSchema = SaveModelInputSchema;
47
+ override readonly outputSchema = SaveModelOutputSchema;
48
+ /**
49
+ * Saving writes to disk — that's a mutation of the environment even though the in-memory
50
+ * model state isn't a target of an `Operation` dispatch. Drop the readOnly claim from the
51
+ * read-base default. Not destructive per the spec definition (no irreversible deletion).
52
+ */
53
+ override readonly readOnlyHint = false;
54
+ override readonly destructiveHint: boolean = false;
55
+ override readonly idempotentHint: boolean = false;
56
+
57
+ @inject(ActionDispatcher) protected actionDispatcher: ActionDispatcher;
58
+ @inject(CommandStack) protected commandStack: CommandStack;
59
+ @inject(McpLogger) protected mcpLogger: McpLogger;
60
+
61
+ protected async createResult({ fileUri }: SaveModelInput): Promise<McpToolResult> {
62
+ if (!this.commandStack.isDirty) {
63
+ this.mcpLogger.info('save-model: nothing to save');
64
+ return this.success('No changes to save', { saved: false, fileUri });
65
+ }
66
+
67
+ await this.actionDispatcher.dispatch(SaveModelAction.create({ fileUri }));
68
+ this.mcpLogger.info(`save-model: model saved${fileUri ? ` to ${fileUri}` : ''}`);
69
+ return this.success('Model saved successfully', { saved: true, fileUri });
70
+ }
71
+ }
@@ -0,0 +1,152 @@
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';
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
+ });
@@ -0,0 +1,97 @@
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 { ClientSessionManager, CommandStack, ModelState } from '@eclipse-glsp/server';
18
+ import { inject, injectable } from 'inversify';
19
+ import * as z from 'zod/v4';
20
+ import { AbstractMcpToolHandler, McpToolError, McpToolResult } from '../../server';
21
+
22
+ export const SessionInfoInputSchema = z.object({
23
+ sessionId: z
24
+ .string()
25
+ .optional()
26
+ .describe('Optional filter — returns only the matching session, or throws if unknown. Omit to list all sessions.')
27
+ });
28
+ export type SessionInfoInput = z.infer<typeof SessionInfoInputSchema>;
29
+
30
+ export const SessionInfoRowSchema = z.object({
31
+ sessionId: z.string(),
32
+ diagramType: z.string(),
33
+ sourceUri: z.string(),
34
+ readOnly: z.boolean(),
35
+ dirty: z.boolean().describe('True when the diagram has unsaved changes — call `save-model` to persist.')
36
+ });
37
+ export type SessionInfoRow = z.infer<typeof SessionInfoRowSchema>;
38
+
39
+ export const SessionInfoOutputSchema = z.object({
40
+ sessions: z.array(SessionInfoRowSchema)
41
+ });
42
+
43
+ @injectable()
44
+ export class SessionInfoMcpToolHandler extends AbstractMcpToolHandler<SessionInfoInput> {
45
+ @inject(ClientSessionManager) protected clientSessionManager: ClientSessionManager;
46
+
47
+ static readonly NAME = 'session-info';
48
+ readonly name = SessionInfoMcpToolHandler.NAME;
49
+ override readonly title = 'GLSP Session Info';
50
+ readonly description =
51
+ 'Report info on the active GLSP client sessions: session id, diagram type, source uri, and read-only status. ' +
52
+ "Pass `sessionId` to retrieve a single session's entry; omit it to list every active session. " +
53
+ 'Useful as a discovery tool — most other diagram-scoped tools take the `sessionId` returned here.';
54
+ readonly inputSchema = SessionInfoInputSchema;
55
+ override readonly outputSchema = SessionInfoOutputSchema;
56
+
57
+ protected createResult({ sessionId }: SessionInfoInput): McpToolResult {
58
+ const sessions = sessionId !== undefined ? this.singleSession(sessionId) : this.clientSessionManager.getSessions();
59
+ const rows = sessions.map(session => this.buildSessionRow(session));
60
+ return this.success(this.summarizeSessions(rows), { sessions: rows });
61
+ }
62
+
63
+ /** Extracts a {@link SessionInfoRow} from a {@link ClientSession}. Override to surface adopter-specific fields. */
64
+ protected buildSessionRow(session: ReturnType<ClientSessionManager['getSessions']>[number]): SessionInfoRow {
65
+ const modelState = session.container.get<ModelState>(ModelState);
66
+ const commandStack = session.container.get<CommandStack>(CommandStack);
67
+ return {
68
+ sessionId: session.id,
69
+ diagramType: session.diagramType,
70
+ // `ModelState.sourceUri` may be undefined for not-yet-persisted diagrams; render as empty string to keep the schema-required string shape.
71
+ sourceUri: modelState.sourceUri ?? '',
72
+ readOnly: modelState.isReadonly,
73
+ dirty: commandStack.isDirty
74
+ };
75
+ }
76
+
77
+ /** Builds the LLM-facing summary. Override to customize per-adopter wording. */
78
+ protected summarizeSessions(rows: SessionInfoRow[]): string {
79
+ if (rows.length === 0) {
80
+ return 'No active sessions.';
81
+ }
82
+ const lines = rows.map(row => {
83
+ const flags = [row.readOnly ? 'read-only' : undefined, row.dirty ? 'dirty' : undefined].filter(Boolean);
84
+ const suffix = flags.length > 0 ? `, ${flags.join(', ')}` : '';
85
+ return `- ${row.sessionId} (${row.diagramType}${suffix})`;
86
+ });
87
+ return `${rows.length} session(s); full details in structuredContent.\n${lines.join('\n')}`;
88
+ }
89
+
90
+ protected singleSession(sessionId: string): ReturnType<ClientSessionManager['getSessions']> {
91
+ const session = this.clientSessionManager.getSession(sessionId);
92
+ if (!session) {
93
+ throw new McpToolError(`Unknown sessionId: ${sessionId}`);
94
+ }
95
+ return [session];
96
+ }
97
+ }
@@ -0,0 +1,118 @@
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 { DefaultMcpLabelProvider, McpElementsNotFoundError, McpIdAliasService, McpLabelProvider, McpToolResult } from '../../server';
31
+ import { SetSelectionInput, SetSelectionMcpToolHandler } from './set-selection-mcp-tool-handler';
32
+
33
+ function makeElement(id: string, type: string): GModelElement {
34
+ return { id, type, children: [] } as unknown as GModelElement;
35
+ }
36
+
37
+ function makeModelState(elements: GModelElement[]): ModelState {
38
+ const byId = new Map(elements.map(el => [el.id, el]));
39
+ return {
40
+ index: {
41
+ allIds: () => [...byId.keys()],
42
+ get: (id: string) => byId.get(id),
43
+ find: (id: string) => byId.get(id)
44
+ }
45
+ } as unknown as ModelState;
46
+ }
47
+
48
+ function makeRecordingDispatcher(): { dispatcher: ActionDispatcher; dispatched: Action[] } {
49
+ const dispatched: Action[] = [];
50
+ const dispatcher = {
51
+ dispatch: async (action: Action) => {
52
+ dispatched.push(action);
53
+ }
54
+ } as unknown as ActionDispatcher;
55
+ return { dispatcher, dispatched };
56
+ }
57
+
58
+ function buildHandler(elements: GModelElement[], dispatcher: ActionDispatcher): SetSelectionMcpToolHandler {
59
+ const container = new Container();
60
+ container.load(
61
+ new ContainerModule(bind => {
62
+ bind(Logger).toConstantValue(new NullLogger());
63
+ bind(ClientId).toConstantValue('test-session');
64
+ bind(ModelState).toConstantValue(makeModelState(elements));
65
+ bind(McpIdAliasService).toConstantValue({
66
+ lookup: (id: string) => id,
67
+ alias: (id: string) => id
68
+ } as McpIdAliasService);
69
+ bind(ActionDispatcher).toConstantValue(dispatcher);
70
+ bind(McpLabelProvider).to(DefaultMcpLabelProvider);
71
+ bind(SetSelectionMcpToolHandler).toSelf();
72
+ })
73
+ );
74
+ return container.get(SetSelectionMcpToolHandler);
75
+ }
76
+
77
+ function callCreateResult(handler: SetSelectionMcpToolHandler, params: SetSelectionInput): Promise<McpToolResult> {
78
+ return (handler as unknown as { createResult: (p: SetSelectionInput) => Promise<McpToolResult> }).createResult(params);
79
+ }
80
+
81
+ describe('SetSelectionMcpToolHandler', () => {
82
+ it('dispatches SelectAllAction(false) before SelectAction when `clear: true`', async () => {
83
+ const { dispatcher, dispatched } = makeRecordingDispatcher();
84
+ const handler = buildHandler([makeElement('n1', 'task'), makeElement('n2', 'task')], dispatcher);
85
+
86
+ await callCreateResult(handler, { sessionId: 's', selectedElementIds: ['n1'], clear: true });
87
+
88
+ expect(dispatched).to.have.lengthOf(2);
89
+ expect(SelectAllAction.is(dispatched[0])).to.equal(true);
90
+ expect((dispatched[0] as SelectAllAction).select).to.equal(false);
91
+ expect(SelectAction.is(dispatched[1])).to.equal(true);
92
+ });
93
+
94
+ it('dispatches SelectAction with resolved selected and deselected ids', async () => {
95
+ const { dispatcher, dispatched } = makeRecordingDispatcher();
96
+ const handler = buildHandler([makeElement('a', 'task'), makeElement('b', 'task'), makeElement('c', 'task')], dispatcher);
97
+
98
+ await callCreateResult(handler, { sessionId: 's', selectedElementIds: ['a', 'b'], deselectedElementIds: ['c'] });
99
+
100
+ expect(dispatched).to.have.lengthOf(1);
101
+ const action = dispatched[0] as SelectAction;
102
+ expect(action.selectedElementsIDs).to.deep.equal(['a', 'b']);
103
+ expect(action.deselectedElementsIDs).to.deep.equal(['c']);
104
+ });
105
+
106
+ it('throws McpElementsNotFoundError when an id is missing from the model', async () => {
107
+ const { dispatcher } = makeRecordingDispatcher();
108
+ const handler = buildHandler([makeElement('n1', 'task')], dispatcher);
109
+
110
+ let error: unknown;
111
+ try {
112
+ await callCreateResult(handler, { sessionId: 's', selectedElementIds: ['n1', 'ghost'] });
113
+ } catch (err: unknown) {
114
+ error = err;
115
+ }
116
+ expect(error).to.be.instanceOf(McpElementsNotFoundError);
117
+ });
118
+ });