@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,389 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2025-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, ClientId, Logger, MaybePromise, ModelState, RequestAction, ResponseAction } from '@eclipse-glsp/server';
18
+ import { CompleteResourceTemplateCallback, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
19
+ import { Variables } from '@modelcontextprotocol/sdk/shared/uriTemplate.js';
20
+ import { Annotations, ListResourcesResult, Role } from '@modelcontextprotocol/sdk/types.js';
21
+ import { inject, injectable, interfaces } from 'inversify';
22
+ import { ZodObject, ZodRawShape } from 'zod/v4';
23
+ import { GLSPMcpServer } from './glsp-mcp-server';
24
+ import {
25
+ McpResourceContent,
26
+ McpResourceResult,
27
+ McpResourceResultContent,
28
+ McpToolError,
29
+ McpToolResult,
30
+ extractErrorMessage,
31
+ requestActionOrFail
32
+ } from './mcp-handler-shared';
33
+ import { McpIdAliasService } from './mcp-id-alias-service';
34
+ import { McpDiagramScopedInput } from './mcp-input-schemas';
35
+ import { McpMimeType } from './mcp-mime-types';
36
+ import { mcpRequestContext } from './mcp-request-context';
37
+
38
+ /**
39
+ * Multi-binding key for **server-scope** resource handlers — singletons that don't target a
40
+ * specific GLSP client session. For diagram-scope resources see
41
+ * {@link McpDiagramResourceHandlerConstructor}.
42
+ *
43
+ * @experimental
44
+ */
45
+ export interface McpResourceHandler {
46
+ registerResource(server: GLSPMcpServer): void;
47
+ /** Optional tool fallback for clients that don't speak the resources protocol. */
48
+ registerToolAlternative?(server: GLSPMcpServer): void;
49
+ }
50
+ export const McpResourceHandler = Symbol('McpResourceHandler');
51
+
52
+ /** Static URI (string) or templated URI (`{ template: string }`). The base branches on shape. */
53
+ export type McpResourceUri = string | { template: string };
54
+
55
+ /** Shared infrastructure for both server- and diagram-scope resource handlers. */
56
+ @injectable()
57
+ abstract class BaseMcpResourceHandler {
58
+ @inject(Logger) protected logger: Logger;
59
+
60
+ /**
61
+ * Resource identifier exposed to the MCP client. Also used to reference this resource from
62
+ * other handlers' prompt or description text — wire via `static readonly NAME = '…'` and
63
+ * `readonly name = ClassName.NAME` so those cross-references survive renames.
64
+ */
65
+ abstract readonly name: string;
66
+ /** LLM-facing explanation surfaced in the resource catalog. Keep concise — clients pass this verbatim to the model. */
67
+ abstract readonly description: string;
68
+ /** MIME type of the resource body. Adopters typically use one of {@link McpMimeType}'s common values; any string the MCP SDK accepts is valid. */
69
+ abstract readonly mimeType: McpMimeType;
70
+ /** Static URI string for fixed resources, or `{ template: string }` for templated URIs (e.g. `glsp://diagrams/{sessionId}/model`). */
71
+ abstract readonly uri: McpResourceUri;
72
+ /** Optional human-friendly display name for UIs that render a friendlier label than `name`. */
73
+ readonly title?: string;
74
+
75
+ // ─── Resource annotations (MCP spec: server/resources#annotations) ────────────
76
+ // Surfaced as flat fields rather than a nested `annotations` object so adopters can
77
+ // override one hint with a one-line `override readonly priority = 0.8;` instead of
78
+ // re-declaring the whole triple. Mirror the {@link BaseMcpToolHandler} pattern.
79
+ // **Untrusted** unless from a trusted server — clients MUST treat these as advisory.
80
+
81
+ /**
82
+ * Intended audience(s) — `"user"` (display the rendered content), `"assistant"` (use as model
83
+ * context), or both. Clients use it to filter/route the resource.
84
+ */
85
+ readonly audience?: Role[];
86
+ /** Importance, 0.0–1.0. Clients use it to prioritize inclusion in context. */
87
+ readonly priority?: number;
88
+ /** ISO 8601 timestamp of last meaningful change. Omit when the server has no clean freshness signal. */
89
+ readonly lastModified?: string;
90
+
91
+ /**
92
+ * Assembles the {@link Annotations} object the SDK expects from the flat-field surface.
93
+ * Returns `undefined` when no field is set so `resources/list` entries don't carry an
94
+ * empty `annotations: {}`.
95
+ */
96
+ toAnnotations(): Annotations | undefined {
97
+ if (this.audience === undefined && this.priority === undefined && this.lastModified === undefined) {
98
+ return undefined;
99
+ }
100
+ return {
101
+ ...(this.audience !== undefined ? { audience: this.audience } : {}),
102
+ ...(this.priority !== undefined ? { priority: this.priority } : {}),
103
+ ...(this.lastModified !== undefined ? { lastModified: this.lastModified } : {})
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Set to a `z.object({...})` schema to also expose the resource as a tool fallback (for MCP
109
+ * clients that don't speak the resources protocol). Field shape mirrors `inputSchema` on
110
+ * tool handlers — declarative, no extra method.
111
+ */
112
+ readonly toolAlternativeInputSchema?: ZodObject<ZodRawShape>;
113
+
114
+ /**
115
+ * Optional dual-emit output schema applied **only** in tool-alternative mode (when
116
+ * {@link toolAlternativeInputSchema} is set). When declared, the handler should populate
117
+ * `structured` on the returned {@link McpResourceContent} so the framework can forward it
118
+ * to `CallToolResult.structuredContent`. Resource-protocol reads ignore this — the spec
119
+ * has no equivalent slot on `ReadResourceResult`.
120
+ */
121
+ readonly toolAlternativeOutputSchema?: ZodObject<ZodRawShape>;
122
+
123
+ /** Override for templated URIs — enumerate matching resources. */
124
+ list?(): MaybePromise<ListResourcesResult>;
125
+ /** Override for templated URIs — completers per template variable. */
126
+ complete?(): Record<string, CompleteResourceTemplateCallback>;
127
+
128
+ /** Resolve `this.uri` (which may be a fixed string or a template object) to a template string. */
129
+ protected uriTemplate(): string {
130
+ return typeof this.uri === 'string' ? this.uri : this.uri.template;
131
+ }
132
+
133
+ /**
134
+ * Replace `{key}` placeholders in the URI template with the supplied values, single-pass
135
+ * (no re-expansion of substituted text) and `encodeURIComponent`-escaped. Unmatched keys
136
+ * pass through verbatim so the caller can spot them.
137
+ */
138
+ protected expandUriTemplate(vars: Record<string, string>): string {
139
+ return this.uriTemplate().replace(/\{(\w+)\}/g, (placeholder, key) => {
140
+ const value = vars[key];
141
+ return value === undefined ? placeholder : encodeURIComponent(value);
142
+ });
143
+ }
144
+
145
+ /** Catches `McpToolError` (→ surfaced as text content + isError) and unexpected errors. */
146
+ protected async execute(producer: () => MaybePromise<McpResourceContent>): Promise<ResourceExecutionResult> {
147
+ try {
148
+ return { ok: true, body: await producer() };
149
+ } catch (err: unknown) {
150
+ if (err instanceof McpToolError) {
151
+ return { ok: false, message: err.message };
152
+ }
153
+ const message = extractErrorMessage(err);
154
+ this.logger.error(`Unexpected error in resource '${this.name}': ${message}`, err);
155
+ return { ok: false, message };
156
+ }
157
+ }
158
+
159
+ /** Wraps the body returned by {@link createResult} with `uri` + `mimeType` for the SDK. */
160
+ toResourceResult(uri: string, result: ResourceExecutionResult): McpResourceResult {
161
+ if (!result.ok) {
162
+ return { contents: [{ uri, mimeType: 'text/plain', text: result.message }], isError: true };
163
+ }
164
+ const content: McpResourceResultContent =
165
+ 'text' in result.body
166
+ ? { uri, mimeType: this.mimeType, text: result.body.text }
167
+ : { uri, mimeType: this.mimeType, blob: result.body.blob };
168
+ return { contents: [content], isError: false };
169
+ }
170
+
171
+ /** Converts the body to a `CallToolResult` for tool-alternative mode. Image MIMEs render as `image` content. */
172
+ toToolResult(result: ResourceExecutionResult): McpToolResult {
173
+ if (!result.ok) {
174
+ return { isError: true, content: [{ type: 'text', text: result.message }] };
175
+ }
176
+ const structuredContent = result.body.structured;
177
+ const baseContent: McpToolResult =
178
+ 'text' in result.body
179
+ ? { isError: false, content: [{ type: 'text', text: result.body.text }] }
180
+ : { isError: false, content: [{ type: 'image', data: result.body.blob, mimeType: this.mimeType }] };
181
+ return structuredContent ? { ...baseContent, structuredContent } : baseContent;
182
+ }
183
+
184
+ /** Builds the SDK `ResourceTemplate` for templated URIs (server-scope path). */
185
+ protected buildResourceTemplate(template: string): ResourceTemplate {
186
+ return new ResourceTemplate(template, {
187
+ list: this.list ? async extra => mcpRequestContext.run(extra, () => this.list!()) : undefined,
188
+ complete: this.complete?.()
189
+ });
190
+ }
191
+ }
192
+
193
+ export type ResourceExecutionResult = { ok: true; body: McpResourceContent } | { ok: false; message: string };
194
+
195
+ /**
196
+ * Server-scope resource base — for resources that don't target a specific GLSP client session
197
+ * (e.g., a hypothetical adopter-supplied "global config" resource that returns the same data
198
+ * regardless of which diagram is open). The instance exists at boot, so `list`/`complete` can
199
+ * `@inject` server-scope deps directly. Bound under {@link McpResourceHandler}; the launcher
200
+ * invokes `registerResource(server)` once per MCP session.
201
+ *
202
+ * @experimental
203
+ */
204
+ @injectable()
205
+ export abstract class AbstractMcpResourceHandler<T = Record<string, unknown>> extends BaseMcpResourceHandler implements McpResourceHandler {
206
+ /** Throw {@link McpToolError} for expected errors; the base wraps. */
207
+ protected abstract createResult(params: T): MaybePromise<McpResourceContent>;
208
+
209
+ registerResource(server: GLSPMcpServer): void {
210
+ const annotations = this.toAnnotations();
211
+ const config = {
212
+ title: this.title,
213
+ description: this.description,
214
+ mimeType: this.mimeType,
215
+ ...(annotations ? { annotations } : {})
216
+ };
217
+ if (typeof this.uri === 'string') {
218
+ const uri = this.uri;
219
+ server.registerResource(this.name, uri, config, async (_uri, extra) =>
220
+ mcpRequestContext.run(extra, async () => this.toResourceResult(uri, await this.execute(() => this.createResult({} as T))))
221
+ );
222
+ } else {
223
+ server.registerResource(this.name, this.buildResourceTemplate(this.uri.template), config, async (uri, params, extra) =>
224
+ mcpRequestContext.run(extra, async () =>
225
+ this.toResourceResult(uri.toString(), await this.execute(() => this.createResult(toParams(params) as T)))
226
+ )
227
+ );
228
+ }
229
+ }
230
+
231
+ /** No-op when no {@link toolAlternativeInputSchema} is declared; otherwise registers as a tool. */
232
+ registerToolAlternative(server: GLSPMcpServer): void {
233
+ if (!this.toolAlternativeInputSchema) {
234
+ return;
235
+ }
236
+ this.doRegisterToolAlternative(server);
237
+ }
238
+
239
+ protected doRegisterToolAlternative(server: GLSPMcpServer): void {
240
+ const inputSchema = this.toolAlternativeInputSchema!;
241
+ // `.strict()` matches the tool-handler policy — see `BaseMcpToolHandler.toRegistrationConfig`
242
+ // for the full rationale (LLM-typoed fields surface as JSON-RPC validation errors instead
243
+ // of being silently stripped).
244
+ server.registerTool(
245
+ this.name,
246
+ {
247
+ title: this.title,
248
+ description: this.description,
249
+ inputSchema: inputSchema.strict(),
250
+ outputSchema: this.toolAlternativeOutputSchema
251
+ },
252
+ async (params, extra) =>
253
+ mcpRequestContext.run(extra, async () => this.toToolResult(await this.execute(() => this.createResult(params as T))))
254
+ );
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Diagram-scope resource base — for resources whose URI templates carry a `sessionId` (e.g.,
260
+ * `glsp://diagrams/{sessionId}/model`).
261
+ *
262
+ * Unlike the server-scope base, the diagram-scope handler does not register itself with the
263
+ * SDK. The launcher's dispatcher reads the per-diagram-type constructor list, registers a
264
+ * single resource entry per `name`, and routes incoming reads to the matching per-GLSP-session
265
+ * handler instance via {@link handleRead}. For `list`/`complete`, the launcher walks all open
266
+ * GLSP sessions and aggregates each instance's slice — see {@link glspSessionScopedComplete}
267
+ * for the cross-GLSP-session-pollution auto-guard.
268
+ *
269
+ * @experimental
270
+ */
271
+ @injectable()
272
+ export abstract class AbstractMcpDiagramResourceHandler<
273
+ T extends McpDiagramScopedInput = McpDiagramScopedInput
274
+ > extends BaseMcpResourceHandler {
275
+ @inject(ClientId) protected clientId: string;
276
+ @inject(ModelState) protected modelState: ModelState;
277
+ @inject(McpIdAliasService) protected aliasService: McpIdAliasService;
278
+ @inject(ActionDispatcher) protected actionDispatcher: ActionDispatcher;
279
+
280
+ /** Throw {@link McpToolError} for expected errors; the base wraps. */
281
+ protected abstract createResult(params: T): MaybePromise<McpResourceContent>;
282
+
283
+ /**
284
+ * Convenience for resource handlers that fulfil a read by initiating a `RequestAction`
285
+ * round-trip to the client (today: `diagram-png` → `RequestExportAction`). Wraps
286
+ * {@link requestActionOrFail} with `this.actionDispatcher` and a default label of
287
+ * `this.name`. Pass an explicit label only to disambiguate between multiple round-trips.
288
+ */
289
+ protected requestAction<R extends ResponseAction>(request: RequestAction<R>, timeoutMs: number, label: string = this.name): Promise<R> {
290
+ return requestActionOrFail(this.actionDispatcher, request, timeoutMs, label);
291
+ }
292
+
293
+ /** Override to opt out of registration when a runtime dependency is missing. Default: `true`. */
294
+ canRegister(): boolean {
295
+ return true;
296
+ }
297
+
298
+ /**
299
+ * Default `list()` for the per-session-single-resource case (diagram-model, diagram-png).
300
+ * Emits one entry resolved against the URI template, named with the handler's title and
301
+ * the GLSP session id, described with the handler's `description`. Multi-resource
302
+ * handlers (e.g. `element-types`, which lists once per diagram type) override.
303
+ */
304
+ override list(): ListResourcesResult {
305
+ return { resources: [this.toListingEntry()] };
306
+ }
307
+
308
+ /**
309
+ * Default `complete()` for templated URIs that include `{sessionId}`. Returns a single
310
+ * completer that resolves to the current GLSP session's id. Handlers with other template
311
+ * variables (e.g. `{diagramType}` on `element-types`) override.
312
+ */
313
+ override complete(): Record<string, CompleteResourceTemplateCallback> {
314
+ if (this.uriTemplate().includes('{sessionId}')) {
315
+ return { sessionId: async () => [this.clientId] };
316
+ }
317
+ return {};
318
+ }
319
+
320
+ /** Default per-session entry built from the handler's metadata. Override to customize. */
321
+ protected toListingEntry(): ListResourcesResult['resources'][number] {
322
+ const annotations = this.toAnnotations();
323
+ return {
324
+ uri: this.expandUriTemplate({ sessionId: this.clientId }),
325
+ name: `${this.title ?? this.name}: ${this.clientId}`,
326
+ description: this.description,
327
+ mimeType: this.mimeType,
328
+ ...(annotations ? { annotations } : {})
329
+ };
330
+ }
331
+
332
+ /**
333
+ * Public dispatch entry point invoked by {@link McpServerLauncher}'s SDK callback for
334
+ * resource reads. The launcher passes the URI it received from the SDK plus the URI-template
335
+ * variable values normalized into a flat record.
336
+ */
337
+ async handleRead(uri: string, params: T): Promise<McpResourceResult> {
338
+ return this.toResourceResult(uri, await this.execute(() => this.createResult(params)));
339
+ }
340
+
341
+ /**
342
+ * Public dispatch entry point invoked by the launcher when the resource is exposed as a
343
+ * tool fallback (`McpServerOptions.resources === false`).
344
+ */
345
+ async handleAsTool(params: T): Promise<McpToolResult> {
346
+ return this.toToolResult(await this.execute(() => this.createResult(params)));
347
+ }
348
+
349
+ /**
350
+ * Wraps adopter-provided {@link complete} callbacks with the cross-GLSP-session-pollution
351
+ * auto-guard: when a templated URI carries `{sessionId}` and the LLM has bound a specific
352
+ * session id, completers for OTHER variables should not leak data from sessions whose ids
353
+ * don't match. The wrapper auto-returns `[]` from a GLSP session whose id doesn't match
354
+ * `ctx.arguments.sessionId`. Adopters write completers as if they only see their own GLSP
355
+ * session's data — the framework enforces the guard.
356
+ *
357
+ * Invoked by the launcher's aggregator, not by adopters directly.
358
+ */
359
+ glspSessionScopedComplete(): Record<string, CompleteResourceTemplateCallback> {
360
+ const raw = this.complete?.() ?? {};
361
+ const myId = this.clientId;
362
+ const isGlspSessionScoped = typeof this.uri === 'object' && this.uri.template.includes('{sessionId}');
363
+ const wrapped: Record<string, CompleteResourceTemplateCallback> = {};
364
+ for (const [variable, completer] of Object.entries(raw)) {
365
+ wrapped[variable] =
366
+ variable === 'sessionId' || !isGlspSessionScoped
367
+ ? completer
368
+ : async (value, ctx) => (ctx?.arguments?.sessionId === myId ? completer(value, ctx) : []);
369
+ }
370
+ return wrapped;
371
+ }
372
+ }
373
+
374
+ /** Normalizes SDK `Variables` (each value is `string | string[]`) to a flat `Record<string, string>`. */
375
+ export function toParams(variables: Variables): Record<string, string> {
376
+ const out: Record<string, string> = {};
377
+ for (const [key, value] of Object.entries(variables)) {
378
+ out[key] = Array.isArray(value) ? value[0] ?? '' : value;
379
+ }
380
+ return out;
381
+ }
382
+
383
+ /**
384
+ * Multi-binding identifier for diagram-scope resource handler constructors. See
385
+ * {@link McpDiagramToolHandlerConstructor} for the lifecycle pattern — same shape, applied to
386
+ * resource handlers.
387
+ */
388
+ export type McpDiagramResourceHandlerConstructor = interfaces.Newable<AbstractMcpDiagramResourceHandler<any>>;
389
+ export const McpDiagramResourceHandlerConstructor = Symbol('McpDiagramResourceHandlerConstructor');
@@ -0,0 +1,173 @@
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 { McpServerInitOptions } from '@eclipse-glsp/protocol';
18
+ import { expect } from 'chai';
19
+ import { version as packageVersion } from '../../package.json';
20
+ import { McpServerLauncher, SERVER_VERSION, assertLoopbackOrAcknowledged, isLoopbackHost, pickInitOptions } from './mcp-server-launcher';
21
+
22
+ describe('McpServerLauncher · SERVER_VERSION', () => {
23
+ it('matches the package.json version (no stale literal)', () => {
24
+ // Regression guard: the launcher used to hard-code '1.0.0'. Pull from package.json so
25
+ // adopters and MCP clients can tell builds apart via the `serverInfo.version` handshake
26
+ // field.
27
+ expect(SERVER_VERSION).to.equal(packageVersion);
28
+ });
29
+ });
30
+
31
+ describe('McpServerLauncher · buildCapabilities', () => {
32
+ /**
33
+ * Sidestep DI: build a stub whose shape matches the fields `buildCapabilities` reads, then
34
+ * invoke the prototype method against it. The method is protected, so we cast through.
35
+ */
36
+ function buildCaps(
37
+ args: {
38
+ toolHandlers?: unknown[];
39
+ promptHandlers?: unknown[];
40
+ resourceHandlers?: unknown[];
41
+ hasDiagramTools?: boolean;
42
+ hasDiagramPrompts?: boolean;
43
+ hasDiagramResources?: boolean;
44
+ },
45
+ resourcesAsResources: boolean
46
+ ): Record<string, unknown> {
47
+ const stub = {
48
+ toolHandlers: args.toolHandlers ?? [],
49
+ promptHandlers: args.promptHandlers ?? [],
50
+ resourceHandlers: args.resourceHandlers ?? [],
51
+ dispatcher: {
52
+ hasDiagramTools: () => args.hasDiagramTools ?? false,
53
+ hasDiagramPrompts: () => args.hasDiagramPrompts ?? false,
54
+ hasDiagramResources: () => args.hasDiagramResources ?? false
55
+ }
56
+ };
57
+ const proto = McpServerLauncher.prototype as unknown as {
58
+ buildCapabilities(this: typeof stub, resourcesAsResources: boolean): Record<string, unknown>;
59
+ };
60
+ return proto.buildCapabilities.call(stub, resourcesAsResources);
61
+ }
62
+
63
+ it('omits `tools`, `resources`, and `prompts` when nothing is bound (regression: resources/list -32601)', () => {
64
+ const caps = buildCaps({}, /* resourcesAsResources */ true);
65
+ expect(caps).to.have.property('logging');
66
+ expect(caps).to.not.have.property('tools');
67
+ expect(caps).to.not.have.property('resources');
68
+ expect(caps).to.not.have.property('prompts');
69
+ });
70
+
71
+ it('declares `tools` with listChanged: false when at least one tool handler binds', () => {
72
+ const caps = buildCaps({ toolHandlers: [{}] }, true);
73
+ expect(caps.tools).to.deep.equal({ listChanged: false });
74
+ expect(caps).to.not.have.property('resources');
75
+ expect(caps).to.not.have.property('prompts');
76
+ });
77
+
78
+ it('declares `prompts` when at least one prompt handler binds (server- or diagram-scope)', () => {
79
+ expect(buildCaps({ promptHandlers: [{}] }, true).prompts).to.deep.equal({ listChanged: false });
80
+ expect(buildCaps({ hasDiagramPrompts: true }, true).prompts).to.deep.equal({ listChanged: false });
81
+ });
82
+
83
+ it('declares `resources` only in dataMode=resources; otherwise resources count toward `tools`', () => {
84
+ // Diagram-scope resources mutate per GLSP session add/remove → `listChanged: true` is honest.
85
+ const asResources = buildCaps({ hasDiagramResources: true }, true);
86
+ expect(asResources.resources).to.deep.equal({ listChanged: true });
87
+ expect(asResources).to.not.have.property('tools');
88
+
89
+ const asTools = buildCaps({ hasDiagramResources: true }, false);
90
+ expect(asTools.tools).to.deep.equal({ listChanged: false });
91
+ expect(asTools).to.not.have.property('resources');
92
+ });
93
+
94
+ it('keeps resources.listChanged: false when only server-scope resources are bound (catalog static)', () => {
95
+ const caps = buildCaps({ resourceHandlers: [{}] }, true);
96
+ expect(caps.resources).to.deep.equal({ listChanged: false });
97
+ });
98
+ });
99
+
100
+ describe('McpServerLauncher · isLoopbackHost', () => {
101
+ it('treats 127.0.0.0/8, localhost, and ::1 as loopback', () => {
102
+ expect(isLoopbackHost('127.0.0.1')).to.equal(true);
103
+ expect(isLoopbackHost('127.55.0.1')).to.equal(true);
104
+ expect(isLoopbackHost('localhost')).to.equal(true);
105
+ expect(isLoopbackHost('::1')).to.equal(true);
106
+ });
107
+
108
+ it('treats unspecified, LAN, and public addresses as non-loopback', () => {
109
+ expect(isLoopbackHost('0.0.0.0')).to.equal(false);
110
+ expect(isLoopbackHost('::')).to.equal(false);
111
+ expect(isLoopbackHost('192.168.1.1')).to.equal(false);
112
+ expect(isLoopbackHost('10.0.0.1')).to.equal(false);
113
+ expect(isLoopbackHost('203.0.113.5')).to.equal(false);
114
+ });
115
+ });
116
+
117
+ describe('McpServerLauncher · assertLoopbackOrAcknowledged (auth footgun)', () => {
118
+ it('passes silently for a loopback bind without acknowledgement', () => {
119
+ expect(() => assertLoopbackOrAcknowledged('127.0.0.1', undefined)).to.not.throw();
120
+ expect(() => assertLoopbackOrAcknowledged('localhost', undefined)).to.not.throw();
121
+ });
122
+
123
+ it('throws an actionable error for a non-loopback bind without acknowledgement', () => {
124
+ expect(() => assertLoopbackOrAcknowledged('0.0.0.0', undefined))
125
+ .to.throw(Error)
126
+ .with.property('message')
127
+ .that.matches(/Refusing to bind/)
128
+ .and.matches(/0\.0\.0\.0/)
129
+ .and.matches(/acknowledgedNoAuth/);
130
+ });
131
+
132
+ it('passes for a non-loopback bind when acknowledgedNoAuth is true', () => {
133
+ expect(() => assertLoopbackOrAcknowledged('0.0.0.0', true)).to.not.throw();
134
+ expect(() => assertLoopbackOrAcknowledged('192.168.1.50', true)).to.not.throw();
135
+ });
136
+
137
+ it('still throws for a non-loopback bind when acknowledgedNoAuth is false (explicit denial)', () => {
138
+ expect(() => assertLoopbackOrAcknowledged('0.0.0.0', false)).to.throw(/Refusing to bind/);
139
+ });
140
+ });
141
+
142
+ describe('McpServerLauncher · pickInitOptions (deploy/init split — defense-in-depth)', () => {
143
+ it('passes through every allowed init-side field unchanged', () => {
144
+ const picked = pickInitOptions({ dataMode: 'resources', agentPersona: 'X', eventStoreLimit: 50 });
145
+ expect(picked).to.deep.equal({ dataMode: 'resources', agentPersona: 'X', eventStoreLimit: 50 });
146
+ });
147
+
148
+ it('omits init-side fields that the caller did not set (no `undefined` sneak-through)', () => {
149
+ const picked = pickInitOptions({ dataMode: 'tools' });
150
+ expect(picked).to.deep.equal({ dataMode: 'tools' });
151
+ expect(picked).to.not.have.property('agentPersona');
152
+ expect(picked).to.not.have.property('eventStoreLimit');
153
+ });
154
+
155
+ it('strips deploy-only keys smuggled in via JSON wire payload (host, allowedHosts, allowedOrigins, acknowledgedNoAuth)', () => {
156
+ // Simulate a malicious/malformed wire payload: the static type rules these out, but
157
+ // JSON parsing does not, so the destructure-pick must drop them.
158
+ const wirePayload = JSON.parse(`{
159
+ "dataMode": "tools",
160
+ "host": "0.0.0.0",
161
+ "allowedHosts": ["evil.example.com"],
162
+ "allowedOrigins": ["https://evil.example.com"],
163
+ "acknowledgedNoAuth": true
164
+ }`) as McpServerInitOptions;
165
+
166
+ const picked = pickInitOptions(wirePayload);
167
+ expect(picked).to.deep.equal({ dataMode: 'tools' });
168
+ expect(picked).to.not.have.property('host');
169
+ expect(picked).to.not.have.property('allowedHosts');
170
+ expect(picked).to.not.have.property('allowedOrigins');
171
+ expect(picked).to.not.have.property('acknowledgedNoAuth');
172
+ });
173
+ });