@dizzlkheinz/ynab-mcpb 0.18.3 → 0.19.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 (346) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CLAUDE.md +87 -8
  3. package/bin/ynab-mcp-server.cjs +2 -2
  4. package/bin/ynab-mcp-server.js +3 -3
  5. package/biome.json +39 -0
  6. package/dist/bundle/index.cjs +67 -67
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +27 -27
  9. package/dist/server/YNABMCPServer.d.ts +3 -4
  10. package/dist/server/YNABMCPServer.js +111 -116
  11. package/dist/server/budgetResolver.d.ts +6 -5
  12. package/dist/server/budgetResolver.js +46 -36
  13. package/dist/server/cacheKeys.js +6 -6
  14. package/dist/server/cacheManager.js +14 -11
  15. package/dist/server/completions.d.ts +2 -2
  16. package/dist/server/completions.js +20 -15
  17. package/dist/server/config.d.ts +10 -5
  18. package/dist/server/config.js +24 -7
  19. package/dist/server/deltaCache.d.ts +2 -2
  20. package/dist/server/deltaCache.js +22 -16
  21. package/dist/server/deltaCache.merge.d.ts +2 -2
  22. package/dist/server/diagnostics.d.ts +4 -4
  23. package/dist/server/diagnostics.js +38 -32
  24. package/dist/server/errorHandler.d.ts +5 -12
  25. package/dist/server/errorHandler.js +219 -217
  26. package/dist/server/prompts.d.ts +2 -2
  27. package/dist/server/prompts.js +45 -45
  28. package/dist/server/rateLimiter.js +4 -4
  29. package/dist/server/requestLogger.d.ts +1 -1
  30. package/dist/server/requestLogger.js +40 -35
  31. package/dist/server/resources.d.ts +3 -3
  32. package/dist/server/resources.js +55 -52
  33. package/dist/server/responseFormatter.js +6 -6
  34. package/dist/server/securityMiddleware.d.ts +2 -2
  35. package/dist/server/securityMiddleware.js +22 -20
  36. package/dist/server/serverKnowledgeStore.js +1 -1
  37. package/dist/server/toolRegistry.d.ts +3 -3
  38. package/dist/server/toolRegistry.js +47 -40
  39. package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
  40. package/dist/tools/__tests__/deltaTestUtils.js +2 -2
  41. package/dist/tools/accountTools.d.ts +9 -8
  42. package/dist/tools/accountTools.js +47 -47
  43. package/dist/tools/adapters.d.ts +13 -8
  44. package/dist/tools/adapters.js +21 -11
  45. package/dist/tools/budgetTools.d.ts +8 -7
  46. package/dist/tools/budgetTools.js +22 -22
  47. package/dist/tools/categoryTools.d.ts +9 -8
  48. package/dist/tools/categoryTools.js +68 -59
  49. package/dist/tools/compareTransactions/formatter.d.ts +3 -3
  50. package/dist/tools/compareTransactions/formatter.js +9 -9
  51. package/dist/tools/compareTransactions/index.d.ts +6 -6
  52. package/dist/tools/compareTransactions/index.js +58 -43
  53. package/dist/tools/compareTransactions/matcher.d.ts +1 -1
  54. package/dist/tools/compareTransactions/matcher.js +28 -15
  55. package/dist/tools/compareTransactions/parser.d.ts +2 -2
  56. package/dist/tools/compareTransactions/parser.js +144 -138
  57. package/dist/tools/compareTransactions/types.d.ts +4 -4
  58. package/dist/tools/compareTransactions.d.ts +1 -1
  59. package/dist/tools/compareTransactions.js +1 -1
  60. package/dist/tools/deltaFetcher.d.ts +2 -2
  61. package/dist/tools/deltaFetcher.js +16 -15
  62. package/dist/tools/deltaSupport.d.ts +4 -4
  63. package/dist/tools/deltaSupport.js +35 -41
  64. package/dist/tools/exportTransactions.d.ts +5 -4
  65. package/dist/tools/exportTransactions.js +61 -59
  66. package/dist/tools/monthTools.d.ts +7 -6
  67. package/dist/tools/monthTools.js +31 -29
  68. package/dist/tools/payeeTools.d.ts +7 -6
  69. package/dist/tools/payeeTools.js +28 -28
  70. package/dist/tools/reconcileAdapter.d.ts +2 -2
  71. package/dist/tools/reconcileAdapter.js +21 -11
  72. package/dist/tools/reconciliation/analyzer.d.ts +4 -4
  73. package/dist/tools/reconciliation/analyzer.js +136 -57
  74. package/dist/tools/reconciliation/csvParser.d.ts +3 -3
  75. package/dist/tools/reconciliation/csvParser.js +128 -104
  76. package/dist/tools/reconciliation/executor.d.ts +4 -4
  77. package/dist/tools/reconciliation/executor.js +148 -109
  78. package/dist/tools/reconciliation/index.d.ts +10 -10
  79. package/dist/tools/reconciliation/index.js +96 -83
  80. package/dist/tools/reconciliation/matcher.d.ts +3 -3
  81. package/dist/tools/reconciliation/matcher.js +17 -16
  82. package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
  83. package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
  84. package/dist/tools/reconciliation/recommendationEngine.js +40 -40
  85. package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
  86. package/dist/tools/reconciliation/reportFormatter.js +79 -54
  87. package/dist/tools/reconciliation/signDetector.d.ts +1 -1
  88. package/dist/tools/reconciliation/types.d.ts +19 -16
  89. package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
  90. package/dist/tools/schemas/common.d.ts +1 -1
  91. package/dist/tools/schemas/common.js +1 -1
  92. package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
  93. package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
  94. package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
  95. package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
  96. package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
  97. package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
  98. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
  99. package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
  100. package/dist/tools/schemas/outputs/index.d.ts +14 -14
  101. package/dist/tools/schemas/outputs/index.js +14 -14
  102. package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
  103. package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
  104. package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
  105. package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
  106. package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
  107. package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
  108. package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
  109. package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
  110. package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
  111. package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
  112. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
  113. package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
  114. package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
  115. package/dist/tools/schemas/shared/commonOutputs.js +15 -9
  116. package/dist/tools/transactionReadTools.d.ts +11 -0
  117. package/dist/tools/transactionReadTools.js +202 -0
  118. package/dist/tools/transactionSchemas.d.ts +309 -0
  119. package/dist/tools/transactionSchemas.js +235 -0
  120. package/dist/tools/transactionTools.d.ts +6 -302
  121. package/dist/tools/transactionTools.js +7 -2054
  122. package/dist/tools/transactionUtils.d.ts +31 -0
  123. package/dist/tools/transactionUtils.js +364 -0
  124. package/dist/tools/transactionWriteTools.d.ts +20 -0
  125. package/dist/tools/transactionWriteTools.js +1342 -0
  126. package/dist/tools/utilityTools.d.ts +5 -4
  127. package/dist/tools/utilityTools.js +11 -11
  128. package/dist/types/index.d.ts +7 -7
  129. package/dist/types/index.js +6 -6
  130. package/dist/types/reconciliation.d.ts +1 -1
  131. package/dist/types/toolRegistration.d.ts +14 -12
  132. package/dist/utils/amountUtils.js +1 -1
  133. package/dist/utils/dateUtils.js +4 -4
  134. package/dist/utils/errors.d.ts +3 -3
  135. package/dist/utils/errors.js +4 -4
  136. package/dist/utils/money.d.ts +2 -2
  137. package/dist/utils/money.js +8 -8
  138. package/dist/utils/validationError.d.ts +1 -1
  139. package/dist/utils/validationError.js +1 -1
  140. package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
  141. package/docs/assets/schemas/reconciliation-v2.json +360 -336
  142. package/docs/plans/2025-12-25-transaction-tools-refactor-design.md +211 -0
  143. package/docs/plans/2025-12-25-transaction-tools-refactor.md +905 -0
  144. package/esbuild.config.mjs +53 -50
  145. package/meta.json +12548 -12548
  146. package/package.json +98 -109
  147. package/scripts/analyze-bundle.mjs +33 -30
  148. package/scripts/create-pr-description.js +169 -120
  149. package/scripts/run-all-tests.js +205 -0
  150. package/scripts/run-domain-integration-tests.js +28 -18
  151. package/scripts/run-generate-mcpb.js +19 -17
  152. package/scripts/run-throttled-integration-tests.js +92 -83
  153. package/scripts/test-delta-params.mjs +149 -120
  154. package/scripts/test-recommendations.ts +36 -32
  155. package/scripts/tmpTransaction.ts +80 -43
  156. package/scripts/validate-env.js +98 -91
  157. package/scripts/verify-build.js +78 -76
  158. package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
  159. package/src/__tests__/performance.test.ts +723 -671
  160. package/src/__tests__/setup.ts +442 -395
  161. package/src/__tests__/smoke.e2e.test.ts +41 -39
  162. package/src/__tests__/testRunner.ts +314 -295
  163. package/src/__tests__/testUtils.ts +456 -364
  164. package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
  165. package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
  166. package/src/index.ts +68 -59
  167. package/src/server/CLAUDE.md +480 -0
  168. package/src/server/YNABMCPServer.ts +821 -794
  169. package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
  170. package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
  171. package/src/server/__tests__/budgetResolver.test.ts +466 -423
  172. package/src/server/__tests__/cacheManager.test.ts +891 -874
  173. package/src/server/__tests__/completions.integration.test.ts +115 -106
  174. package/src/server/__tests__/completions.test.ts +334 -313
  175. package/src/server/__tests__/config.test.ts +98 -86
  176. package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
  177. package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
  178. package/src/server/__tests__/deltaCache.test.ts +946 -759
  179. package/src/server/__tests__/diagnostics.test.ts +825 -792
  180. package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
  181. package/src/server/__tests__/errorHandler.test.ts +402 -397
  182. package/src/server/__tests__/prompts.test.ts +424 -347
  183. package/src/server/__tests__/rateLimiter.test.ts +313 -309
  184. package/src/server/__tests__/requestLogger.test.ts +443 -403
  185. package/src/server/__tests__/resources.template.test.ts +196 -185
  186. package/src/server/__tests__/resources.test.ts +294 -288
  187. package/src/server/__tests__/security.integration.test.ts +487 -421
  188. package/src/server/__tests__/securityMiddleware.test.ts +519 -444
  189. package/src/server/__tests__/server-startup.integration.test.ts +509 -490
  190. package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
  191. package/src/server/__tests__/toolRegistration.test.ts +239 -210
  192. package/src/server/__tests__/toolRegistry.test.ts +907 -845
  193. package/src/server/budgetResolver.ts +221 -181
  194. package/src/server/cacheKeys.ts +6 -6
  195. package/src/server/cacheManager.ts +498 -484
  196. package/src/server/completions.ts +267 -243
  197. package/src/server/config.ts +35 -14
  198. package/src/server/deltaCache.merge.ts +146 -128
  199. package/src/server/deltaCache.ts +352 -309
  200. package/src/server/diagnostics.ts +257 -242
  201. package/src/server/errorHandler.ts +747 -744
  202. package/src/server/prompts.ts +181 -176
  203. package/src/server/rateLimiter.ts +131 -129
  204. package/src/server/requestLogger.ts +350 -322
  205. package/src/server/resources.ts +442 -374
  206. package/src/server/responseFormatter.ts +41 -37
  207. package/src/server/securityMiddleware.ts +223 -205
  208. package/src/server/serverKnowledgeStore.ts +67 -67
  209. package/src/server/toolRegistry.ts +508 -474
  210. package/src/tools/CLAUDE.md +604 -0
  211. package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
  212. package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
  213. package/src/tools/__tests__/accountTools.test.ts +685 -638
  214. package/src/tools/__tests__/adapters.test.ts +142 -108
  215. package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
  216. package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
  217. package/src/tools/__tests__/budgetTools.test.ts +442 -413
  218. package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
  219. package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
  220. package/src/tools/__tests__/categoryTools.test.ts +656 -625
  221. package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
  222. package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
  223. package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
  224. package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
  225. package/src/tools/__tests__/compareTransactions.test.ts +352 -332
  226. package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
  227. package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
  228. package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
  229. package/src/tools/__tests__/deltaSupport.test.ts +211 -184
  230. package/src/tools/__tests__/deltaTestUtils.ts +37 -33
  231. package/src/tools/__tests__/exportTransactions.test.ts +205 -200
  232. package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
  233. package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
  234. package/src/tools/__tests__/monthTools.test.ts +561 -512
  235. package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
  236. package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
  237. package/src/tools/__tests__/payeeTools.test.ts +486 -434
  238. package/src/tools/__tests__/transactionSchemas.test.ts +1204 -0
  239. package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
  240. package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
  241. package/src/tools/__tests__/transactionUtils.test.ts +1016 -0
  242. package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
  243. package/src/tools/__tests__/utilityTools.test.ts +68 -58
  244. package/src/tools/accountTools.ts +293 -271
  245. package/src/tools/adapters.ts +120 -63
  246. package/src/tools/budgetTools.ts +121 -116
  247. package/src/tools/categoryTools.ts +379 -339
  248. package/src/tools/compareTransactions/formatter.ts +131 -119
  249. package/src/tools/compareTransactions/index.ts +249 -214
  250. package/src/tools/compareTransactions/matcher.ts +259 -209
  251. package/src/tools/compareTransactions/parser.ts +517 -487
  252. package/src/tools/compareTransactions/types.ts +38 -38
  253. package/src/tools/compareTransactions.ts +1 -1
  254. package/src/tools/deltaFetcher.ts +281 -260
  255. package/src/tools/deltaSupport.ts +264 -259
  256. package/src/tools/exportTransactions.ts +230 -218
  257. package/src/tools/monthTools.ts +180 -165
  258. package/src/tools/payeeTools.ts +152 -140
  259. package/src/tools/reconcileAdapter.ts +297 -246
  260. package/src/tools/reconciliation/CLAUDE.md +506 -0
  261. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +135 -112
  262. package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -227
  263. package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -335
  264. package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
  265. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
  266. package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
  267. package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
  268. package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
  269. package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
  270. package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
  271. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -986
  272. package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
  273. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -530
  274. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -71
  275. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -58
  276. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
  277. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +58 -43
  278. package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
  279. package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
  280. package/src/tools/reconciliation/analyzer.ts +582 -406
  281. package/src/tools/reconciliation/csvParser.ts +656 -609
  282. package/src/tools/reconciliation/executor.ts +1290 -1128
  283. package/src/tools/reconciliation/index.ts +580 -528
  284. package/src/tools/reconciliation/matcher.ts +256 -240
  285. package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
  286. package/src/tools/reconciliation/recommendationEngine.ts +357 -345
  287. package/src/tools/reconciliation/reportFormatter.ts +349 -276
  288. package/src/tools/reconciliation/signDetector.ts +89 -83
  289. package/src/tools/reconciliation/types.ts +164 -153
  290. package/src/tools/reconciliation/ynabAdapter.ts +17 -15
  291. package/src/tools/schemas/CLAUDE.md +546 -0
  292. package/src/tools/schemas/common.ts +1 -1
  293. package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
  294. package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
  295. package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
  296. package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
  297. package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
  298. package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
  299. package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
  300. package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
  301. package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
  302. package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
  303. package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
  304. package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
  305. package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
  306. package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
  307. package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
  308. package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
  309. package/src/tools/schemas/outputs/index.ts +163 -163
  310. package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
  311. package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
  312. package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
  313. package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
  314. package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
  315. package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
  316. package/src/tools/schemas/shared/commonOutputs.ts +27 -19
  317. package/src/tools/toolCategories.ts +114 -114
  318. package/src/tools/transactionReadTools.ts +327 -0
  319. package/src/tools/transactionSchemas.ts +484 -0
  320. package/src/tools/transactionTools.ts +107 -2990
  321. package/src/tools/transactionUtils.ts +621 -0
  322. package/src/tools/transactionWriteTools.ts +2110 -0
  323. package/src/tools/utilityTools.ts +46 -41
  324. package/src/types/CLAUDE.md +477 -0
  325. package/src/types/__tests__/index.test.ts +51 -51
  326. package/src/types/index.ts +43 -39
  327. package/src/types/integration-tests.d.ts +26 -26
  328. package/src/types/reconciliation.ts +29 -29
  329. package/src/types/toolAnnotations.ts +30 -30
  330. package/src/types/toolRegistration.ts +43 -32
  331. package/src/utils/CLAUDE.md +508 -0
  332. package/src/utils/__tests__/dateUtils.test.ts +174 -168
  333. package/src/utils/__tests__/money.test.ts +193 -187
  334. package/src/utils/amountUtils.ts +5 -5
  335. package/src/utils/baseError.ts +5 -5
  336. package/src/utils/dateUtils.ts +29 -26
  337. package/src/utils/errors.ts +14 -14
  338. package/src/utils/money.ts +66 -52
  339. package/src/utils/validationError.ts +1 -1
  340. package/tsconfig.json +29 -29
  341. package/tsconfig.prod.json +16 -16
  342. package/vitest-reporters/split-json-reporter.ts +247 -204
  343. package/vitest.config.ts +99 -95
  344. package/.prettierignore +0 -10
  345. package/.prettierrc.json +0 -10
  346. package/eslint.config.js +0 -49
