@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,59 @@
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 * as http from 'http';
18
+
19
+ export interface RawHttpResponse {
20
+ status: number;
21
+ contentType: string | undefined;
22
+ body: string;
23
+ }
24
+
25
+ /**
26
+ * Issue a raw HTTP request to a transport's listening port. Used by spec files that need
27
+ * to bypass the SDK Client to assert wire-level behavior (status codes, error envelopes).
28
+ */
29
+ export function rawHttpRequest(
30
+ port: number,
31
+ method: 'POST' | 'GET' | 'DELETE',
32
+ headers: http.OutgoingHttpHeaders,
33
+ body?: unknown
34
+ ): Promise<RawHttpResponse> {
35
+ return new Promise((resolve, reject) => {
36
+ const req = http.request(
37
+ {
38
+ hostname: '127.0.0.1',
39
+ port,
40
+ path: '/mcp',
41
+ method,
42
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream', ...headers }
43
+ },
44
+ res => {
45
+ const chunks: Buffer[] = [];
46
+ res.on('data', chunk => chunks.push(chunk));
47
+ res.on('end', () =>
48
+ resolve({
49
+ status: res.statusCode ?? 0,
50
+ contentType: res.headers['content-type'],
51
+ body: Buffer.concat(chunks).toString('utf8')
52
+ })
53
+ );
54
+ }
55
+ );
56
+ req.on('error', reject);
57
+ req.end(body !== undefined ? JSON.stringify(body) : undefined);
58
+ });
59
+ }
@@ -0,0 +1,99 @@
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 { ClientId, GModelElement, Logger, ModelState, NullLogger } from '@eclipse-glsp/server';
18
+ import { expect } from 'chai';
19
+ import { Container, ContainerModule } from 'inversify';
20
+ import { DefaultMcpLabelProvider, McpIdAliasService, McpLabelProvider, McpToolResult } from '../../server';
21
+ import { CountElementsMcpToolHandler } from './count-elements-mcp-tool-handler';
22
+
23
+ function makeElement(id: string, type: string): GModelElement {
24
+ return { id, type, children: [] } as unknown as GModelElement;
25
+ }
26
+
27
+ function makeModelState(elements: GModelElement[]): ModelState {
28
+ const byId = new Map(elements.map(el => [el.id, el]));
29
+ return {
30
+ index: {
31
+ allIds: () => [...byId.keys()],
32
+ get: (id: string) => byId.get(id),
33
+ find: (id: string) => byId.get(id)
34
+ }
35
+ } as unknown as ModelState;
36
+ }
37
+
38
+ function buildHandler(elements: GModelElement[]): CountElementsMcpToolHandler {
39
+ const container = new Container();
40
+ container.load(
41
+ new ContainerModule(bind => {
42
+ bind(Logger).toConstantValue(new NullLogger());
43
+ bind(ClientId).toConstantValue('test-session');
44
+ bind(ModelState).toConstantValue(makeModelState(elements));
45
+ bind(McpIdAliasService).toConstantValue({
46
+ lookup: (id: string) => id,
47
+ alias: (id: string) => id
48
+ } as McpIdAliasService);
49
+ bind(McpLabelProvider).to(DefaultMcpLabelProvider);
50
+ bind(CountElementsMcpToolHandler).toSelf();
51
+ })
52
+ );
53
+ return container.get(CountElementsMcpToolHandler);
54
+ }
55
+
56
+ function callCreateResult(handler: CountElementsMcpToolHandler): Promise<McpToolResult> {
57
+ return (handler as unknown as { createResult: (params: { sessionId: string }) => Promise<McpToolResult> }).createResult({
58
+ sessionId: 'test-session'
59
+ });
60
+ }
61
+
62
+ describe('CountElementsMcpToolHandler', () => {
63
+ it('aggregates element counts grouped by `type`', async () => {
64
+ const handler = buildHandler([
65
+ makeElement('n1', 'task:manual'),
66
+ makeElement('n2', 'task:manual'),
67
+ makeElement('n3', 'task:automated'),
68
+ makeElement('e1', 'edge')
69
+ ]);
70
+
71
+ const result = await callCreateResult(handler);
72
+ expect(result.structuredContent).to.deep.equal({
73
+ total: 4,
74
+ countsByType: { 'task:manual': 2, 'task:automated': 1, edge: 1 }
75
+ });
76
+ });
77
+
78
+ it('orders rendered rows by count desc, then type alpha asc on ties', async () => {
79
+ const handler = buildHandler([
80
+ makeElement('e1', 'edge'),
81
+ makeElement('e2', 'edge'),
82
+ makeElement('n1', 'task:automated'),
83
+ makeElement('n2', 'task:manual'),
84
+ makeElement('n3', 'task:manual')
85
+ ]);
86
+
87
+ const result = await callCreateResult(handler);
88
+ const text = (result.content[0] as { text: string }).text;
89
+ // Counts: edge=2, task:manual=2, task:automated=1.
90
+ // Expected order: edge before task:manual (alpha tiebreak on count=2), then task:automated.
91
+ const expectedOrder = ['edge', 'task:manual', 'task:automated'];
92
+ const indices = expectedOrder.map(type => text.indexOf(`- ${type}: `));
93
+ expect(
94
+ indices.every(i => i >= 0),
95
+ 'expected all type rows to be rendered'
96
+ ).to.equal(true);
97
+ expect(indices).to.deep.equal([...indices].sort((a, b) => a - b));
98
+ });
99
+ });
@@ -0,0 +1,66 @@
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 { injectable } from 'inversify';
18
+ import * as z from 'zod/v4';
19
+ import { McpDiagramScopedInputSchema, AbstractMcpDiagramToolHandler, McpToolResult } from '../../server';
20
+
21
+ export const CountElementsInputSchema = McpDiagramScopedInputSchema;
22
+ export type CountElementsInput = z.infer<typeof CountElementsInputSchema>;
23
+
24
+ export const CountElementsOutputSchema = z.object({
25
+ total: z.number().int().describe('Total element count across the diagram (root included).'),
26
+ countsByType: z.record(z.string(), z.number().int()).describe('Element count grouped by `GModelElement.type`.')
27
+ });
28
+
29
+ /**
30
+ * Counts elements in the diagram, grouped by type. Cheap alternative to dumping the full
31
+ * `diagram-model` resource when the agent only needs to know "how big is this" or "do any
32
+ * elements of type X exist".
33
+ */
34
+ @injectable()
35
+ export class CountElementsMcpToolHandler extends AbstractMcpDiagramToolHandler<CountElementsInput> {
36
+ static readonly NAME = 'count-elements';
37
+ readonly name = CountElementsMcpToolHandler.NAME;
38
+ override readonly title = 'Count Diagram Elements';
39
+ readonly description =
40
+ 'Count the elements in the session diagram, grouped by element type. ' +
41
+ 'Cheap sizing primitive — useful before deciding whether to load the full model with `diagram-model` ' +
42
+ '(expensive on large diagrams) or whether a filtered `query-elements` call would suffice. ' +
43
+ 'Returns total count plus a per-type breakdown.';
44
+ readonly inputSchema = CountElementsInputSchema;
45
+ override readonly outputSchema = CountElementsOutputSchema;
46
+
47
+ protected async createResult(_params: CountElementsInput): Promise<McpToolResult> {
48
+ const countsByType: Record<string, number> = {};
49
+ let total = 0;
50
+ for (const id of this.modelState.index.allIds()) {
51
+ const element = this.modelState.index.get(id);
52
+ if (!element) {
53
+ continue;
54
+ }
55
+ countsByType[element.type] = (countsByType[element.type] ?? 0) + 1;
56
+ total += 1;
57
+ }
58
+ return this.success(this.renderMarkdown(total, countsByType), { total, countsByType });
59
+ }
60
+
61
+ protected renderMarkdown(total: number, countsByType: Record<string, number>): string {
62
+ const sorted = Object.entries(countsByType).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
63
+ const rows = sorted.map(([type, count]) => `- ${type}: ${count}`).join('\n');
64
+ return `Total elements: ${total}\n\nBy type:\n${rows}`;
65
+ }
66
+ }
@@ -0,0 +1,196 @@
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
+ DiagramConfiguration,
22
+ EdgeCreationChecker,
23
+ EdgeTypeHint,
24
+ GModelElement,
25
+ Logger,
26
+ ModelState,
27
+ NullLogger
28
+ } from '@eclipse-glsp/server';
29
+ import { expect } from 'chai';
30
+ import { Container, ContainerModule } from 'inversify';
31
+ import { DefaultMcpLabelProvider, McpIdAliasService, McpLabelProvider, McpToolResult } from '../../server';
32
+ import { CreateEdgesInput, CreateEdgesMcpToolHandler } from './create-edges-mcp-tool-handler';
33
+
34
+ function makeElement(id: string, type: string): GModelElement {
35
+ return { id, type, children: [] } as unknown as GModelElement;
36
+ }
37
+
38
+ function makeModelState(elements: GModelElement[]): ModelState {
39
+ const byId = new Map(elements.map(el => [el.id, el]));
40
+ return {
41
+ index: {
42
+ allIds: () => [...byId.keys()],
43
+ get: (id: string) => byId.get(id),
44
+ find: (id: string) => byId.get(id)
45
+ },
46
+ isReadonly: false
47
+ } as unknown as ModelState;
48
+ }
49
+
50
+ class StubEdgeCreationChecker implements EdgeCreationChecker {
51
+ public lastTargetCall?: { edgeType: string; source: GModelElement; target: GModelElement };
52
+ constructor(private readonly result: boolean) {}
53
+ isValidSource(): boolean {
54
+ return this.result;
55
+ }
56
+ isValidTarget(edgeType: string, source: GModelElement, target: GModelElement): boolean {
57
+ this.lastTargetCall = { edgeType, source, target };
58
+ return this.result;
59
+ }
60
+ }
61
+
62
+ interface BuildArgs {
63
+ elements: GModelElement[];
64
+ edgeTypeHints: Pick<EdgeTypeHint, 'elementTypeId' | 'dynamic'>[];
65
+ checker?: EdgeCreationChecker;
66
+ }
67
+
68
+ /** Records dispatched actions so create-mode tests can assert no dispatch happened. */
69
+ class RecordingDispatcher {
70
+ public dispatched: Action[] = [];
71
+ async dispatch(action: Action): Promise<void> {
72
+ this.dispatched.push(action);
73
+ }
74
+ async dispatchAll(): Promise<void> {
75
+ // not used by create-edges
76
+ }
77
+ dispatchAfterNextUpdate(): void {
78
+ // not used
79
+ }
80
+ }
81
+
82
+ function buildHandler({ elements, edgeTypeHints, checker }: BuildArgs): {
83
+ handler: CreateEdgesMcpToolHandler;
84
+ dispatcher: RecordingDispatcher;
85
+ } {
86
+ const dispatcher = new RecordingDispatcher();
87
+ const container = new Container();
88
+ container.load(
89
+ new ContainerModule(bind => {
90
+ bind(Logger).toConstantValue(new NullLogger());
91
+ bind(ClientId).toConstantValue('test-session');
92
+ bind(ModelState).toConstantValue(makeModelState(elements));
93
+ bind(McpIdAliasService).toConstantValue({
94
+ lookup: (id: string) => id,
95
+ alias: (id: string) => id
96
+ } as McpIdAliasService);
97
+ bind(DiagramConfiguration).toConstantValue({ edgeTypeHints } as unknown as DiagramConfiguration);
98
+ bind(ActionDispatcher).toConstantValue(dispatcher as unknown as ActionDispatcher);
99
+ if (checker) {
100
+ bind(EdgeCreationChecker).toConstantValue(checker);
101
+ }
102
+ bind(McpLabelProvider).to(DefaultMcpLabelProvider);
103
+ bind(CreateEdgesMcpToolHandler).toSelf();
104
+ })
105
+ );
106
+ return { handler: container.get(CreateEdgesMcpToolHandler), dispatcher };
107
+ }
108
+
109
+ function callCreateResult(handler: CreateEdgesMcpToolHandler, params: CreateEdgesInput): Promise<McpToolResult> {
110
+ return (handler as unknown as { createResult: (p: CreateEdgesInput) => Promise<McpToolResult> }).createResult(params);
111
+ }
112
+
113
+ interface DryRunStructured {
114
+ createdEdges: { id: string; elementTypeId: string; label?: string }[];
115
+ errors: string[];
116
+ validationResults: { edgeType: string; sourceElementId: string; targetElementId: string; isValid: boolean; reason?: string }[];
117
+ }
118
+
119
+ describe('CreateEdgesMcpToolHandler · dryRun', () => {
120
+ const baseInput = (overrides: Partial<CreateEdgesInput> = {}): CreateEdgesInput => ({
121
+ sessionId: 's',
122
+ dryRun: true,
123
+ edges: [{ elementTypeId: 'edge:dynamic', sourceElementId: 's', targetElementId: 't' }],
124
+ ...overrides
125
+ });
126
+
127
+ it('returns isValid:true with a "no dynamic hint" reason when the edgeType has no dynamic hint', async () => {
128
+ const { handler, dispatcher } = buildHandler({
129
+ elements: [makeElement('s', 'task'), makeElement('t', 'task')],
130
+ edgeTypeHints: [{ elementTypeId: 'edge:static', dynamic: false }]
131
+ });
132
+
133
+ const result = await callCreateResult(
134
+ handler,
135
+ baseInput({ edges: [{ elementTypeId: 'edge:static', sourceElementId: 's', targetElementId: 't' }] })
136
+ );
137
+
138
+ const structured = result.structuredContent as unknown as DryRunStructured;
139
+ expect(structured.validationResults).to.have.lengthOf(1);
140
+ expect(structured.validationResults[0].isValid).to.equal(true);
141
+ expect(structured.validationResults[0].reason).to.match(/no dynamic edge-type hint/i);
142
+ expect(structured.createdEdges).to.deep.equal([]);
143
+ expect(dispatcher.dispatched).to.have.lengthOf(0);
144
+ });
145
+
146
+ it('flags isValid:false with a config-gap reason when edgeType declares a dynamic hint but no checker is bound', async () => {
147
+ const { handler, dispatcher } = buildHandler({
148
+ elements: [makeElement('s', 'task'), makeElement('t', 'task')],
149
+ edgeTypeHints: [{ elementTypeId: 'edge:dynamic', dynamic: true }]
150
+ });
151
+
152
+ const result = await callCreateResult(handler, baseInput());
153
+
154
+ const structured = result.structuredContent as unknown as DryRunStructured;
155
+ expect(structured.validationResults[0].isValid).to.equal(false);
156
+ expect(structured.validationResults[0].reason).to.include('EdgeCreationChecker is not bound');
157
+ expect(dispatcher.dispatched).to.have.lengthOf(0);
158
+ });
159
+
160
+ it('delegates to EdgeCreationChecker.isValidTarget when target + dynamic hint + checker are present', async () => {
161
+ const sourceElement = makeElement('s', 'task');
162
+ const targetElement = makeElement('t', 'task');
163
+ const checker = new StubEdgeCreationChecker(false);
164
+ const { handler, dispatcher } = buildHandler({
165
+ elements: [sourceElement, targetElement],
166
+ edgeTypeHints: [{ elementTypeId: 'edge:dynamic', dynamic: true }],
167
+ checker
168
+ });
169
+
170
+ const result = await callCreateResult(handler, baseInput());
171
+
172
+ expect(checker.lastTargetCall).to.deep.equal({
173
+ edgeType: 'edge:dynamic',
174
+ source: sourceElement,
175
+ target: targetElement
176
+ });
177
+ const structured = result.structuredContent as unknown as DryRunStructured;
178
+ expect(structured.validationResults[0].isValid).to.equal(false);
179
+ expect(dispatcher.dispatched).to.have.lengthOf(0);
180
+ });
181
+
182
+ it('reports source/target not-found inline as a per-edge validation failure (does not throw)', async () => {
183
+ const { handler } = buildHandler({
184
+ elements: [makeElement('s', 'task')],
185
+ edgeTypeHints: [{ elementTypeId: 'edge:dynamic', dynamic: true }]
186
+ });
187
+
188
+ const result = await callCreateResult(
189
+ handler,
190
+ baseInput({ edges: [{ elementTypeId: 'edge:dynamic', sourceElementId: 's', targetElementId: 'missing' }] })
191
+ );
192
+ const structured = result.structuredContent as unknown as DryRunStructured;
193
+ expect(structured.validationResults[0].isValid).to.equal(false);
194
+ expect(structured.validationResults[0].reason).to.include('Target element not found');
195
+ });
196
+ });
@@ -0,0 +1,205 @@
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 { ChangeRoutingPointsOperation, CreateEdgeOperation, DiagramConfiguration, EdgeCreationChecker } from '@eclipse-glsp/server';
18
+ import { inject, injectable, optional } from 'inversify';
19
+ import * as z from 'zod/v4';
20
+ import {
21
+ ElementIdentity,
22
+ ElementIdentitySchema,
23
+ McpDiagramScopedInputSchema,
24
+ McpToolResult,
25
+ OperationMcpDiagramToolHandler,
26
+ position
27
+ } from '../../server';
28
+ import { formatNoticeList } from '../../util';
29
+
30
+ /** Single edge-creation entry. Strict so an LLM-typoed field surfaces as a validation error instead of being silently dropped. */
31
+ export const CreateEdgeSpecSchema = z.strictObject({
32
+ elementTypeId: z.string().describe('Edge type ID (e.g., `edge`, `transition`). Use the `element-types` tool to discover valid IDs.'),
33
+ sourceElementId: z.string().describe('ID of the source element (must exist in the diagram)'),
34
+ targetElementId: z.string().describe('ID of the target element (must exist in the diagram)'),
35
+ routingPoints: z.array(position).optional().describe('Optional array of routing point coordinates that allow for a complex edge path.'),
36
+ // `args` stays open (`record(...)`) — adopter-specific extension surface for per-edge-type creation hints.
37
+ args: z.record(z.string(), z.any()).optional().describe('Additional type-specific arguments for edge creation (varies by edge type)')
38
+ });
39
+
40
+ export const CreateEdgesInputSchema = McpDiagramScopedInputSchema.extend({
41
+ edges: z.array(CreateEdgeSpecSchema).min(1).describe('Array of edges to create. Must include at least one edge.'),
42
+ dryRun: z
43
+ .boolean()
44
+ .optional()
45
+ .describe(
46
+ 'When true, validate each edge against the type-hint rules without creating anything; returns per-edge `validationResults`.'
47
+ )
48
+ });
49
+ export type CreateEdgesInput = z.infer<typeof CreateEdgesInputSchema>;
50
+
51
+ export const CreateEdgesValidationResultSchema = z.object({
52
+ edgeType: z.string(),
53
+ sourceElementId: z.string(),
54
+ targetElementId: z.string(),
55
+ isValid: z.boolean(),
56
+ reason: z.string().optional().describe('Brief reason explaining the verdict (always present when `isValid: false`).')
57
+ });
58
+
59
+ export const CreateEdgesOutputSchema = z.object({
60
+ createdEdges: z.array(ElementIdentitySchema).describe('Identity of each edge successfully created. Empty in `dryRun` mode.'),
61
+ errors: z.array(z.string()).describe('Per-input failure messages; absent or empty when every input succeeded.'),
62
+ validationResults: z
63
+ .array(CreateEdgesValidationResultSchema)
64
+ .optional()
65
+ .describe('Per-input validation results. Present only in `dryRun` mode.')
66
+ });
67
+
68
+ type EdgeInput = CreateEdgesInput['edges'][number];
69
+ type ValidationResult = z.infer<typeof CreateEdgesValidationResultSchema>;
70
+
71
+ @injectable()
72
+ export class CreateEdgesMcpToolHandler extends OperationMcpDiagramToolHandler<CreateEdgesInput> {
73
+ static readonly NAME = 'create-edges';
74
+ readonly name = CreateEdgesMcpToolHandler.NAME;
75
+ override readonly title = 'Create Diagram Edges';
76
+ readonly description =
77
+ 'Create one or multiple new edges connecting two elements in the diagram. ' +
78
+ 'Set `dryRun: true` to validate proposed edges (per the diagram-type type-hint rules) ' +
79
+ 'without creating anything; the result then carries per-edge `validationResults`. ' +
80
+ 'Without `dryRun`, this operation modifies the diagram state and requires user approval. ' +
81
+ 'Use the `element-types` tool to discover valid edge type IDs.';
82
+ readonly inputSchema = CreateEdgesInputSchema;
83
+ override readonly outputSchema = CreateEdgesOutputSchema;
84
+
85
+ @inject(DiagramConfiguration) protected diagramConfiguration: DiagramConfiguration;
86
+ @inject(EdgeCreationChecker) @optional() protected edgeCreationChecker?: EdgeCreationChecker;
87
+
88
+ protected async createResult({ edges, dryRun }: CreateEdgesInput): Promise<McpToolResult> {
89
+ if (dryRun) {
90
+ return this.runDryRun(edges);
91
+ }
92
+ return this.runCreate(edges);
93
+ }
94
+
95
+ protected runDryRun(edges: EdgeInput[]): McpToolResult {
96
+ const validationResults: ValidationResult[] = edges.map(edge => this.validateEdge(edge));
97
+ const validCount = validationResults.filter(result => result.isValid).length;
98
+ const summary =
99
+ `Dry run: validated ${edges.length} edge(s); ${validCount} would be accepted, ${edges.length - validCount} rejected.\n` +
100
+ validationResults
101
+ .map(
102
+ result =>
103
+ `- ${result.edgeType} ${result.sourceElementId} → ${result.targetElementId}: ` +
104
+ `${result.isValid ? 'valid' : `invalid (${result.reason})`}`
105
+ )
106
+ .join('\n');
107
+ return this.success(summary, { createdEdges: [], errors: [], validationResults });
108
+ }
109
+
110
+ protected async runCreate(edges: EdgeInput[]): Promise<McpToolResult> {
111
+ let beforeIds = this.modelState.index.allIds();
112
+
113
+ const errors: string[] = [];
114
+ const createdEdges: ElementIdentity[] = [];
115
+ let dispatchedOperations = 0;
116
+ // Sequential — each iteration must isolate its own creation in the post-dispatch diff.
117
+ for (const edge of edges) {
118
+ const { elementTypeId, routingPoints, args } = edge;
119
+ const sourceElementId = this.aliasService.lookup(edge.sourceElementId);
120
+ const targetElementId = this.aliasService.lookup(edge.targetElementId);
121
+
122
+ const source = this.modelState.index.find(sourceElementId);
123
+ if (!source) {
124
+ errors.push(`Source element not found: ${edge.sourceElementId}`);
125
+ continue;
126
+ }
127
+ const target = this.modelState.index.find(targetElementId);
128
+ if (!target) {
129
+ errors.push(`Target element not found: ${edge.targetElementId}`);
130
+ continue;
131
+ }
132
+
133
+ const operation = CreateEdgeOperation.create({ elementTypeId, sourceElementId, targetElementId, args });
134
+ await this.actionDispatcher.dispatch(operation);
135
+ dispatchedOperations++;
136
+
137
+ const afterIds = this.modelState.index.allIds();
138
+ const newIds = afterIds.filter(id => !beforeIds.includes(id));
139
+ const newElements = newIds.map(id => this.modelState.index.find(id)).filter(element => element?.type === elementTypeId);
140
+ const newElement = newElements[0];
141
+ if (newElements.length > 1) {
142
+ this.logger.warn('More than 1 new element created');
143
+ }
144
+ beforeIds = afterIds;
145
+
146
+ // Operations don't surface failure directly — infer from absence of a new id of the requested type.
147
+ if (!newElement) {
148
+ errors.push(`Edge creation likely failed because no new element ID was found for input: ${JSON.stringify(edge)}`);
149
+ continue;
150
+ }
151
+
152
+ if (routingPoints) {
153
+ const routingPointsOperation = ChangeRoutingPointsOperation.create([
154
+ { elementId: newElement.id, newRoutingPoints: routingPoints }
155
+ ]);
156
+ await this.actionDispatcher.dispatch(routingPointsOperation);
157
+ dispatchedOperations++;
158
+ }
159
+
160
+ createdEdges.push(this.describeResolvedElement(newElement));
161
+ }
162
+
163
+ const successListStr = createdEdges.map(({ id, elementTypeId }) => `- ${elementTypeId} (#${id})`).join('\n');
164
+ // Per-input errors are surfaced in `errors`; the call itself still succeeds — rolling back partial creates would require operation-level transactions.
165
+ return this.success(
166
+ `Successfully created ${createdEdges.length} edge(s) (in ${dispatchedOperations} commands):\n${successListStr}${formatNoticeList('errors', errors)}`,
167
+ { createdEdges, errors }
168
+ );
169
+ }
170
+
171
+ /** Mirrors `RequestCheckEdgeAction`: existence check, then dynamic-hint → checker; static or unknown edgeType → valid. */
172
+ protected validateEdge(edge: EdgeInput): ValidationResult {
173
+ const { elementTypeId } = edge;
174
+ const sourceRealId = this.aliasService.lookup(edge.sourceElementId);
175
+ const targetRealId = this.aliasService.lookup(edge.targetElementId);
176
+ const echo: Pick<ValidationResult, 'edgeType' | 'sourceElementId' | 'targetElementId'> = {
177
+ edgeType: elementTypeId,
178
+ sourceElementId: edge.sourceElementId,
179
+ targetElementId: edge.targetElementId
180
+ };
181
+
182
+ const source = this.modelState.index.find(sourceRealId);
183
+ if (!source) {
184
+ return { ...echo, isValid: false, reason: `Source element not found: ${edge.sourceElementId}` };
185
+ }
186
+ const target = this.modelState.index.find(targetRealId);
187
+ if (!target) {
188
+ return { ...echo, isValid: false, reason: `Target element not found: ${edge.targetElementId}` };
189
+ }
190
+
191
+ const hasDynamicHint = this.diagramConfiguration.edgeTypeHints.some(hint => hint.elementTypeId === elementTypeId && hint.dynamic);
192
+ if (!hasDynamicHint) {
193
+ return { ...echo, isValid: true, reason: 'no dynamic edge-type hint — static hints apply' };
194
+ }
195
+ if (!this.edgeCreationChecker) {
196
+ return {
197
+ ...echo,
198
+ isValid: false,
199
+ reason: `EdgeCreationChecker is not bound although edge type '${elementTypeId}' declares a dynamic hint.`
200
+ };
201
+ }
202
+ const isValid = this.edgeCreationChecker.isValidTarget(elementTypeId, source, target);
203
+ return { ...echo, isValid, ...(isValid ? {} : { reason: 'rejected by EdgeCreationChecker' }) };
204
+ }
205
+ }