@vheins/local-memory-mcp 0.5.32 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/README.md +9 -6
  2. package/dist/chunk-XIJO63UU.js +3618 -0
  3. package/dist/dashboard/public/assets/{index-C8FB4maW.css → index-Bd7v94SO.css} +1 -1
  4. package/dist/dashboard/public/assets/index-Df97JpLg.js +84 -0
  5. package/dist/dashboard/public/index.html +2 -2
  6. package/dist/dashboard/server.js +790 -451
  7. package/dist/mcp/server.js +2121 -305
  8. package/dist/{mcp/prompts/definitions → prompts}/create-task.md +4 -3
  9. package/dist/{mcp/prompts/definitions → prompts}/memory-agent-core.md +2 -0
  10. package/dist/{mcp/prompts/definitions → prompts}/memory-index-policy.md +2 -0
  11. package/dist/{mcp/prompts/definitions → prompts}/review-and-audit.md +3 -1
  12. package/dist/{mcp/prompts/definitions → prompts}/review-and-post-issue.md +3 -1
  13. package/dist/prompts/task-management-guidelines.md +28 -0
  14. package/dist/{mcp/prompts/definitions → prompts}/task-memory-executor.md +6 -5
  15. package/package.json +22 -6
  16. package/dist/capabilities.d.ts +0 -22
  17. package/dist/capabilities.d.ts.map +0 -1
  18. package/dist/capabilities.js +0 -38
  19. package/dist/capabilities.js.map +0 -1
  20. package/dist/completion.d.ts +0 -25
  21. package/dist/completion.d.ts.map +0 -1
  22. package/dist/completion.js +0 -127
  23. package/dist/completion.js.map +0 -1
  24. package/dist/dashboard/dashboard.test.d.ts +0 -2
  25. package/dist/dashboard/dashboard.test.d.ts.map +0 -1
  26. package/dist/dashboard/dashboard.test.js +0 -370
  27. package/dist/dashboard/dashboard.test.js.map +0 -1
  28. package/dist/dashboard/public/assets/index-DIhCu9qA.js +0 -78
  29. package/dist/dashboard/server.d.ts +0 -3
  30. package/dist/dashboard/server.d.ts.map +0 -1
  31. package/dist/dashboard/server.js.map +0 -1
  32. package/dist/e2e.test.d.ts +0 -2
  33. package/dist/e2e.test.d.ts.map +0 -1
  34. package/dist/e2e.test.js +0 -250
  35. package/dist/e2e.test.js.map +0 -1
  36. package/dist/mcp/capabilities.d.ts +0 -22
  37. package/dist/mcp/capabilities.d.ts.map +0 -1
  38. package/dist/mcp/capabilities.js +0 -38
  39. package/dist/mcp/capabilities.js.map +0 -1
  40. package/dist/mcp/client.d.ts +0 -34
  41. package/dist/mcp/client.d.ts.map +0 -1
  42. package/dist/mcp/client.js +0 -188
  43. package/dist/mcp/client.js.map +0 -1
  44. package/dist/mcp/client.test.d.ts +0 -2
  45. package/dist/mcp/client.test.d.ts.map +0 -1
  46. package/dist/mcp/client.test.js +0 -130
  47. package/dist/mcp/client.test.js.map +0 -1
  48. package/dist/mcp/completion.d.ts +0 -25
  49. package/dist/mcp/completion.d.ts.map +0 -1
  50. package/dist/mcp/completion.js +0 -127
  51. package/dist/mcp/completion.js.map +0 -1
  52. package/dist/mcp/elicitation.d.ts +0 -24
  53. package/dist/mcp/elicitation.d.ts.map +0 -1
  54. package/dist/mcp/elicitation.js +0 -13
  55. package/dist/mcp/elicitation.js.map +0 -1
  56. package/dist/mcp/prompts/definitions/task-management-guidelines.md +0 -30
  57. package/dist/mcp/prompts/loader.d.ts +0 -10
  58. package/dist/mcp/prompts/loader.d.ts.map +0 -1
  59. package/dist/mcp/prompts/loader.js +0 -31
  60. package/dist/mcp/prompts/loader.js.map +0 -1
  61. package/dist/mcp/prompts/registry.d.ts +0 -35
  62. package/dist/mcp/prompts/registry.d.ts.map +0 -1
  63. package/dist/mcp/prompts/registry.js +0 -95
  64. package/dist/mcp/prompts/registry.js.map +0 -1
  65. package/dist/mcp/resources/index.d.ts +0 -68
  66. package/dist/mcp/resources/index.d.ts.map +0 -1
  67. package/dist/mcp/resources/index.js +0 -359
  68. package/dist/mcp/resources/index.js.map +0 -1
  69. package/dist/mcp/router.d.ts +0 -14
  70. package/dist/mcp/router.d.ts.map +0 -1
  71. package/dist/mcp/router.js +0 -255
  72. package/dist/mcp/router.js.map +0 -1
  73. package/dist/mcp/sampling.d.ts +0 -69
  74. package/dist/mcp/sampling.d.ts.map +0 -1
  75. package/dist/mcp/sampling.js +0 -13
  76. package/dist/mcp/sampling.js.map +0 -1
  77. package/dist/mcp/server.d.ts +0 -3
  78. package/dist/mcp/server.d.ts.map +0 -1
  79. package/dist/mcp/server.js.map +0 -1
  80. package/dist/mcp/session.d.ts +0 -28
  81. package/dist/mcp/session.d.ts.map +0 -1
  82. package/dist/mcp/session.js +0 -106
  83. package/dist/mcp/session.js.map +0 -1
  84. package/dist/mcp/storage/sqlite.d.ts +0 -87
  85. package/dist/mcp/storage/sqlite.d.ts.map +0 -1
  86. package/dist/mcp/storage/sqlite.js +0 -1327
  87. package/dist/mcp/storage/sqlite.js.map +0 -1
  88. package/dist/mcp/storage/vectors.d.ts +0 -19
  89. package/dist/mcp/storage/vectors.d.ts.map +0 -1
  90. package/dist/mcp/storage/vectors.js +0 -74
  91. package/dist/mcp/storage/vectors.js.map +0 -1
  92. package/dist/mcp/storage/vectors.stub.d.ts +0 -12
  93. package/dist/mcp/storage/vectors.stub.d.ts.map +0 -1
  94. package/dist/mcp/storage/vectors.stub.js +0 -88
  95. package/dist/mcp/storage/vectors.stub.js.map +0 -1
  96. package/dist/mcp/tests/client.test.d.ts +0 -2
  97. package/dist/mcp/tests/client.test.d.ts.map +0 -1
  98. package/dist/mcp/tests/client.test.js +0 -130
  99. package/dist/mcp/tests/client.test.js.map +0 -1
  100. package/dist/mcp/tests/dashboard.test.d.ts +0 -2
  101. package/dist/mcp/tests/dashboard.test.d.ts.map +0 -1
  102. package/dist/mcp/tests/dashboard.test.js +0 -370
  103. package/dist/mcp/tests/dashboard.test.js.map +0 -1
  104. package/dist/mcp/tests/detail-tools.test.d.ts +0 -2
  105. package/dist/mcp/tests/detail-tools.test.d.ts.map +0 -1
  106. package/dist/mcp/tests/detail-tools.test.js +0 -109
  107. package/dist/mcp/tests/detail-tools.test.js.map +0 -1
  108. package/dist/mcp/tests/e2e.test.d.ts +0 -2
  109. package/dist/mcp/tests/e2e.test.d.ts.map +0 -1
  110. package/dist/mcp/tests/e2e.test.js +0 -255
  111. package/dist/mcp/tests/e2e.test.js.map +0 -1
  112. package/dist/mcp/tests/index.test.d.ts +0 -2
  113. package/dist/mcp/tests/index.test.d.ts.map +0 -1
  114. package/dist/mcp/tests/index.test.js +0 -185
  115. package/dist/mcp/tests/index.test.js.map +0 -1
  116. package/dist/mcp/tests/logger.test.d.ts +0 -2
  117. package/dist/mcp/tests/logger.test.d.ts.map +0 -1
  118. package/dist/mcp/tests/logger.test.js +0 -104
  119. package/dist/mcp/tests/logger.test.js.map +0 -1
  120. package/dist/mcp/tests/memory.bulk.test.d.ts +0 -2
  121. package/dist/mcp/tests/memory.bulk.test.d.ts.map +0 -1
  122. package/dist/mcp/tests/memory.bulk.test.js +0 -53
  123. package/dist/mcp/tests/memory.bulk.test.js.map +0 -1
  124. package/dist/mcp/tests/memory.search.test.d.ts +0 -2
  125. package/dist/mcp/tests/memory.search.test.d.ts.map +0 -1
  126. package/dist/mcp/tests/memory.search.test.js +0 -181
  127. package/dist/mcp/tests/memory.search.test.js.map +0 -1
  128. package/dist/mcp/tests/normalize.test.d.ts +0 -2
  129. package/dist/mcp/tests/normalize.test.d.ts.map +0 -1
  130. package/dist/mcp/tests/normalize.test.js +0 -181
  131. package/dist/mcp/tests/normalize.test.js.map +0 -1
  132. package/dist/mcp/tests/query-expander.test.d.ts +0 -2
  133. package/dist/mcp/tests/query-expander.test.d.ts.map +0 -1
  134. package/dist/mcp/tests/query-expander.test.js +0 -33
  135. package/dist/mcp/tests/query-expander.test.js.map +0 -1
  136. package/dist/mcp/tests/router.test.d.ts +0 -2
  137. package/dist/mcp/tests/router.test.d.ts.map +0 -1
  138. package/dist/mcp/tests/router.test.js +0 -481
  139. package/dist/mcp/tests/router.test.js.map +0 -1
  140. package/dist/mcp/tests/spec_compliance.test.d.ts +0 -2
  141. package/dist/mcp/tests/spec_compliance.test.d.ts.map +0 -1
  142. package/dist/mcp/tests/spec_compliance.test.js +0 -61
  143. package/dist/mcp/tests/spec_compliance.test.js.map +0 -1
  144. package/dist/mcp/tests/sqlite.test.d.ts +0 -2
  145. package/dist/mcp/tests/sqlite.test.d.ts.map +0 -1
  146. package/dist/mcp/tests/sqlite.test.js +0 -367
  147. package/dist/mcp/tests/sqlite.test.js.map +0 -1
  148. package/dist/mcp/tests/tasks-search.test.d.ts +0 -2
  149. package/dist/mcp/tests/tasks-search.test.d.ts.map +0 -1
  150. package/dist/mcp/tests/tasks-search.test.js +0 -156
  151. package/dist/mcp/tests/tasks-search.test.js.map +0 -1
  152. package/dist/mcp/tests/tasks-transition.test.d.ts +0 -2
  153. package/dist/mcp/tests/tasks-transition.test.d.ts.map +0 -1
  154. package/dist/mcp/tests/tasks-transition.test.js +0 -174
  155. package/dist/mcp/tests/tasks-transition.test.js.map +0 -1
  156. package/dist/mcp/tests/tasks.bulk.test.d.ts +0 -2
  157. package/dist/mcp/tests/tasks.bulk.test.d.ts.map +0 -1
  158. package/dist/mcp/tests/tasks.bulk.test.js +0 -410
  159. package/dist/mcp/tests/tasks.bulk.test.js.map +0 -1
  160. package/dist/mcp/tests/tasks.e2e.test.d.ts +0 -2
  161. package/dist/mcp/tests/tasks.e2e.test.d.ts.map +0 -1
  162. package/dist/mcp/tests/tasks.e2e.test.js +0 -289
  163. package/dist/mcp/tests/tasks.e2e.test.js.map +0 -1
  164. package/dist/mcp/tests/tasks.pending-limit-refined.test.d.ts +0 -2
  165. package/dist/mcp/tests/tasks.pending-limit-refined.test.d.ts.map +0 -1
  166. package/dist/mcp/tests/tasks.pending-limit-refined.test.js +0 -72
  167. package/dist/mcp/tests/tasks.pending-limit-refined.test.js.map +0 -1
  168. package/dist/mcp/tests/v2-features.test.d.ts +0 -2
  169. package/dist/mcp/tests/v2-features.test.d.ts.map +0 -1
  170. package/dist/mcp/tests/v2-features.test.js +0 -209
  171. package/dist/mcp/tests/v2-features.test.js.map +0 -1
  172. package/dist/mcp/tools/memory.acknowledge.d.ts +0 -4
  173. package/dist/mcp/tools/memory.acknowledge.d.ts.map +0 -1
  174. package/dist/mcp/tools/memory.acknowledge.js +0 -32
  175. package/dist/mcp/tools/memory.acknowledge.js.map +0 -1
  176. package/dist/mcp/tools/memory.bulk-delete.d.ts +0 -4
  177. package/dist/mcp/tools/memory.bulk-delete.d.ts.map +0 -1
  178. package/dist/mcp/tools/memory.bulk-delete.js +0 -40
  179. package/dist/mcp/tools/memory.bulk-delete.js.map +0 -1
  180. package/dist/mcp/tools/memory.delete.d.ts +0 -9
  181. package/dist/mcp/tools/memory.delete.d.ts.map +0 -1
  182. package/dist/mcp/tools/memory.delete.js +0 -40
  183. package/dist/mcp/tools/memory.delete.js.map +0 -1
  184. package/dist/mcp/tools/memory.get.d.ts +0 -3
  185. package/dist/mcp/tools/memory.get.d.ts.map +0 -1
  186. package/dist/mcp/tools/memory.get.js +0 -24
  187. package/dist/mcp/tools/memory.get.js.map +0 -1
  188. package/dist/mcp/tools/memory.recap.d.ts +0 -4
  189. package/dist/mcp/tools/memory.recap.d.ts.map +0 -1
  190. package/dist/mcp/tools/memory.recap.js +0 -52
  191. package/dist/mcp/tools/memory.recap.js.map +0 -1
  192. package/dist/mcp/tools/memory.search.d.ts +0 -5
  193. package/dist/mcp/tools/memory.search.d.ts.map +0 -1
  194. package/dist/mcp/tools/memory.search.js +0 -144
  195. package/dist/mcp/tools/memory.search.js.map +0 -1
  196. package/dist/mcp/tools/memory.store.d.ts +0 -5
  197. package/dist/mcp/tools/memory.store.d.ts.map +0 -1
  198. package/dist/mcp/tools/memory.store.js +0 -120
  199. package/dist/mcp/tools/memory.store.js.map +0 -1
  200. package/dist/mcp/tools/memory.summarize.d.ts +0 -4
  201. package/dist/mcp/tools/memory.summarize.d.ts.map +0 -1
  202. package/dist/mcp/tools/memory.summarize.js +0 -32
  203. package/dist/mcp/tools/memory.summarize.js.map +0 -1
  204. package/dist/mcp/tools/memory.synthesize.d.ts +0 -14
  205. package/dist/mcp/tools/memory.synthesize.d.ts.map +0 -1
  206. package/dist/mcp/tools/memory.synthesize.js +0 -230
  207. package/dist/mcp/tools/memory.synthesize.js.map +0 -1
  208. package/dist/mcp/tools/memory.update.d.ts +0 -5
  209. package/dist/mcp/tools/memory.update.d.ts.map +0 -1
  210. package/dist/mcp/tools/memory.update.js +0 -74
  211. package/dist/mcp/tools/memory.update.js.map +0 -1
  212. package/dist/mcp/tools/schemas.d.ts +0 -3177
  213. package/dist/mcp/tools/schemas.d.ts.map +0 -1
  214. package/dist/mcp/tools/schemas.js +0 -1081
  215. package/dist/mcp/tools/schemas.js.map +0 -1
  216. package/dist/mcp/tools/task.bulk-manage.d.ts +0 -4
  217. package/dist/mcp/tools/task.bulk-manage.d.ts.map +0 -1
  218. package/dist/mcp/tools/task.bulk-manage.js +0 -190
  219. package/dist/mcp/tools/task.bulk-manage.js.map +0 -1
  220. package/dist/mcp/tools/task.get.d.ts +0 -3
  221. package/dist/mcp/tools/task.get.d.ts.map +0 -1
  222. package/dist/mcp/tools/task.get.js +0 -32
  223. package/dist/mcp/tools/task.get.js.map +0 -1
  224. package/dist/mcp/tools/task.manage.d.ts +0 -18
  225. package/dist/mcp/tools/task.manage.d.ts.map +0 -1
  226. package/dist/mcp/tools/task.manage.js +0 -477
  227. package/dist/mcp/tools/task.manage.js.map +0 -1
  228. package/dist/mcp/types.d.ts +0 -87
  229. package/dist/mcp/types.d.ts.map +0 -1
  230. package/dist/mcp/types.js +0 -3
  231. package/dist/mcp/types.js.map +0 -1
  232. package/dist/mcp/utils/completion.d.ts +0 -2
  233. package/dist/mcp/utils/completion.d.ts.map +0 -1
  234. package/dist/mcp/utils/completion.js +0 -28
  235. package/dist/mcp/utils/completion.js.map +0 -1
  236. package/dist/mcp/utils/git-scope.d.ts +0 -8
  237. package/dist/mcp/utils/git-scope.d.ts.map +0 -1
  238. package/dist/mcp/utils/git-scope.js +0 -38
  239. package/dist/mcp/utils/git-scope.js.map +0 -1
  240. package/dist/mcp/utils/logger.d.ts +0 -25
  241. package/dist/mcp/utils/logger.d.ts.map +0 -1
  242. package/dist/mcp/utils/logger.js +0 -152
  243. package/dist/mcp/utils/logger.js.map +0 -1
  244. package/dist/mcp/utils/mcp-response.d.ts +0 -82
  245. package/dist/mcp/utils/mcp-response.d.ts.map +0 -1
  246. package/dist/mcp/utils/mcp-response.js +0 -182
  247. package/dist/mcp/utils/mcp-response.js.map +0 -1
  248. package/dist/mcp/utils/normalize.d.ts +0 -9
  249. package/dist/mcp/utils/normalize.d.ts.map +0 -1
  250. package/dist/mcp/utils/normalize.js +0 -62
  251. package/dist/mcp/utils/normalize.js.map +0 -1
  252. package/dist/mcp/utils/pagination.d.ts +0 -6
  253. package/dist/mcp/utils/pagination.d.ts.map +0 -1
  254. package/dist/mcp/utils/pagination.js +0 -32
  255. package/dist/mcp/utils/pagination.js.map +0 -1
  256. package/dist/mcp/utils/query-expander.d.ts +0 -2
  257. package/dist/mcp/utils/query-expander.d.ts.map +0 -1
  258. package/dist/mcp/utils/query-expander.js +0 -29
  259. package/dist/mcp/utils/query-expander.js.map +0 -1
  260. package/dist/memory.bulk.test.d.ts +0 -2
  261. package/dist/memory.bulk.test.d.ts.map +0 -1
  262. package/dist/memory.bulk.test.js +0 -52
  263. package/dist/memory.bulk.test.js.map +0 -1
  264. package/dist/memory.db +0 -0
  265. package/dist/prompts/registry.d.ts +0 -314
  266. package/dist/prompts/registry.d.ts.map +0 -1
  267. package/dist/prompts/registry.js +0 -936
  268. package/dist/prompts/registry.js.map +0 -1
  269. package/dist/resources/index.d.ts +0 -68
  270. package/dist/resources/index.d.ts.map +0 -1
  271. package/dist/resources/index.js +0 -323
  272. package/dist/resources/index.js.map +0 -1
  273. package/dist/resources/index.test.d.ts +0 -2
  274. package/dist/resources/index.test.d.ts.map +0 -1
  275. package/dist/resources/index.test.js +0 -105
  276. package/dist/resources/index.test.js.map +0 -1
  277. package/dist/router.d.ts +0 -14
  278. package/dist/router.d.ts.map +0 -1
  279. package/dist/router.js +0 -242
  280. package/dist/router.js.map +0 -1
  281. package/dist/router.test.d.ts +0 -2
  282. package/dist/router.test.d.ts.map +0 -1
  283. package/dist/router.test.js +0 -122
  284. package/dist/router.test.js.map +0 -1
  285. package/dist/server.d.ts +0 -3
  286. package/dist/server.d.ts.map +0 -1
  287. package/dist/server.js +0 -338
  288. package/dist/server.js.map +0 -1
  289. package/dist/storage/sqlite.d.ts +0 -79
  290. package/dist/storage/sqlite.d.ts.map +0 -1
  291. package/dist/storage/sqlite.js +0 -1148
  292. package/dist/storage/sqlite.js.map +0 -1
  293. package/dist/storage/sqlite.test.d.ts +0 -2
  294. package/dist/storage/sqlite.test.d.ts.map +0 -1
  295. package/dist/storage/sqlite.test.js +0 -367
  296. package/dist/storage/sqlite.test.js.map +0 -1
  297. package/dist/storage/vectors.d.ts +0 -19
  298. package/dist/storage/vectors.d.ts.map +0 -1
  299. package/dist/storage/vectors.js +0 -74
  300. package/dist/storage/vectors.js.map +0 -1
  301. package/dist/storage/vectors.stub.d.ts +0 -12
  302. package/dist/storage/vectors.stub.d.ts.map +0 -1
  303. package/dist/storage/vectors.stub.js +0 -88
  304. package/dist/storage/vectors.stub.js.map +0 -1
  305. package/dist/tasks.archive.test.d.ts +0 -2
  306. package/dist/tasks.archive.test.d.ts.map +0 -1
  307. package/dist/tasks.archive.test.js +0 -75
  308. package/dist/tasks.archive.test.js.map +0 -1
  309. package/dist/tasks.bulk.test.d.ts +0 -2
  310. package/dist/tasks.bulk.test.d.ts.map +0 -1
  311. package/dist/tasks.bulk.test.js +0 -250
  312. package/dist/tasks.bulk.test.js.map +0 -1
  313. package/dist/tasks.e2e.test.d.ts +0 -2
  314. package/dist/tasks.e2e.test.d.ts.map +0 -1
  315. package/dist/tasks.e2e.test.js +0 -289
  316. package/dist/tasks.e2e.test.js.map +0 -1
  317. package/dist/tests/client.test.d.ts +0 -2
  318. package/dist/tests/client.test.d.ts.map +0 -1
  319. package/dist/tests/client.test.js +0 -130
  320. package/dist/tests/client.test.js.map +0 -1
  321. package/dist/tests/dashboard.test.d.ts +0 -2
  322. package/dist/tests/dashboard.test.d.ts.map +0 -1
  323. package/dist/tests/dashboard.test.js +0 -370
  324. package/dist/tests/dashboard.test.js.map +0 -1
  325. package/dist/tests/e2e.test.d.ts +0 -2
  326. package/dist/tests/e2e.test.d.ts.map +0 -1
  327. package/dist/tests/e2e.test.js +0 -250
  328. package/dist/tests/e2e.test.js.map +0 -1
  329. package/dist/tests/index.test.d.ts +0 -2
  330. package/dist/tests/index.test.d.ts.map +0 -1
  331. package/dist/tests/index.test.js +0 -185
  332. package/dist/tests/index.test.js.map +0 -1
  333. package/dist/tests/logger.test.d.ts +0 -2
  334. package/dist/tests/logger.test.d.ts.map +0 -1
  335. package/dist/tests/logger.test.js +0 -104
  336. package/dist/tests/logger.test.js.map +0 -1
  337. package/dist/tests/memory.bulk.test.d.ts +0 -2
  338. package/dist/tests/memory.bulk.test.d.ts.map +0 -1
  339. package/dist/tests/memory.bulk.test.js +0 -52
  340. package/dist/tests/memory.bulk.test.js.map +0 -1
  341. package/dist/tests/memory.search.test.d.ts +0 -2
  342. package/dist/tests/memory.search.test.d.ts.map +0 -1
  343. package/dist/tests/memory.search.test.js +0 -181
  344. package/dist/tests/memory.search.test.js.map +0 -1
  345. package/dist/tests/normalize.test.d.ts +0 -2
  346. package/dist/tests/normalize.test.d.ts.map +0 -1
  347. package/dist/tests/normalize.test.js +0 -181
  348. package/dist/tests/normalize.test.js.map +0 -1
  349. package/dist/tests/query-expander.test.d.ts +0 -2
  350. package/dist/tests/query-expander.test.d.ts.map +0 -1
  351. package/dist/tests/query-expander.test.js +0 -33
  352. package/dist/tests/query-expander.test.js.map +0 -1
  353. package/dist/tests/router.test.d.ts +0 -2
  354. package/dist/tests/router.test.d.ts.map +0 -1
  355. package/dist/tests/router.test.js +0 -470
  356. package/dist/tests/router.test.js.map +0 -1
  357. package/dist/tests/sqlite.test.d.ts +0 -2
  358. package/dist/tests/sqlite.test.d.ts.map +0 -1
  359. package/dist/tests/sqlite.test.js +0 -367
  360. package/dist/tests/sqlite.test.js.map +0 -1
  361. package/dist/tests/tasks-search.test.d.ts +0 -2
  362. package/dist/tests/tasks-search.test.d.ts.map +0 -1
  363. package/dist/tests/tasks-search.test.js +0 -154
  364. package/dist/tests/tasks-search.test.js.map +0 -1
  365. package/dist/tests/tasks-transition.test.d.ts +0 -2
  366. package/dist/tests/tasks-transition.test.d.ts.map +0 -1
  367. package/dist/tests/tasks-transition.test.js +0 -174
  368. package/dist/tests/tasks-transition.test.js.map +0 -1
  369. package/dist/tests/tasks.bulk.test.d.ts +0 -2
  370. package/dist/tests/tasks.bulk.test.d.ts.map +0 -1
  371. package/dist/tests/tasks.bulk.test.js +0 -254
  372. package/dist/tests/tasks.bulk.test.js.map +0 -1
  373. package/dist/tests/tasks.e2e.test.d.ts +0 -2
  374. package/dist/tests/tasks.e2e.test.d.ts.map +0 -1
  375. package/dist/tests/tasks.e2e.test.js +0 -289
  376. package/dist/tests/tasks.e2e.test.js.map +0 -1
  377. package/dist/tests/tasks.pending-limit-refined.test.d.ts +0 -2
  378. package/dist/tests/tasks.pending-limit-refined.test.d.ts.map +0 -1
  379. package/dist/tests/tasks.pending-limit-refined.test.js +0 -72
  380. package/dist/tests/tasks.pending-limit-refined.test.js.map +0 -1
  381. package/dist/tests/v2-features.test.d.ts +0 -2
  382. package/dist/tests/v2-features.test.d.ts.map +0 -1
  383. package/dist/tests/v2-features.test.js +0 -209
  384. package/dist/tests/v2-features.test.js.map +0 -1
  385. package/dist/tools/memory.acknowledge.d.ts +0 -4
  386. package/dist/tools/memory.acknowledge.d.ts.map +0 -1
  387. package/dist/tools/memory.acknowledge.js +0 -30
  388. package/dist/tools/memory.acknowledge.js.map +0 -1
  389. package/dist/tools/memory.bulk-delete.d.ts +0 -4
  390. package/dist/tools/memory.bulk-delete.d.ts.map +0 -1
  391. package/dist/tools/memory.bulk-delete.js +0 -39
  392. package/dist/tools/memory.bulk-delete.js.map +0 -1
  393. package/dist/tools/memory.delete.d.ts +0 -9
  394. package/dist/tools/memory.delete.d.ts.map +0 -1
  395. package/dist/tools/memory.delete.js +0 -39
  396. package/dist/tools/memory.delete.js.map +0 -1
  397. package/dist/tools/memory.recap.d.ts +0 -4
  398. package/dist/tools/memory.recap.d.ts.map +0 -1
  399. package/dist/tools/memory.recap.js +0 -90
  400. package/dist/tools/memory.recap.js.map +0 -1
  401. package/dist/tools/memory.search.d.ts +0 -5
  402. package/dist/tools/memory.search.d.ts.map +0 -1
  403. package/dist/tools/memory.search.js +0 -134
  404. package/dist/tools/memory.search.js.map +0 -1
  405. package/dist/tools/memory.search.test.d.ts +0 -2
  406. package/dist/tools/memory.search.test.d.ts.map +0 -1
  407. package/dist/tools/memory.search.test.js +0 -181
  408. package/dist/tools/memory.search.test.js.map +0 -1
  409. package/dist/tools/memory.store.d.ts +0 -5
  410. package/dist/tools/memory.store.d.ts.map +0 -1
  411. package/dist/tools/memory.store.js +0 -117
  412. package/dist/tools/memory.store.js.map +0 -1
  413. package/dist/tools/memory.summarize.d.ts +0 -4
  414. package/dist/tools/memory.summarize.d.ts.map +0 -1
  415. package/dist/tools/memory.summarize.js +0 -31
  416. package/dist/tools/memory.summarize.js.map +0 -1
  417. package/dist/tools/memory.synthesize.d.ts +0 -14
  418. package/dist/tools/memory.synthesize.d.ts.map +0 -1
  419. package/dist/tools/memory.synthesize.js +0 -228
  420. package/dist/tools/memory.synthesize.js.map +0 -1
  421. package/dist/tools/memory.update.d.ts +0 -5
  422. package/dist/tools/memory.update.d.ts.map +0 -1
  423. package/dist/tools/memory.update.js +0 -73
  424. package/dist/tools/memory.update.js.map +0 -1
  425. package/dist/tools/schemas.d.ts +0 -2762
  426. package/dist/tools/schemas.d.ts.map +0 -1
  427. package/dist/tools/schemas.js +0 -952
  428. package/dist/tools/schemas.js.map +0 -1
  429. package/dist/tools/task.bulk-manage.d.ts +0 -4
  430. package/dist/tools/task.bulk-manage.d.ts.map +0 -1
  431. package/dist/tools/task.bulk-manage.js +0 -146
  432. package/dist/tools/task.bulk-manage.js.map +0 -1
  433. package/dist/tools/task.manage.d.ts +0 -16
  434. package/dist/tools/task.manage.d.ts.map +0 -1
  435. package/dist/tools/task.manage.js +0 -395
  436. package/dist/tools/task.manage.js.map +0 -1
  437. package/dist/tools/tasks-transition.test.d.ts +0 -2
  438. package/dist/tools/tasks-transition.test.d.ts.map +0 -1
  439. package/dist/tools/tasks-transition.test.js +0 -174
  440. package/dist/tools/tasks-transition.test.js.map +0 -1
  441. package/dist/tools/tasks.pending-limit-refined.test.d.ts +0 -2
  442. package/dist/tools/tasks.pending-limit-refined.test.d.ts.map +0 -1
  443. package/dist/tools/tasks.pending-limit-refined.test.js +0 -72
  444. package/dist/tools/tasks.pending-limit-refined.test.js.map +0 -1
  445. package/dist/types.d.ts +0 -87
  446. package/dist/types.d.ts.map +0 -1
  447. package/dist/types.js +0 -3
  448. package/dist/types.js.map +0 -1
  449. package/dist/utils/completion.d.ts +0 -2
  450. package/dist/utils/completion.d.ts.map +0 -1
  451. package/dist/utils/completion.js +0 -28
  452. package/dist/utils/completion.js.map +0 -1
  453. package/dist/utils/git-scope.d.ts +0 -8
  454. package/dist/utils/git-scope.d.ts.map +0 -1
  455. package/dist/utils/git-scope.js +0 -38
  456. package/dist/utils/git-scope.js.map +0 -1
  457. package/dist/utils/logger.d.ts +0 -25
  458. package/dist/utils/logger.d.ts.map +0 -1
  459. package/dist/utils/logger.js +0 -152
  460. package/dist/utils/logger.js.map +0 -1
  461. package/dist/utils/logger.test.d.ts +0 -2
  462. package/dist/utils/logger.test.d.ts.map +0 -1
  463. package/dist/utils/logger.test.js +0 -76
  464. package/dist/utils/logger.test.js.map +0 -1
  465. package/dist/utils/mcp-response.d.ts +0 -96
  466. package/dist/utils/mcp-response.d.ts.map +0 -1
  467. package/dist/utils/mcp-response.js +0 -131
  468. package/dist/utils/mcp-response.js.map +0 -1
  469. package/dist/utils/normalize.d.ts +0 -9
  470. package/dist/utils/normalize.d.ts.map +0 -1
  471. package/dist/utils/normalize.js +0 -62
  472. package/dist/utils/normalize.js.map +0 -1
  473. package/dist/utils/normalize.test.d.ts +0 -2
  474. package/dist/utils/normalize.test.d.ts.map +0 -1
  475. package/dist/utils/normalize.test.js +0 -159
  476. package/dist/utils/normalize.test.js.map +0 -1
  477. package/dist/utils/pagination.d.ts +0 -6
  478. package/dist/utils/pagination.d.ts.map +0 -1
  479. package/dist/utils/pagination.js +0 -32
  480. package/dist/utils/pagination.js.map +0 -1
  481. package/dist/utils/query-expander.d.ts +0 -2
  482. package/dist/utils/query-expander.d.ts.map +0 -1
  483. package/dist/utils/query-expander.js +0 -29
  484. package/dist/utils/query-expander.js.map +0 -1
  485. package/dist/utils/query-expander.test.d.ts +0 -2
  486. package/dist/utils/query-expander.test.d.ts.map +0 -1
  487. package/dist/utils/query-expander.test.js +0 -33
  488. package/dist/utils/query-expander.test.js.map +0 -1
  489. package/dist/v2-features.test.d.ts +0 -2
  490. package/dist/v2-features.test.d.ts.map +0 -1
  491. package/dist/v2-features.test.js +0 -209
  492. package/dist/v2-features.test.js.map +0 -1
  493. /package/dist/{mcp/prompts/definitions → prompts}/architecture-design.md +0 -0
  494. /package/dist/{mcp/prompts/definitions → prompts}/documentation-sync.md +0 -0
  495. /package/dist/{mcp/prompts/definitions → prompts}/fix-suggestion.md +0 -0
  496. /package/dist/{mcp/prompts/definitions → prompts}/import-github-issues.md +0 -0
  497. /package/dist/{mcp/prompts/definitions → prompts}/learning-retrospective.md +0 -0
  498. /package/dist/{mcp/prompts/definitions → prompts}/memory-guided-review.md +0 -0
  499. /package/dist/{mcp/prompts/definitions → prompts}/project-briefing.md +0 -0
  500. /package/dist/{mcp/prompts/definitions → prompts}/root-cause-analysis.md +0 -0
  501. /package/dist/{mcp/prompts/definitions → prompts}/security-triage.md +0 -0
  502. /package/dist/{mcp/prompts/definitions → prompts}/senior-code-review.md +0 -0
  503. /package/dist/{mcp/prompts/definitions → prompts}/session-planner.md +0 -0
  504. /package/dist/{mcp/prompts/definitions → prompts}/tech-affinity-scout.md +0 -0
  505. /package/dist/{mcp/prompts/definitions → prompts}/technical-planning.md +0 -0
  506. /package/dist/{mcp/prompts/definitions → prompts}/tool-usage-guidelines.md +0 -0