@@ -1,194 +1,231 @@
1
- import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
- import { ErrorHandler } from './errorHandler.js';
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ import { type ErrorHandler, createErrorHandler } from "./errorHandler.js";
3
+
4
+ /**
5
+ * Module-level fallback ErrorHandler for convenience functions and backward compatibility.
6
+ * Uses a simple JSON formatter; callers that have access to the injected formatter
7
+ * should pass their own ErrorHandler instance via the optional parameter.
8
+ */
9
+ const fallbackErrorHandler = createErrorHandler({
10
+ format: (value: unknown) => JSON.stringify(value, null, 2),
11
+ });
3
12
 
4
13
  /**
5
14
  * Centralized budget resolution helper that standardizes budget ID validation
6
15
  * and resolution logic across the entire YNAB MCP server
7
16
  */
17
+ // biome-ignore lint/complexity/noStaticOnlyClass: static utility class
8
18
  export class BudgetResolver {
9
- /**
10
- * UUID format validation regex (accepts UUID versions 1-5)
11
- */
12
- private static readonly UUID_REGEX =
13
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
14
-
15
- /**
16
- * Special keywords that are allowed as budget IDs
17
- */
18
- private static readonly ALLOWED_KEYWORDS = ['default'] as const;
19
-
20
- /**
21
- * Resolves a budget ID using provided ID or default, with standardized error handling.
22
- * Maps keywords ('default') to concrete budget IDs to prevent 404s from YNAB API.
23
- *
24
- * Note: While YNAB API natively supports the "default" keyword, this MCP server
25
- * intentionally intercepts it to use its own locally-stored default budget ID
26
- * (set via set_default_budget tool). This provides caching, consistency, and
27
- * allows the MCP server to maintain default budget state independently of YNAB's
28
- * OAuth default budget setting.
29
- *
30
- * @param providedId - The budget ID provided by the user (optional)
31
- * @param defaultId - The default budget ID to fall back to (optional)
32
- * @returns The resolved budget ID string or CallToolResult with error
33
- */
34
- static resolveBudgetId(providedId?: string, defaultId?: string): string | CallToolResult {
35
- // If a budget ID is provided (including empty strings), handle keywords first
36
- if (providedId !== undefined && providedId !== null) {
37
- const trimmed = providedId.trim();
38
-
39
- // Handle special keywords
40
- if (trimmed === 'default') {
41
- // For "default" keyword, we use the MCP server's stored default budget ID
42
- // rather than passing "default" to YNAB API (see function JSDoc for rationale)
43
- if (defaultId) {
44
- return this.validateBudgetId(defaultId);
45
- }
46
- // No default budget set in MCP server, return error
47
- return this.createMissingBudgetError();
48
- }
49
-
50
- if (trimmed === 'last-used') {
51
- // "last-used" keyword is not currently supported
52
- return ErrorHandler.createValidationError(
53
- 'Unsupported keyword',
54
- 'The "last-used" keyword is not supported yet. Please use a specific budget ID or set a default budget.',
55
- [
56
- 'Use a specific budget ID (UUID format)',
57
- 'Set a default budget using the set_default_budget tool',
58
- 'Use the "default" keyword after setting a default budget',
59
- 'Run the list_budgets tool to see available budget IDs',
60
- ],
61
- );
62
- }
63
-
64
- // For non-keyword IDs, validate normally (including empty strings)
65
- return this.validateBudgetId(providedId);
66
- }
67
-
68
- // If no budget ID provided, try to use the default
69
- if (defaultId) {
70
- return this.validateBudgetId(defaultId);
71
- }
72
-
73
- // No budget ID provided and no default set
74
- return this.createMissingBudgetError();
75
- }
76
-
77
- /**
78
- * Validates that a budget ID has the correct format
79
- *
80
- * @param budgetId - The budget ID to validate
81
- * @returns The validated budget ID or CallToolResult with error
82
- */
83
- static validateBudgetId(budgetId: string): string | CallToolResult {
84
- if (!budgetId || typeof budgetId !== 'string') {
85
- return this.createInvalidBudgetError('Budget ID must be provided as a non-empty string');
86
- }
87
-
88
- const trimmed = budgetId.trim();
89
- if (!trimmed) {
90
- return this.createInvalidBudgetError('Budget ID cannot be empty or whitespace only');
91
- }
92
-
93
- // Allow simplified identifiers in test environments
94
- if (process.env['NODE_ENV'] === 'test') {
95
- const testIdentifierPattern =
96
- /^(test|budget|account|category|transaction|payee|mock)-[a-z0-9_-]+$/i;
97
- if (testIdentifierPattern.test(trimmed)) {
98
- return trimmed;
99
- }
100
- }
101
-
102
- // Validate UUID format
103
- if (!this.UUID_REGEX.test(trimmed)) {
104
- return this.createInvalidBudgetError(
105
- `Invalid budget ID format: '${trimmed}'. Must be a valid UUID format (versions 1-5)`,
106
- );
107
- }
108
-
109
- return trimmed;
110
- }
111
-
112
- /**
113
- * Creates a standardized error response for missing budget scenarios
114
- *
115
- * @returns CallToolResult with standardized error response
116
- */
117
- static createMissingBudgetError(): CallToolResult {
118
- const detailMessage = `A budget ID is required for this operation. You can either:
19
+ /**
20
+ * UUID format validation regex (accepts UUID versions 1-5)
21
+ */
22
+ private static readonly UUID_REGEX =
23
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
24
+
25
+ /**
26
+ * Special keywords that are allowed as budget IDs
27
+ */
28
+ private static readonly ALLOWED_KEYWORDS = ["default"] as const;
29
+
30
+ /**
31
+ * Resolves a budget ID using provided ID or default, with standardized error handling.
32
+ * Maps keywords ('default') to concrete budget IDs to prevent 404s from YNAB API.
33
+ *
34
+ * Note: While YNAB API natively supports the "default" keyword, this MCP server
35
+ * intentionally intercepts it to use its own locally-stored default budget ID
36
+ * (set via set_default_budget tool). This provides caching, consistency, and
37
+ * allows the MCP server to maintain default budget state independently of YNAB's
38
+ * OAuth default budget setting.
39
+ *
40
+ * @param providedId - The budget ID provided by the user (optional)
41
+ * @param defaultId - The default budget ID to fall back to (optional)
42
+ * @returns The resolved budget ID string or CallToolResult with error
43
+ */
44
+ static resolveBudgetId(
45
+ providedId?: string,
46
+ defaultId?: string,
47
+ errorHandler?: ErrorHandler,
48
+ ): string | CallToolResult {
49
+ // If a budget ID is provided (including empty strings), handle keywords first
50
+ if (providedId !== undefined && providedId !== null) {
51
+ const trimmed = providedId.trim();
52
+
53
+ // Handle special keywords
54
+ if (trimmed === "default") {
55
+ // For "default" keyword, we use the MCP server's stored default budget ID
56
+ // rather than passing "default" to YNAB API (see function JSDoc for rationale)
57
+ if (defaultId) {
58
+ return BudgetResolver.validateBudgetId(defaultId, errorHandler);
59
+ }
60
+ // No default budget set in MCP server, return error
61
+ return BudgetResolver.createMissingBudgetError(errorHandler);
62
+ }
63
+
64
+ if (trimmed === "last-used") {
65
+ // "last-used" keyword is not currently supported
66
+ const eh = errorHandler ?? fallbackErrorHandler;
67
+ return eh.createValidationError(
68
+ "Unsupported keyword",
69
+ 'The "last-used" keyword is not supported yet. Please use a specific budget ID or set a default budget.',
70
+ [
71
+ "Use a specific budget ID (UUID format)",
72
+ "Set a default budget using the set_default_budget tool",
73
+ 'Use the "default" keyword after setting a default budget',
74
+ "Run the list_budgets tool to see available budget IDs",
75
+ ],
76
+ );
77
+ }
78
+
79
+ // For non-keyword IDs, validate normally (including empty strings)
80
+ return BudgetResolver.validateBudgetId(providedId, errorHandler);
81
+ }
82
+
83
+ // If no budget ID provided, try to use the default
84
+ if (defaultId) {
85
+ return BudgetResolver.validateBudgetId(defaultId, errorHandler);
86
+ }
87
+
88
+ // No budget ID provided and no default set
89
+ return BudgetResolver.createMissingBudgetError(errorHandler);
90
+ }
91
+
92
+ /**
93
+ * Validates that a budget ID has the correct format
94
+ *
95
+ * @param budgetId - The budget ID to validate
96
+ * @returns The validated budget ID or CallToolResult with error
97
+ */
98
+ static validateBudgetId(
99
+ budgetId: string,
100
+ errorHandler?: ErrorHandler,
101
+ ): string | CallToolResult {
102
+ if (!budgetId || typeof budgetId !== "string") {
103
+ return BudgetResolver.createInvalidBudgetError(
104
+ "Budget ID must be provided as a non-empty string",
105
+ errorHandler,
106
+ );
107
+ }
108
+
109
+ const trimmed = budgetId.trim();
110
+ if (!trimmed) {
111
+ return BudgetResolver.createInvalidBudgetError(
112
+ "Budget ID cannot be empty or whitespace only",
113
+ errorHandler,
114
+ );
115
+ }
116
+
117
+ // Allow simplified identifiers in test environments
118
+ if (process.env["NODE_ENV"] === "test") {
119
+ const testIdentifierPattern =
120
+ /^(test|budget|account|category|transaction|payee|mock)-[a-z0-9_-]+$/i;
121
+ if (testIdentifierPattern.test(trimmed)) {
122
+ return trimmed;
123
+ }
124
+ }
125
+
126
+ // Validate UUID format
127
+ if (!BudgetResolver.UUID_REGEX.test(trimmed)) {
128
+ return BudgetResolver.createInvalidBudgetError(
129
+ `Invalid budget ID format: '${trimmed}'. Must be a valid UUID format (versions 1-5)`,
130
+ errorHandler,
131
+ );
132
+ }
133
+
134
+ return trimmed;
135
+ }
136
+
137
+ /**
138
+ * Creates a standardized error response for missing budget scenarios
139
+ *
140
+ * @returns CallToolResult with standardized error response
141
+ */
142
+ static createMissingBudgetError(errorHandler?: ErrorHandler): CallToolResult {
143
+ const detailMessage = `A budget ID is required for this operation. You can either:
119
144
  1. Provide a specific budget_id parameter
120
145
  2. Set a default budget using the set_default_budget tool first`;
121
146
 
122
- return ErrorHandler.createValidationError(
123
- 'No budget ID provided and no default budget set',
124
- detailMessage,
125
- [
126
- 'Set a default budget first using the set_default_budget tool',
127
- 'Provide a budget_id parameter when invoking the tool',
128
- ],
129
- );
130
- }
131
-
132
- /**
133
- * Creates a standardized error response for invalid budget ID format
134
- *
135
- * @param details - Specific details about the validation failure
136
- * @returns CallToolResult with standardized error response
137
- */
138
- static createInvalidBudgetError(details: string): CallToolResult {
139
- const detailMessage = `${details}
147
+ const eh = errorHandler ?? fallbackErrorHandler;
148
+ return eh.createValidationError(
149
+ "No budget ID provided and no default budget set",
150
+ detailMessage,
151
+ [
152
+ "Set a default budget first using the set_default_budget tool",
153
+ "Provide a budget_id parameter when invoking the tool",
154
+ ],
155
+ );
156
+ }
157
+
158
+ /**
159
+ * Creates a standardized error response for invalid budget ID format
160
+ *
161
+ * @param details - Specific details about the validation failure
162
+ * @returns CallToolResult with standardized error response
163
+ */
164
+ static createInvalidBudgetError(
165
+ details: string,
166
+ errorHandler?: ErrorHandler,
167
+ ): CallToolResult {
168
+ const detailMessage = `${details}
140
169
 
141
170
  Valid formats:
142
171
  - UUID format (versions 1-5, e.g., "123e4567-e89b-12d3-a456-426614174000")
143
- - Special keywords: ${this.ALLOWED_KEYWORDS.map((k) => `"${k}"`).join(', ')}
172
+ - Special keywords: ${BudgetResolver.ALLOWED_KEYWORDS.map((k) => `"${k}"`).join(", ")}
144
173
 
145
174
  You can use the list_budgets tool to see available budget IDs.`;
146
175
 
147
- return ErrorHandler.createValidationError('Invalid budget ID format', detailMessage, [
148
- 'Use a valid UUID format (UUID v1-v5, e.g., 123e4567-e89b-12d3-a456-426614174000; standard UUID v4 format works as well)',
149
- 'Run the list_budgets tool to view available budget IDs',
150
- 'Use the special keyword "default" for convenience',
151
- ]);
152
- }
153
-
154
- /**
155
- * Convenience function that throws an error if budget resolution fails
156
- *
157
- * @param providedId - The budget ID provided by the user (optional)
158
- * @param defaultId - The default budget ID to fall back to (optional)
159
- * @returns The resolved budget ID string
160
- * @throws Error if resolution fails
161
- */
162
- static resolveBudgetIdOrThrow(providedId?: string, defaultId?: string): string {
163
- const result = this.resolveBudgetId(providedId, defaultId);
164
- if (typeof result === 'string') {
165
- return result;
166
- }
167
-
168
- // Extract error message from CallToolResult for throwing
169
- const errorText =
170
- result.content?.[0]?.type === 'text' ? result.content[0].text : 'Budget resolution failed';
171
- throw new Error(errorText);
172
- }
173
-
174
- /**
175
- * Convenience function that validates budget ID and throws an error if validation fails
176
- *
177
- * @param budgetId - The budget ID to validate
178
- * @returns The validated budget ID string
179
- * @throws Error if validation fails
180
- */
181
- static validateBudgetIdOrThrow(budgetId: string): string {
182
- const result = this.validateBudgetId(budgetId);
183
- if (typeof result === 'string') {
184
- return result;
185
- }
186
-
187
- // Extract error message from CallToolResult for throwing
188
- const errorText =
189
- result.content?.[0]?.type === 'text' ? result.content[0].text : 'Budget validation failed';
190
- throw new Error(errorText);
191
- }
176
+ const eh = errorHandler ?? fallbackErrorHandler;
177
+ return eh.createValidationError("Invalid budget ID format", detailMessage, [
178
+ "Use a valid UUID format (UUID v1-v5, e.g., 123e4567-e89b-12d3-a456-426614174000; standard UUID v4 format works as well)",
179
+ "Run the list_budgets tool to view available budget IDs",
180
+ 'Use the special keyword "default" for convenience',
181
+ ]);
182
+ }
183
+
184
+ /**
185
+ * Convenience function that throws an error if budget resolution fails
186
+ *
187
+ * @param providedId - The budget ID provided by the user (optional)
188
+ * @param defaultId - The default budget ID to fall back to (optional)
189
+ * @returns The resolved budget ID string
190
+ * @throws Error if resolution fails
191
+ */
192
+ static resolveBudgetIdOrThrow(
193
+ providedId?: string,
194
+ defaultId?: string,
195
+ ): string {
196
+ const result = BudgetResolver.resolveBudgetId(providedId, defaultId);
197
+ if (typeof result === "string") {
198
+ return result;
199
+ }
200
+
201
+ // Extract error message from CallToolResult for throwing
202
+ const errorText =
203
+ result.content?.[0]?.type === "text"
204
+ ? result.content[0].text
205
+ : "Budget resolution failed";
206
+ throw new Error(errorText);
207
+ }
208
+
209
+ /**
210
+ * Convenience function that validates budget ID and throws an error if validation fails
211
+ *
212
+ * @param budgetId - The budget ID to validate
213
+ * @returns The validated budget ID string
214
+ * @throws Error if validation fails
215
+ */
216
+ static validateBudgetIdOrThrow(budgetId: string): string {
217
+ const result = BudgetResolver.validateBudgetId(budgetId);
218
+ if (typeof result === "string") {
219
+ return result;
220
+ }
221
+
222
+ // Extract error message from CallToolResult for throwing
223
+ const errorText =
224
+ result.content?.[0]?.type === "text"
225
+ ? result.content[0].text
226
+ : "Budget validation failed";
227
+ throw new Error(errorText);
228
+ }
192
229
  }
