@dizzlkheinz/ynab-mcpb 0.18.4 → 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 (343) hide show
  1. package/CLAUDE.md +87 -8
  2. package/bin/ynab-mcp-server.cjs +2 -2
  3. package/bin/ynab-mcp-server.js +3 -3
  4. package/biome.json +39 -0
  5. package/dist/bundle/index.cjs +67 -67
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +27 -27
  8. package/dist/server/YNABMCPServer.d.ts +3 -4
  9. package/dist/server/YNABMCPServer.js +111 -116
  10. package/dist/server/budgetResolver.d.ts +6 -5
  11. package/dist/server/budgetResolver.js +46 -36
  12. package/dist/server/cacheKeys.js +6 -6
  13. package/dist/server/cacheManager.js +14 -11
  14. package/dist/server/completions.d.ts +2 -2
  15. package/dist/server/completions.js +20 -15
  16. package/dist/server/config.d.ts +10 -5
  17. package/dist/server/config.js +24 -7
  18. package/dist/server/deltaCache.d.ts +2 -2
  19. package/dist/server/deltaCache.js +22 -16
  20. package/dist/server/deltaCache.merge.d.ts +2 -2
  21. package/dist/server/diagnostics.d.ts +4 -4
  22. package/dist/server/diagnostics.js +38 -32
  23. package/dist/server/errorHandler.d.ts +5 -12
  24. package/dist/server/errorHandler.js +219 -217
  25. package/dist/server/prompts.d.ts +2 -2
  26. package/dist/server/prompts.js +45 -45
  27. package/dist/server/rateLimiter.js +4 -4
  28. package/dist/server/requestLogger.d.ts +1 -1
  29. package/dist/server/requestLogger.js +40 -35
  30. package/dist/server/resources.d.ts +3 -3
  31. package/dist/server/resources.js +55 -52
  32. package/dist/server/responseFormatter.js +6 -6
  33. package/dist/server/securityMiddleware.d.ts +2 -2
  34. package/dist/server/securityMiddleware.js +22 -20
  35. package/dist/server/serverKnowledgeStore.js +1 -1
  36. package/dist/server/toolRegistry.d.ts +3 -3
  37. package/dist/server/toolRegistry.js +47 -40
  38. package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
  39. package/dist/tools/__tests__/deltaTestUtils.js +2 -2
  40. package/dist/tools/accountTools.d.ts +9 -8
  41. package/dist/tools/accountTools.js +47 -47
  42. package/dist/tools/adapters.d.ts +13 -8
  43. package/dist/tools/adapters.js +21 -11
  44. package/dist/tools/budgetTools.d.ts +8 -7
  45. package/dist/tools/budgetTools.js +22 -22
  46. package/dist/tools/categoryTools.d.ts +9 -8
  47. package/dist/tools/categoryTools.js +68 -59
  48. package/dist/tools/compareTransactions/formatter.d.ts +3 -3
  49. package/dist/tools/compareTransactions/formatter.js +9 -9
  50. package/dist/tools/compareTransactions/index.d.ts +6 -6
  51. package/dist/tools/compareTransactions/index.js +58 -43
  52. package/dist/tools/compareTransactions/matcher.d.ts +1 -1
  53. package/dist/tools/compareTransactions/matcher.js +28 -15
  54. package/dist/tools/compareTransactions/parser.d.ts +2 -2
  55. package/dist/tools/compareTransactions/parser.js +144 -138
  56. package/dist/tools/compareTransactions/types.d.ts +4 -4
  57. package/dist/tools/compareTransactions.d.ts +1 -1
  58. package/dist/tools/compareTransactions.js +1 -1
  59. package/dist/tools/deltaFetcher.d.ts +2 -2
  60. package/dist/tools/deltaFetcher.js +16 -15
  61. package/dist/tools/deltaSupport.d.ts +4 -4
  62. package/dist/tools/deltaSupport.js +35 -41
  63. package/dist/tools/exportTransactions.d.ts +5 -4
  64. package/dist/tools/exportTransactions.js +61 -59
  65. package/dist/tools/monthTools.d.ts +7 -6
  66. package/dist/tools/monthTools.js +31 -29
  67. package/dist/tools/payeeTools.d.ts +7 -6
  68. package/dist/tools/payeeTools.js +28 -28
  69. package/dist/tools/reconcileAdapter.d.ts +2 -2
  70. package/dist/tools/reconcileAdapter.js +19 -12
  71. package/dist/tools/reconciliation/analyzer.d.ts +4 -4
  72. package/dist/tools/reconciliation/analyzer.js +73 -59
  73. package/dist/tools/reconciliation/csvParser.d.ts +3 -3
  74. package/dist/tools/reconciliation/csvParser.js +128 -104
  75. package/dist/tools/reconciliation/executor.d.ts +4 -4
  76. package/dist/tools/reconciliation/executor.js +148 -109
  77. package/dist/tools/reconciliation/index.d.ts +10 -10
  78. package/dist/tools/reconciliation/index.js +96 -83
  79. package/dist/tools/reconciliation/matcher.d.ts +3 -3
  80. package/dist/tools/reconciliation/matcher.js +17 -16
  81. package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
  82. package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
  83. package/dist/tools/reconciliation/recommendationEngine.js +40 -40
  84. package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
  85. package/dist/tools/reconciliation/reportFormatter.js +59 -58
  86. package/dist/tools/reconciliation/signDetector.d.ts +1 -1
  87. package/dist/tools/reconciliation/types.d.ts +16 -16
  88. package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
  89. package/dist/tools/schemas/common.d.ts +1 -1
  90. package/dist/tools/schemas/common.js +1 -1
  91. package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
  92. package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
  93. package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
  94. package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
  95. package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
  96. package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
  97. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
  98. package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
  99. package/dist/tools/schemas/outputs/index.d.ts +14 -14
  100. package/dist/tools/schemas/outputs/index.js +14 -14
  101. package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
  102. package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
  103. package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
  104. package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
  105. package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
  106. package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
  107. package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
  108. package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
  109. package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
  110. package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
  111. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
  112. package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
  113. package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
  114. package/dist/tools/schemas/shared/commonOutputs.js +15 -9
  115. package/dist/tools/transactionReadTools.d.ts +11 -0
  116. package/dist/tools/transactionReadTools.js +202 -0
  117. package/dist/tools/transactionSchemas.d.ts +7 -7
  118. package/dist/tools/transactionSchemas.js +77 -57
  119. package/dist/tools/transactionTools.d.ts +6 -24
  120. package/dist/tools/transactionTools.js +7 -1499
  121. package/dist/tools/transactionUtils.d.ts +6 -6
  122. package/dist/tools/transactionUtils.js +78 -63
  123. package/dist/tools/transactionWriteTools.d.ts +20 -0
  124. package/dist/tools/transactionWriteTools.js +1342 -0
  125. package/dist/tools/utilityTools.d.ts +5 -4
  126. package/dist/tools/utilityTools.js +11 -11
  127. package/dist/types/index.d.ts +7 -7
  128. package/dist/types/index.js +6 -6
  129. package/dist/types/reconciliation.d.ts +1 -1
  130. package/dist/types/toolRegistration.d.ts +14 -12
  131. package/dist/utils/amountUtils.js +1 -1
  132. package/dist/utils/dateUtils.js +4 -4
  133. package/dist/utils/errors.d.ts +3 -3
  134. package/dist/utils/errors.js +4 -4
  135. package/dist/utils/money.d.ts +2 -2
  136. package/dist/utils/money.js +8 -8
  137. package/dist/utils/validationError.d.ts +1 -1
  138. package/dist/utils/validationError.js +1 -1
  139. package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
  140. package/docs/assets/schemas/reconciliation-v2.json +360 -336
  141. package/esbuild.config.mjs +53 -50
  142. package/meta.json +12548 -12548
  143. package/package.json +98 -111
  144. package/scripts/analyze-bundle.mjs +33 -30
  145. package/scripts/create-pr-description.js +169 -120
  146. package/scripts/run-all-tests.js +178 -169
  147. package/scripts/run-domain-integration-tests.js +28 -18
  148. package/scripts/run-generate-mcpb.js +19 -17
  149. package/scripts/run-throttled-integration-tests.js +92 -83
  150. package/scripts/test-delta-params.mjs +149 -120
  151. package/scripts/test-recommendations.ts +36 -32
  152. package/scripts/tmpTransaction.ts +80 -43
  153. package/scripts/validate-env.js +98 -91
  154. package/scripts/verify-build.js +78 -76
  155. package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
  156. package/src/__tests__/performance.test.ts +723 -671
  157. package/src/__tests__/setup.ts +442 -395
  158. package/src/__tests__/smoke.e2e.test.ts +41 -39
  159. package/src/__tests__/testRunner.ts +314 -295
  160. package/src/__tests__/testUtils.ts +456 -364
  161. package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
  162. package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
  163. package/src/index.ts +68 -59
  164. package/src/server/CLAUDE.md +480 -0
  165. package/src/server/YNABMCPServer.ts +821 -794
  166. package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
  167. package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
  168. package/src/server/__tests__/budgetResolver.test.ts +466 -423
  169. package/src/server/__tests__/cacheManager.test.ts +891 -874
  170. package/src/server/__tests__/completions.integration.test.ts +115 -106
  171. package/src/server/__tests__/completions.test.ts +334 -313
  172. package/src/server/__tests__/config.test.ts +98 -86
  173. package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
  174. package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
  175. package/src/server/__tests__/deltaCache.test.ts +946 -759
  176. package/src/server/__tests__/diagnostics.test.ts +825 -792
  177. package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
  178. package/src/server/__tests__/errorHandler.test.ts +402 -397
  179. package/src/server/__tests__/prompts.test.ts +424 -347
  180. package/src/server/__tests__/rateLimiter.test.ts +313 -309
  181. package/src/server/__tests__/requestLogger.test.ts +443 -403
  182. package/src/server/__tests__/resources.template.test.ts +196 -185
  183. package/src/server/__tests__/resources.test.ts +294 -288
  184. package/src/server/__tests__/security.integration.test.ts +487 -421
  185. package/src/server/__tests__/securityMiddleware.test.ts +519 -444
  186. package/src/server/__tests__/server-startup.integration.test.ts +509 -490
  187. package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
  188. package/src/server/__tests__/toolRegistration.test.ts +239 -210
  189. package/src/server/__tests__/toolRegistry.test.ts +907 -845
  190. package/src/server/budgetResolver.ts +221 -181
  191. package/src/server/cacheKeys.ts +6 -6
  192. package/src/server/cacheManager.ts +498 -484
  193. package/src/server/completions.ts +267 -243
  194. package/src/server/config.ts +35 -14
  195. package/src/server/deltaCache.merge.ts +146 -128
  196. package/src/server/deltaCache.ts +352 -309
  197. package/src/server/diagnostics.ts +257 -242
  198. package/src/server/errorHandler.ts +747 -744
  199. package/src/server/prompts.ts +181 -176
  200. package/src/server/rateLimiter.ts +131 -129
  201. package/src/server/requestLogger.ts +350 -322
  202. package/src/server/resources.ts +442 -374
  203. package/src/server/responseFormatter.ts +41 -37
  204. package/src/server/securityMiddleware.ts +223 -205
  205. package/src/server/serverKnowledgeStore.ts +67 -67
  206. package/src/server/toolRegistry.ts +508 -474
  207. package/src/tools/CLAUDE.md +604 -0
  208. package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
  209. package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
  210. package/src/tools/__tests__/accountTools.test.ts +685 -638
  211. package/src/tools/__tests__/adapters.test.ts +142 -108
  212. package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
  213. package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
  214. package/src/tools/__tests__/budgetTools.test.ts +442 -413
  215. package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
  216. package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
  217. package/src/tools/__tests__/categoryTools.test.ts +656 -625
  218. package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
  219. package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
  220. package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
  221. package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
  222. package/src/tools/__tests__/compareTransactions.test.ts +352 -332
  223. package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
  224. package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
  225. package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
  226. package/src/tools/__tests__/deltaSupport.test.ts +211 -184
  227. package/src/tools/__tests__/deltaTestUtils.ts +37 -33
  228. package/src/tools/__tests__/exportTransactions.test.ts +205 -200
  229. package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
  230. package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
  231. package/src/tools/__tests__/monthTools.test.ts +561 -512
  232. package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
  233. package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
  234. package/src/tools/__tests__/payeeTools.test.ts +486 -434
  235. package/src/tools/__tests__/transactionSchemas.test.ts +1202 -1186
  236. package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
  237. package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
  238. package/src/tools/__tests__/transactionUtils.test.ts +1004 -977
  239. package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
  240. package/src/tools/__tests__/utilityTools.test.ts +68 -58
  241. package/src/tools/accountTools.ts +293 -271
  242. package/src/tools/adapters.ts +120 -63
  243. package/src/tools/budgetTools.ts +121 -116
  244. package/src/tools/categoryTools.ts +379 -339
  245. package/src/tools/compareTransactions/formatter.ts +131 -119
  246. package/src/tools/compareTransactions/index.ts +249 -214
  247. package/src/tools/compareTransactions/matcher.ts +259 -209
  248. package/src/tools/compareTransactions/parser.ts +517 -487
  249. package/src/tools/compareTransactions/types.ts +38 -38
  250. package/src/tools/compareTransactions.ts +1 -1
  251. package/src/tools/deltaFetcher.ts +281 -260
  252. package/src/tools/deltaSupport.ts +264 -259
  253. package/src/tools/exportTransactions.ts +230 -218
  254. package/src/tools/monthTools.ts +180 -165
  255. package/src/tools/payeeTools.ts +152 -140
  256. package/src/tools/reconcileAdapter.ts +297 -252
  257. package/src/tools/reconciliation/CLAUDE.md +506 -0
  258. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +133 -124
  259. package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -230
  260. package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -400
  261. package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
  262. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
  263. package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
  264. package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
  265. package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
  266. package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
  267. package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
  268. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -989
  269. package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
  270. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -533
  271. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -74
  272. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -62
  273. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
  274. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +56 -55
  275. package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
  276. package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
  277. package/src/tools/reconciliation/analyzer.ts +564 -504
  278. package/src/tools/reconciliation/csvParser.ts +656 -609
  279. package/src/tools/reconciliation/executor.ts +1290 -1128
  280. package/src/tools/reconciliation/index.ts +580 -528
  281. package/src/tools/reconciliation/matcher.ts +256 -240
  282. package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
  283. package/src/tools/reconciliation/recommendationEngine.ts +357 -345
  284. package/src/tools/reconciliation/reportFormatter.ts +343 -307
  285. package/src/tools/reconciliation/signDetector.ts +89 -83
  286. package/src/tools/reconciliation/types.ts +164 -159
  287. package/src/tools/reconciliation/ynabAdapter.ts +17 -15
  288. package/src/tools/schemas/CLAUDE.md +546 -0
  289. package/src/tools/schemas/common.ts +1 -1
  290. package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
  291. package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
  292. package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
  293. package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
  294. package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
  295. package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
  296. package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
  297. package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
  298. package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
  299. package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
  300. package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
  301. package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
  302. package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
  303. package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
  304. package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
  305. package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
  306. package/src/tools/schemas/outputs/index.ts +163 -163
  307. package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
  308. package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
  309. package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
  310. package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
  311. package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
  312. package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
  313. package/src/tools/schemas/shared/commonOutputs.ts +27 -19
  314. package/src/tools/toolCategories.ts +114 -114
  315. package/src/tools/transactionReadTools.ts +327 -0
  316. package/src/tools/transactionSchemas.ts +322 -291
  317. package/src/tools/transactionTools.ts +84 -2246
  318. package/src/tools/transactionUtils.ts +507 -422
  319. package/src/tools/transactionWriteTools.ts +2110 -0
  320. package/src/tools/utilityTools.ts +46 -41
  321. package/src/types/CLAUDE.md +477 -0
  322. package/src/types/__tests__/index.test.ts +51 -51
  323. package/src/types/index.ts +43 -39
  324. package/src/types/integration-tests.d.ts +26 -26
  325. package/src/types/reconciliation.ts +29 -29
  326. package/src/types/toolAnnotations.ts +30 -30
  327. package/src/types/toolRegistration.ts +43 -32
  328. package/src/utils/CLAUDE.md +508 -0
  329. package/src/utils/__tests__/dateUtils.test.ts +174 -168
  330. package/src/utils/__tests__/money.test.ts +193 -187
  331. package/src/utils/amountUtils.ts +5 -5
  332. package/src/utils/baseError.ts +5 -5
  333. package/src/utils/dateUtils.ts +29 -26
  334. package/src/utils/errors.ts +14 -14
  335. package/src/utils/money.ts +66 -52
  336. package/src/utils/validationError.ts +1 -1
  337. package/tsconfig.json +29 -29
  338. package/tsconfig.prod.json +16 -16
  339. package/vitest-reporters/split-json-reporter.ts +247 -204
  340. package/vitest.config.ts +99 -95
  341. package/.prettierignore +0 -10
  342. package/.prettierrc.json +0 -10
  343. 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;