@@ -1,338 +1,2154 @@
1
1
  #!/usr/bin/env node
2
- import readline from "node:readline";
3
- import { createRouter } from "./router.js";
4
- import { SQLiteStore } from "./storage/sqlite.js";
5
- import { RealVectorStore } from "./storage/vectors.js";
6
- import { CAPABILITIES, MCP_PROTOCOL_VERSION } from "./capabilities.js";
7
- import { createSessionContext, extractRootsFromResult, updateSessionFromInitialize, updateSessionRoots, } from "./session.js";
8
- import { addLogSink, logger } from "./utils/logger.js";
2
+ import {
3
+ CAPABILITIES,
4
+ LOG_LEVEL_VALUES,
5
+ MCP_PROTOCOL_VERSION,
6
+ MemoryAcknowledgeSchema,
7
+ MemoryDeleteSchema,
8
+ MemoryDetailSchema,
9
+ MemoryRecapSchema,
10
+ MemorySearchSchema,
11
+ MemoryStoreSchema,
12
+ MemorySummarizeSchema,
13
+ MemorySynthesizeSchema,
14
+ MemoryUpdateSchema,
15
+ SQLiteStore,
16
+ TOOL_DEFINITIONS,
17
+ TaskCreateInteractiveSchema,
18
+ TaskCreateSchema,
19
+ TaskDeleteSchema,
20
+ TaskGetSchema,
21
+ TaskListSchema,
22
+ TaskUpdateSchema,
23
+ addLogSink,
24
+ completePromptArgument,
25
+ completeResourceArgument,
26
+ createSessionContext,
27
+ decodeCursor,
28
+ encodeCursor,
29
+ extractRootsFromResult,
30
+ findContainingRoot,
31
+ getFilesystemRoots,
32
+ getLogLevel,
33
+ getPrompt,
34
+ inferRepoFromSession,
35
+ isPathWithinRoots,
36
+ listPrompts,
37
+ listResourceTemplates,
38
+ listResources,
39
+ logger,
40
+ normalizeRepo,
41
+ readResource,
42
+ setLogLevel,
43
+ updateSessionFromInitialize,
44
+ updateSessionRoots
45
+ } from "../chunk-XIJO63UU.js";
46
+
47
+ // src/mcp/server.ts
48
+ import readline from "readline";
49
+
50
+ // src/mcp/router.ts
51
+ import path2 from "path";
52
+
53
+ // src/mcp/completion.ts
9
54
  import fs from "fs";
