vikunja-mcp-ng 0.4.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 (582) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -0
  3. package/dist/auth/AuthManager.d.ts +63 -0
  4. package/dist/auth/AuthManager.d.ts.map +1 -0
  5. package/dist/auth/AuthManager.js +137 -0
  6. package/dist/auth/AuthManager.js.map +1 -0
  7. package/dist/auth/index.d.ts +7 -0
  8. package/dist/auth/index.d.ts.map +1 -0
  9. package/dist/auth/index.js +14 -0
  10. package/dist/auth/index.js.map +1 -0
  11. package/dist/auth/permissions.d.ts +60 -0
  12. package/dist/auth/permissions.d.ts.map +1 -0
  13. package/dist/auth/permissions.js +173 -0
  14. package/dist/auth/permissions.js.map +1 -0
  15. package/dist/client/VikunjaClientFactory.d.ts +41 -0
  16. package/dist/client/VikunjaClientFactory.d.ts.map +1 -0
  17. package/dist/client/VikunjaClientFactory.js +58 -0
  18. package/dist/client/VikunjaClientFactory.js.map +1 -0
  19. package/dist/client.d.ts +70 -0
  20. package/dist/client.d.ts.map +1 -0
  21. package/dist/client.js +136 -0
  22. package/dist/client.js.map +1 -0
  23. package/dist/config/ConfigurationManager.d.ts +106 -0
  24. package/dist/config/ConfigurationManager.d.ts.map +1 -0
  25. package/dist/config/ConfigurationManager.js +509 -0
  26. package/dist/config/ConfigurationManager.js.map +1 -0
  27. package/dist/config/index.d.ts +9 -0
  28. package/dist/config/index.d.ts.map +1 -0
  29. package/dist/config/index.js +36 -0
  30. package/dist/config/index.js.map +1 -0
  31. package/dist/config/secrets.d.ts +33 -0
  32. package/dist/config/secrets.d.ts.map +1 -0
  33. package/dist/config/secrets.js +90 -0
  34. package/dist/config/secrets.js.map +1 -0
  35. package/dist/config/types.d.ts +1113 -0
  36. package/dist/config/types.d.ts.map +1 -0
  37. package/dist/config/types.js +189 -0
  38. package/dist/config/types.js.map +1 -0
  39. package/dist/formatters/BatchImportResponseFormatter.d.ts +89 -0
  40. package/dist/formatters/BatchImportResponseFormatter.d.ts.map +1 -0
  41. package/dist/formatters/BatchImportResponseFormatter.js +125 -0
  42. package/dist/formatters/BatchImportResponseFormatter.js.map +1 -0
  43. package/dist/index.d.ts +20 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +130 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/middleware/direct-middleware.d.ts +9 -0
  48. package/dist/middleware/direct-middleware.d.ts.map +1 -0
  49. package/dist/middleware/direct-middleware.js +49 -0
  50. package/dist/middleware/direct-middleware.js.map +1 -0
  51. package/dist/middleware/index.d.ts +8 -0
  52. package/dist/middleware/index.d.ts.map +1 -0
  53. package/dist/middleware/index.js +21 -0
  54. package/dist/middleware/index.js.map +1 -0
  55. package/dist/middleware/simplified-rate-limit.d.ts +147 -0
  56. package/dist/middleware/simplified-rate-limit.d.ts.map +1 -0
  57. package/dist/middleware/simplified-rate-limit.js +533 -0
  58. package/dist/middleware/simplified-rate-limit.js.map +1 -0
  59. package/dist/parsers/CSVParser.d.ts +36 -0
  60. package/dist/parsers/CSVParser.d.ts.map +1 -0
  61. package/dist/parsers/CSVParser.js +69 -0
  62. package/dist/parsers/CSVParser.js.map +1 -0
  63. package/dist/parsers/InputParserFactory.d.ts +17 -0
  64. package/dist/parsers/InputParserFactory.d.ts.map +1 -0
  65. package/dist/parsers/InputParserFactory.js +137 -0
  66. package/dist/parsers/InputParserFactory.js.map +1 -0
  67. package/dist/parsers/JSONParser.d.ts +74 -0
  68. package/dist/parsers/JSONParser.d.ts.map +1 -0
  69. package/dist/parsers/JSONParser.js +69 -0
  70. package/dist/parsers/JSONParser.js.map +1 -0
  71. package/dist/services/EntityResolver.d.ts +92 -0
  72. package/dist/services/EntityResolver.d.ts.map +1 -0
  73. package/dist/services/EntityResolver.js +201 -0
  74. package/dist/services/EntityResolver.js.map +1 -0
  75. package/dist/services/TaskCreationService.d.ts +99 -0
  76. package/dist/services/TaskCreationService.d.ts.map +1 -0
  77. package/dist/services/TaskCreationService.js +392 -0
  78. package/dist/services/TaskCreationService.js.map +1 -0
  79. package/dist/storage/SimpleFilterStorage.d.ts +103 -0
  80. package/dist/storage/SimpleFilterStorage.d.ts.map +1 -0
  81. package/dist/storage/SimpleFilterStorage.js +350 -0
  82. package/dist/storage/SimpleFilterStorage.js.map +1 -0
  83. package/dist/storage/filtering/FilterSerializer.d.ts +45 -0
  84. package/dist/storage/filtering/FilterSerializer.d.ts.map +1 -0
  85. package/dist/storage/filtering/FilterSerializer.js +171 -0
  86. package/dist/storage/filtering/FilterSerializer.js.map +1 -0
  87. package/dist/storage/filtering/FilterValidator.d.ts +59 -0
  88. package/dist/storage/filtering/FilterValidator.d.ts.map +1 -0
  89. package/dist/storage/filtering/FilterValidator.js +183 -0
  90. package/dist/storage/filtering/FilterValidator.js.map +1 -0
  91. package/dist/storage/index.d.ts +61 -0
  92. package/dist/storage/index.d.ts.map +1 -0
  93. package/dist/storage/index.js +125 -0
  94. package/dist/storage/index.js.map +1 -0
  95. package/dist/storage/templateFileStore.d.ts +62 -0
  96. package/dist/storage/templateFileStore.d.ts.map +1 -0
  97. package/dist/storage/templateFileStore.js +151 -0
  98. package/dist/storage/templateFileStore.js.map +1 -0
  99. package/dist/tools/admin.d.ts +51 -0
  100. package/dist/tools/admin.d.ts.map +1 -0
  101. package/dist/tools/admin.js +207 -0
  102. package/dist/tools/admin.js.map +1 -0
  103. package/dist/tools/auth.d.ts +9 -0
  104. package/dist/tools/auth.d.ts.map +1 -0
  105. package/dist/tools/auth.js +175 -0
  106. package/dist/tools/auth.js.map +1 -0
  107. package/dist/tools/batch-import.d.ts +5 -0
  108. package/dist/tools/batch-import.d.ts.map +1 -0
  109. package/dist/tools/batch-import.js +153 -0
  110. package/dist/tools/batch-import.js.map +1 -0
  111. package/dist/tools/caldav-tokens.d.ts +45 -0
  112. package/dist/tools/caldav-tokens.d.ts.map +1 -0
  113. package/dist/tools/caldav-tokens.js +93 -0
  114. package/dist/tools/caldav-tokens.js.map +1 -0
  115. package/dist/tools/export.d.ts +14 -0
  116. package/dist/tools/export.d.ts.map +1 -0
  117. package/dist/tools/export.js +252 -0
  118. package/dist/tools/export.js.map +1 -0
  119. package/dist/tools/filters.d.ts +50 -0
  120. package/dist/tools/filters.d.ts.map +1 -0
  121. package/dist/tools/filters.js +512 -0
  122. package/dist/tools/filters.js.map +1 -0
  123. package/dist/tools/index.d.ts +53 -0
  124. package/dist/tools/index.d.ts.map +1 -0
  125. package/dist/tools/index.js +217 -0
  126. package/dist/tools/index.js.map +1 -0
  127. package/dist/tools/labels.d.ts +22 -0
  128. package/dist/tools/labels.d.ts.map +1 -0
  129. package/dist/tools/labels.js +206 -0
  130. package/dist/tools/labels.js.map +1 -0
  131. package/dist/tools/notifications.d.ts +17 -0
  132. package/dist/tools/notifications.d.ts.map +1 -0
  133. package/dist/tools/notifications.js +171 -0
  134. package/dist/tools/notifications.js.map +1 -0
  135. package/dist/tools/projects/backgrounds.d.ts +80 -0
  136. package/dist/tools/projects/backgrounds.d.ts.map +1 -0
  137. package/dist/tools/projects/backgrounds.js +154 -0
  138. package/dist/tools/projects/backgrounds.js.map +1 -0
  139. package/dist/tools/projects/buckets.d.ts +147 -0
  140. package/dist/tools/projects/buckets.d.ts.map +1 -0
  141. package/dist/tools/projects/buckets.js +291 -0
  142. package/dist/tools/projects/buckets.js.map +1 -0
  143. package/dist/tools/projects/crud.d.ts +145 -0
  144. package/dist/tools/projects/crud.d.ts.map +1 -0
  145. package/dist/tools/projects/crud.js +425 -0
  146. package/dist/tools/projects/crud.js.map +1 -0
  147. package/dist/tools/projects/duplicate.d.ts +41 -0
  148. package/dist/tools/projects/duplicate.d.ts.map +1 -0
  149. package/dist/tools/projects/duplicate.js +49 -0
  150. package/dist/tools/projects/duplicate.js.map +1 -0
  151. package/dist/tools/projects/hierarchy.d.ts +77 -0
  152. package/dist/tools/projects/hierarchy.d.ts.map +1 -0
  153. package/dist/tools/projects/hierarchy.js +300 -0
  154. package/dist/tools/projects/hierarchy.js.map +1 -0
  155. package/dist/tools/projects/index.d.ts +34 -0
  156. package/dist/tools/projects/index.d.ts.map +1 -0
  157. package/dist/tools/projects/index.js +522 -0
  158. package/dist/tools/projects/index.js.map +1 -0
  159. package/dist/tools/projects/permission.d.ts +26 -0
  160. package/dist/tools/projects/permission.d.ts.map +1 -0
  161. package/dist/tools/projects/permission.js +53 -0
  162. package/dist/tools/projects/permission.js.map +1 -0
  163. package/dist/tools/projects/response-formatter.d.ts +54 -0
  164. package/dist/tools/projects/response-formatter.d.ts.map +1 -0
  165. package/dist/tools/projects/response-formatter.js +139 -0
  166. package/dist/tools/projects/response-formatter.js.map +1 -0
  167. package/dist/tools/projects/sharing-access.d.ts +165 -0
  168. package/dist/tools/projects/sharing-access.d.ts.map +1 -0
  169. package/dist/tools/projects/sharing-access.js +450 -0
  170. package/dist/tools/projects/sharing-access.js.map +1 -0
  171. package/dist/tools/projects/sharing.d.ts +105 -0
  172. package/dist/tools/projects/sharing.d.ts.map +1 -0
  173. package/dist/tools/projects/sharing.js +258 -0
  174. package/dist/tools/projects/sharing.js.map +1 -0
  175. package/dist/tools/projects/validation.d.ts +51 -0
  176. package/dist/tools/projects/validation.d.ts.map +1 -0
  177. package/dist/tools/projects/validation.js +160 -0
  178. package/dist/tools/projects/validation.js.map +1 -0
  179. package/dist/tools/projects/views.d.ts +161 -0
  180. package/dist/tools/projects/views.d.ts.map +1 -0
  181. package/dist/tools/projects/views.js +234 -0
  182. package/dist/tools/projects/views.js.map +1 -0
  183. package/dist/tools/projects.d.ts +20 -0
  184. package/dist/tools/projects.d.ts.map +1 -0
  185. package/dist/tools/projects.js +66 -0
  186. package/dist/tools/projects.js.map +1 -0
  187. package/dist/tools/reactions.d.ts +25 -0
  188. package/dist/tools/reactions.d.ts.map +1 -0
  189. package/dist/tools/reactions.js +108 -0
  190. package/dist/tools/reactions.js.map +1 -0
  191. package/dist/tools/subscriptions.d.ts +23 -0
  192. package/dist/tools/subscriptions.d.ts.map +1 -0
  193. package/dist/tools/subscriptions.js +111 -0
  194. package/dist/tools/subscriptions.js.map +1 -0
  195. package/dist/tools/task-assignees.d.ts +13 -0
  196. package/dist/tools/task-assignees.d.ts.map +1 -0
  197. package/dist/tools/task-assignees.js +74 -0
  198. package/dist/tools/task-assignees.js.map +1 -0
  199. package/dist/tools/task-bulk.d.ts +13 -0
  200. package/dist/tools/task-bulk.d.ts.map +1 -0
  201. package/dist/tools/task-bulk.js +117 -0
  202. package/dist/tools/task-bulk.js.map +1 -0
  203. package/dist/tools/task-comments.d.ts +12 -0
  204. package/dist/tools/task-comments.d.ts.map +1 -0
  205. package/dist/tools/task-comments.js +65 -0
  206. package/dist/tools/task-comments.js.map +1 -0
  207. package/dist/tools/task-crud.d.ts +13 -0
  208. package/dist/tools/task-crud.d.ts.map +1 -0
  209. package/dist/tools/task-crud.js +168 -0
  210. package/dist/tools/task-crud.js.map +1 -0
  211. package/dist/tools/task-labels.d.ts +13 -0
  212. package/dist/tools/task-labels.d.ts.map +1 -0
  213. package/dist/tools/task-labels.js +64 -0
  214. package/dist/tools/task-labels.js.map +1 -0
  215. package/dist/tools/task-relations.d.ts +13 -0
  216. package/dist/tools/task-relations.d.ts.map +1 -0
  217. package/dist/tools/task-relations.js +74 -0
  218. package/dist/tools/task-relations.js.map +1 -0
  219. package/dist/tools/task-reminders.d.ts +13 -0
  220. package/dist/tools/task-reminders.d.ts.map +1 -0
  221. package/dist/tools/task-reminders.js +69 -0
  222. package/dist/tools/task-reminders.js.map +1 -0
  223. package/dist/tools/tasks/assignees/AssigneeOperationsService.d.ts +63 -0
  224. package/dist/tools/tasks/assignees/AssigneeOperationsService.d.ts.map +1 -0
  225. package/dist/tools/tasks/assignees/AssigneeOperationsService.js +152 -0
  226. package/dist/tools/tasks/assignees/AssigneeOperationsService.js.map +1 -0
  227. package/dist/tools/tasks/assignees/AssigneeResponseFormatter.d.ts +28 -0
  228. package/dist/tools/tasks/assignees/AssigneeResponseFormatter.d.ts.map +1 -0
  229. package/dist/tools/tasks/assignees/AssigneeResponseFormatter.js +73 -0
  230. package/dist/tools/tasks/assignees/AssigneeResponseFormatter.js.map +1 -0
  231. package/dist/tools/tasks/assignees/AssigneeValidationService.d.ts +46 -0
  232. package/dist/tools/tasks/assignees/AssigneeValidationService.d.ts.map +1 -0
  233. package/dist/tools/tasks/assignees/AssigneeValidationService.js +70 -0
  234. package/dist/tools/tasks/assignees/AssigneeValidationService.js.map +1 -0
  235. package/dist/tools/tasks/assignees/index.d.ts +52 -0
  236. package/dist/tools/tasks/assignees/index.d.ts.map +1 -0
  237. package/dist/tools/tasks/assignees/index.js +103 -0
  238. package/dist/tools/tasks/assignees/index.js.map +1 -0
  239. package/dist/tools/tasks/attach.d.ts +39 -0
  240. package/dist/tools/tasks/attach.d.ts.map +1 -0
  241. package/dist/tools/tasks/attach.js +90 -0
  242. package/dist/tools/tasks/attach.js.map +1 -0
  243. package/dist/tools/tasks/attachments.d.ts +67 -0
  244. package/dist/tools/tasks/attachments.d.ts.map +1 -0
  245. package/dist/tools/tasks/attachments.js +153 -0
  246. package/dist/tools/tasks/attachments.js.map +1 -0
  247. package/dist/tools/tasks/buckets.d.ts +41 -0
  248. package/dist/tools/tasks/buckets.d.ts.map +1 -0
  249. package/dist/tools/tasks/buckets.js +75 -0
  250. package/dist/tools/tasks/buckets.js.map +1 -0
  251. package/dist/tools/tasks/bulk/BatchProcessorFactory.d.ts +30 -0
  252. package/dist/tools/tasks/bulk/BatchProcessorFactory.d.ts.map +1 -0
  253. package/dist/tools/tasks/bulk/BatchProcessorFactory.js +69 -0
  254. package/dist/tools/tasks/bulk/BatchProcessorFactory.js.map +1 -0
  255. package/dist/tools/tasks/bulk/BulkOperationErrorHandler.d.ts +50 -0
  256. package/dist/tools/tasks/bulk/BulkOperationErrorHandler.d.ts.map +1 -0
  257. package/dist/tools/tasks/bulk/BulkOperationErrorHandler.js +183 -0
  258. package/dist/tools/tasks/bulk/BulkOperationErrorHandler.js.map +1 -0
  259. package/dist/tools/tasks/bulk/BulkOperationProcessor.d.ts +101 -0
  260. package/dist/tools/tasks/bulk/BulkOperationProcessor.d.ts.map +1 -0
  261. package/dist/tools/tasks/bulk/BulkOperationProcessor.js +416 -0
  262. package/dist/tools/tasks/bulk/BulkOperationProcessor.js.map +1 -0
  263. package/dist/tools/tasks/bulk/BulkOperationTypes.d.ts +50 -0
  264. package/dist/tools/tasks/bulk/BulkOperationTypes.d.ts.map +1 -0
  265. package/dist/tools/tasks/bulk/BulkOperationTypes.js +6 -0
  266. package/dist/tools/tasks/bulk/BulkOperationTypes.js.map +1 -0
  267. package/dist/tools/tasks/bulk/BulkOperationValidator.d.ts +53 -0
  268. package/dist/tools/tasks/bulk/BulkOperationValidator.d.ts.map +1 -0
  269. package/dist/tools/tasks/bulk/BulkOperationValidator.js +216 -0
  270. package/dist/tools/tasks/bulk/BulkOperationValidator.js.map +1 -0
  271. package/dist/tools/tasks/bulk/index.d.ts +9 -0
  272. package/dist/tools/tasks/bulk/index.d.ts.map +1 -0
  273. package/dist/tools/tasks/bulk/index.js +15 -0
  274. package/dist/tools/tasks/bulk/index.js.map +1 -0
  275. package/dist/tools/tasks/bulk-operations-simplified.d.ts +62 -0
  276. package/dist/tools/tasks/bulk-operations-simplified.d.ts.map +1 -0
  277. package/dist/tools/tasks/bulk-operations-simplified.js +416 -0
  278. package/dist/tools/tasks/bulk-operations-simplified.js.map +1 -0
  279. package/dist/tools/tasks/bulk-operations.d.ts +8 -0
  280. package/dist/tools/tasks/bulk-operations.d.ts.map +1 -0
  281. package/dist/tools/tasks/bulk-operations.js +13 -0
  282. package/dist/tools/tasks/bulk-operations.js.map +1 -0
  283. package/dist/tools/tasks/by-index.d.ts +36 -0
  284. package/dist/tools/tasks/by-index.d.ts.map +1 -0
  285. package/dist/tools/tasks/by-index.js +52 -0
  286. package/dist/tools/tasks/by-index.js.map +1 -0
  287. package/dist/tools/tasks/comments/CommentOperationsService.d.ts +44 -0
  288. package/dist/tools/tasks/comments/CommentOperationsService.d.ts.map +1 -0
  289. package/dist/tools/tasks/comments/CommentOperationsService.js +86 -0
  290. package/dist/tools/tasks/comments/CommentOperationsService.js.map +1 -0
  291. package/dist/tools/tasks/comments/CommentResponseFormatter.d.ts +41 -0
  292. package/dist/tools/tasks/comments/CommentResponseFormatter.d.ts.map +1 -0
  293. package/dist/tools/tasks/comments/CommentResponseFormatter.js +114 -0
  294. package/dist/tools/tasks/comments/CommentResponseFormatter.js.map +1 -0
  295. package/dist/tools/tasks/comments/CommentValidationService.d.ts +62 -0
  296. package/dist/tools/tasks/comments/CommentValidationService.d.ts.map +1 -0
  297. package/dist/tools/tasks/comments/CommentValidationService.js +105 -0
  298. package/dist/tools/tasks/comments/CommentValidationService.js.map +1 -0
  299. package/dist/tools/tasks/comments/index.d.ts +66 -0
  300. package/dist/tools/tasks/comments/index.d.ts.map +1 -0
  301. package/dist/tools/tasks/comments/index.js +99 -0
  302. package/dist/tools/tasks/comments/index.js.map +1 -0
  303. package/dist/tools/tasks/constants.d.ts +19 -0
  304. package/dist/tools/tasks/constants.d.ts.map +1 -0
  305. package/dist/tools/tasks/constants.js +82 -0
  306. package/dist/tools/tasks/constants.js.map +1 -0
  307. package/dist/tools/tasks/crud/TaskCreationService.d.ts +29 -0
  308. package/dist/tools/tasks/crud/TaskCreationService.d.ts.map +1 -0
  309. package/dist/tools/tasks/crud/TaskCreationService.js +274 -0
  310. package/dist/tools/tasks/crud/TaskCreationService.js.map +1 -0
  311. package/dist/tools/tasks/crud/TaskDeletionService.d.ts +19 -0
  312. package/dist/tools/tasks/crud/TaskDeletionService.d.ts.map +1 -0
  313. package/dist/tools/tasks/crud/TaskDeletionService.js +96 -0
  314. package/dist/tools/tasks/crud/TaskDeletionService.js.map +1 -0
  315. package/dist/tools/tasks/crud/TaskReadService.d.ts +19 -0
  316. package/dist/tools/tasks/crud/TaskReadService.d.ts.map +1 -0
  317. package/dist/tools/tasks/crud/TaskReadService.js +66 -0
  318. package/dist/tools/tasks/crud/TaskReadService.js.map +1 -0
  319. package/dist/tools/tasks/crud/TaskResponseFormatter.d.ts +18 -0
  320. package/dist/tools/tasks/crud/TaskResponseFormatter.d.ts.map +1 -0
  321. package/dist/tools/tasks/crud/TaskResponseFormatter.js +248 -0
  322. package/dist/tools/tasks/crud/TaskResponseFormatter.js.map +1 -0
  323. package/dist/tools/tasks/crud/TaskUpdateService.d.ts +33 -0
  324. package/dist/tools/tasks/crud/TaskUpdateService.d.ts.map +1 -0
  325. package/dist/tools/tasks/crud/TaskUpdateService.js +294 -0
  326. package/dist/tools/tasks/crud/TaskUpdateService.js.map +1 -0
  327. package/dist/tools/tasks/crud/index.d.ts +17 -0
  328. package/dist/tools/tasks/crud/index.d.ts.map +1 -0
  329. package/dist/tools/tasks/crud/index.js +20 -0
  330. package/dist/tools/tasks/crud/index.js.map +1 -0
  331. package/dist/tools/tasks/duplicate.d.ts +28 -0
  332. package/dist/tools/tasks/duplicate.d.ts.map +1 -0
  333. package/dist/tools/tasks/duplicate.js +41 -0
  334. package/dist/tools/tasks/duplicate.js.map +1 -0
  335. package/dist/tools/tasks/filtering/FilterExecutor.d.ts +39 -0
  336. package/dist/tools/tasks/filtering/FilterExecutor.d.ts.map +1 -0
  337. package/dist/tools/tasks/filtering/FilterExecutor.js +214 -0
  338. package/dist/tools/tasks/filtering/FilterExecutor.js.map +1 -0
  339. package/dist/tools/tasks/filtering/FilterValidator.d.ts +59 -0
  340. package/dist/tools/tasks/filtering/FilterValidator.d.ts.map +1 -0
  341. package/dist/tools/tasks/filtering/FilterValidator.js +257 -0
  342. package/dist/tools/tasks/filtering/FilterValidator.js.map +1 -0
  343. package/dist/tools/tasks/filtering/TaskFilteringOrchestrator.d.ts +78 -0
  344. package/dist/tools/tasks/filtering/TaskFilteringOrchestrator.d.ts.map +1 -0
  345. package/dist/tools/tasks/filtering/TaskFilteringOrchestrator.js +195 -0
  346. package/dist/tools/tasks/filtering/TaskFilteringOrchestrator.js.map +1 -0
  347. package/dist/tools/tasks/filtering/evaluators.d.ts +41 -0
  348. package/dist/tools/tasks/filtering/evaluators.d.ts.map +1 -0
  349. package/dist/tools/tasks/filtering/evaluators.js +224 -0
  350. package/dist/tools/tasks/filtering/evaluators.js.map +1 -0
  351. package/dist/tools/tasks/filtering/index.d.ts +11 -0
  352. package/dist/tools/tasks/filtering/index.d.ts.map +1 -0
  353. package/dist/tools/tasks/filtering/index.js +26 -0
  354. package/dist/tools/tasks/filtering/index.js.map +1 -0
  355. package/dist/tools/tasks/index.d.ts +9 -0
  356. package/dist/tools/tasks/index.d.ts.map +1 -0
  357. package/dist/tools/tasks/index.js +337 -0
  358. package/dist/tools/tasks/index.js.map +1 -0
  359. package/dist/tools/tasks/labels.d.ts +49 -0
  360. package/dist/tools/tasks/labels.d.ts.map +1 -0
  361. package/dist/tools/tasks/labels.js +223 -0
  362. package/dist/tools/tasks/labels.js.map +1 -0
  363. package/dist/tools/tasks/mark-read.d.ts +27 -0
  364. package/dist/tools/tasks/mark-read.d.ts.map +1 -0
  365. package/dist/tools/tasks/mark-read.js +38 -0
  366. package/dist/tools/tasks/mark-read.js.map +1 -0
  367. package/dist/tools/tasks/position.d.ts +53 -0
  368. package/dist/tools/tasks/position.d.ts.map +1 -0
  369. package/dist/tools/tasks/position.js +83 -0
  370. package/dist/tools/tasks/position.js.map +1 -0
  371. package/dist/tools/tasks/reminders.d.ts +56 -0
  372. package/dist/tools/tasks/reminders.d.ts.map +1 -0
  373. package/dist/tools/tasks/reminders.js +213 -0
  374. package/dist/tools/tasks/reminders.js.map +1 -0
  375. package/dist/tools/tasks/subtasks.d.ts +85 -0
  376. package/dist/tools/tasks/subtasks.d.ts.map +1 -0
  377. package/dist/tools/tasks/subtasks.js +286 -0
  378. package/dist/tools/tasks/subtasks.js.map +1 -0
  379. package/dist/tools/tasks/types/filters.d.ts +136 -0
  380. package/dist/tools/tasks/types/filters.d.ts.map +1 -0
  381. package/dist/tools/tasks/types/filters.js +7 -0
  382. package/dist/tools/tasks/types/filters.js.map +1 -0
  383. package/dist/tools/tasks/validation.d.ts +47 -0
  384. package/dist/tools/tasks/validation.d.ts.map +1 -0
  385. package/dist/tools/tasks/validation.js +131 -0
  386. package/dist/tools/tasks/validation.js.map +1 -0
  387. package/dist/tools/tasks-relations.d.ts +25 -0
  388. package/dist/tools/tasks-relations.d.ts.map +1 -0
  389. package/dist/tools/tasks-relations.js +271 -0
  390. package/dist/tools/tasks-relations.js.map +1 -0
  391. package/dist/tools/tasks.d.ts +6 -0
  392. package/dist/tools/tasks.d.ts.map +1 -0
  393. package/dist/tools/tasks.js +10 -0
  394. package/dist/tools/tasks.js.map +1 -0
  395. package/dist/tools/teams.d.ts +9 -0
  396. package/dist/tools/teams.d.ts.map +1 -0
  397. package/dist/tools/teams.js +255 -0
  398. package/dist/tools/teams.js.map +1 -0
  399. package/dist/tools/templates.d.ts +9 -0
  400. package/dist/tools/templates.d.ts.map +1 -0
  401. package/dist/tools/templates.js +450 -0
  402. package/dist/tools/templates.js.map +1 -0
  403. package/dist/tools/tokens.d.ts +37 -0
  404. package/dist/tools/tokens.d.ts.map +1 -0
  405. package/dist/tools/tokens.js +122 -0
  406. package/dist/tools/tokens.js.map +1 -0
  407. package/dist/tools/user-deletion.d.ts +41 -0
  408. package/dist/tools/user-deletion.d.ts.map +1 -0
  409. package/dist/tools/user-deletion.js +126 -0
  410. package/dist/tools/user-deletion.js.map +1 -0
  411. package/dist/tools/users.d.ts +9 -0
  412. package/dist/tools/users.d.ts.map +1 -0
  413. package/dist/tools/users.js +418 -0
  414. package/dist/tools/users.js.map +1 -0
  415. package/dist/tools/webhooks.d.ts +31 -0
  416. package/dist/tools/webhooks.d.ts.map +1 -0
  417. package/dist/tools/webhooks.js +414 -0
  418. package/dist/tools/webhooks.js.map +1 -0
  419. package/dist/transforms/base.d.ts +204 -0
  420. package/dist/transforms/base.d.ts.map +1 -0
  421. package/dist/transforms/base.js +175 -0
  422. package/dist/transforms/base.js.map +1 -0
  423. package/dist/transforms/field-selector.d.ts +27 -0
  424. package/dist/transforms/field-selector.d.ts.map +1 -0
  425. package/dist/transforms/field-selector.js +91 -0
  426. package/dist/transforms/field-selector.js.map +1 -0
  427. package/dist/transforms/index.d.ts +15 -0
  428. package/dist/transforms/index.d.ts.map +1 -0
  429. package/dist/transforms/index.js +50 -0
  430. package/dist/transforms/index.js.map +1 -0
  431. package/dist/transforms/size-calculator.d.ts +131 -0
  432. package/dist/transforms/size-calculator.d.ts.map +1 -0
  433. package/dist/transforms/size-calculator.js +284 -0
  434. package/dist/transforms/size-calculator.js.map +1 -0
  435. package/dist/transforms/task.d.ts +207 -0
  436. package/dist/transforms/task.d.ts.map +1 -0
  437. package/dist/transforms/task.js +259 -0
  438. package/dist/transforms/task.js.map +1 -0
  439. package/dist/types/errors.d.ts +65 -0
  440. package/dist/types/errors.d.ts.map +1 -0
  441. package/dist/types/errors.js +44 -0
  442. package/dist/types/errors.js.map +1 -0
  443. package/dist/types/filters.d.ts +149 -0
  444. package/dist/types/filters.d.ts.map +1 -0
  445. package/dist/types/filters.js +26 -0
  446. package/dist/types/filters.js.map +1 -0
  447. package/dist/types/index.d.ts +147 -0
  448. package/dist/types/index.d.ts.map +1 -0
  449. package/dist/types/index.js +24 -0
  450. package/dist/types/index.js.map +1 -0
  451. package/dist/types/node-vikunja-extended.d.ts +36 -0
  452. package/dist/types/node-vikunja-extended.d.ts.map +1 -0
  453. package/dist/types/node-vikunja-extended.js +25 -0
  454. package/dist/types/node-vikunja-extended.js.map +1 -0
  455. package/dist/types/responses.d.ts +121 -0
  456. package/dist/types/responses.d.ts.map +1 -0
  457. package/dist/types/responses.js +26 -0
  458. package/dist/types/responses.js.map +1 -0
  459. package/dist/types/vikunja.d.ts +319 -0
  460. package/dist/types/vikunja.d.ts.map +1 -0
  461. package/dist/types/vikunja.js +7 -0
  462. package/dist/types/vikunja.js.map +1 -0
  463. package/dist/utils/auth-error-handler.d.ts +23 -0
  464. package/dist/utils/auth-error-handler.d.ts.map +1 -0
  465. package/dist/utils/auth-error-handler.js +162 -0
  466. package/dist/utils/auth-error-handler.js.map +1 -0
  467. package/dist/utils/composite-operation.d.ts +184 -0
  468. package/dist/utils/composite-operation.d.ts.map +1 -0
  469. package/dist/utils/composite-operation.js +293 -0
  470. package/dist/utils/composite-operation.js.map +1 -0
  471. package/dist/utils/error-handler.d.ts +39 -0
  472. package/dist/utils/error-handler.d.ts.map +1 -0
  473. package/dist/utils/error-handler.js +336 -0
  474. package/dist/utils/error-handler.js.map +1 -0
  475. package/dist/utils/filtering/ClientSideFilteringStrategy.d.ts +13 -0
  476. package/dist/utils/filtering/ClientSideFilteringStrategy.d.ts.map +1 -0
  477. package/dist/utils/filtering/ClientSideFilteringStrategy.js +129 -0
  478. package/dist/utils/filtering/ClientSideFilteringStrategy.js.map +1 -0
  479. package/dist/utils/filtering/FilteringContext.d.ts +40 -0
  480. package/dist/utils/filtering/FilteringContext.d.ts.map +1 -0
  481. package/dist/utils/filtering/FilteringContext.js +58 -0
  482. package/dist/utils/filtering/FilteringContext.js.map +1 -0
  483. package/dist/utils/filtering/HybridFilteringStrategy.d.ts +15 -0
  484. package/dist/utils/filtering/HybridFilteringStrategy.d.ts.map +1 -0
  485. package/dist/utils/filtering/HybridFilteringStrategy.js +55 -0
  486. package/dist/utils/filtering/HybridFilteringStrategy.js.map +1 -0
  487. package/dist/utils/filtering/RestCrossProjectFilteringStrategy.d.ts +42 -0
  488. package/dist/utils/filtering/RestCrossProjectFilteringStrategy.d.ts.map +1 -0
  489. package/dist/utils/filtering/RestCrossProjectFilteringStrategy.js +120 -0
  490. package/dist/utils/filtering/RestCrossProjectFilteringStrategy.js.map +1 -0
  491. package/dist/utils/filtering/ServerSideFilteringStrategy.d.ts +13 -0
  492. package/dist/utils/filtering/ServerSideFilteringStrategy.d.ts.map +1 -0
  493. package/dist/utils/filtering/ServerSideFilteringStrategy.js +88 -0
  494. package/dist/utils/filtering/ServerSideFilteringStrategy.js.map +1 -0
  495. package/dist/utils/filtering/TaskFilteringStrategy.d.ts +19 -0
  496. package/dist/utils/filtering/TaskFilteringStrategy.d.ts.map +1 -0
  497. package/dist/utils/filtering/TaskFilteringStrategy.js +10 -0
  498. package/dist/utils/filtering/TaskFilteringStrategy.js.map +1 -0
  499. package/dist/utils/filtering/index.d.ts +14 -0
  500. package/dist/utils/filtering/index.d.ts.map +1 -0
  501. package/dist/utils/filtering/index.js +22 -0
  502. package/dist/utils/filtering/index.js.map +1 -0
  503. package/dist/utils/filtering/types.d.ts +108 -0
  504. package/dist/utils/filtering/types.d.ts.map +1 -0
  505. package/dist/utils/filtering/types.js +6 -0
  506. package/dist/utils/filtering/types.js.map +1 -0
  507. package/dist/utils/filters.d.ts +70 -0
  508. package/dist/utils/filters.d.ts.map +1 -0
  509. package/dist/utils/filters.js +812 -0
  510. package/dist/utils/filters.js.map +1 -0
  511. package/dist/utils/http-error-detail.d.ts +28 -0
  512. package/dist/utils/http-error-detail.d.ts.map +1 -0
  513. package/dist/utils/http-error-detail.js +67 -0
  514. package/dist/utils/http-error-detail.js.map +1 -0
  515. package/dist/utils/label-bulk.d.ts +21 -0
  516. package/dist/utils/label-bulk.d.ts.map +1 -0
  517. package/dist/utils/label-bulk.js +51 -0
  518. package/dist/utils/label-bulk.js.map +1 -0
  519. package/dist/utils/logger.d.ts +20 -0
  520. package/dist/utils/logger.d.ts.map +1 -0
  521. package/dist/utils/logger.js +66 -0
  522. package/dist/utils/logger.js.map +1 -0
  523. package/dist/utils/memory.d.ts +73 -0
  524. package/dist/utils/memory.d.ts.map +1 -0
  525. package/dist/utils/memory.js +194 -0
  526. package/dist/utils/memory.js.map +1 -0
  527. package/dist/utils/performance/batch-processor.d.ts +77 -0
  528. package/dist/utils/performance/batch-processor.d.ts.map +1 -0
  529. package/dist/utils/performance/batch-processor.js +218 -0
  530. package/dist/utils/performance/batch-processor.js.map +1 -0
  531. package/dist/utils/performance/index.d.ts +42 -0
  532. package/dist/utils/performance/index.d.ts.map +1 -0
  533. package/dist/utils/performance/index.js +49 -0
  534. package/dist/utils/performance/index.js.map +1 -0
  535. package/dist/utils/performance/performance-monitor.d.ts +116 -0
  536. package/dist/utils/performance/performance-monitor.d.ts.map +1 -0
  537. package/dist/utils/performance/performance-monitor.js +307 -0
  538. package/dist/utils/performance/performance-monitor.js.map +1 -0
  539. package/dist/utils/read-only.d.ts +111 -0
  540. package/dist/utils/read-only.d.ts.map +1 -0
  541. package/dist/utils/read-only.js +503 -0
  542. package/dist/utils/read-only.js.map +1 -0
  543. package/dist/utils/response-factory.d.ts +81 -0
  544. package/dist/utils/response-factory.d.ts.map +1 -0
  545. package/dist/utils/response-factory.js +86 -0
  546. package/dist/utils/response-factory.js.map +1 -0
  547. package/dist/utils/retry.d.ts +160 -0
  548. package/dist/utils/retry.d.ts.map +1 -0
  549. package/dist/utils/retry.js +316 -0
  550. package/dist/utils/retry.js.map +1 -0
  551. package/dist/utils/security.d.ts +70 -0
  552. package/dist/utils/security.d.ts.map +1 -0
  553. package/dist/utils/security.js +358 -0
  554. package/dist/utils/security.js.map +1 -0
  555. package/dist/utils/simple-response.d.ts +75 -0
  556. package/dist/utils/simple-response.d.ts.map +1 -0
  557. package/dist/utils/simple-response.js +311 -0
  558. package/dist/utils/simple-response.js.map +1 -0
  559. package/dist/utils/storage-errors.d.ts +9 -0
  560. package/dist/utils/storage-errors.d.ts.map +1 -0
  561. package/dist/utils/storage-errors.js +20 -0
  562. package/dist/utils/storage-errors.js.map +1 -0
  563. package/dist/utils/task-rest-transport.d.ts +28 -0
  564. package/dist/utils/task-rest-transport.d.ts.map +1 -0
  565. package/dist/utils/task-rest-transport.js +33 -0
  566. package/dist/utils/task-rest-transport.js.map +1 -0
  567. package/dist/utils/unicode-fix.d.ts +19 -0
  568. package/dist/utils/unicode-fix.d.ts.map +1 -0
  569. package/dist/utils/unicode-fix.js +70 -0
  570. package/dist/utils/unicode-fix.js.map +1 -0
  571. package/dist/utils/validation.d.ts +75 -0
  572. package/dist/utils/validation.d.ts.map +1 -0
  573. package/dist/utils/validation.js +758 -0
  574. package/dist/utils/validation.js.map +1 -0
  575. package/dist/utils/vikunja-rest.d.ts +159 -0
  576. package/dist/utils/vikunja-rest.d.ts.map +1 -0
  577. package/dist/utils/vikunja-rest.js +378 -0
  578. package/dist/utils/vikunja-rest.js.map +1 -0
  579. package/docs/CONFIGURATION.md +957 -0
  580. package/docs/DOCKER-DESKTOP-MCP.md +207 -0
  581. package/docs/TOOLS.md +422 -0
  582. package/package.json +145 -0