193
230
 
194
231
  /**
@@ -202,8 +239,11 @@ You can use the list_budgets tool to see available budget IDs.`;
202
239
  * @param defaultId - Default budget ID to use if `providedId` is not present (optional)
203
240
  * @returns The resolved budget ID string, or a `CallToolResult` describing a validation error
204
241
  */
205
- export function resolveBudgetId(providedId?: string, defaultId?: string): string | CallToolResult {
206
- return BudgetResolver.resolveBudgetId(providedId, defaultId);
242
+ export function resolveBudgetId(
243
+ providedId?: string,
244
+ defaultId?: string,
245
+ ): string | CallToolResult {
246
+ return BudgetResolver.resolveBudgetId(providedId, defaultId);
207
247
  }
208
248
 
209
249
  /**
@@ -212,7 +252,7 @@ export function resolveBudgetId(providedId?: string, defaultId?: string): string
212
252
  * @returns The validated budget ID string, or a CallToolResult describing the validation error.
213
253
  */
214
254
  export function validateBudgetId(budgetId: string): string | CallToolResult {
215
- return BudgetResolver.validateBudgetId(budgetId);
255
+ return BudgetResolver.validateBudgetId(budgetId);
216
256
  }
217
257
 
218
258
  /**
@@ -221,7 +261,7 @@ export function validateBudgetId(budgetId: string): string | CallToolResult {
221
261
  * @returns A CallToolResult representing a validation error that explains a budget_id is missing and provides guidance to supply a `budget_id` or configure a default budget.
222
262
  */
223
263
  export function createMissingBudgetError(): CallToolResult {
224
- return BudgetResolver.createMissingBudgetError();
264
+ return BudgetResolver.createMissingBudgetError();
225
265
  }
226
266
 
227
267
  /**
@@ -231,5 +271,5 @@ export function createMissingBudgetError(): CallToolResult {
231
271
  * @returns CallToolResult with standardized error response
232
272
  */
233
273
  export function createInvalidBudgetError(details: string): CallToolResult {
234
- return BudgetResolver.createInvalidBudgetError(details);
274
+ return BudgetResolver.createInvalidBudgetError(details);
235
275
  }
@@ -1,8 +1,8 @@
1
1
  export const CacheKeys = {
2
- ACCOUNTS: 'accounts',
3
- BUDGETS: 'budgets',
4
- CATEGORIES: 'categories',
5
- PAYEES: 'payees',
6
- TRANSACTIONS: 'transactions',
7
- MONTHS: 'months',
2
+ ACCOUNTS: "accounts",
3
+ BUDGETS: "budgets",
4
+ CATEGORIES: "categories",
5
+ PAYEES: "payees",
6
+ TRANSACTIONS: "transactions",
7
+ MONTHS: "months",
8
8
  } as const;