10
- // --- CLI Doctor Mode ---
11
- if (process.argv.includes("doctor")) {
12
- process.stderr.write("\n🏥 MCP Local Memory - System Diagnosis\n\n");
13
- const db = new SQLiteStore();
14
- const dbPath = db.getDbPath();
15
- process.stderr.write(`📂 Database Path: ${dbPath}\n`);
16
- process.stderr.write(`📄 Database Status: ${fs.existsSync(dbPath) ? "✅ Exists" : "❌ Not Found"}\n`);
55
+ import path from "path";
56
+ var MAX_COMPLETION_VALUES = 100;
57
+ var MAX_FILE_SCAN_RESULTS = 300;
58
+ async function complete(params, db2, session2) {
59
+ const refType = params?.ref?.type;
60
+ const argumentName = typeof params?.argument?.name === "string" ? params.argument.name : "";
61
+ const argumentValue = typeof params?.argument?.value === "string" ? params.argument.value : "";
62
+ const contextArguments = params?.context?.arguments ?? {};
63
+ if (!refType || !argumentName) {
64
+ throw invalidCompletionParams("completion/complete requires ref.type and argument.name");
65
+ }
66
+ const dataSources = {
67
+ repos: getSuggestedRepos(db2, session2),
68
+ tags: getSuggestedTags(db2),
69
+ filePaths: getSuggestedFilePaths(session2),
70
+ tasks: getSuggestedTasks(db2, session2, contextArguments)
71
+ };
72
+ if (refType === "ref/prompt") {
73
+ const promptName = typeof params?.ref?.name === "string" ? params.ref.name : "";
74
+ if (!promptName) {
75
+ throw invalidCompletionParams("Prompt completion requires ref.name");
76
+ }
77
+ return {
78
+ completion: buildCompletionResult(
79
+ await completePromptArgument(promptName, argumentName, argumentValue, contextArguments, dataSources)
80
+ )
81
+ };
82
+ }
83
+ if (refType === "ref/resource") {
84
+ const resourceUri = typeof params?.ref?.uri === "string" ? params.ref.uri : "";
85
+ if (!resourceUri) {
86
+ throw invalidCompletionParams("Resource completion requires ref.uri");
87
+ }
88
+ return {
89
+ completion: buildCompletionResult(
90
+ completeResourceArgument(resourceUri, argumentName, argumentValue, contextArguments, dataSources)
91
+ )
92
+ };
93
+ }
94
+ throw invalidCompletionParams(`Unsupported completion ref type: ${refType}`);
95
+ }
96
+ function buildCompletionResult(values) {
97
+ const capped = values.slice(0, MAX_COMPLETION_VALUES);
98
+ return {
99
+ values: capped,
100
+ total: values.length,
101
+ hasMore: values.length > capped.length
102
+ };
103
+ }
104
+ function getSuggestedRepos(db2, session2) {
105
+ const values = /* @__PURE__ */ new Set();
106
+ const inferredRepo = inferRepoFromSession(session2);
107
+ if (inferredRepo) values.add(inferredRepo);
108
+ for (const rootPath of getFilesystemRoots(session2)) {
109
+ values.add(path.basename(rootPath));
110
+ }
111
+ for (const repo of db2.system.listRepos()) {
112
+ values.add(repo);
113
+ }
114
+ return [...values].sort((a, b) => a.localeCompare(b));
115
+ }
116
+ function getSuggestedTags(db2) {
117
+ const values = /* @__PURE__ */ new Set();
118
+ const memories = db2.memories.getRecentMemories("", 1e3);
119
+ for (const memory of memories) {
120
+ for (const tag of memory.tags || []) {
121
+ if (typeof tag === "string" && tag.trim()) {
122
+ values.add(tag.trim());
123
+ }
124
+ }
125
+ }
126
+ return [...values].sort((a, b) => a.localeCompare(b));
127
+ }
128
+ function getSuggestedTasks(db2, session2, contextArguments) {
129
+ const repo = typeof contextArguments.repo === "string" && contextArguments.repo.trim() ? contextArguments.repo.trim() : inferRepoFromSession(session2);
130
+ if (!repo) return [];
131
+ return db2.tasks.getTasksByRepo(repo, void 0, 100).map((task) => ({
132
+ id: task.id,
133
+ task_code: task.task_code,
134
+ title: task.title
135
+ }));
136
+ }
137
+ function getSuggestedFilePaths(session2) {
138
+ const roots = getFilesystemRoots(session2);
139
+ const results = [];
140
+ for (const rootPath of roots) {
141
+ collectFiles(rootPath, rootPath, results);
142
+ if (results.length >= MAX_FILE_SCAN_RESULTS) break;
143
+ }
144
+ return results.sort((a, b) => a.localeCompare(b));
145
+ }
146
+ function collectFiles(rootPath, currentPath, results) {
147
+ if (results.length >= MAX_FILE_SCAN_RESULTS) return;
148
+ let entries;
149
+ try {
150
+ entries = fs.readdirSync(currentPath, { withFileTypes: true });
151
+ } catch {
152
+ return;
153
+ }
154
+ for (const entry of entries) {
155
+ if (results.length >= MAX_FILE_SCAN_RESULTS) return;
156
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist") continue;
157
+ const fullPath = path.join(currentPath, entry.name);
158
+ if (entry.isDirectory()) {
159
+ collectFiles(rootPath, fullPath, results);
160
+ continue;
161
+ }
162
+ if (!entry.isFile()) continue;
163
+ results.push(path.relative(rootPath, fullPath) || entry.name);
164
+ }
165
+ }
166
+ function invalidCompletionParams(message) {
167
+ const error = new Error(message);
168
+ error.code = -32602;
169
+ return error;
170
+ }
171
+
172
+ // src/mcp/tools/memory.store.ts
173
+ import { randomUUID } from "crypto";
174
+
175
+ // src/mcp/utils/mcp-response.ts
176
+ import { z } from "zod";
177
+ var McpAnnotationsSchema = z.object({
178
+ audience: z.array(z.enum(["user", "assistant"])).optional(),
179
+ priority: z.number().min(0).max(1).optional(),
180
+ lastModified: z.string().optional()
181
+ }).strict().optional();
182
+ var McpContentSchema = z.discriminatedUnion("type", [
183
+ z.object({
184
+ type: z.literal("text"),
185
+ text: z.string(),
186
+ annotations: McpAnnotationsSchema
187
+ }),
188
+ z.object({
189
+ type: z.literal("image"),
190
+ data: z.string(),
191
+ mimeType: z.string(),
192
+ annotations: McpAnnotationsSchema
193
+ }),
194
+ z.object({
195
+ type: z.literal("resource_link"),
196
+ uri: z.string(),
197
+ name: z.string(),
198
+ description: z.string().optional(),
199
+ mimeType: z.string().optional(),
200
+ annotations: McpAnnotationsSchema
201
+ }),
202
+ z.object({
203
+ type: z.literal("resource"),
204
+ resource: z.object({
205
+ uri: z.string(),
206
+ mimeType: z.string().optional(),
207
+ text: z.string().optional(),
208
+ annotations: McpAnnotationsSchema
209
+ })
210
+ })
211
+ ]);
212
+ function createMcpResponse(data, summary, options) {
213
+ const {
214
+ resourceLinks,
215
+ structuredContentPathHint,
216
+ contentSummary,
217
+ includeSerializedStructuredContent = "auto"
218
+ } = options || {};
219
+ void includeSerializedStructuredContent;
220
+ let finalData = data;
221
+ if (data && typeof data === "object") {
222
+ const cloned = JSON.parse(JSON.stringify(data));
223
+ finalData = cloned;
224
+ const arrayKeys = ["results", "tasks", "memories", "items"];
225
+ let foundArray = false;
226
+ for (const key of arrayKeys) {
227
+ const value = cloned[key];
228
+ if (Array.isArray(value)) {
229
+ cloned[key] = value.map(
230
+ (item) => pruneMetadata(item)
231
+ );
232
+ foundArray = true;
233
+ }
234
+ }
235
+ if (Array.isArray(cloned)) {
236
+ finalData = cloned.map((item) => pruneMetadata(item));
237
+ } else if (!foundArray) {
238
+ finalData = pruneMetadata(cloned);
239
+ }
240
+ }
241
+ const content = [];
242
+ if (contentSummary?.trim().length) {
243
+ content.push({
244
+ type: "text",
245
+ text: contentSummary.trim()
246
+ });
247
+ } else if (summary.trim().length > 0) {
248
+ const pointerText = structuredContentPathHint ? `Read structuredContent.${structuredContentPathHint} for details.` : `Read structuredContent for machine-readable results.`;
249
+ content.push({
250
+ type: "text",
251
+ text: `${summary.trim()} ${pointerText}`
252
+ });
253
+ }
254
+ for (const link of resourceLinks || []) {
255
+ content.push({
256
+ type: "resource_link",
257
+ uri: link.uri,
258
+ name: link.name,
259
+ mimeType: link.mimeType,
260
+ annotations: link.annotations
261
+ });
262
+ }
263
+ const response = {
264
+ structuredContent: finalData,
265
+ isError: false
266
+ };
267
+ response.content = content;
268
+ return response;
269
+ }
270
+ function pruneMetadata(item) {
271
+ if (!item || typeof item !== "object") return item;
272
+ const pruned = { ...item };
273
+ const toRemove = [
274
+ "hit_count",
275
+ "recall_count",
276
+ "last_used_at",
277
+ "expires_at",
278
+ "agent",
279
+ "role",
280
+ "model",
281
+ "recall_rate",
282
+ "vector_version",
283
+ "similarity"
284
+ // Similarity is useful but adds noise if many results
285
+ ];
286
+ for (const field of toRemove) {
287
+ delete pruned[field];
288
+ }
289
+ return pruned;
290
+ }
291
+ function getPrimaryTextContent(response) {
292
+ if (!Array.isArray(response.content)) return "";
293
+ const textItem = response.content.find((item) => item.type === "text");
294
+ return textItem?.type === "text" ? textItem.text : "";
295
+ }
296
+
297
+ // src/mcp/tools/memory.store.ts
298
+ function hasMetadataLikeTitle(title) {
299
+ const normalized = title.trim();
300
+ return /^\[[^\]]{0,200}(agent:|role:|model:|\d{4}-\d{2}-\d{2}|source_)[^\]]*\]/i.test(normalized);
301
+ }
302
+ async function handleMemoryStore(params, db2, vectors2) {
303
+ const validated = MemoryStoreSchema.parse(params);
304
+ if (hasMetadataLikeTitle(validated.title)) {
305
+ throw new Error(
306
+ "Title appears to contain metadata. Keep title concise and move agent/role/date details into metadata or dedicated fields."
307
+ );
308
+ }
309
+ const now = (/* @__PURE__ */ new Date()).toISOString();
310
+ const expires_at = validated.ttlDays != null ? new Date(Date.now() + validated.ttlDays * 864e5).toISOString() : null;
311
+ if (!validated.supersedes && validated.type !== "task_archive") {
312
+ const conflict = await db2.memories.checkConflicts(
313
+ validated.content,
314
+ validated.scope.repo,
315
+ validated.type,
316
+ vectors2,
317
+ 0.55
318
+ );
319
+ if (conflict) {
320
+ return createMcpResponse(
321
+ {
322
+ success: false,
323
+ error: "MEMORY_CONFLICT",
324
+ message: `This memory content overlaps significantly with an existing memory (ID: ${conflict.id}).`,
325
+ conflicting_memory: {
326
+ id: conflict.id,
327
+ title: conflict.title,
328
+ content: conflict.content
329
+ },
330
+ instruction: "Use 'memory-update' on the existing ID, or provide 'supersedes' if this new memory replaces it. If the old memory is no longer relevant, you can delete it first."
331
+ },
332
+ `Rejected due to conflict with ${conflict.id}. Hint: Use 'supersedes' if this replaces the old memory, or 'memory-update' if you're updating it. If the old memory is no longer relevant, delete it first using 'memory-delete'.`,
333
+ {
334
+ structuredContentPathHint: "conflicting_memory"
335
+ }
336
+ );
337
+ }
338
+ }
339
+ if (validated.supersedes) {
340
+ const oldMemory = db2.memories.getById(validated.supersedes);
341
+ if (oldMemory) {
342
+ db2.memories.update(oldMemory.id, { status: "archived" });
343
+ logger.info("[MCP] memory.store - archived superseded memory", {
344
+ oldId: oldMemory.id,
345
+ newId: validated.supersedes
346
+ });
347
+ }
348
+ }
349
+ const tags = validated.tags ?? [];
350
+ if (validated.scope.language && !tags.includes(validated.scope.language.toLowerCase())) {
351
+ tags.push(validated.scope.language.toLowerCase());
352
+ }
353
+ const entry = {
354
+ id: randomUUID(),
355
+ type: validated.type,
356
+ title: validated.title,
357
+ content: validated.content,
358
+ importance: validated.importance,
359
+ agent: validated.agent,
360
+ role: validated.role,
361
+ model: validated.model,
362
+ scope: validated.scope,
363
+ created_at: now,
364
+ updated_at: now,
365
+ completed_at: null,
366
+ hit_count: 0,
367
+ recall_count: 0,
368
+ last_used_at: null,
369
+ expires_at,
370
+ supersedes: validated.supersedes ?? null,
371
+ status: "active",
372
+ tags,
373
+ metadata: validated.metadata ?? {},
374
+ is_global: validated.is_global
375
+ };
376
+ db2.memories.insert(entry);
377
+ try {
378
+ await vectors2.upsert(entry.id, entry.content);
379
+ } catch (error) {
380
+ logger.warn("Failed to generate vector embedding", { error: String(error) });
381
+ }
382
+ logger.info("[MCP] memory.store", {
383
+ repo: validated.scope.repo,
384
+ id: entry.id,
385
+ title: entry.title,
386
+ type: entry.type,
387
+ importance: entry.importance
388
+ });
389
+ return createMcpResponse(
390
+ {
391
+ success: true,
392
+ id: entry.id,
393
+ repo: entry.scope.repo,
394
+ type: entry.type,
395
+ title: entry.title
396
+ },
397
+ `Stored memory "${entry.title}" in repo "${entry.scope.repo}".`,
398
+ {
399
+ structuredContentPathHint: "id",
400
+ resourceLinks: [
401
+ {
402
+ uri: `memory://${entry.id}`,
403
+ name: entry.title,
404
+ description: `Stored memory in repo ${entry.scope.repo}`,
405
+ mimeType: "application/json",
406
+ annotations: {
407
+ audience: ["assistant"],
408
+ priority: 0.9,
409
+ lastModified: entry.updated_at
410
+ }
411
+ },
412
+ {
413
+ uri: `repository://${encodeURIComponent(entry.scope.repo)}/memories`,
414
+ name: `Memory Index (${entry.scope.repo})`,
415
+ description: "Repository memory index",
416
+ mimeType: "application/json",
417
+ annotations: {
418
+ audience: ["assistant"],
419
+ priority: 0.6
420
+ }
421
+ }
422
+ ]
423
+ }
424
+ );
425
+ }
426
+
427
+ // src/mcp/tools/memory.update.ts
428
+ function hasMetadataLikeTitle2(title) {
429
+ const normalized = title.trim();
430
+ return /^\[[^\]]{0,200}(agent:|role:|model:|\d{4}-\d{2}-\d{2}|source_)[^\]]*\]/i.test(normalized);
431
+ }
432
+ async function handleMemoryUpdate(params, db2, vectors2) {
433
+ const validated = MemoryUpdateSchema.parse(params);
434
+ const existing = db2.memories.getById(validated.id);
435
+ if (!existing) {
436
+ throw new Error(`Memory not found: ${validated.id}`);
437
+ }
438
+ const repoFilter = params?.repo || params?.scope?.repo;
439
+ if (repoFilter && repoFilter !== existing.scope.repo) {
440
+ throw new Error(
441
+ `Repository mismatch: provided repo "${repoFilter}" does not match memory repo "${existing.scope.repo}"`
442
+ );
443
+ }
444
+ if (validated.title !== void 0 && hasMetadataLikeTitle2(validated.title)) {
445
+ throw new Error(
446
+ "Title appears to contain metadata. Keep title concise and move agent/role/date details into metadata or dedicated fields."
447
+ );
448
+ }
449
+ const updates = {};
450
+ if (validated.type !== void 0) updates.type = validated.type;
451
+ if (validated.title !== void 0) updates.title = validated.title;
452
+ if (validated.content !== void 0) updates.content = validated.content;
453
+ if (validated.importance !== void 0) updates.importance = validated.importance;
454
+ if (validated.agent !== void 0) updates.agent = validated.agent;
455
+ if (validated.role !== void 0) updates.role = validated.role;
456
+ if (validated.status !== void 0) updates.status = validated.status;
457
+ if (validated.supersedes !== void 0) updates.supersedes = validated.supersedes;
458
+ if (validated.tags !== void 0) updates.tags = validated.tags;
459
+ if (validated.metadata !== void 0) updates.metadata = validated.metadata;
460
+ if (validated.is_global !== void 0) updates.is_global = validated.is_global;
461
+ if (validated.completed_at !== void 0) updates.completed_at = validated.completed_at;
462
+ db2.memories.update(validated.id, updates);
463
+ if (validated.content !== void 0) {
464
+ await vectors2.upsert(validated.id, validated.content);
465
+ }
466
+ db2.actions.logAction("update", existing.scope.repo, { memoryId: validated.id, resultCount: 1 });
467
+ logger.info("[MCP] memory.update", { repo: existing.scope.repo, id: validated.id, fields: Object.keys(updates) });
468
+ return createMcpResponse(
469
+ {
470
+ success: true,
471
+ id: validated.id,
472
+ repo: existing.scope.repo,
473
+ updatedFields: Object.keys(updates)
474
+ },
475
+ `Updated memory ${validated.id} in repo "${existing.scope.repo}". Fields: ${Object.keys(updates).join(", ") || "none"}.`,
476
+ {
477
+ structuredContentPathHint: "updatedFields",
478
+ resourceLinks: [
479
+ {
480
+ uri: `memory://${validated.id}`,
481
+ name: existing.title || validated.id,
482
+ description: `Updated memory in repo ${existing.scope.repo}`,
483
+ mimeType: "application/json",
484
+ annotations: {
485
+ audience: ["assistant"],
486
+ priority: 0.9
487
+ }
488
+ }
489
+ ]
490
+ }
491
+ );
492
+ }
493
+
494
+ // src/mcp/utils/query-expander.ts
495
+ var TECH_SYNONYMS = {
496
+ update: ["migrate", "change", "alter", "modify", "patch", "upgrade"],
497
+ database: ["db", "sql", "schema", "persistence", "storage", "sqlite", "postgres"],
498
+ auth: ["login", "security", "token", "session", "permission", "authorization", "authentication", "system"],
499
+ authentication: ["auth", "login", "security", "token", "session", "permission"],
500
+ system: ["architecture", "structure", "design", "security"],
501
+ error: ["bug", "issue", "failure", "mistake", "fix", "exception"],
502
+ ui: ["frontend", "component", "styling", "css", "layout", "view"],
503
+ deploy: ["publish", "release", "ci", "cd", "pipeline"]
504
+ };
505
+ function expandQuery(query, prompt) {
506
+ const baseText = prompt ? `${query} ${prompt}` : query;
507
+ const words = baseText.toLowerCase().split(/\s+/);
508
+ const expansions = new Set(words);
509
+ for (const word of words) {
510
+ const cleanWord = word.replace(/[?.!,]/g, "");
511
+ if (TECH_SYNONYMS[cleanWord]) {
512
+ TECH_SYNONYMS[cleanWord].forEach((syn) => expansions.add(syn));
513
+ }
514
+ }
515
+ return Array.from(expansions).join(" ");
516
+ }
517
+
518
+ // src/mcp/tools/memory.search.ts
519
+ var HYBRID_WEIGHTS_VECTOR = {
520
+ similarity: 0.4,
521
+ vector: 0.4,
522
+ importance: 0.2
523
+ };
524
+ async function handleMemorySearch(params, db2, vectors2) {
525
+ const validated = MemorySearchSchema.parse(params);
526
+ const searchQuery = expandQuery(validated.query, validated.prompt);
527
+ const fetchLimit = (validated.offset + validated.limit) * 3;
528
+ const similarityResults = db2.memories.searchBySimilarity(
529
+ searchQuery,
530
+ validated.repo,
531
+ fetchLimit,
532
+ validated.include_archived,
533
+ validated.current_tags ?? []
534
+ );
535
+ let candidates = similarityResults.map((r) => ({
536
+ memory: r,
537
+ similarityScore: r.similarity
538
+ }));
539
+ if (candidates.length > 0) {
540
+ const currentPath = validated.current_file_path?.toLowerCase();
541
+ const currentTags = (validated.current_tags || []).map((t) => t.toLowerCase());
542
+ const currentBranch = validated.scope?.branch;
543
+ candidates = candidates.map((c) => {
544
+ let boost = 0;
545
+ if (currentBranch && c.memory.scope.branch === currentBranch) {
546
+ boost += 0.1;
547
+ }
548
+ if (currentPath && c.memory.scope.folder && currentPath.includes(c.memory.scope.folder.toLowerCase())) {
549
+ boost += 0.15;
550
+ }
551
+ if (currentPath && c.memory.scope.language) {
552
+ const ext = currentPath.split(".").pop();
553
+ if (ext && ext.includes(c.memory.scope.language.toLowerCase())) boost += 0.1;
554
+ }
555
+ if (currentTags.length > 0 && c.memory.tags.some((t) => currentTags.includes(t.toLowerCase()))) {
556
+ boost += 0.2;
557
+ }
558
+ return { ...c, similarityScore: Math.min(1, c.similarityScore + boost) };
559
+ });
560
+ }
561
+ let scoredMemories = [];
562
+ try {
563
+ const vectorResults = await vectors2.search(searchQuery, candidates.length || 10, validated.repo);
564
+ const vectorScoreMap = new Map(vectorResults.map((vr) => [vr.id, vr.score]));
565
+ if (candidates.length > 0) {
566
+ scoredMemories = candidates.map((c) => {
567
+ const vScore = vectorScoreMap.get(c.memory.id) ?? 0;
568
+ const impBoost = c.memory.importance / 5;
569
+ const finalScore = c.similarityScore * HYBRID_WEIGHTS_VECTOR.similarity + vScore * HYBRID_WEIGHTS_VECTOR.vector + impBoost * HYBRID_WEIGHTS_VECTOR.importance;
570
+ return { ...c, vectorScore: vScore, finalScore };
571
+ });
572
+ } else if (vectorResults.length > 0) {
573
+ const vectorIds = vectorResults.map((vr) => vr.id);
574
+ const fetchedMemories = db2.memories.getByIds(vectorIds);
575
+ const memoryMap = new Map(fetchedMemories.map((m) => [m.id, m]));
576
+ for (const vr of vectorResults) {
577
+ const mem = memoryMap.get(vr.id);
578
+ if (mem) {
579
+ const impBoost = mem.importance / 5;
580
+ scoredMemories.push({
581
+ memory: mem,
582
+ similarityScore: 0,
583
+ vectorScore: vr.score,
584
+ finalScore: vr.score * 0.8 + impBoost * 0.2
585
+ });
586
+ }
587
+ }
588
+ }
589
+ } catch (error) {
590
+ logger.warn("Vector search failed, using similarity only", { error: String(error) });
591
+ scoredMemories = candidates.map((c) => ({
592
+ ...c,
593
+ vectorScore: 0,
594
+ finalScore: c.similarityScore * 0.8 + c.memory.importance / 5 * 0.2
595
+ }));
596
+ }
597
+ scoredMemories.sort((a, b) => b.finalScore - a.finalScore);
598
+ const threshold = scoredMemories.length <= 5 ? 0.1 : 0.4;
599
+ let allMatches = scoredMemories.filter((sm) => sm.finalScore >= threshold).map((sm) => sm.memory);
600
+ if (allMatches.length === 0 && scoredMemories.length > 0) {
601
+ allMatches = [scoredMemories[0].memory];
602
+ }
603
+ const total = allMatches.length;
604
+ const paginatedResults = allMatches.slice(validated.offset, validated.offset + validated.limit);
605
+ db2.memories.incrementHitCounts(paginatedResults.map((m) => m.id));
606
+ logger.info("[MCP] memory.search", {
607
+ repo: validated.repo,
608
+ query: validated.query,
609
+ total,
610
+ offset: validated.offset,
611
+ returned: paginatedResults.length
612
+ });
613
+ const COLUMNS = ["id", "title", "type", "importance"];
614
+ const rows = paginatedResults.map((m) => [m.id, m.title ?? "Untitled", m.type, m.importance]);
615
+ const structuredContent = {
616
+ schema: "memory-search",
617
+ query: validated.query,
618
+ count: paginatedResults.length,
619
+ total,
620
+ offset: validated.offset,
621
+ limit: validated.limit,
622
+ results: {
623
+ columns: [...COLUMNS],
624
+ rows
625
+ }
626
+ };
627
+ const memoryList = paginatedResults.map((m) => `"${m.title}" (ID: ${m.id})`).join(", ");
628
+ const contentSummary = paginatedResults.length > 0 ? `Found ${total} memories for "${validated.query}" (showing ${paginatedResults.length} at offset ${validated.offset}): ${memoryList}. Use memory-detail to read full content.` : `No memories found for "${validated.query}" in repo "${validated.repo}".`;
629
+ return createMcpResponse(structuredContent, contentSummary, {
630
+ contentSummary,
631
+ includeSerializedStructuredContent: false
632
+ });
633
+ }
634
+
635
+ // src/mcp/tools/memory.summarize.ts
636
+ async function handleMemorySummarize(params, db2) {
637
+ const validated = MemorySummarizeSchema.parse(params);
638
+ const summary = validated.signals.join("\n- ");
639
+ const fullSummary = `Project summary:
640
+ - ${summary}`;
641
+ db2.summaries.upsertSummary(validated.repo, fullSummary);
642
+ return createMcpResponse(
643
+ {
644
+ success: true,
645
+ repo: validated.repo,
646
+ summary: fullSummary,
647
+ signalCount: validated.signals.length
648
+ },
649
+ `Updated summary for repo "${validated.repo}" with ${validated.signals.length} signals.`,
650
+ {
651
+ structuredContentPathHint: "summary",
652
+ resourceLinks: [
653
+ {
654
+ uri: `repository://${encodeURIComponent(validated.repo)}/summary`,
655
+ name: `Repository Summary (${validated.repo})`,
656
+ description: "Repository summary resource",
657
+ mimeType: "text/plain",
658
+ annotations: {
659
+ audience: ["assistant"],
660
+ priority: 0.9
661
+ }
662
+ }
663
+ ]
664
+ }
665
+ );
666
+ }
667
+
668
+ // src/mcp/sampling.ts
669
+ function asArray(value) {
670
+ return Array.isArray(value) ? value : [value];
671
+ }
672
+ function extractTextFromContent(content) {
673
+ return asArray(content).filter((entry) => entry.type === "text").map((entry) => entry.text).join("\n");
674
+ }
675
+ function extractToolUses(content) {
676
+ return asArray(content).filter(
677
+ (entry) => entry.type === "tool_use"
678
+ );
679
+ }
680
+
681
+ // src/mcp/elicitation.ts
682
+ function extractAcceptedElicitationContent(result) {
683
+ if (result?.action === "accept" && result.content && typeof result.content === "object") {
684
+ return result.content;
685
+ }
686
+ if (result?.action === "decline") {
687
+ throw new Error("User declined the elicitation request");
688
+ }
689
+ if (result?.action === "cancel") {
690
+ throw new Error("User canceled the elicitation request");
691
+ }
692
+ throw new Error("Elicitation did not return accepted content");
693
+ }
694
+
695
+ // src/mcp/tools/memory.recap.ts
696
+ async function handleMemoryRecap(params, db2) {
697
+ const validated = MemoryRecapSchema.parse(params);
698
+ logger.info("[MCP] memory.recap", { repo: validated.repo, limit: validated.limit, offset: validated.offset });
699
+ const stats = db2.memories.getStats(validated.repo);
700
+ const total = db2.memories.getTotalCount(validated.repo, false, ["task_archive"]);
701
+ const rows = db2.memories.getRecentMemories(validated.repo, validated.limit, validated.offset, false, [
702
+ "task_archive"
703
+ ]);
704
+ const COLUMNS = ["id", "title", "type", "importance"];
705
+ const topRows = rows.map((row) => [row.id, row.title ?? "Untitled", row.type, row.importance]);
706
+ const byType = {};
707
+ for (const [type, count] of Object.entries(stats.byType)) {
708
+ if (type !== "task_archive") {
709
+ byType[type] = count;
710
+ }
711
+ }
712
+ const structuredContent = {
713
+ schema: "memory-recap",
714
+ repo: validated.repo,
715
+ count: rows.length,
716
+ total,
717
+ offset: validated.offset,
718
+ limit: validated.limit,
719
+ stats: {
720
+ by_type: byType
721
+ },
722
+ top: {
723
+ columns: [...COLUMNS],
724
+ rows: topRows
725
+ }
726
+ };
727
+ const memoryList = rows.map((row) => `"${row.title}" (ID: ${row.id})`).join(", ");
728
+ const contentSummary = total > 0 ? `Repo "${validated.repo}" has ${total} active memories. Showing ${rows.length} at offset ${validated.offset}: ${memoryList}. Use memory-detail to read full content.` : `No memories found for repo "${validated.repo}".`;
729
+ return createMcpResponse(structuredContent, contentSummary, {
730
+ contentSummary,
731
+ includeSerializedStructuredContent: false
732
+ });
733
+ }
734
+
735
+ // src/mcp/tools/task.manage.ts
736
+ import { randomUUID as randomUUID2 } from "crypto";
737
+ function describeTaskListFilter(status) {
738
+ if (!status) return "active";
739
+ if (status === "all") return "all";
740
+ const labels = status.split(",").map((part) => part.trim()).filter(Boolean).map((part) => {
741
+ switch (part) {
742
+ case "in_progress":
743
+ return "in progress";
744
+ default:
745
+ return part;
746
+ }
747
+ });
748
+ if (labels.length === 0) return "active";
749
+ if (labels.length === 1) return labels[0];
750
+ if (labels.length === 2) return `${labels[0]} and ${labels[1]}`;
751
+ return `${labels.slice(0, -1).join(", ")}, and ${labels[labels.length - 1]}`;
752
+ }
753
+ function buildTaskListSummary(repo, count, status, phase, search, stats) {
754
+ const filterLabel = describeTaskListFilter(status);
755
+ const taskLabel = count === 1 ? "task" : "tasks";
756
+ const parts = [`Found ${count} ${filterLabel} ${taskLabel} in repo "${repo}".`];
757
+ if (phase) {
758
+ parts.push(`Phase filter: ${phase}.`);
759
+ }
760
+ if (search) {
761
+ parts.push(`Search filter: "${search}".`);
762
+ }
763
+ parts.push(`Pending: ${stats?.todo ?? 0}.`);
764
+ parts.push(`In progress: ${stats?.inProgress ?? 0}.`);
765
+ parts.push(`Use task-detail with Task ID or task_code to read full details.`);
766
+ return parts.join(" ");
767
+ }
768
+ function deriveTaskStatusTimestamps(status, now, existingTask) {
769
+ const timestamps = {
770
+ in_progress_at: null,
771
+ finished_at: null,
772
+ canceled_at: null
773
+ };
774
+ if (status === "in_progress" && existingTask?.status !== "in_progress") {
775
+ timestamps.in_progress_at = now;
776
+ }
777
+ if (status === "completed") {
778
+ timestamps.finished_at = now;
779
+ }
780
+ if (status === "canceled") {
781
+ timestamps.canceled_at = now;
782
+ }
783
+ return timestamps;
784
+ }
785
+ async function archiveTaskToMemory(taskId, repo, storage, vectors2) {
786
+ const task = storage.tasks.getTaskById(taskId);
787
+ if (!task) return;
788
+ const comments = storage.tasks.getTaskCommentsByTaskId(taskId);
789
+ let content = `Task: [${task.task_code}] ${task.title}
790
+ `;
791
+ content += `Phase: ${task.phase}
792
+ `;
793
+ content += `Description: ${task.description || "No description"}
794
+ `;
795
+ if (comments && comments.length > 0) {
796
+ content += `
797
+ Comments & History:
798
+ `;
799
+ const chronComments = [...comments].reverse();
800
+ for (const c of chronComments) {
801
+ const statusInfo = c.next_status ? ` (Status: ${c.previous_status} -> ${c.next_status})` : "";
802
+ content += `- [${c.created_at}] ${c.agent}${statusInfo}: ${c.comment}
803
+ `;
804
+ }
805
+ }
806
+ const metadata = {
807
+ task_id: taskId,
808
+ task_code: task.task_code,
809
+ original_metadata: task.metadata
810
+ };
811
+ const title = `Completed Task: ${task.title}`;
812
+ const truncatedTitle = title.length > 100 ? title.substring(0, 97) + "..." : title;
813
+ try {
814
+ await handleMemoryStore(
815
+ {
816
+ type: "task_archive",
817
+ title: truncatedTitle,
818
+ content,
819
+ importance: Math.min(5, task.priority + 1),
820
+ // Slightly higher importance for archived tasks
821
+ agent: task.agent || "system",
822
+ role: task.role || "unknown",
823
+ model: "system",
824
+ scope: { repo },
825
+ tags: ["task-archive", ...task.tags],
826
+ metadata
827
+ },
828
+ storage,
829
+ vectors2
830
+ );
831
+ } catch (error) {
832
+ console.error("Failed to archive task to memory", error);
833
+ }
834
+ }
835
+ async function handleTaskList(args, storage) {
836
+ const {
837
+ repo,
838
+ status = "backlog,pending,in_progress,blocked",
839
+ phase,
840
+ query,
841
+ limit,
842
+ offset
843
+ } = TaskListSchema.parse(args);
844
+ let statuses = [];
845
+ if (status !== "all") {
846
+ statuses = status.split(",").map((s) => s.trim()).filter(Boolean);
847
+ }
848
+ const tasks = storage.tasks.getTasksByMultipleStatuses(repo, statuses, limit, offset, query);
849
+ const filteredTasks = phase ? tasks.filter((t) => t.phase.toLowerCase() === phase.toLowerCase()) : tasks;
850
+ const COLUMNS = ["id", "task_code", "title", "status", "priority", "comments_count"];
851
+ const rows = filteredTasks.map((t) => [
852
+ t.id,
853
+ t.task_code,
854
+ t.title,
855
+ t.status,
856
+ t.priority,
857
+ t.comments_count || 0
858
+ ]);
859
+ const structured = {
860
+ schema: "task-list",
861
+ tasks: {
862
+ columns: [...COLUMNS],
863
+ rows
864
+ },
865
+ count: rows.length,
866
+ offset
867
+ };
868
+ const _taskList = filteredTasks.map((t) => `[${t.task_code}] ${t.title} (ID: ${t.id})`).join(", ");
869
+ const taskStats = storage.tasks.getTaskStats(repo);
870
+ const summary = buildTaskListSummary(repo, rows.length, status, phase, query, taskStats);
871
+ return createMcpResponse(structured, summary, {
872
+ contentSummary: summary,
873
+ includeSerializedStructuredContent: false
874
+ });
875
+ }
876
+ async function handleTaskCreate(args, storage) {
877
+ const parsed = TaskCreateSchema.parse(args);
878
+ const { repo, tasks: bulkTasks, ...singleTask } = parsed;
879
+ if (bulkTasks) {
880
+ const createdTasks = [];
881
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
882
+ const codesInRequest = /* @__PURE__ */ new Set();
883
+ for (const taskData of bulkTasks) {
884
+ if (codesInRequest.has(taskData.task_code)) {
885
+ throw new Error(`Duplicate task_code in request: '${taskData.task_code}'`);
886
+ }
887
+ if (storage.tasks.isTaskCodeDuplicate(repo, taskData.task_code)) {
888
+ throw new Error(`Duplicate task_code: '${taskData.task_code}' already exists in repository '${repo}'`);
889
+ }
890
+ codesInRequest.add(taskData.task_code);
891
+ const normalizedStatus = taskData.status || "backlog";
892
+ if (normalizedStatus !== "backlog" && normalizedStatus !== "pending") {
893
+ throw new Error(`New tasks must be 'backlog' or 'pending'. Task '${taskData.task_code}' has status '${normalizedStatus}'.`);
894
+ }
895
+ if (normalizedStatus === "pending") {
896
+ const stats = storage.tasks.getTaskStats(repo);
897
+ const pendingInRequest = createdTasks.filter((c) => {
898
+ const t = bulkTasks.find((bt) => bt.task_code === c);
899
+ return t?.status === "pending";
900
+ }).length;
901
+ if (stats.todo + pendingInRequest >= 10) {
902
+ throw new Error(`Cannot create task '${taskData.task_code}' as 'pending'. Maximum of 10 pending tasks reached.`);
903
+ }
904
+ }
905
+ const statusTimestamps2 = deriveTaskStatusTimestamps(normalizedStatus, now2);
906
+ const task2 = {
907
+ id: randomUUID2(),
908
+ repo,
909
+ task_code: taskData.task_code,
910
+ phase: taskData.phase,
911
+ title: taskData.title,
912
+ description: taskData.description,
913
+ status: normalizedStatus,
914
+ priority: taskData.priority || 3,
915
+ agent: taskData.agent || singleTask.agent || "unknown",
916
+ role: taskData.role || singleTask.role || "unknown",
917
+ doc_path: taskData.doc_path || null,
918
+ created_at: now2,
919
+ updated_at: now2,
920
+ in_progress_at: statusTimestamps2.in_progress_at,
921
+ finished_at: statusTimestamps2.finished_at,
922
+ canceled_at: statusTimestamps2.canceled_at,
923
+ est_tokens: taskData.est_tokens ?? 0,
924
+ tags: taskData.tags || [],
925
+ metadata: taskData.metadata || {},
926
+ parent_id: taskData.parent_id || null,
927
+ depends_on: taskData.depends_on || null
928
+ };
929
+ storage.tasks.insertTask(task2);
930
+ createdTasks.push(task2.task_code);
931
+ }
932
+ return createMcpResponse(
933
+ { success: true, repo, createdCount: bulkTasks.length, taskCodes: createdTasks },
934
+ `Created ${bulkTasks.length} tasks in repo "${repo}".`
935
+ );
936
+ }
937
+ const { task_code, phase, title, description, status, priority, agent, role, doc_path, tags, metadata, parent_id, depends_on, est_tokens } = singleTask;
938
+ if (!task_code || !phase || !title || !description) {
939
+ throw new Error("Missing required fields for single task creation (task_code, phase, title, description)");
940
+ }
941
+ if (storage.tasks.isTaskCodeDuplicate(repo, task_code)) {
942
+ throw new Error(`Duplicate task_code: '${task_code}' already exists in repository '${repo}'`);
943
+ }
944
+ if (status !== "backlog" && status !== "pending" && status !== void 0) {
945
+ throw new Error("New tasks must be created with status 'backlog' or 'pending'.");
946
+ }
947
+ if (status === "pending") {
948
+ const stats = storage.tasks.getTaskStats(repo);
949
+ if (stats.todo >= 10) {
950
+ throw new Error(`Cannot create task as 'pending'. Maximum of 10 pending tasks reached.`);
951
+ }
952
+ }
953
+ const taskId = randomUUID2();
954
+ const now = (/* @__PURE__ */ new Date()).toISOString();
955
+ const statusTimestamps = deriveTaskStatusTimestamps(status || "backlog", now);
956
+ const task = {
957
+ id: taskId,
958
+ repo,
959
+ task_code,
960
+ phase,
961
+ title,
962
+ description,
963
+ status: status || "backlog",
964
+ priority: priority || 3,
965
+ agent: agent || "unknown",
966
+ role: role || "unknown",
967
+ doc_path: doc_path || null,
968
+ created_at: now,
969
+ updated_at: now,
970
+ in_progress_at: statusTimestamps.in_progress_at,
971
+ finished_at: statusTimestamps.finished_at,
972
+ canceled_at: statusTimestamps.canceled_at,
973
+ est_tokens: est_tokens ?? 0,
974
+ tags: tags || [],
975
+ metadata: metadata || {},
976
+ parent_id: parent_id || null,
977
+ depends_on: depends_on || null
978
+ };
979
+ storage.tasks.insertTask(task);
980
+ return createMcpResponse(
981
+ {
982
+ success: true,
983
+ id: task.id,
984
+ repo: task.repo,
985
+ task_code: task.task_code,
986
+ phase: task.phase,
987
+ title: task.title,
988
+ status: task.status,
989
+ priority: task.priority,
990
+ depends_on: task.depends_on
991
+ },
992
+ `Created task [${task.task_code}] ${task.title} in repo "${task.repo}" with status "${task.status}".`
993
+ );
994
+ }
995
+ async function handleTaskCreateInteractive(args, storage, options = {}) {
996
+ const partialTaskData = TaskCreateInteractiveSchema.parse(args ?? {});
997
+ const inferredRepo = partialTaskData.repo ?? inferRepoFromSession(options.session);
998
+ const draft = {
999
+ ...partialTaskData,
1000
+ repo: inferredRepo
1001
+ };
1002
+ const requestedSchema = buildMissingTaskSchema(draft);
1003
+ let completedDraft = draft;
1004
+ if (Object.keys(requestedSchema.properties).length > 0) {
1005
+ if (!options.session?.supportsElicitationForm || !options.elicit) {
1006
+ throw new Error("Client does not advertise MCP elicitation form support");
1007
+ }
1008
+ const elicited = extractAcceptedElicitationContent(
1009
+ await options.elicit({
1010
+ mode: "form",
1011
+ message: "Please complete the missing task details to create a new task.",
1012
+ requestedSchema
1013
+ })
1014
+ );
1015
+ completedDraft = {
1016
+ ...draft,
1017
+ ...elicited
1018
+ };
1019
+ }
1020
+ return await handleTaskCreate(
1021
+ {
1022
+ ...completedDraft,
1023
+ status: completedDraft.status ?? "backlog",
1024
+ priority: completedDraft.priority ?? 3
1025
+ },
1026
+ storage
1027
+ );
1028
+ }
1029
+ function buildMissingTaskSchema(task) {
1030
+ const properties = {};
1031
+ const required = [];
1032
+ addRequiredStringField(properties, required, task, "repo", {
1033
+ title: "Repository",
1034
+ description: "Name of the repository for this task.",
1035
+ minLength: 1
1036
+ });
1037
+ addRequiredStringField(properties, required, task, "task_code", {
1038
+ title: "Task Code",
1039
+ description: "Unique task code in this repository.",
1040
+ minLength: 1
1041
+ });
1042
+ addRequiredStringField(properties, required, task, "phase", {
1043
+ title: "Phase",
1044
+ description: "Project phase or milestone for this task.",
1045
+ minLength: 1
1046
+ });
1047
+ addRequiredStringField(properties, required, task, "title", {
1048
+ title: "Title",
1049
+ description: "Short task title.",
1050
+ minLength: 3,
1051
+ maxLength: 100
1052
+ });
1053
+ addRequiredStringField(properties, required, task, "description", {
1054
+ title: "Description",
1055
+ description: "Detailed description of the task.",
1056
+ minLength: 1
1057
+ });
1058
+ if (!task.status) {
1059
+ properties.status = {
1060
+ type: "string",
1061
+ title: "Status",
1062
+ description: "Initial task status.",
1063
+ enum: ["backlog", "pending"],
1064
+ default: "backlog"
1065
+ };
1066
+ }
1067
+ if (task.priority === void 0) {
1068
+ properties.priority = {
1069
+ type: "integer",
1070
+ title: "Priority",
1071
+ description: "Task priority from 1 to 5.",
1072
+ minimum: 1,
1073
+ maximum: 5,
1074
+ default: 3
1075
+ };
1076
+ }
1077
+ return {
1078
+ type: "object",
1079
+ properties,
1080
+ required
1081
+ };
1082
+ }
1083
+ function addRequiredStringField(properties, required, task, field, schema) {
1084
+ if (typeof task[field] === "string" && task[field].trim()) {
1085
+ return;
1086
+ }
1087
+ properties[field] = {
1088
+ type: "string",
1089
+ ...schema
1090
+ };
1091
+ required.push(field);
1092
+ }
1093
+ async function handleTaskUpdate(args, storage, vectors2) {
1094
+ const updateData = TaskUpdateSchema.parse(args);
1095
+ const { repo, id, ids, comment, force, ...updates } = updateData;
1096
+ const targetIds = ids || (id ? [id] : []);
1097
+ if (targetIds.length === 0) {
1098
+ throw new Error("Either 'id' or 'ids' must be provided for update");
1099
+ }
1100
+ let updatedCount = 0;
1101
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1102
+ const isStatusChangingGlobal = updates.status !== void 0;
1103
+ for (const targetId of targetIds) {
1104
+ const existingTask = storage.tasks.getTaskById(targetId);
1105
+ if (!existingTask) {
1106
+ if (id) throw new Error(`Task not found: ${targetId}`);
1107
+ continue;
1108
+ }
1109
+ const isStatusChanging = isStatusChangingGlobal && updates.status !== existingTask.status;
1110
+ if (isStatusChanging && !force) {
1111
+ if (!comment || comment.trim() === "") {
1112
+ throw new Error("comment is required when changing task status");
1113
+ }
1114
+ if ((existingTask.status === "backlog" || existingTask.status === "pending" || existingTask.status === "blocked") && updates.status === "completed") {
1115
+ throw new Error(`Cannot transition task ${targetId} from '${existingTask.status}' directly to 'completed'. Must be 'in_progress' first.`);
1116
+ }
1117
+ }
1118
+ if (updates.status === "completed" && isStatusChanging && updates.est_tokens === void 0) {
1119
+ throw new Error("est_tokens is required when changing task status to completed");
1120
+ }
1121
+ if (updates.task_code && storage.tasks.isTaskCodeDuplicate(repo, updates.task_code, targetId)) {
1122
+ throw new Error(`Duplicate task_code: '${updates.task_code}' already exists`);
1123
+ }
1124
+ const finalUpdates = { ...updates };
1125
+ if (updates.status === "completed") finalUpdates.finished_at = now;
1126
+ else if (updates.status === "canceled") finalUpdates.canceled_at = now;
1127
+ else if (updates.status === "in_progress" && existingTask.status !== "in_progress") finalUpdates.in_progress_at = now;
1128
+ storage.tasks.updateTask(targetId, finalUpdates);
1129
+ if (comment !== void 0 || isStatusChanging) {
1130
+ storage.tasks.insertTaskComment({
1131
+ id: randomUUID2(),
1132
+ task_id: targetId,
1133
+ repo,
1134
+ comment: comment || `Status updated to ${updates.status}`,
1135
+ agent: updates.agent || existingTask.agent || "unknown",
1136
+ role: updates.role || existingTask.role || "unknown",
1137
+ model: updates.model || "unknown",
1138
+ previous_status: isStatusChanging ? existingTask.status : null,
1139
+ next_status: isStatusChanging ? updates.status : null,
1140
+ created_at: now
1141
+ });
1142
+ }
1143
+ if (updates.status === "completed" && existingTask.status !== "completed") {
1144
+ await archiveTaskToMemory(targetId, repo, storage, vectors2);
1145
+ }
1146
+ updatedCount++;
1147
+ }
1148
+ return createMcpResponse(
1149
+ {
1150
+ success: true,
1151
+ id: id || void 0,
1152
+ ids: ids || void 0,
1153
+ repo,
1154
+ status: updates.status,
1155
+ updatedCount,
1156
+ updatedFields: Object.keys(updates)
1157
+ },
1158
+ `Updated ${updatedCount} task(s) in repo "${repo}".`
1159
+ );
1160
+ }
1161
+ async function handleTaskDelete(args, storage) {
1162
+ const { repo, id, ids } = TaskDeleteSchema.parse(args);
1163
+ const targetIds = ids || (id ? [id] : []);
1164
+ if (targetIds.length === 0) {
1165
+ throw new Error("Either 'id' or 'ids' must be provided for deletion");
1166
+ }
1167
+ for (const targetId of targetIds) {
1168
+ storage.tasks.deleteTask(targetId);
1169
+ }
1170
+ return createMcpResponse(
1171
+ {
1172
+ success: true,
1173
+ id: id || void 0,
1174
+ ids: ids || void 0,
1175
+ repo,
1176
+ deletedCount: targetIds.length
1177
+ },
1178
+ `Deleted ${targetIds.length} task(s) from repo "${repo}".`
1179
+ );
1180
+ }
1181
+
1182
+ // src/mcp/tools/memory.synthesize.ts
1183
+ async function handleMemorySynthesize(params, db2, vectors2, options = {}) {
1184
+ const validated = MemorySynthesizeSchema.parse(params);
1185
+ const session2 = options.session;
1186
+ if (!options.sampleMessage || !session2?.supportsSampling) {
1187
+ throw new Error("Client does not advertise MCP sampling support");
1188
+ }
1189
+ const repo = await resolveRepository(validated.repo, session2, options.elicit);
1190
+ if (!repo) {
1191
+ throw new Error("repo is required when repo cannot be inferred from active MCP roots");
1192
+ }
1193
+ const recap = await handleMemoryRecap({ repo, limit: 8, offset: 0 }, db2);
1194
+ const recapText = getPrimaryTextContent(recap);
1195
+ const summary = validated.include_summary ? db2.summaries.getSummary(repo)?.summary : "";
1196
+ const taskSnapshot = validated.include_tasks ? await handleTaskList({ repo, status: "backlog,pending,in_progress,blocked", limit: 15, offset: 0 }, db2) : null;
1197
+ const taskText = taskSnapshot ? getPrimaryTextContent(taskSnapshot) : "";
1198
+ const systemPrompt = [
1199
+ "You are a repository memory synthesizer.",
1200
+ "Answer strictly from grounded MCP context and tool results.",
1201
+ "If memory is insufficient, say so explicitly instead of inventing details.",
1202
+ "Prefer concise, technical answers with explicit caveats when evidence is incomplete."
1203
+ ].join(" ");
1204
+ const contextBlock = [
1205
+ `Repository: ${repo}`,
1206
+ validated.current_file_path ? `Current file: ${validated.current_file_path}` : "",
1207
+ summary ? `Summary:
1208
+ ${summary}` : "",
1209
+ recapText ? `Recent context:
1210
+ ${recapText}` : "",
1211
+ taskText ? `Active tasks:
1212
+ ${taskText}` : ""
1213
+ ].filter(Boolean).join("\n\n");
1214
+ const messages = [
1215
+ {
1216
+ role: "user",
1217
+ content: {
1218
+ type: "text",
1219
+ text: `Objective: ${validated.objective}
1220
+
1221
+ Grounding context:
1222
+ ${contextBlock || "No additional context provided."}`
1223
+ }
1224
+ }
1225
+ ];
1226
+ const toolDefinitions = buildSamplingTools(session2, validated.use_tools);
1227
+ let lastResponse = null;
1228
+ let totalToolCalls = 0;
1229
+ let iterations = 0;
1230
+ while (iterations < validated.max_iterations) {
1231
+ iterations += 1;
1232
+ const response = await options.sampleMessage({
1233
+ messages,
1234
+ systemPrompt,
1235
+ maxTokens: validated.max_tokens,
1236
+ tools: toolDefinitions.length > 0 ? toolDefinitions : void 0,
1237
+ toolChoice: toolDefinitions.length > 0 ? { mode: iterations === validated.max_iterations ? "none" : "auto" } : void 0,
1238
+ modelPreferences: {
1239
+ intelligencePriority: 0.9,
1240
+ speedPriority: 0.4
1241
+ }
1242
+ });
1243
+ lastResponse = response;
1244
+ messages.push({
1245
+ role: "assistant",
1246
+ content: response.content
1247
+ });
1248
+ const toolUses = extractToolUses(response.content);
1249
+ if (toolUses.length === 0) {
1250
+ break;
1251
+ }
1252
+ totalToolCalls += toolUses.length;
1253
+ const toolResults = await Promise.all(
1254
+ toolUses.map(async (toolUse) => ({
1255
+ type: "tool_result",
1256
+ toolUseId: toolUse.id,
1257
+ content: [
1258
+ {
1259
+ type: "text",
1260
+ text: await executeSamplingTool(toolUse.name, toolUse.input, db2, vectors2)
1261
+ }
1262
+ ]
1263
+ }))
1264
+ );
1265
+ messages.push({
1266
+ role: "user",
1267
+ content: toolResults
1268
+ });
1269
+ }
1270
+ const answer = lastResponse ? extractTextFromContent(lastResponse.content).trim() : "";
1271
+ if (!answer) {
1272
+ throw new Error("Sampling did not return a final text answer");
1273
+ }
1274
+ logger.info("[MCP] memory.synthesize", {
1275
+ repo,
1276
+ objective: validated.objective,
1277
+ iterations,
1278
+ toolCalls: totalToolCalls
1279
+ });
1280
+ return createMcpResponse(
1281
+ {
1282
+ repo,
1283
+ objective: validated.objective,
1284
+ answer,
1285
+ model: lastResponse?.model,
1286
+ stopReason: lastResponse?.stopReason,
1287
+ iterations,
1288
+ toolCalls: totalToolCalls
1289
+ },
1290
+ `Synthesized answer for "${validated.objective}" using repository "${repo}".`,
1291
+ {
1292
+ structuredContentPathHint: "answer"
1293
+ }
1294
+ );
1295
+ }
1296
+ async function resolveRepository(repo, session2, elicit) {
1297
+ if (repo) return normalizeRepo(repo);
1298
+ const inferredRepo = inferRepoFromSession(session2);
1299
+ if (inferredRepo) return normalizeRepo(inferredRepo);
1300
+ if (!session2?.supportsElicitationForm || !elicit) {
1301
+ return void 0;
1302
+ }
1303
+ const elicited = extractAcceptedElicitationContent(
1304
+ await elicit({
1305
+ mode: "form",
1306
+ message: "Repository tidak bisa diinfer dari roots aktif. Pilih repository yang ingin disintesis.",
1307
+ requestedSchema: {
1308
+ type: "object",
1309
+ properties: {
1310
+ repo: {
1311
+ type: "string",
1312
+ title: "Repository",
1313
+ description: "Nama repository yang akan dipakai untuk sintesis memori.",
1314
+ minLength: 1
1315
+ }
1316
+ },
1317
+ required: ["repo"]
1318
+ }
1319
+ })
1320
+ );
1321
+ return typeof elicited.repo === "string" && elicited.repo.trim() ? normalizeRepo(elicited.repo.trim()) : void 0;
1322
+ }
1323
+ function buildSamplingTools(session2, useTools) {
1324
+ if (!useTools || !session2?.supportsSamplingTools) {
1325
+ return [];
1326
+ }
1327
+ return [
1328
+ {
1329
+ name: "memory_search",
1330
+ description: "Search local repository memories for relevant context.",
1331
+ inputSchema: {
1332
+ type: "object",
1333
+ properties: {
1334
+ repo: { type: "string" },
1335
+ query: { type: "string" },
1336
+ limit: { type: "number", minimum: 1, maximum: 10 }
1337
+ },
1338
+ required: ["repo", "query"]
1339
+ }
1340
+ },
1341
+ {
1342
+ name: "memory_recap",
1343
+ description: "Fetch a recap of the most recent memories and active tasks.",
1344
+ inputSchema: {
1345
+ type: "object",
1346
+ properties: {
1347
+ repo: { type: "string" },
1348
+ limit: { type: "number", minimum: 1, maximum: 20 }
1349
+ },
1350
+ required: ["repo"]
1351
+ }
1352
+ },
1353
+ {
1354
+ name: "task_list",
1355
+ description: "List tasks by status or search term for the repository.",
1356
+ inputSchema: {
1357
+ type: "object",
1358
+ properties: {
1359
+ repo: { type: "string" },
1360
+ status: { type: "string" },
1361
+ search: { type: "string" },
1362
+ limit: { type: "number", minimum: 1, maximum: 20 }
1363
+ },
1364
+ required: ["repo"]
1365
+ }
1366
+ }
1367
+ ];
1368
+ }
1369
+ async function executeSamplingTool(toolName, rawInput, db2, vectors2) {
1370
+ switch (toolName) {
1371
+ case "memory_search": {
1372
+ const response = await handleMemorySearch(
1373
+ {
1374
+ repo: rawInput.repo,
1375
+ query: rawInput.query,
1376
+ limit: rawInput.limit ?? 5
1377
+ },
1378
+ db2,
1379
+ vectors2
1380
+ );
1381
+ return getPrimaryTextContent(response);
1382
+ }
1383
+ case "memory_recap": {
1384
+ const response = await handleMemoryRecap(
1385
+ {
1386
+ repo: rawInput.repo,
1387
+ limit: rawInput.limit ?? 8,
1388
+ offset: 0
1389
+ },
1390
+ db2
1391
+ );
1392
+ return getPrimaryTextContent(response);
1393
+ }
1394
+ case "task_list": {
1395
+ const response = await handleTaskList(
1396
+ {
1397
+ repo: rawInput.repo,
1398
+ status: rawInput.status,
1399
+ search: rawInput.search,
1400
+ limit: rawInput.limit ?? 10,
1401
+ offset: 0
1402
+ },
1403
+ db2
1404
+ );
1405
+ return getPrimaryTextContent(response);
1406
+ }
1407
+ default:
1408
+ throw new Error(`Unsupported sampling tool: ${toolName}`);
1409
+ }
1410
+ }
1411
+
1412
+ // src/mcp/tools/memory.delete.ts
1413
+ async function handleMemoryDelete(params, db2, vectors2, onProgress) {
1414
+ const { id, ids, repo } = MemoryDeleteSchema.parse(params);
1415
+ const targetIds = ids || (id ? [id] : []);
1416
+ if (targetIds.length === 0) {
1417
+ throw new Error("Either 'id' or 'ids' must be provided for deletion");
1418
+ }
1419
+ let deletedCount = 0;
1420
+ let lastRepo = repo || "unknown";
1421
+ const total = targetIds.length;
1422
+ let progress = 0;
1423
+ for (const targetId of targetIds) {
1424
+ if (onProgress) {
1425
+ onProgress(progress, total);
1426
+ }
1427
+ const existing = db2.memories.getById(targetId);
1428
+ if (existing) {
1429
+ lastRepo = existing.scope.repo;
1430
+ db2.memories.delete(targetId);
1431
+ await vectors2.remove(targetId);
1432
+ deletedCount++;
1433
+ } else if (id) {
1434
+ throw new Error(`Memory not found: ${targetId}`);
1435
+ }
1436
+ progress++;
1437
+ }
1438
+ if (onProgress) {
1439
+ onProgress(progress, total);
1440
+ }
1441
+ logger.info("[MCP] memory.delete", { repo: lastRepo, count: deletedCount });
1442
+ return createMcpResponse(
1443
+ {
1444
+ success: true,
1445
+ id: id || void 0,
1446
+ ids: ids || void 0,
1447
+ repo: lastRepo,
1448
+ deletedCount
1449
+ },
1450
+ `Deleted ${deletedCount} memory entry(ies) from repo "${lastRepo}".`,
1451
+ {
1452
+ structuredContentPathHint: "deletedCount",
1453
+ resourceLinks: [
1454
+ {
1455
+ uri: `repository://${encodeURIComponent(lastRepo)}/memories`,
1456
+ name: `Memory Index (${lastRepo})`,
1457
+ description: "Repository memory index after deletion",
1458
+ mimeType: "application/json",
1459
+ annotations: {
1460
+ audience: ["assistant"],
1461
+ priority: 0.5
1462
+ }
1463
+ }
1464
+ ]
1465
+ }
1466
+ );
1467
+ }
1468
+
1469
+ // src/mcp/tools/memory.acknowledge.ts
1470
+ async function handleMemoryAcknowledge(params, db2) {
1471
+ const validated = MemoryAcknowledgeSchema.parse(params);
1472
+ const memory = db2.memories.getById(validated.memory_id);
1473
+ if (!memory) {
1474
+ throw new Error(`Memory with ID ${validated.memory_id} not found.`);
1475
+ }
1476
+ if (validated.status === "used") {
1477
+ db2.memories.incrementRecallCount(memory.id);
1478
+ logger.info("[MCP] memory.acknowledge - used", { id: memory.id, context: validated.application_context });
1479
+ } else if (validated.status === "contradictory") {
1480
+ logger.warn("[MCP] memory.acknowledge - contradiction reported", {
1481
+ id: memory.id,
1482
+ context: validated.application_context
1483
+ });
1484
+ } else {
1485
+ logger.info("[MCP] memory.acknowledge - irrelevant", { id: memory.id, context: validated.application_context });
1486
+ }
1487
+ return createMcpResponse(
1488
+ {
1489
+ success: true,
1490
+ id: memory.id,
1491
+ status: validated.status
1492
+ },
1493
+ `Acknowledged memory ${memory.id} as "${validated.status}".`,
1494
+ {
1495
+ structuredContentPathHint: "status"
1496
+ }
1497
+ );
1498
+ }
1499
+
1500
+ // src/mcp/tools/memory.detail.ts
1501
+ async function handleMemoryDetail(args, storage) {
1502
+ const { id } = MemoryDetailSchema.parse(args);
1503
+ const memory = storage.memories.getById(id);
1504
+ if (!memory) {
1505
+ throw new Error(`Memory not found: ${id}`);
1506
+ }
1507
+ storage.memories.incrementHitCount(id);
1508
+ const summary = `Memory [${memory.type}] ${memory.title}: ${memory.content.substring(0, 100)}${memory.content.length > 100 ? "..." : ""}`;
1509
+ return createMcpResponse(memory, summary, {
1510
+ contentSummary: summary,
1511
+ includeSerializedStructuredContent: true,
1512
+ resourceLinks: [
1513
+ {
1514
+ uri: `memory://${id}`,
1515
+ name: `Memory: ${memory.title}`,
1516
+ mimeType: "application/json"
1517
+ }
1518
+ ]
1519
+ });
1520
+ }
1521
+
1522
+ // src/mcp/tools/task.get.ts
1523
+ async function handleTaskGet(args, storage) {
1524
+ const { repo, id, task_code } = TaskGetSchema.parse(args);
1525
+ let task;
1526
+ if (id) {
1527
+ task = storage.tasks.getTaskById(id);
1528
+ } else if (task_code) {
1529
+ task = storage.tasks.getTaskByCode(repo, task_code);
1530
+ } else {
1531
+ throw new Error("Either id or task_code must be provided");
1532
+ }
1533
+ if (!task) {
1534
+ throw new Error(`Task not found: ${id || task_code} in repo ${repo}`);
1535
+ }
1536
+ const summary = `Task [${task.task_code}] ${task.title} (${task.status})`;
1537
+ return createMcpResponse(task, summary, {
1538
+ contentSummary: summary,
1539
+ includeSerializedStructuredContent: true,
1540
+ resourceLinks: [
1541
+ {
1542
+ uri: `task://${task.id}`,
1543
+ name: `Task: ${task.title}`,
1544
+ mimeType: "application/json"
1545
+ }
1546
+ ]
1547
+ });
1548
+ }
1549
+
1550
+ // src/mcp/router.ts
1551
+ function createRouter(db2, vectors2, options) {
1552
+ const getSessionContext = options?.getSessionContext;
1553
+ async function handleMethod2(method, params, signal, onProgress) {
1554
+ switch (method) {
1555
+ // ---- tools ----
1556
+ case "tools/list":
1557
+ return listTools(getSessionContext?.(), params);
1558
+ case "tools/call":
1559
+ return await handleToolCall(
1560
+ params,
1561
+ params?.signal,
1562
+ onProgress
1563
+ );
1564
+ // ---- resources ----
1565
+ case "resources/list":
1566
+ return listResources(getSessionContext?.(), params);
1567
+ case "resources/templates/list":
1568
+ return listResourceTemplates(params);
1569
+ case "resources/read":
1570
+ return readResource(params?.uri, db2, getSessionContext?.());
1571
+ // ---- prompts ----
1572
+ case "prompts/list":
1573
+ return listPrompts(db2, getSessionContext?.(), params);
1574
+ case "logging/setLevel": {
1575
+ const requestedLevel = typeof params?.level === "string" ? params.level : "";
1576
+ const previousLevel = getLogLevel();
1577
+ const level = setLogLevel(requestedLevel);
1578
+ return {
1579
+ level,
1580
+ supportedLevels: LOG_LEVEL_VALUES,
1581
+ previousLevel
1582
+ };
1583
+ }
1584
+ case "prompts/get": {
1585
+ return getPrompt(params?.name, params?.arguments || {}, db2, getSessionContext?.());
1586
+ }
1587
+ case "completion/complete":
1588
+ return complete(params, db2, getSessionContext?.());
1589
+ default:
1590
+ throw new Error(`Unsupported method: ${method}`);
1591
+ }
1592
+ }
1593
+ async function handleToolCall(params, signal, onProgress) {
1594
+ const { name } = params || {};
1595
+ const args = normalizeToolArguments(params?.arguments, getSessionContext?.());
1596
+ const toolName = String(name).replace(/\./g, "-");
1597
+ let result;
1598
+ const repo = args?.repo || args?.scope?.repo || "unknown";
1599
+ switch (toolName) {
1600
+ case "memory-store":
1601
+ result = await handleMemoryStore(args, db2, vectors2);
1602
+ break;
1603
+ case "memory-acknowledge":
1604
+ result = await handleMemoryAcknowledge(args, db2);
1605
+ break;
1606
+ case "memory-update":
1607
+ result = await handleMemoryUpdate(args, db2, vectors2);
1608
+ break;
1609
+ case "memory-recap":
1610
+ result = await handleMemoryRecap(args, db2);
1611
+ break;
1612
+ case "memory-search":
1613
+ result = await handleMemorySearch(args, db2, vectors2);
1614
+ break;
1615
+ case "memory-summarize":
1616
+ result = await handleMemorySummarize(args, db2);
1617
+ break;
1618
+ case "memory-synthesize":
1619
+ result = await handleMemorySynthesize(args, db2, vectors2, {
1620
+ session: getSessionContext?.(),
1621
+ sampleMessage: options?.sampleMessage,
1622
+ elicit: options?.elicit
1623
+ });
1624
+ break;
1625
+ case "memory-delete":
1626
+ case "memory-bulk-delete":
1627
+ result = await handleMemoryDelete(args, db2, vectors2, onProgress);
1628
+ break;
1629
+ case "memory-detail":
1630
+ result = await handleMemoryDetail(args, db2);
1631
+ break;
1632
+ case "task-create":
1633
+ result = await handleTaskCreate(args, db2);
1634
+ break;
1635
+ case "task-create-interactive":
1636
+ result = await handleTaskCreateInteractive(args, db2, {
1637
+ session: getSessionContext?.(),
1638
+ elicit: options?.elicit
1639
+ });
1640
+ break;
1641
+ case "task-update":
1642
+ result = await handleTaskUpdate(args, db2, vectors2);
1643
+ break;
1644
+ case "task-delete":
1645
+ result = await handleTaskDelete(args, db2);
1646
+ break;
1647
+ case "task-list":
1648
+ result = await handleTaskList(args, db2);
1649
+ break;
1650
+ case "task-detail":
1651
+ result = await handleTaskGet(args, db2);
1652
+ break;
1653
+ default:
1654
+ throw new Error(`Unknown tool: ${name}`);
1655
+ }
1656
+ try {
1657
+ const actionType = toolName.split("-")[1] || toolName;
1658
+ const res = result;
1659
+ const sc = res?.structuredData;
1660
+ const logOptions = {
1661
+ query: args?.query || args?.title || args?.task_code || (toolName === "memory-recap" ? `Offset: ${args?.offset || 0}` : void 0),
1662
+ response: result,
1663
+ memoryId: args?.id || args?.memory_id || sc?.id,
1664
+ taskId: args?.id || args?.task_id || sc?.id,
1665
+ resultCount: Array.isArray(sc?.results) ? sc.results.length : sc?.count || 0
1666
+ };
1667
+ db2.actions.logAction(actionType, repo, logOptions);
1668
+ } catch (e) {
1669
+ logger.error("Failed to log action", { toolName, error: String(e) });
1670
+ }
1671
+ const affectedResources = collectAffectedResourceUris(toolName, args, result);
1672
+ if (affectedResources.length > 0) {
1673
+ options?.onResourcesMutated?.(affectedResources);
1674
+ }
1675
+ return result;
1676
+ }
1677
+ return handleMethod2;
1678
+ }
1679
+ function listTools(session2, params) {
1680
+ const tools = getAvailableToolDefinitions(session2);
1681
+ const limit = normalizePageLimit(params?.limit, tools.length || 1);
1682
+ const start = decodeCursor(params?.cursor);
1683
+ const compliantTools = tools.map((tool) => {
1684
+ const { name, description, inputSchema } = tool;
1685
+ return { name, description, inputSchema };
1686
+ });
1687
+ const page = compliantTools.slice(start, start + limit);
1688
+ const nextCursor = start + limit < tools.length ? encodeCursor(start + limit) : void 0;
1689
+ return {
1690
+ tools: page,
1691
+ nextCursor
1692
+ };
1693
+ }
1694
+ function getAvailableToolDefinitions(session2) {
1695
+ return TOOL_DEFINITIONS.filter((tool) => {
1696
+ if (tool.name === "memory-synthesize" && !session2?.supportsSampling) {
1697
+ return false;
1698
+ }
1699
+ if (tool.name === "task-create-interactive" && !session2?.supportsElicitationForm) {
1700
+ return false;
1701
+ }
1702
+ return true;
1703
+ });
1704
+ }
1705
+ function collectAffectedResourceUris(toolName, args, result) {
1706
+ const res = result;
1707
+ const repo = args?.repo || args?.scope?.repo || res?.data?.repo;
1708
+ const uris = /* @__PURE__ */ new Set();
1709
+ const touchesMemory = toolName.startsWith("memory-") || toolName === "task-update" || toolName === "task-delete";
1710
+ const touchesTasks = toolName.startsWith("task-");
1711
+ if (touchesMemory && repo) {
1712
+ uris.add(`repository://${encodeURIComponent(repo)}/memories`);
1713
+ }
1714
+ if (touchesTasks && repo) {
1715
+ uris.add(`repository://${encodeURIComponent(repo)}/tasks`);
1716
+ }
1717
+ if (repo) {
1718
+ uris.add("repository://index");
1719
+ }
1720
+ const memoryId = args?.id || args?.memory_id || res?.data?.id;
1721
+ if (typeof memoryId === "string" && /^[0-9a-f-]{36}$/i.test(memoryId) && toolName.startsWith("memory-")) {
1722
+ uris.add(`memory://${memoryId}`);
1723
+ }
1724
+ const taskId = args?.id || args?.task_id || res?.structuredData?.id;
1725
+ if (typeof taskId === "string" && /^[0-9a-f-]{36}$/i.test(taskId) && toolName.startsWith("task-")) {
1726
+ uris.add(`task://${taskId}`);
1727
+ }
1728
+ return [...uris];
1729
+ }
1730
+ function normalizeToolArguments(args, session2) {
1731
+ if (!args || typeof args !== "object") {
1732
+ return args;
1733
+ }
1734
+ const anyArgs = args;
1735
+ const nextArgs = {
1736
+ ...anyArgs,
1737
+ scope: anyArgs.scope ? { ...anyArgs.scope } : void 0
1738
+ };
1739
+ validateRootBoundPath(nextArgs.current_file_path, "current_file_path", session2);
1740
+ validateRootBoundPath(nextArgs.doc_path, "doc_path", session2);
1741
+ if (!nextArgs.repo) {
1742
+ nextArgs.repo = inferRepoFromSession(session2);
1743
+ }
1744
+ const scope = nextArgs.scope;
1745
+ if (scope && !scope.repo) {
1746
+ scope.repo = nextArgs.repo ?? inferRepoFromSession(session2);
1747
+ }
1748
+ if (typeof nextArgs.current_file_path === "string" && scope) {
1749
+ const containingRoot = path2.isAbsolute(nextArgs.current_file_path) ? findContainingRoot(nextArgs.current_file_path, session2) : null;
1750
+ if (containingRoot) {
1751
+ const relativePath = path2.relative(containingRoot, path2.resolve(nextArgs.current_file_path));
1752
+ const relativeFolder = path2.dirname(relativePath);
1753
+ if (relativeFolder && relativeFolder !== "." && !scope.folder) {
1754
+ scope.folder = relativeFolder;
1755
+ }
1756
+ }
1757
+ }
1758
+ return nextArgs;
1759
+ }
1760
+ function validateRootBoundPath(value, field, session2) {
1761
+ if (typeof value !== "string" || !path2.isAbsolute(value)) {
1762
+ return;
1763
+ }
1764
+ if (!isPathWithinRoots(value, session2)) {
1765
+ throw new Error(`${field} must stay within the active MCP roots`);
1766
+ }
1767
+ }
1768
+ function normalizePageLimit(value, fallback) {
1769
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
1770
+ return Math.max(1, fallback);
1771
+ }
1772
+ return Math.min(value, 100);
1773
+ }
1774
+
1775
+ // src/mcp/storage/vectors.ts
1776
+ import { pipeline } from "@xenova/transformers";
1777
+ var RealVectorStore = class {
1778
+ db;
1779
+ extractor = null;
1780
+ modelName = "Xenova/all-MiniLM-L6-v2";
1781
+ constructor(db2) {
1782
+ this.db = db2;
1783
+ }
1784
+ /**
1785
+ * Triggers background loading of the vector model.
1786
+ * Useful for avoiding timeouts on the first search/upsert request.
1787
+ */
1788
+ async initialize() {
1789
+ await this.getExtractor();
1790
+ }
1791
+ async getExtractor() {
1792
+ if (!this.extractor) {
1793
+ this.extractor = await pipeline("feature-extraction", this.modelName);
1794
+ }
1795
+ return this.extractor;
1796
+ }
1797
+ cosineSimilarity(v1, v2) {
1798
+ let dotProduct = 0;
1799
+ let mag1 = 0;
1800
+ let mag2 = 0;
1801
+ for (let i = 0; i < v1.length; i++) {
1802
+ dotProduct += v1[i] * v2[i];
1803
+ mag1 += v1[i] * v1[i];
1804
+ mag2 += v2[i] * v2[i];
1805
+ }
1806
+ const mag = Math.sqrt(mag1) * Math.sqrt(mag2);
1807
+ return mag === 0 ? 0 : dotProduct / mag;
1808
+ }
1809
+ async upsert(id, text) {
1810
+ try {
1811
+ const extractor = await this.getExtractor();
1812
+ const output = await extractor(text, { pooling: "mean", normalize: true });
1813
+ const vector = Array.from(output.data);
1814
+ this.db.memories.upsertVectorEmbedding(id, vector);
1815
+ } catch (error) {
1816
+ logger.error("[Vectors] Error during upsert", { id, error: String(error) });
1817
+ throw error;
1818
+ }
1819
+ }
1820
+ async remove(id) {
1821
+ if (!id) return;
1822
+ }
1823
+ async search(query, limit, repo) {
17
1824
  try {
18
- const stats = db.getStats();
19
- process.stderr.write(`📊 Memory Count: ${stats.total} entries\n`);
20
- process.stderr.write(`✅ SQLite Connection: Functional\n`);
21
- }
22
- catch (err) {
23
- process.stderr.write(`❌ SQLite Connection: Failed (${String(err)})\n`);
24
- }
25
- process.stderr.write(`🤖 AI Model: Xenova/all-MiniLM-L6-v2\n`);
26
- process.stderr.write(`⚙️ Mode: Local-First (ONNX Runtime)\n`);
27
- const isAutoArchive = process.env.ENABLE_AUTO_ARCHIVE === "true";
28
- process.stderr.write(`📉 Auto-Archive: ${isAutoArchive ? "Enabled" : "Disabled (Default)"}\n`);
29
- process.stderr.write("\n✨ Diagnosis complete.\n\n");
30
- process.exit(0);
31
- }
32
- // Create storage instances
33
- const db = new SQLiteStore();
34
- const vectors = new RealVectorStore(db);
35
- // Pre-load vector model in background to avoid initial request timeout
36
- vectors.initialize().catch(err => {
37
- logger.warn("[Server] Initial vector model loading failed. Will retry on first use.", { error: String(err) });
1825
+ const extractor = await this.getExtractor();
1826
+ const output = await extractor(query, { pooling: "mean", normalize: true });
1827
+ const queryVector = Array.from(output.data);
1828
+ const rows = this.db.memories.getVectorCandidates(repo, 100);
1829
+ const results = rows.map((row) => {
1830
+ const memoryVector = JSON.parse(row.vector);
1831
+ return {
1832
+ id: row.memory_id,
1833
+ score: this.cosineSimilarity(queryVector, memoryVector)
1834
+ };
1835
+ });
1836
+ return results.sort((a, b) => b.score - a.score).slice(0, limit);
1837
+ } catch (error) {
1838
+ logger.error("[Vectors] Error during search", { error: String(error) });
1839
+ return [];
1840
+ }
1841
+ }
1842
+ };
1843
+
1844
+ // src/mcp/server.ts
1845
+ import fs2 from "fs";
1846
+ if (process.argv.includes("doctor")) {
1847
+ process.stderr.write("\n\u{1F3E5} MCP Local Memory - System Diagnosis\n\n");
1848
+ const db2 = new SQLiteStore();
1849
+ const dbPath = db2.getDbPath();
1850
+ process.stderr.write(`\u{1F4C2} Database Path: ${dbPath}
1851
+ `);
1852
+ process.stderr.write(`\u{1F4C4} Database Status: ${fs2.existsSync(dbPath) ? "\u2705 Exists" : "\u274C Not Found"}
1853
+ `);
1854
+ try {
1855
+ const stats = db2.system.getStats();
1856
+ process.stderr.write(`\u{1F4CA} Memory Count: ${stats.total} entries
1857
+ `);
1858
+ process.stderr.write(`\u2705 SQLite Connection: Functional
1859
+ `);
1860
+ } catch (err) {
1861
+ process.stderr.write(`\u274C SQLite Connection: Failed (${String(err)})
1862
+ `);
1863
+ }
1864
+ process.stderr.write(`\u{1F916} AI Model: Xenova/all-MiniLM-L6-v2
1865
+ `);
1866
+ process.stderr.write(`\u2699\uFE0F Mode: Local-First (ONNX Runtime)
1867
+ `);
1868
+ const isAutoArchive = process.env.ENABLE_AUTO_ARCHIVE === "true";
1869
+ process.stderr.write(`\u{1F4C9} Auto-Archive: ${isAutoArchive ? "Enabled" : "Disabled (Default)"}
1870
+ `);
1871
+ process.stderr.write("\n\u2728 Diagnosis complete.\n\n");
1872
+ process.exit(0);
1873
+ }
1874
+ var db = new SQLiteStore();
1875
+ var vectors = new RealVectorStore(db);
1876
+ vectors.initialize().catch((err) => {
1877
+ logger.warn("[Server] Initial vector model loading failed. Will retry on first use.", { error: String(err) });
38
1878
  });