@@ -0,0 +1,957 @@
1
+ # Configuration Management
2
+
3
+ ## Overview
4
+
5
+ The Vikunja MCP server uses a centralized configuration system that replaces scattered `process.env` usage with type-safe, validated configuration management. This system addresses TD-002 (Environment Variable Sprawl) by consolidating 33 environment variables into a unified architecture.
6
+
7
+ ## Quick Start
8
+
9
+ ### Basic Usage
10
+
11
+ ```typescript
12
+ import { getConfiguration, getAuthConfig, getRateLimitConfig } from './config';
13
+
14
+ // Get complete configuration
15
+ const config = await getConfiguration();
16
+
17
+ // Get specific sections
18
+ const authConfig = await getAuthConfig();
19
+ const rateLimiting = await getRateLimitConfig();
20
+
21
+ // Check feature flags
22
+ const isEnabled = await isFeatureEnabled('enableServerSideFiltering');
23
+ ```
24
+
25
+ ### Environment Setup
26
+
27
+ 1. Copy `.env.example` to `.env`
28
+ 2. Configure your Vikunja connection:
29
+ ```env
30
+ VIKUNJA_URL=https://your-vikunja-instance.com
31
+ VIKUNJA_API_TOKEN=your-api-token-here
32
+ ```
33
+ 3. Set your environment:
34
+ ```env
35
+ NODE_ENV=development # or test, production
36
+ ```
37
+
38
+ ## Configuration Architecture
39
+
40
+ ### Configuration Sections
41
+
42
+ The configuration is organized into four main sections:
43
+
44
+ #### 1. Authentication (`AuthConfig`)
45
+ ```typescript
46
+ interface AuthConfig {
47
+ vikunjaUrl?: string; // Vikunja server URL
48
+ vikunjaToken?: string; // API or JWT token
49
+ mcpMode?: string; // MCP server mode
50
+ }
51
+ ```
52
+
53
+ #### 2. Logging (`LoggingConfig`)
54
+ ```typescript
55
+ interface LoggingConfig {
56
+ level: 'error' | 'warn' | 'info' | 'debug';
57
+ debug: boolean;
58
+ environment: Environment;
59
+ }
60
+ ```
61
+
62
+ #### 3. Rate Limiting (`RateLimitConfig`)
63
+ ```typescript
64
+ interface RateLimitConfig {
65
+ enabled: boolean;
66
+ default: RateLimitSettings; // Standard tools
67
+ expensive: RateLimitSettings; // Resource-intensive operations
68
+ bulk: RateLimitSettings; // Batch operations
69
+ export: RateLimitSettings; // Export operations
70
+ }
71
+
72
+ interface RateLimitSettings {
73
+ requestsPerMinute: number;
74
+ requestsPerHour: number;
75
+ maxRequestSize: number;
76
+ maxResponseSize: number;
77
+ executionTimeout: number;
78
+ }
79
+ ```
80
+
81
+ #### 4. Feature Flags (`FeatureFlagsConfig`)
82
+ ```typescript
83
+ interface FeatureFlagsConfig {
84
+ enableServerSideFiltering: boolean;
85
+ enableAdvancedMetrics: boolean;
86
+ enableExperimentalFeatures: boolean;
87
+ }
88
+ ```
89
+
90
+ #### 5. Templates (`TemplatesConfig`)
91
+ ```typescript
92
+ interface TemplatesConfig {
93
+ persistPath?: string; // File-backed persistence — see Templates Persistence below
94
+ }
95
+ ```
96
+
97
+ ### Environment Profiles
98
+
99
+ The system automatically applies environment-specific defaults:
100
+
101
+ #### Development Profile
102
+ - **Logging**: Debug level, verbose output
103
+ - **Rate Limiting**: Disabled for easier testing
104
+ - **Features**: Experimental features enabled
105
+
106
+ #### Test Profile
107
+ - **Logging**: Error level only to reduce noise
108
+ - **Rate Limiting**: Disabled for test performance
109
+ - **Features**: Conservative settings for consistent behavior
110
+
111
+ #### Production Profile
112
+ - **Logging**: Info level for operational visibility
113
+ - **Rate Limiting**: Full protection enabled
114
+ - **Features**: Only stable features enabled
115
+
116
+ ## Configuration Priority
117
+
118
+ Configuration values are resolved in the following priority order (highest to lowest):
119
+
120
+ 1. **Programmatic Sources** - Direct configuration objects (used by tests / embedders)
121
+ 2. **Environment Variables** - System environment variables — **always win over the config file**
122
+ 3. **Config File** - Optional `vikunja-mcp.config.json` (see [Config File](#config-file) below)
123
+ 4. **Environment Profiles** - Dev/test/production defaults
124
+ 5. **Schema Defaults** - Fallback values defined in schema
125
+
126
+ ## Config File
127
+
128
+ Non-sensitive configuration can be layered in from an optional JSON file. It is safe to
129
+ commit, safe to mount read-only into a container (e.g. as a Docker `config`), and is
130
+ **never** the place for secrets — see [Secrets Management](#secrets-management).
131
+
132
+ - **Default path**: `vikunja-mcp.config.json` in the process's current working directory.
133
+ If it doesn't exist, it is silently skipped — the file is entirely optional.
134
+ - **Override path**: set `VIKUNJA_MCP_CONFIG=/path/to/file.json`. When this variable is
135
+ set explicitly, a missing or unreadable file is a **hard startup error** (fail fast) —
136
+ an explicit path that can't be read is assumed to be a misconfiguration, not something
137
+ to silently ignore.
138
+ - **Malformed file**: invalid JSON, or JSON whose top-level value isn't an object, is
139
+ always a hard startup error with a message naming the file path and the parse problem —
140
+ regardless of whether the path was explicit or the default.
141
+ - **Shape**: the file mirrors `ApplicationConfig` — any of `auth` (non-secret fields
142
+ only), `logging`, `rateLimiting`, `featureFlags`, `modules`, `templates` may be
143
+ present; anything omitted falls back to the environment profile / schema default.
144
+
145
+ Example `vikunja-mcp.config.json`:
146
+
147
+ ```json
148
+ {
149
+ "modules": {
150
+ "webhooks": false,
151
+ "batchImport": { "enabled": true }
152
+ },
153
+ "logging": {
154
+ "level": "info"
155
+ },
156
+ "readOnly": false
157
+ }
158
+ ```
159
+
160
+ ## Module Gating
161
+
162
+ Each Vikunja entity's tools live behind a **module** toggle, resolved once at
163
+ tool-registration time (`registerTools` in `src/tools/index.ts`). A disabled module's
164
+ tools are never registered with the MCP server — they are invisible to the client, not
165
+ merely rejected at call time.
166
+
167
+ ### Module Config Shape
168
+
169
+ A module's value is a plain boolean today, but the object form is accepted so
170
+ per-subcommand granularity can be added later **without a breaking change**:
171
+
172
+ ```json
173
+ { "modules": { "tasks": false } }
174
+ ```
175
+
176
+ ```json
177
+ { "modules": { "tasks": { "enabled": true } } }
178
+ ```
179
+
180
+ The object form already tolerates (and ignores, for now) extra boolean keys, so a future
181
+ release can start honoring `{"tasks": {"enabled": true, "delete": false}}` without
182
+ requiring any config migration.
183
+
184
+ ### Known Modules
185
+
186
+ | Module | Default | Notes |
187
+ |---|---|---|
188
+ | `tasks` | **ON** | Gates the entire task tool family (CRUD, bulk, assignees, comments, reminders, labels, relations) together |
189
+ | `projects` | **ON** | |
190
+ | `labels` | **ON** | |
191
+ | `teams` | **ON** | |
192
+ | `users` | **ON** | Also requires JWT authentication — see [Composing with Auth-Type Gating](#composing-with-auth-type-gating) |
193
+ | `webhooks` | **ON** | |
194
+ | `filters` | **ON** | |
195
+ | `templates` | **ON** | |
196
+ | `export` | **ON** | Also requires JWT authentication |
197
+ | `batchImport` | **ON** | |
198
+ | `notifications` | **ON** | Gates `vikunja_notifications` |
199
+ | `subscriptions` | **ON** | Gates `vikunja_subscriptions` |
200
+ | `reactions` | **ON** | Gates `vikunja_reactions` |
201
+ | `admin` | **OFF** ⚠️ | Gates `vikunja_admin` (instance-admin operations: overview, all-projects listing + owner reassignment, user list/create/delete, admin-flag + status toggles). Deny-by-default AND JWT-only — see [Composing with Auth-Type Gating](#composing-with-auth-type-gating). `delete-user` additionally requires an explicit `confirm: true` tool argument. |
202
+ | `userDeletion` | **OFF** ⚠️⚠️ | Gates `vikunja_user_deletion` (`request`/`confirm`/`cancel` self-deletion of the **currently authenticated account**). Deny-by-default AND JWT-only — see [Composing with Auth-Type Gating](#composing-with-auth-type-gating). `request` and `confirm` additionally require an explicit `confirm: true` tool argument; both are genuinely irreversible once the emailed confirmation token is used — do not enable this module unless you specifically want an AI assistant able to delete the connected Vikunja account. `cancel` (the safe undo) does not require `confirm: true`. |
203
+ | `tokenManagement` | **OFF** ⚠️ | Gates `vikunja_tokens` (API token list/create/delete for the connected account). Deny-by-default — credential-adjacent. No auth-type restriction at registration time (unlike `admin`/`users`/`export`), but the underlying `/tokens` endpoints may reject API-token sessions server-side — see `src/tools/tokens.ts`. |
204
+ | `caldavTokens` | **OFF** ⚠️ | Gates `vikunja_caldav_tokens` (CalDAV token list/create/delete for the connected account). Deny-by-default — credential-adjacent, and a created token's secret is shown only once. Unlike `tokenManagement`, the underlying `/user/settings/token/caldav*` endpoints ARE JWT-only per the vendored OpenAPI spec, so registration composes with the same JWT-only gate as `users`/`export`/`admin` — see [Composing with Auth-Type Gating](#composing-with-auth-type-gating) and `src/tools/caldav-tokens.ts`. |
205
+ | `backgrounds` | **OFF** (opt-in) | Gates three `vikunja_projects` subcommands — `remove-background`, `set-unsplash-background`, `search-unsplash` (G7, project backgrounds) — **not** a whole tool. Deny-by-default for the opposite reason to `admin`/`userDeletion`/`tokenManagement`: not dangerous, just low-value/cosmetic for a task-management assistant. See [Subcommand-Level Gating: `backgrounds`](#subcommand-level-gating-backgrounds) below. |
206
+
207
+ Ordinary modules default **ON** (matching pre-existing behavior — this system is
208
+ additive, not a breaking change). The four reserved "dangerous" modules default **OFF**
209
+ (deny-by-default): `admin`, `tokenManagement`, `caldavTokens`, and `userDeletion` now all
210
+ have tools wired to them and ship already gated closed until an operator opts in.
211
+ `userDeletion` deserves particular caution — read its row above in full before enabling
212
+ it. `backgrounds` is also **OFF** by default, but as an **opt-in cosmetic** module rather
213
+ than a dangerous one — see below.
214
+
215
+ ### Subcommand-Level Gating: `backgrounds`
216
+
217
+ Every module above gates a whole standalone tool at registration time. `backgrounds` is
218
+ the one exception: it gates only three subcommands *within* the always-registered
219
+ `vikunja_projects` tool (`remove-background`, `set-unsplash-background`,
220
+ `search-unsplash`), because bundling low-value cosmetic operations into their own tool
221
+ would be worse ergonomics than adding them to the tool that already owns project state.
222
+
223
+ The same "invisible to the client, not merely rejected at call time" contract still
224
+ holds — it is just enforced one level down. `registerProjectsTool`
225
+ (`src/tools/projects/index.ts`) builds `vikunja_projects`'s subcommand **enum** itself
226
+ conditionally on whether `backgrounds` is enabled: when it isn't (the default), those
227
+ three strings are not present in the enum at all, so a call naming one of them fails
228
+ MCP schema validation (an unrecognized enum value) rather than reaching any handler
229
+ logic. Enable the module and the enum includes them; disable it and they vanish from
230
+ the schema again — exactly mirroring what module gating does for a whole tool.
231
+
232
+ ```json
233
+ { "modules": { "backgrounds": true } }
234
+ ```
235
+
236
+ ```env
237
+ VIKUNJA_MCP_MODULE_BACKGROUNDS=true
238
+ ```
239
+
240
+ Two of the three subcommands (`set-unsplash-background`, `search-unsplash`) only work
241
+ when the connected Vikunja server itself has an Unsplash provider configured
242
+ (an admin-side API key) — when it doesn't, the server's error is recognized and
243
+ rewritten into a friendly, actionable message rather than surfaced as opaque server
244
+ text. The binary image bytes (upload, and fetching the actual image/thumbnail) stay
245
+ parked — MCP has no content channel for them; see
246
+ [docs/ENDPOINT-TAIL-RETRIAGE.md](ENDPOINT-TAIL-RETRIAGE.md) item G7.
247
+
248
+ ### Module Env Var Overrides
249
+
250
+ Each module has a matching boolean-only env var override (env vars carry the boolean
251
+ shorthand only — the object form with future per-subcommand keys is a config-file-only
252
+ feature):
253
+
254
+ ```env
255
+ VIKUNJA_MCP_MODULE_TASKS=true
256
+ VIKUNJA_MCP_MODULE_PROJECTS=true
257
+ VIKUNJA_MCP_MODULE_LABELS=true
258
+ VIKUNJA_MCP_MODULE_TEAMS=true
259
+ VIKUNJA_MCP_MODULE_USERS=true
260
+ VIKUNJA_MCP_MODULE_WEBHOOKS=true
261
+ VIKUNJA_MCP_MODULE_FILTERS=true
262
+ VIKUNJA_MCP_MODULE_TEMPLATES=true
263
+ VIKUNJA_MCP_MODULE_EXPORT=true
264
+ VIKUNJA_MCP_MODULE_BATCH_IMPORT=true
265
+ VIKUNJA_MCP_MODULE_NOTIFICATIONS=true
266
+ VIKUNJA_MCP_MODULE_SUBSCRIPTIONS=true
267
+ VIKUNJA_MCP_MODULE_REACTIONS=true
268
+
269
+ # Reserved / dangerous — deny-by-default
270
+ VIKUNJA_MCP_MODULE_ADMIN=false
271
+ VIKUNJA_MCP_MODULE_USER_DELETION=false
272
+ VIKUNJA_MCP_MODULE_TOKEN_MANAGEMENT=false
273
+ VIKUNJA_MCP_MODULE_CALDAV_TOKENS=false
274
+
275
+ # Opt-in cosmetic — deny-by-default (not dangerous, just low-value)
276
+ VIKUNJA_MCP_MODULE_BACKGROUNDS=false
277
+ ```
278
+
279
+ As with every other setting, these env vars always win over the config file.
280
+
281
+ ### Composing with Auth-Type Gating
282
+
283
+ Module config can only **narrow** what authentication already allows — it can never
284
+ **expand** it. The `users` and `export` tools have always required JWT authentication
285
+ (API-token auth excludes them for backward compatibility); `admin` composes with the
286
+ same JWT-only gate. Module gating is applied *in addition to*, never instead of, that
287
+ check:
288
+
289
+ ```typescript
290
+ const jwtAuthenticated = authManager.isAuthenticated() && authManager.getAuthType() === 'jwt';
291
+ if (jwtAuthenticated && isModuleEnabled(modules.users)) {
292
+ registerUsersTool(server, authManager, clientFactory);
293
+ }
294
+ // ... and further down:
295
+ if (jwtAuthenticated && isModuleEnabled(modules.admin)) {
296
+ registerAdminTool(server, authManager, clientFactory);
297
+ }
298
+ if (jwtAuthenticated && isModuleEnabled(modules.userDeletion)) {
299
+ registerUserDeletionTool(server, authManager, clientFactory);
300
+ }
301
+ ```
302
+
303
+ Setting `VIKUNJA_MCP_MODULE_USERS=true` while authenticated with an API token does
304
+ **not** register the users tool — there is no config setting that can grant access auth
305
+ doesn't already permit. The same is true of `VIKUNJA_MCP_MODULE_ADMIN=true` and
306
+ `VIKUNJA_MCP_MODULE_USER_DELETION=true`: with an API-token session, `vikunja_admin` and
307
+ `vikunja_user_deletion` both stay unregistered regardless of the config value — per
308
+ docs/VIKUNJA_API_ISSUES.md, every `/user/*` endpoint (including `/user/deletion/*`)
309
+ rejects `tk_*` API tokens server-side, so this JWT-only gate is not just a local policy
310
+ choice here. `tokenManagement` is the one deny-by-default module that does **not**
311
+ compose with the JWT-only gate — `vikunja_tokens` registers for either session type once
312
+ its module key is enabled, since the underlying endpoints' auth requirement is a runtime
313
+ server behavior rather than something this server enforces at registration time.
314
+
315
+ `caldavTokens`, by contrast, DOES compose with the JWT-only gate, the same way
316
+ `admin` does — `vikunja_caldav_tokens` registers only when both
317
+ `VIKUNJA_MCP_MODULE_CALDAV_TOKENS=true` (or the config-file equivalent) AND the
318
+ session is JWT-authenticated, because the vendored OpenAPI spec scopes every
319
+ `/user/settings/token/caldav*` operation to `JWTKeyAuth` only (no `APIKeyAuth` entry) —
320
+ unlike `/tokens`, this is enforced at registration time, not left for the server to
321
+ reject at runtime.
322
+
323
+ ## Global Read-Only Safety Mode
324
+
325
+ A separate, orthogonal safety layer from module gating: instead of hiding a tool from the
326
+ MCP client entirely (module gating), read-only mode keeps every tool **visible and
327
+ registered**, but rejects any subcommand that writes or destroys data on the connected
328
+ Vikunja instance — read subcommands (`list`, `get`, `status`, ...) keep working normally.
329
+ This is useful for a read-only "explore my tasks" session, a demo environment, or any
330
+ deployment where an operator wants to guarantee an AI assistant cannot mutate data no
331
+ matter what a tool call asks for.
332
+
333
+ - **Config file key**: `readOnly` (boolean, default `false`) at the top level of
334
+ `vikunja-mcp.config.json` — a peer of `modules`/`logging`/etc., not nested under either.
335
+ - **Env override**: `VIKUNJA_MCP_READ_ONLY` (boolean shorthand, `true`/`false`) — as with
336
+ every other setting, the env var always wins over the config file.
337
+
338
+ ```json
339
+ { "readOnly": true }
340
+ ```
341
+
342
+ ```env
343
+ VIKUNJA_MCP_READ_ONLY=true
344
+ ```
345
+
346
+ ### What gets rejected
347
+
348
+ Rejection happens at dispatch, inside each tool's handler, via a single shared guard
349
+ (`assertWriteAllowed` in `src/utils/read-only.ts`) that every tool dispatcher calls once,
350
+ right after its existing auth check — not 24 copy-pasted `if (readOnly)` checks. The guard
351
+ consults one classification table per tool (`subcommand -> read | write | destructive`),
352
+ built from the actual Vikunja API semantics of each subcommand (see the module's doc
353
+ comment for the full rubric and the rationale behind edge cases like the dual-purpose
354
+ `comment` subcommand, `vikunja_batch_import`'s `dryRun`, and the fully-exempt
355
+ `vikunja_auth` tool). A rejected call fails with a consistent, clearly-worded error:
356
+
357
+ ```
358
+ server is in read-only mode: 'vikunja_tasks' subcommand 'delete' is a destructive
359
+ operation and is rejected. Set 'readOnly' to false in vikunja-mcp.config.json
360
+ (or unset VIKUNJA_MCP_READ_ONLY) to allow writes.
361
+ ```
362
+
363
+ Notes on scope:
364
+
365
+ - **`vikunja_auth`** (`connect`/`status`/`refresh`/`disconnect`/`info`) is entirely exempt
366
+ — those subcommands only manage the MCP server's local session, never a Vikunja
367
+ resource, so read-only mode never blocks them.
368
+ - **Dynamic classification**: a small number of subcommands classify themselves based on
369
+ the actual call arguments rather than a fixed table entry — `vikunja_tasks`'/
370
+ `vikunja_task_comments`' dual-purpose `comment` subcommand (creates when text is
371
+ supplied, otherwise lists — classified `read` only when no comment text is given) and
372
+ `vikunja_batch_import` (classified `read` only when `dryRun: true`, since a dry run
373
+ never writes).
374
+ - Module gating and read-only mode compose independently: a module disabled entirely
375
+ (§ Module Gating above) is invisible regardless of `readOnly`; a module left enabled
376
+ under `readOnly: true` stays visible but write/destructive calls into it are rejected.
377
+
378
+ ### MCP Tool Annotations
379
+
380
+ The same per-tool classification tables drive the MCP SDK's `ToolAnnotations`
381
+ (`readOnlyHint` / `destructiveHint` / `idempotentHint`), registered alongside every tool's
382
+ schema so MCP clients can render tool cards accurately and apply their own consent/
383
+ confirmation UX for destructive calls. Because a single MCP tool name here fans out to
384
+ several subcommands with different semantics, the tool-level hints are derived
385
+ conservatively:
386
+
387
+ - `readOnlyHint` is `true` only when **every** subcommand on that tool is classified
388
+ `read` (today: `vikunja_auth` and `vikunja_export_project`).
389
+ - `destructiveHint` is `true` when **any** subcommand is classified `destructive` (true
390
+ for nearly every CRUD-shaped tool, since almost all of them expose a `delete`-shaped
391
+ operation).
392
+ - `idempotentHint` is set `true` only for tools on an explicit, hand-reviewed allowlist
393
+ where every non-read subcommand is genuinely idempotent — currently just
394
+ `vikunja_notifications` (`mark-read`/`mark-all-read` are ensure-semantics: marking an
395
+ already-read notification as read again is a no-op). It is intentionally *not* inferred
396
+ automatically from the read/write/destructive table, since idempotency is a semantic
397
+ judgment call the three-way classification doesn't capture (e.g. `vikunja_teams`'
398
+ `members:toggleAdmin` is a `write`, not a `delete`, but explicitly is **not**
399
+ idempotent — it flips a flag rather than setting it).
400
+
401
+ ## Templates Persistence
402
+
403
+ `vikunja_templates` templates are **session-only by default**: they live in the same
404
+ in-memory `SimpleFilterStorage` as saved filters, scoped to the connected session, and
405
+ are lost when the server process restarts. Set a persist path to make them durable
406
+ across restarts.
407
+
408
+ - **Config key**: `templates.persistPath` in `vikunja-mcp.config.json`.
409
+ - **Env var**: `VIKUNJA_MCP_TEMPLATES_FILE=/path/to/templates.json` — **wins over the
410
+ config file**, same precedence as every other setting (see
411
+ [Configuration Priority](#configuration-priority)).
412
+ - **Unset (default)**: templates stay in-memory only — behavior is byte-identical to
413
+ before this feature existed.
414
+ - **Set**: the file is loaded once, tolerantly, the first time `vikunja_templates` is
415
+ used after startup — a missing file (first run / fresh volume) or a corrupt/malformed
416
+ file both fall back to an empty template set (logged as a warning for the corrupt
417
+ case), **never** a crash. Every `create` / `update` / `delete` mutation then
418
+ write-throughs the full current template set back to the file, **atomically**: written
419
+ to a temp file in the same directory, then renamed over the target, so a reader never
420
+ observes a half-written file and a crash mid-write can't corrupt the previous good
421
+ state. The parent directory is created automatically if it doesn't exist yet.
422
+
423
+ This is intentionally a plain JSON file, not a database — SQLite was evaluated for this
424
+ work item and parked (native-dependency cost outweighs the need for a single opt-in
425
+ file; see `docs/ROADMAP.md`). The path is a single file, which makes it trivial to mount
426
+ as a Docker volume:
427
+
428
+ ```yaml
429
+ services:
430
+ vikunja-mcp:
431
+ image: ghcr.io/netadvanced/vikunja-mcp-ng:latest
432
+ environment:
433
+ VIKUNJA_URL: "https://vikunja.example.com/api/v1"
434
+ VIKUNJA_API_TOKEN_FILE: /run/secrets/vikunja_api_token
435
+ VIKUNJA_MCP_TEMPLATES_FILE: /data/templates.json
436
+ volumes:
437
+ - vikunja-mcp-templates:/data
438
+ secrets:
439
+ - source: vikunja_api_token
440
+ target: vikunja_api_token
441
+ mode: 0400
442
+
443
+ volumes:
444
+ vikunja-mcp-templates:
445
+
446
+ secrets:
447
+ vikunja_api_token:
448
+ file: ./secrets/vikunja_api_token.txt
449
+ ```
450
+
451
+ Or via the config file instead of the env var:
452
+
453
+ ```json
454
+ {
455
+ "templates": {
456
+ "persistPath": "/data/templates.json"
457
+ }
458
+ }
459
+ ```
460
+
461
+ The template file contains no credentials — it's a plain JSON array of template
462
+ definitions (project/task shape, no auth data) — so, like the rest of the config file, it
463
+ doesn't need Docker-secrets treatment; only the volume itself needs to persist across
464
+ container recreations.
465
+
466
+ ## Secrets Management
467
+
468
+ **The config file is for non-sensitive settings only.** It's designed to be safe to
469
+ commit to source control and safe to mount as a read-only Docker/Swarm `config` — so
470
+ credentials must never be written into it. Secrets belong in environment variables.
471
+
472
+ ### The `*_FILE` Convention
473
+
474
+ Every sensitive environment variable also accepts a `<NAME>_FILE` variant that names a
475
+ file whose contents are read at startup and used in place of the plain variable — the
476
+ same convention the official `postgres`/`mysql` Docker images use
477
+ (`POSTGRES_PASSWORD_FILE`, etc.), which plugs directly into Docker/Swarm/Kubernetes
478
+ secrets mounted as files.
479
+
480
+ Currently sensitive variables (audited against every `process.env.*` read under `src/`):
481
+
482
+ | Variable | `_FILE` variant |
483
+ |---|---|
484
+ | `VIKUNJA_API_TOKEN` | `VIKUNJA_API_TOKEN_FILE` |
485
+
486
+ Behavior:
487
+
488
+ - File contents are read once at startup and **trimmed of surrounding whitespace**
489
+ (trailing newlines from `echo`/`printf`-created secret files are common and would
490
+ otherwise silently corrupt the token).
491
+ - Setting **both** the plain variable and its `_FILE` variant is a **hard startup
492
+ error** — never a silent precedence choice. This matches the postgres-image
493
+ convention and avoids a class of bug where an operator believes they've moved a
494
+ secret into a file but the plain env var (e.g. left over in a `.env` file) is still
495
+ silently taking priority.
496
+ - Neither set: the plain variable's absence is handled exactly as before (e.g. no
497
+ auto-authentication).
498
+
499
+ ```env
500
+ # Use a file-mounted secret instead of the plain token
501
+ VIKUNJA_API_TOKEN_FILE=/run/secrets/vikunja_token
502
+ ```
503
+
504
+ ```env
505
+ # Hard error at startup — remove one of these
506
+ VIKUNJA_API_TOKEN=tk_xxx
507
+ VIKUNJA_API_TOKEN_FILE=/run/secrets/vikunja_token
508
+ ```
509
+
510
+ ### Docker Swarm Example
511
+
512
+ Config file mounted as a `config` (non-sensitive), token mounted as a `secret`:
513
+
514
+ ```yaml
515
+ version: "3.8"
516
+
517
+ services:
518
+ vikunja-mcp:
519
+ image: ghcr.io/netadvanced/vikunja-mcp-ng:latest
520
+ environment:
521
+ VIKUNJA_URL: "https://vikunja.example.com/api/v1"
522
+ VIKUNJA_API_TOKEN_FILE: /run/secrets/vikunja_api_token
523
+ VIKUNJA_MCP_CONFIG: /etc/vikunja-mcp/vikunja-mcp.config.json
524
+ configs:
525
+ - source: vikunja_mcp_config
526
+ target: /etc/vikunja-mcp/vikunja-mcp.config.json
527
+ mode: 0444
528
+ secrets:
529
+ - source: vikunja_api_token
530
+ target: vikunja_api_token
531
+ mode: 0400
532
+
533
+ configs:
534
+ vikunja_mcp_config:
535
+ file: ./vikunja-mcp.config.json
536
+
537
+ secrets:
538
+ vikunja_api_token:
539
+ file: ./secrets/vikunja_api_token.txt
540
+ ```
541
+
542
+ Deploy with:
543
+
544
+ ```bash
545
+ docker swarm init # if not already a swarm manager
546
+ docker stack deploy -c docker-compose.yml vikunja-mcp
547
+ ```
548
+
549
+ The token file (`./secrets/vikunja_api_token.txt`) should contain only the token,
550
+ optionally with a trailing newline — it will be trimmed automatically. It should never
551
+ be committed to source control; the config file (`./vikunja-mcp.config.json`) is safe to
552
+ commit since it contains no credentials.
553
+
554
+ ## Environment Variables Reference
555
+
556
+ ### Authentication Variables
557
+ ```env
558
+ VIKUNJA_URL=https://vikunja.example.com
559
+ VIKUNJA_API_TOKEN=tk_your_token_here # or VIKUNJA_API_TOKEN_FILE=/path/to/token — see Secrets Management
560
+ MCP_MODE=server
561
+ ```
562
+
563
+ ### Config File Variable
564
+ ```env
565
+ VIKUNJA_MCP_CONFIG=/path/to/vikunja-mcp.config.json # optional; see Config File
566
+ ```
567
+
568
+ ### Module Gating Variables
569
+ ```env
570
+ VIKUNJA_MCP_MODULE_TASKS=true # see Module Gating for the full list and defaults
571
+ ```
572
+
573
+ ### Global Read-Only Mode Variable
574
+ ```env
575
+ VIKUNJA_MCP_READ_ONLY=true # optional, default false; see Global Read-Only Safety Mode
576
+ ```
577
+
578
+ ### Templates Persistence Variable
579
+ ```env
580
+ VIKUNJA_MCP_TEMPLATES_FILE=/path/to/templates.json # optional; see Templates Persistence
581
+ ```
582
+
583
+ ### Logging Variables
584
+ ```env
585
+ LOG_LEVEL=info # error, warn, info, debug
586
+ DEBUG=false # true/false
587
+ NODE_ENV=production # development, test, production
588
+ ```
589
+
590
+ ### Rate Limiting Variables
591
+ ```env
592
+ # Global control
593
+ RATE_LIMIT_ENABLED=true
594
+
595
+ # Default tool limits
596
+ RATE_LIMIT_PER_MINUTE=60
597
+ RATE_LIMIT_PER_HOUR=1000
598
+ MAX_REQUEST_SIZE=1048576 # 1MB in bytes
599
+ MAX_RESPONSE_SIZE=10485760 # 10MB in bytes
600
+ TOOL_TIMEOUT=30000 # 30 seconds in milliseconds
601
+
602
+ # Expensive tool limits
603
+ EXPENSIVE_RATE_LIMIT_PER_MINUTE=10
604
+ EXPENSIVE_RATE_LIMIT_PER_HOUR=100
605
+ EXPENSIVE_MAX_REQUEST_SIZE=2097152
606
+ EXPENSIVE_MAX_RESPONSE_SIZE=52428800
607
+ EXPENSIVE_TOOL_TIMEOUT=120000
608
+
609
+ # Bulk operation limits
610
+ BULK_RATE_LIMIT_PER_MINUTE=5
611
+ BULK_RATE_LIMIT_PER_HOUR=50
612
+ BULK_MAX_REQUEST_SIZE=5242880
613
+ BULK_MAX_RESPONSE_SIZE=104857600
614
+ BULK_TOOL_TIMEOUT=300000
615
+
616
+ # Export operation limits
617
+ EXPORT_RATE_LIMIT_PER_MINUTE=2
618
+ EXPORT_RATE_LIMIT_PER_HOUR=10
619
+ EXPORT_MAX_REQUEST_SIZE=1048576
620
+ EXPORT_MAX_RESPONSE_SIZE=1073741824
621
+ EXPORT_TOOL_TIMEOUT=600000
622
+ ```
623
+
624
+ ### Feature Flag Variables
625
+ ```env
626
+ VIKUNJA_ENABLE_SERVER_SIDE_FILTERING=true
627
+ ```
628
+
629
+ ## Usage Patterns
630
+
631
+ ### Application Initialization
632
+
633
+ ```typescript
634
+ import { ConfigurationManager, getConfiguration } from './config';
635
+
636
+ async function initializeApplication() {
637
+ try {
638
+ // Load and validate configuration early
639
+ const config = await getConfiguration();
640
+
641
+ // Use validated configuration
642
+ if (config.auth.vikunjaUrl && config.auth.vikunjaToken) {
643
+ await connectToVikunja(config.auth.vikunjaUrl, config.auth.vikunjaToken);
644
+ }
645
+
646
+ // Configure components
647
+ const logger = await createLogger(config.logging);
648
+ const rateLimiter = await createRateLimiter(config.rateLimiting);
649
+
650
+ } catch (error) {
651
+ console.error('Configuration error:', error);
652
+ process.exit(1);
653
+ }
654
+ }
655
+ ```
656
+
657
+ ### Component Configuration
658
+
659
+ ```typescript
660
+ // Before: Direct environment usage
661
+ export class RateLimitingMiddleware {
662
+ constructor() {
663
+ this.requestsPerMinute = parseInt(process.env.RATE_LIMIT_PER_MINUTE || '60', 10);
664
+ this.enabled = process.env.RATE_LIMIT_ENABLED !== 'false';
665
+ }
666
+ }
667
+
668
+ // After: Configuration injection
669
+ export class RateLimitingMiddleware {
670
+ constructor(private config: RateLimitConfig) {
671
+ // Configuration already validated and typed
672
+ }
673
+
674
+ static async create(): Promise<RateLimitingMiddleware> {
675
+ const config = await getRateLimitConfig();
676
+ return new RateLimitingMiddleware(config);
677
+ }
678
+ }
679
+ ```
680
+
681
+ ### Feature Flag Checks
682
+
683
+ ```typescript
684
+ // Simple boolean check
685
+ if (await isFeatureEnabled('enableServerSideFiltering')) {
686
+ return useServerSideStrategy();
687
+ } else {
688
+ return useClientSideStrategy();
689
+ }
690
+
691
+ // Configuration-dependent logic
692
+ const featureFlags = await getFeatureFlagsConfig();
693
+ const strategy = featureFlags.enableServerSideFiltering ?
694
+ 'server-side' : 'client-side';
695
+ ```
696
+
697
+ ## Testing with Configuration
698
+
699
+ ### Test Configuration Injection
700
+
701
+ ```typescript
702
+ import { ConfigurationManager } from '../src/config';
703
+
704
+ describe('RateLimitingMiddleware', () => {
705
+ beforeEach(() => {
706
+ // Reset singleton for clean test state
707
+ ConfigurationManager.reset();
708
+ });
709
+
710
+ it('should respect custom rate limits', async () => {
711
+ // Inject test configuration
712
+ const manager = ConfigurationManager.getInstance({
713
+ sources: {
714
+ rateLimiting: {
715
+ enabled: true,
716
+ default: {
717
+ requestsPerMinute: 5, // Very low for testing
718
+ requestsPerHour: 50,
719
+ maxRequestSize: 1000,
720
+ maxResponseSize: 10000,
721
+ executionTimeout: 5000,
722
+ }
723
+ }
724
+ }
725
+ });
726
+
727
+ const config = await manager.getRateLimitConfig();
728
+ expect(config.default.requestsPerMinute).toBe(5);
729
+ });
730
+ });
731
+ ```
732
+
733
+ ### Environment-Specific Testing
734
+
735
+ ```typescript
736
+ // Test development profile behavior
737
+ it('should disable rate limiting in development', async () => {
738
+ const manager = ConfigurationManager.getInstance({
739
+ environment: Environment.DEVELOPMENT
740
+ });
741
+
742
+ const config = await manager.getRateLimitConfig();
743
+ expect(config.enabled).toBe(false);
744
+ });
745
+
746
+ // Test validation errors
747
+ it('should reject invalid configuration', async () => {
748
+ const manager = ConfigurationManager.getInstance({
749
+ sources: {
750
+ rateLimiting: {
751
+ default: {
752
+ requestsPerMinute: -1 // Invalid negative value
753
+ }
754
+ }
755
+ }
756
+ });
757
+
758
+ await expect(manager.getConfiguration()).rejects.toThrow(ConfigurationError);
759
+ });
760
+ ```
761
+
762
+ ## Error Handling
763
+
764
+ ### Configuration Errors
765
+
766
+ The system provides detailed validation errors:
767
+
768
+ ```typescript
769
+ try {
770
+ const config = await getConfiguration();
771
+ } catch (error) {
772
+ if (error instanceof ConfigurationError) {
773
+ console.error('Configuration validation failed:');
774
+ console.error(`Field: ${error.field}`);
775
+ console.error(`Message: ${error.message}`);
776
+ console.error(`Value: ${error.value}`);
777
+ }
778
+ }
779
+ ```
780
+
781
+ ### Common Error Scenarios
782
+
783
+ 1. **Invalid URL Format**
784
+ ```
785
+ Configuration error in validation: Configuration validation failed:
786
+ - auth.vikunjaUrl: Invalid url
787
+ ```
788
+
789
+ 2. **Negative Rate Limits**
790
+ ```
791
+ Configuration error in validation: Configuration validation failed:
792
+ - rateLimiting.default.requestsPerMinute: Number must be greater than 0
793
+ ```
794
+
795
+ 3. **Invalid Log Level**
796
+ ```
797
+ Configuration error in validation: Configuration validation failed:
798
+ - logging.level: Invalid enum value. Expected 'error' | 'warn' | 'info' | 'debug', received 'verbose'
799
+ ```
800
+
801
+ ## Migration from Legacy Configuration
802
+
803
+ ### Step 1: Update Imports
804
+ ```typescript
805
+ // Before
806
+ const maxSize = parseInt(process.env.MAX_REQUEST_SIZE || '1048576', 10);
807
+
808
+ // After
809
+ import { getRateLimitConfig } from './config';
810
+ const config = await getRateLimitConfig();
811
+ const maxSize = config.default.maxRequestSize;
812
+ ```
813
+
814
+ ### Step 2: Handle Async Configuration
815
+ ```typescript
816
+ // Before: Synchronous constructor
817
+ class Logger {
818
+ constructor() {
819
+ this.level = process.env.LOG_LEVEL || 'info';
820
+ }
821
+ }
822
+
823
+ // After: Async factory
824
+ class Logger {
825
+ constructor(private config: LoggingConfig) {}
826
+
827
+ static async create(): Promise<Logger> {
828
+ const config = await getLoggingConfig();
829
+ return new Logger(config);
830
+ }
831
+ }
832
+ ```
833
+
834
+ ### Step 3: Update Tests
835
+ ```typescript
836
+ // Before: Environment manipulation
837
+ beforeEach(() => {
838
+ process.env.RATE_LIMIT_PER_MINUTE = '30';
839
+ });
840
+
841
+ // After: Configuration injection
842
+ beforeEach(() => {
843
+ ConfigurationManager.reset();
844
+ ConfigurationManager.getInstance({
845
+ sources: { rateLimiting: { default: { requestsPerMinute: 30 } } }
846
+ });
847
+ });
848
+ ```
849
+
850
+ ## Performance Considerations
851
+
852
+ ### Configuration Caching
853
+ - Configuration is loaded once and cached per ConfigurationManager instance
854
+ - Subsequent calls to `getConfiguration()` return cached values
855
+ - Use `ConfigurationManager.reset()` to clear cache (testing only)
856
+
857
+ ### Memory Usage
858
+ - Configuration schemas use minimal memory overhead
859
+ - Zod validation occurs only during initial load
860
+ - No performance impact on application runtime
861
+
862
+ ### Startup Performance
863
+ - Configuration loading adds ~1-5ms to application startup
864
+ - All validation errors are caught early in startup process
865
+ - Async loading prevents blocking main application logic
866
+
867
+ ## Best Practices
868
+
869
+ ### 1. Load Configuration Early
870
+ ```typescript
871
+ // Good: Load configuration at application startup
872
+ async function main() {
873
+ const config = await getConfiguration();
874
+ // Initialize components with configuration
875
+ }
876
+
877
+ // Avoid: Loading configuration in hot paths
878
+ async function handleRequest() {
879
+ const config = await getConfiguration(); // Cache hit, but still async
880
+ // Handle request
881
+ }
882
+ ```
883
+
884
+ ### 2. Use Type-Safe Configuration
885
+ ```typescript
886
+ // Good: Use typed configuration sections
887
+ const rateLimits = await getRateLimitConfig();
888
+ const limit = rateLimits.default.requestsPerMinute; // TypeScript knows this is number
889
+
890
+ // Avoid: Accessing nested properties without types
891
+ const config = await getConfiguration();
892
+ const limit = (config as any).rateLimiting.default.requestsPerMinute;
893
+ ```
894
+
895
+ ### 3. Handle Configuration Errors Gracefully
896
+ ```typescript
897
+ // Good: Specific error handling
898
+ try {
899
+ const config = await getConfiguration();
900
+ } catch (error) {
901
+ if (error instanceof ConfigurationError) {
902
+ logger.error('Configuration validation failed', { error: error.message });
903
+ process.exit(1);
904
+ }
905
+ throw error; // Re-throw unexpected errors
906
+ }
907
+
908
+ // Avoid: Generic error handling
909
+ try {
910
+ const config = await getConfiguration();
911
+ } catch (error) {
912
+ console.error('Something went wrong:', error);
913
+ }
914
+ ```
915
+
916
+ ### 4. Test with Configuration Injection
917
+ ```typescript
918
+ // Good: Inject test configuration
919
+ const testManager = ConfigurationManager.getInstance({
920
+ sources: { /* test configuration */ }
921
+ });
922
+
923
+ // Avoid: Manipulating process.env in tests
924
+ process.env.RATE_LIMIT_PER_MINUTE = '30';
925
+ ```
926
+
927
+ ## Troubleshooting
928
+
929
+ ### Common Issues
930
+
931
+ 1. **Configuration Not Loading**
932
+ - Check that `getConfiguration()` is awaited
933
+ - Verify environment variables are set correctly
934
+ - Check for validation errors in logs
935
+
936
+ 2. **Environment Variables Not Recognized**
937
+ - Verify variable names match `.env.example`
938
+ - Check for typos in environment variable names
939
+ - Ensure values are in correct format (numbers, booleans)
940
+
941
+ 3. **Test Failures After Migration**
942
+ - Reset ConfigurationManager in test setup
943
+ - Replace environment manipulation with configuration injection
944
+ - Update mocks to use new configuration patterns
945
+
946
+ ### Debug Configuration Loading
947
+
948
+ ```typescript
949
+ // Enable detailed configuration logging
950
+ const config = await ConfigurationManager.getInstance({
951
+ sources: { logging: { level: 'debug' } }
952
+ }).getConfiguration();
953
+
954
+ // Configuration loading details will be logged
955
+ ```
956
+
957
+ This centralized configuration system eliminates the 57 hours of technical debt from environment variable sprawl while providing type safety, better testing capabilities, and improved developer experience.