39
- // Optional: Automatic cleanup of expired/low-utility memories (default: disabled)
40
- // ... (rest of cleanup code) ...
41
- const expiredArchived = db.archiveExpiredMemories();
42
- const lowScoreArchived = db.archiveLowScoreMemories();
43
- const totalArchived = (expiredArchived || 0) + (lowScoreArchived || 0);
1879
+ var expiredArchived = db.memories.archiveExpiredMemories();
1880
+ var lowScoreArchived = db.memories.archiveLowScoreMemories();
1881
+ var totalArchived = (expiredArchived || 0) + (lowScoreArchived || 0);
44
1882
  if (totalArchived > 0) {
45
- logger.info(`[Server] Archived ${totalArchived} memories (expired: ${expiredArchived}, low-score: ${lowScoreArchived}) on startup.`);
1883
+ logger.info(
1884
+ `[Server] Archived ${totalArchived} memories (expired: ${expiredArchived}, low-score: ${lowScoreArchived}) on startup.`
1885
+ );
46
1886
  }
47
- // Ignore EPIPE errors on stdout/stderr (e.g. if the client disconnects prematurely)
48
- process.stdout.on('error', (err) => {
49
- if (err.code === 'EPIPE')
50
- return;
51
- logger.error("stdout error", { error: String(err) });
1887
+ process.stdout.on("error", (err) => {
1888
+ if (err.code === "EPIPE") return;
1889
+ logger.error("stdout error", { error: String(err) });
52
1890
  });
53
- process.stderr.on('error', (err) => {
54
- if (err.code === 'EPIPE')
55
- return;
56
- logger.error("stderr error", { error: String(err) });
1891
+ process.stderr.on("error", (err) => {
1892
+ if (err.code === "EPIPE") return;
1893
+ logger.error("stderr error", { error: String(err) });
57
1894
  });
58
- // Wire router with injected storage
59
- const session = createSessionContext();
60
- const resourceSubscriptions = new Set();
61
- let logNotificationsEnabled = false;
62
- const handleMethod = createRouter(db, vectors, {
63
- getSessionContext: () => session,
64
- sampleMessage: (params) => requestClient("sampling/createMessage", params),
65
- elicit: (params) => requestClient("elicitation/create", params),
66
- onResourcesMutated: (uris) => notifyUpdatedResources(uris),
1895
+ var session = createSessionContext();
1896
+ var resourceSubscriptions = /* @__PURE__ */ new Set();
1897
+ var logNotificationsEnabled = false;
1898
+ var handleMethod = createRouter(db, vectors, {
1899
+ getSessionContext: () => session,
1900
+ sampleMessage: (params) => requestClient("sampling/createMessage", params),
1901
+ elicit: (params) => requestClient("elicitation/create", params),
1902
+ onResourcesMutated: (uris) => notifyUpdatedResources(uris)
67
1903
  });
68
1904
  addLogSink((payload) => {
69
- if (!logNotificationsEnabled) {
70
- return;
71
- }
72
- reply({
73
- jsonrpc: "2.0",
74
- method: "notifications/message",
75
- params: payload,
76
- });
1905
+ if (!logNotificationsEnabled) {
1906
+ return;
1907
+ }
1908
+ reply({
1909
+ jsonrpc: "2.0",
1910
+ method: "notifications/message",
1911
+ params: payload
1912
+ });
77
1913
  });
78
- // Cleanup on exit
79
1914
  process.on("SIGINT", () => {
80
- for (const pending of pendingClientRequests.values()) {
81
- pending.reject(new Error("Server stopped"));
82
- }
83
- pendingClientRequests.clear();
84
- db.close();
85
- process.exit(0);
1915
+ for (const pending of pendingClientRequests.values()) {
1916
+ pending.reject(new Error("Server stopped"));
1917
+ }
1918
+ pendingClientRequests.clear();
1919
+ db.close();
1920
+ process.exit(0);
86
1921
  });
87
1922
  process.on("SIGTERM", () => {
88
- for (const pending of pendingClientRequests.values()) {
89
- pending.reject(new Error("Server stopped"));
90
- }
91
- pendingClientRequests.clear();
92
- db.close();
93
- process.exit(0);
1923
+ for (const pending of pendingClientRequests.values()) {
1924
+ pending.reject(new Error("Server stopped"));
1925
+ }
1926
+ pendingClientRequests.clear();
1927
+ db.close();
1928
+ process.exit(0);
94
1929
  });
95
- const rl = readline.createInterface({
96
- input: process.stdin,
97
- output: process.stdout,
98
- terminal: false
1930
+ var rl = readline.createInterface({
1931
+ input: process.stdin,
1932
+ output: process.stdout,
1933
+ terminal: false
99
1934
  });
100
1935
  function reply(payload) {
101
- try {
102
- process.stdout.write(JSON.stringify(payload) + "\n");
103
- }
104
- catch (err) {
105
- // Other errors still logged
106
- logger.error("Reply error", { error: String(err) });
107
- }
1936
+ try {
1937
+ process.stdout.write(JSON.stringify(payload) + "\n");
1938
+ } catch (err) {
1939
+ logger.error("Reply error", { error: String(err) });
1940
+ }
108
1941
  }
109
- let isInitialized = false;
110
- const activeRequests = new Map();
111
- const pendingClientRequests = new Map();
112
- let outgoingRequestId = 0;
1942
+ var isInitialized = false;
1943
+ var activeRequests = /* @__PURE__ */ new Map();
1944
+ var pendingClientRequests = /* @__PURE__ */ new Map();
1945
+ var outgoingRequestId = 0;
113
1946
  function requestClient(method, params = {}) {
114
- const id = `server:${++outgoingRequestId}`;
115
- reply({
116
- jsonrpc: "2.0",
117
- id,
118
- method,
119
- params,
120
- });
121
- return new Promise((resolve, reject) => {
122
- pendingClientRequests.set(id, { method, resolve, reject });
123
- });
1947
+ const id = `server:${++outgoingRequestId}`;
1948
+ reply({
1949
+ jsonrpc: "2.0",
1950
+ id,
1951
+ method,
1952
+ params
1953
+ });
1954
+ return new Promise((resolve, reject) => {
1955
+ pendingClientRequests.set(id, { method, resolve, reject });
1956
+ });
124
1957
  }
125
1958
  async function refreshRoots(trigger) {
126
- if (!session.supportsRoots)
127
- return;
128
- try {
129
- const result = await requestClient("roots/list");
130
- const changed = updateSessionRoots(session, extractRootsFromResult(result));
131
- logger.info("[Server] Refreshed client roots", {
132
- trigger,
133
- count: session.roots.length,
134
- changed,
135
- });
136
- if (changed) {
137
- reply({
138
- jsonrpc: "2.0",
139
- method: "notifications/resources/list_changed",
140
- });
141
- reply({
142
- jsonrpc: "2.0",
143
- method: "notifications/prompts/list_changed",
144
- });
145
- }
146
- }
147
- catch (error) {
148
- logger.warn("[Server] Failed to refresh client roots", {
149
- trigger,
150
- error: String(error),
151
- });
1959
+ if (!session.supportsRoots) return;
1960
+ try {
1961
+ const result = await requestClient("roots/list");
1962
+ const changed = updateSessionRoots(session, extractRootsFromResult(result));
1963
+ logger.info("[Server] Refreshed client roots", {
1964
+ trigger,
1965
+ count: session.roots.length,
1966
+ changed
1967
+ });
1968
+ if (changed) {
1969
+ reply({
1970
+ jsonrpc: "2.0",
1971
+ method: "notifications/resources/list_changed"
1972
+ });
1973
+ reply({
1974
+ jsonrpc: "2.0",
1975
+ method: "notifications/prompts/list_changed"
1976
+ });
152
1977
  }
1978
+ } catch (error) {
1979
+ logger.warn("[Server] Failed to refresh client roots", {
1980
+ trigger,
1981
+ error: String(error)
1982
+ });
1983
+ }
153
1984
  }
154
1985
  function notifyUpdatedResources(uris) {
155
- const seen = new Set();
156
- for (const uri of uris) {
157
- if (seen.has(uri))
158
- continue;
159
- seen.add(uri);
160
- if (!resourceSubscriptions.has(uri)) {
161
- continue;
162
- }
163
- reply({
164
- jsonrpc: "2.0",
165
- method: "notifications/resources/updated",
166
- params: { uri },
167
- });
1986
+ const seen = /* @__PURE__ */ new Set();
1987
+ for (const uri of uris) {
1988
+ if (seen.has(uri)) continue;
1989
+ seen.add(uri);
1990
+ if (!resourceSubscriptions.has(uri)) {
1991
+ continue;
168
1992
  }
1993
+ reply({
1994
+ jsonrpc: "2.0",
1995
+ method: "notifications/resources/updated",
1996
+ params: { uri }
1997
+ });
1998
+ }
169
1999
  }
170
2000
  rl.on("line", async (line) => {
171
- if (!line.trim())
172
- return;
173
- let msg;
174
- try {
175
- msg = JSON.parse(line);
176
- }
177
- catch {
178
- return;
179
- }
180
- const { id, method, params } = msg;
181
- const isNotification = id === undefined || id === null;
182
- if ((method === undefined || method === null) && id !== undefined && pendingClientRequests.has(id)) {
183
- const pending = pendingClientRequests.get(id);
184
- pendingClientRequests.delete(id);
185
- if (msg.error) {
186
- pending.reject(new Error(msg.error.message || `Client request failed: ${pending.method}`));
187
- }
188
- else {
189
- pending.resolve(msg.result);
190
- }
191
- return;
192
- }
193
- // --- initialize (Request) ---
194
- if (method === "initialize" && !isNotification) {
195
- updateSessionFromInitialize(session, params);
196
- reply({
197
- jsonrpc: "2.0",
198
- id,
199
- result: {
200
- protocolVersion: MCP_PROTOCOL_VERSION,
201
- serverInfo: CAPABILITIES.serverInfo,
202
- capabilities: CAPABILITIES.capabilities
203
- }
204
- });
205
- return;
206
- }
207
- // --- notifications ---
208
- if (isNotification) {
209
- // We ignore all notifications by default, especially standard ones
210
- if (method === "notifications/initialized") {
211
- isInitialized = true;
212
- logNotificationsEnabled = true;
213
- logger.debug("[Server] Client initialized");
214
- void refreshRoots("initialized");
215
- }
216
- else if (method === "notifications/roots/list_changed") {
217
- logger.debug("[Server] Client roots changed");
218
- void refreshRoots("roots/list_changed");
219
- }
220
- else if (method === "notifications/cancelled") {
221
- const requestId = params?.requestId;
222
- if (requestId !== undefined && activeRequests.has(requestId)) {
223
- activeRequests.get(requestId).abort();
224
- activeRequests.delete(requestId);
225
- logger.debug(`[Server] Request ${requestId} cancelled`);
226
- }
227
- else {
228
- logger.debug(`[Server] Cancelled notification for unknown or completed request ${requestId}`);
229
- }
230
- }
231
- return;
232
- }
233
- // --- ping (Request) ---
234
- if (method === "ping") {
235
- reply({
236
- jsonrpc: "2.0",
237
- id,
238
- result: {}
239
- });
240
- return;
241
- }
242
- if (method === "resources/subscribe") {
243
- const uri = typeof params?.uri === "string" ? params.uri : "";
244
- if (!uri) {
245
- reply({
246
- jsonrpc: "2.0",
247
- id,
248
- error: {
249
- code: -32602,
250
- message: "resources/subscribe requires a uri",
251
- }
252
- });
253
- return;
254
- }
255
- resourceSubscriptions.add(uri);
256
- reply({
257
- jsonrpc: "2.0",
258
- id,
259
- result: {},
260
- });
261
- return;
262
- }
263
- if (method === "resources/unsubscribe") {
264
- const uri = typeof params?.uri === "string" ? params.uri : "";
265
- if (!uri) {
266
- reply({
267
- jsonrpc: "2.0",
268
- id,
269
- error: {
270
- code: -32602,
271
- message: "resources/unsubscribe requires a uri",
272
- }
273
- });
274
- return;
275
- }
276
- resourceSubscriptions.delete(uri);
277
- reply({
278
- jsonrpc: "2.0",
279
- id,
280
- result: {},
281
- });
282
- return;
283
- }
284
- // --- Ensure initialized before processing other requests ---
285
- if (!isInitialized) {
286
- reply({
287
- jsonrpc: "2.0",
288
- id,
289
- error: {
290
- code: -32002,
291
- message: "Server is not fully initialized yet. Please send notifications/initialized."
292
- }
293
- });
294
- return;
295
- }
296
- const abortController = new AbortController();
297
- activeRequests.set(id, abortController);
298
- const progressToken = params?._meta?.progressToken;
299
- const onProgress = progressToken !== undefined ? (progress, total) => {
300
- reply({
301
- jsonrpc: "2.0",
302
- method: "notifications/progress",
303
- params: {
304
- progressToken,
305
- progress,
306
- total
307
- }
308
- });
309
- } : undefined;
310
- // --- route request ---
311
- try {
312
- const result = await handleMethod(method, params, abortController.signal, onProgress);
313
- if (!abortController.signal.aborted) {
314
- reply({
315
- jsonrpc: "2.0",
316
- id,
317
- result
318
- });
2001
+ if (!line.trim()) return;
2002
+ let msg;
2003
+ try {
2004
+ msg = JSON.parse(line);
2005
+ } catch {
2006
+ return;
2007
+ }
2008
+ const { id, method, params } = msg;
2009
+ const isNotification = id === void 0 || id === null;
2010
+ if ((method === void 0 || method === null) && id !== void 0 && pendingClientRequests.has(id)) {
2011
+ const pending = pendingClientRequests.get(id);
2012
+ pendingClientRequests.delete(id);
2013
+ if (msg.error) {
2014
+ pending.reject(new Error(msg.error.message || `Client request failed: ${pending.method}`));
2015
+ } else {
2016
+ pending.resolve(msg.result);
2017
+ }
2018
+ return;
2019
+ }
2020
+ if (method === "initialize" && !isNotification) {
2021
+ updateSessionFromInitialize(session, params);
2022
+ reply({
2023
+ jsonrpc: "2.0",
2024
+ id,
2025
+ result: {
2026
+ protocolVersion: MCP_PROTOCOL_VERSION,
2027
+ serverInfo: CAPABILITIES.serverInfo,
2028
+ capabilities: CAPABILITIES.capabilities
2029
+ }
2030
+ });
2031
+ return;
2032
+ }
2033
+ if (isNotification) {
2034
+ if (method === "notifications/initialized") {
2035
+ isInitialized = true;
2036
+ logNotificationsEnabled = true;
2037
+ logger.debug("[Server] Client initialized");
2038
+ void refreshRoots("initialized");
2039
+ } else if (method === "notifications/roots/list_changed") {
2040
+ logger.debug("[Server] Client roots changed");
2041
+ void refreshRoots("roots/list_changed");
2042
+ } else if (method === "notifications/cancelled") {
2043
+ const requestId = params?.requestId;
2044
+ if (requestId !== void 0 && activeRequests.has(requestId)) {
2045
+ activeRequests.get(requestId).abort();
2046
+ activeRequests.delete(requestId);
2047
+ logger.debug(`[Server] Request ${requestId} cancelled`);
2048
+ } else {
2049
+ logger.debug(`[Server] Cancelled notification for unknown or completed request ${requestId}`);
2050
+ }
2051
+ }
2052
+ return;
2053
+ }
2054
+ if (method === "ping") {
2055
+ reply({
2056
+ jsonrpc: "2.0",
2057
+ id,
2058
+ result: {}
2059
+ });
2060
+ return;
2061
+ }
2062
+ if (method === "resources/subscribe") {
2063
+ const uri = typeof params?.uri === "string" ? params.uri : "";
2064
+ if (!uri) {
2065
+ reply({
2066
+ jsonrpc: "2.0",
2067
+ id,
2068
+ error: {
2069
+ code: -32602,
2070
+ message: "resources/subscribe requires a uri"
319
2071
  }
2072
+ });
2073
+ return;
320
2074
  }
321
- catch (err) {
322
- if (!abortController.signal.aborted) {
323
- logger.error("Method handler error", { method, id, message: err.message });
324
- reply({
325
- jsonrpc: "2.0",
326
- id,
327
- error: {
328
- code: typeof err?.code === "number" ? err.code : -32603,
329
- message: err.message || "Internal error"
330
- }
331
- });
2075
+ resourceSubscriptions.add(uri);
2076
+ reply({
2077
+ jsonrpc: "2.0",
2078
+ id,
2079
+ result: {}
2080
+ });
2081
+ return;
2082
+ }
2083
+ if (method === "resources/unsubscribe") {
2084
+ const uri = typeof params?.uri === "string" ? params.uri : "";
2085
+ if (!uri) {
2086
+ reply({
2087
+ jsonrpc: "2.0",
2088
+ id,
2089
+ error: {
2090
+ code: -32602,
2091
+ message: "resources/unsubscribe requires a uri"
332
2092
  }
2093
+ });
2094
+ return;
333
2095
  }
334
- finally {
335
- activeRequests.delete(id);
2096
+ resourceSubscriptions.delete(uri);
2097
+ reply({
2098
+ jsonrpc: "2.0",
2099
+ id,
2100
+ result: {}
2101
+ });
2102
+ return;
2103
+ }
2104
+ if (!isInitialized) {
2105
+ reply({
2106
+ jsonrpc: "2.0",
2107
+ id,
2108
+ error: {
2109
+ code: -32002,
2110
+ message: "Server is not fully initialized yet. Please send notifications/initialized."
2111
+ }
2112
+ });
2113
+ return;
2114
+ }
2115
+ const abortController = new AbortController();
2116
+ activeRequests.set(id, abortController);
2117
+ const progressToken = params?._meta?.progressToken;
2118
+ const onProgress = progressToken !== void 0 ? (progress, total) => {
2119
+ reply({
2120
+ jsonrpc: "2.0",
2121
+ method: "notifications/progress",
2122
+ params: {
2123
+ progressToken,
2124
+ progress,
2125
+ total
2126
+ }
2127
+ });
2128
+ } : void 0;
2129
+ try {
2130
+ const result = await handleMethod(method, params, abortController.signal, onProgress);
2131
+ if (!abortController.signal.aborted) {
2132
+ reply({
2133
+ jsonrpc: "2.0",
2134
+ id,
2135
+ result
2136
+ });
2137
+ }
2138
+ } catch (err) {
2139
+ if (!abortController.signal.aborted) {
2140
+ const error = err;
2141
+ logger.error("Method handler error", { method, id, message: error.message });
2142
+ reply({
2143
+ jsonrpc: "2.0",
2144
+ id,
2145
+ error: {
2146
+ code: typeof error?.code === "number" ? error.code : -32603,
2147
+ message: error.message || "Internal error"
2148
+ }
2149
+ });
336
2150
  }
2151
+ } finally {
2152
+ activeRequests.delete(id);
2153
+ }
337
2154
  });
338
- //# sourceMappingURL=server.js.map