@dizzlkheinz/ynab-mcpb 0.12.1

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 (435) hide show
  1. package/.chunkhound.json +11 -0
  2. package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +8 -0
  3. package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +3 -0
  4. package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +3 -0
  5. package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +1 -0
  6. package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +12 -0
  7. package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +1 -0
  8. package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +1 -0
  9. package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +5 -0
  10. package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +1453 -0
  11. package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +8 -0
  12. package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +9 -0
  13. package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +1 -0
  14. package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +5 -0
  15. package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +81 -0
  16. package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +610 -0
  17. package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +1 -0
  18. package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +1 -0
  19. package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +1 -0
  20. package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +3 -0
  21. package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +8 -0
  22. package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +5 -0
  23. package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +1 -0
  24. package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +9 -0
  25. package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +1 -0
  26. package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +13 -0
  27. package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +14 -0
  28. package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +5 -0
  29. package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +5 -0
  30. package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +5 -0
  31. package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +3 -0
  32. package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +1 -0
  33. package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +1 -0
  34. package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +5 -0
  35. package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +5 -0
  36. package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +8 -0
  37. package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +8 -0
  38. package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +5 -0
  39. package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +1 -0
  40. package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +1 -0
  41. package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +1 -0
  42. package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +1 -0
  43. package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +1 -0
  44. package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +5 -0
  45. package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +1 -0
  46. package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +1 -0
  47. package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +1 -0
  48. package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +3 -0
  49. package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +8 -0
  50. package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +1 -0
  51. package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +1 -0
  52. package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +8 -0
  53. package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +3 -0
  54. package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +5 -0
  55. package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +1 -0
  56. package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +1 -0
  57. package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +5 -0
  58. package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +1 -0
  59. package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +14 -0
  60. package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +1 -0
  61. package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +5 -0
  62. package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +5 -0
  63. package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +11 -0
  64. package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +1 -0
  65. package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +8 -0
  66. package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +1 -0
  67. package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +3 -0
  68. package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +3 -0
  69. package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +8 -0
  70. package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +1 -0
  71. package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +5 -0
  72. package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +1 -0
  73. package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +1 -0
  74. package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +1 -0
  75. package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +1 -0
  76. package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +3 -0
  77. package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +8 -0
  78. package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +1 -0
  79. package/.dxtignore +57 -0
  80. package/.env.example +44 -0
  81. package/.gemini/settings.json +8 -0
  82. package/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
  83. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  84. package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  85. package/.github/ISSUE_TEMPLATE/release_checklist.md +31 -0
  86. package/.github/pull_request_template.md +41 -0
  87. package/.github/workflows/ci-tests.yml +41 -0
  88. package/.github/workflows/claude-code-review.yml +57 -0
  89. package/.github/workflows/claude.yml +50 -0
  90. package/.github/workflows/full-integration.yml +22 -0
  91. package/.github/workflows/pr-description-check.yml +88 -0
  92. package/.github/workflows/publish.yml +33 -0
  93. package/.github/workflows/release.yml +89 -0
  94. package/.mcpbignore +58 -0
  95. package/.prettierignore +10 -0
  96. package/.prettierrc.json +10 -0
  97. package/ADOS-2-Module-1-Complete-Manual.md +757 -0
  98. package/AGENTS.md +36 -0
  99. package/CHANGELOG.md +187 -0
  100. package/CLAUDE.md +414 -0
  101. package/CODEREVIEW_RESPONSE.md +128 -0
  102. package/LICENSE +17 -0
  103. package/NUL +1 -0
  104. package/README.md +222 -0
  105. package/SCHEMA_IMPROVEMENT_SUMMARY.md +120 -0
  106. package/TESTING_NOTES.md +217 -0
  107. package/WARP.md +245 -0
  108. package/accountactivity-merged.csv +149 -0
  109. package/bin/ynab-mcp-server.cjs +4 -0
  110. package/bin/ynab-mcp-server.js +8 -0
  111. package/bundle-analysis.html +13110 -0
  112. package/dist/bundle/index.cjs +124 -0
  113. package/dist/index.d.ts +2 -0
  114. package/dist/index.js +85 -0
  115. package/dist/server/YNABMCPServer.d.ts +264 -0
  116. package/dist/server/YNABMCPServer.js +845 -0
  117. package/dist/server/budgetResolver.d.ts +15 -0
  118. package/dist/server/budgetResolver.js +99 -0
  119. package/dist/server/cacheManager.d.ts +74 -0
  120. package/dist/server/cacheManager.js +306 -0
  121. package/dist/server/config.d.ts +3 -0
  122. package/dist/server/config.js +19 -0
  123. package/dist/server/deltaCache.d.ts +61 -0
  124. package/dist/server/deltaCache.js +206 -0
  125. package/dist/server/deltaCache.merge.d.ts +9 -0
  126. package/dist/server/deltaCache.merge.js +111 -0
  127. package/dist/server/diagnostics.d.ts +90 -0
  128. package/dist/server/diagnostics.js +163 -0
  129. package/dist/server/errorHandler.d.ts +69 -0
  130. package/dist/server/errorHandler.js +524 -0
  131. package/dist/server/prompts.d.ts +31 -0
  132. package/dist/server/prompts.js +205 -0
  133. package/dist/server/rateLimiter.d.ts +27 -0
  134. package/dist/server/rateLimiter.js +82 -0
  135. package/dist/server/requestLogger.d.ts +62 -0
  136. package/dist/server/requestLogger.js +190 -0
  137. package/dist/server/resources.d.ts +39 -0
  138. package/dist/server/resources.js +85 -0
  139. package/dist/server/responseFormatter.d.ts +14 -0
  140. package/dist/server/responseFormatter.js +42 -0
  141. package/dist/server/securityMiddleware.d.ts +87 -0
  142. package/dist/server/securityMiddleware.js +117 -0
  143. package/dist/server/serverKnowledgeStore.d.ts +11 -0
  144. package/dist/server/serverKnowledgeStore.js +42 -0
  145. package/dist/server/toolRegistry.d.ts +85 -0
  146. package/dist/server/toolRegistry.js +272 -0
  147. package/dist/tools/__tests__/deltaTestUtils.d.ts +18 -0
  148. package/dist/tools/__tests__/deltaTestUtils.js +26 -0
  149. package/dist/tools/accountTools.d.ts +37 -0
  150. package/dist/tools/accountTools.js +175 -0
  151. package/dist/tools/budgetTools.d.ts +10 -0
  152. package/dist/tools/budgetTools.js +68 -0
  153. package/dist/tools/categoryTools.d.ts +27 -0
  154. package/dist/tools/categoryTools.js +232 -0
  155. package/dist/tools/compareTransactions/formatter.d.ts +71 -0
  156. package/dist/tools/compareTransactions/formatter.js +97 -0
  157. package/dist/tools/compareTransactions/index.d.ts +30 -0
  158. package/dist/tools/compareTransactions/index.js +160 -0
  159. package/dist/tools/compareTransactions/matcher.d.ts +12 -0
  160. package/dist/tools/compareTransactions/matcher.js +140 -0
  161. package/dist/tools/compareTransactions/parser.d.ts +14 -0
  162. package/dist/tools/compareTransactions/parser.js +430 -0
  163. package/dist/tools/compareTransactions/types.d.ts +27 -0
  164. package/dist/tools/compareTransactions/types.js +1 -0
  165. package/dist/tools/compareTransactions.d.ts +1 -0
  166. package/dist/tools/compareTransactions.js +1 -0
  167. package/dist/tools/deltaFetcher.d.ts +22 -0
  168. package/dist/tools/deltaFetcher.js +137 -0
  169. package/dist/tools/deltaSupport.d.ts +20 -0
  170. package/dist/tools/deltaSupport.js +176 -0
  171. package/dist/tools/exportTransactions.d.ts +17 -0
  172. package/dist/tools/exportTransactions.js +191 -0
  173. package/dist/tools/monthTools.d.ts +16 -0
  174. package/dist/tools/monthTools.js +107 -0
  175. package/dist/tools/payeeTools.d.ts +17 -0
  176. package/dist/tools/payeeTools.js +82 -0
  177. package/dist/tools/reconcileAdapter.d.ts +25 -0
  178. package/dist/tools/reconcileAdapter.js +167 -0
  179. package/dist/tools/reconciliation/analyzer.d.ts +3 -0
  180. package/dist/tools/reconciliation/analyzer.js +567 -0
  181. package/dist/tools/reconciliation/executor.d.ts +94 -0
  182. package/dist/tools/reconciliation/executor.js +611 -0
  183. package/dist/tools/reconciliation/index.d.ts +54 -0
  184. package/dist/tools/reconciliation/index.js +249 -0
  185. package/dist/tools/reconciliation/matcher.d.ts +3 -0
  186. package/dist/tools/reconciliation/matcher.js +160 -0
  187. package/dist/tools/reconciliation/payeeNormalizer.d.ts +6 -0
  188. package/dist/tools/reconciliation/payeeNormalizer.js +77 -0
  189. package/dist/tools/reconciliation/recommendationEngine.d.ts +2 -0
  190. package/dist/tools/reconciliation/recommendationEngine.js +273 -0
  191. package/dist/tools/reconciliation/reportFormatter.d.ts +13 -0
  192. package/dist/tools/reconciliation/reportFormatter.js +214 -0
  193. package/dist/tools/reconciliation/types.d.ts +172 -0
  194. package/dist/tools/reconciliation/types.js +7 -0
  195. package/dist/tools/schemas/outputs/accountOutputs.d.ts +58 -0
  196. package/dist/tools/schemas/outputs/accountOutputs.js +24 -0
  197. package/dist/tools/schemas/outputs/budgetOutputs.d.ts +48 -0
  198. package/dist/tools/schemas/outputs/budgetOutputs.js +15 -0
  199. package/dist/tools/schemas/outputs/categoryOutputs.d.ts +93 -0
  200. package/dist/tools/schemas/outputs/categoryOutputs.js +37 -0
  201. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +269 -0
  202. package/dist/tools/schemas/outputs/comparisonOutputs.js +181 -0
  203. package/dist/tools/schemas/outputs/index.d.ts +14 -0
  204. package/dist/tools/schemas/outputs/index.js +14 -0
  205. package/dist/tools/schemas/outputs/monthOutputs.d.ts +122 -0
  206. package/dist/tools/schemas/outputs/monthOutputs.js +51 -0
  207. package/dist/tools/schemas/outputs/payeeOutputs.d.ts +34 -0
  208. package/dist/tools/schemas/outputs/payeeOutputs.js +16 -0
  209. package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +1275 -0
  210. package/dist/tools/schemas/outputs/reconciliationOutputs.js +377 -0
  211. package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +717 -0
  212. package/dist/tools/schemas/outputs/transactionMutationOutputs.js +260 -0
  213. package/dist/tools/schemas/outputs/transactionOutputs.d.ts +98 -0
  214. package/dist/tools/schemas/outputs/transactionOutputs.js +49 -0
  215. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +219 -0
  216. package/dist/tools/schemas/outputs/utilityOutputs.js +120 -0
  217. package/dist/tools/schemas/shared/commonOutputs.d.ts +24 -0
  218. package/dist/tools/schemas/shared/commonOutputs.js +27 -0
  219. package/dist/tools/toolCategories.d.ts +32 -0
  220. package/dist/tools/toolCategories.js +32 -0
  221. package/dist/tools/transactionTools.d.ts +315 -0
  222. package/dist/tools/transactionTools.js +1722 -0
  223. package/dist/tools/utilityTools.d.ts +10 -0
  224. package/dist/tools/utilityTools.js +56 -0
  225. package/dist/types/index.d.ts +20 -0
  226. package/dist/types/index.js +16 -0
  227. package/dist/types/toolAnnotations.d.ts +7 -0
  228. package/dist/types/toolAnnotations.js +1 -0
  229. package/dist/utils/amountUtils.d.ts +3 -0
  230. package/dist/utils/amountUtils.js +10 -0
  231. package/dist/utils/dateUtils.d.ts +9 -0
  232. package/dist/utils/dateUtils.js +43 -0
  233. package/dist/utils/money.d.ts +21 -0
  234. package/dist/utils/money.js +51 -0
  235. package/docs/README.md +72 -0
  236. package/docs/assets/examples/reconciliation-with-recommendations.json +68 -0
  237. package/docs/assets/schemas/reconciliation-v2.json +338 -0
  238. package/docs/getting-started/CONFIGURATION.md +175 -0
  239. package/docs/getting-started/INSTALLATION.md +333 -0
  240. package/docs/getting-started/QUICKSTART.md +282 -0
  241. package/docs/guides/ARCHITECTURE.md +650 -0
  242. package/docs/guides/DEPLOYMENT.md +189 -0
  243. package/docs/guides/INTEGRATION_TESTING.md +730 -0
  244. package/docs/guides/TESTING.md +591 -0
  245. package/docs/reconciliation-flow.md +83 -0
  246. package/docs/reference/API.md +1450 -0
  247. package/docs/reference/EXAMPLES.md +946 -0
  248. package/docs/reference/TOOLS.md +348 -0
  249. package/docs/reference/TROUBLESHOOTING.md +481 -0
  250. package/esbuild.config.mjs +68 -0
  251. package/eslint.config.js +49 -0
  252. package/fix-types.sh +17 -0
  253. package/meta.json +12550 -0
  254. package/package.json +105 -0
  255. package/package.json.tmp +105 -0
  256. package/scripts/analyze-bundle.mjs +41 -0
  257. package/scripts/create-pr-description.js +203 -0
  258. package/scripts/generate-mcpb.ps1 +96 -0
  259. package/scripts/run-domain-integration-tests.js +33 -0
  260. package/scripts/run-generate-mcpb.js +29 -0
  261. package/scripts/run-throttled-integration-tests.js +116 -0
  262. package/scripts/test-delta-params.mjs +140 -0
  263. package/scripts/test-recommendations.ts +53 -0
  264. package/scripts/tmpTransaction.ts +48 -0
  265. package/scripts/validate-env.js +122 -0
  266. package/scripts/verify-build.js +105 -0
  267. package/scripts/watch-and-restart.ps1 +50 -0
  268. package/src/__tests__/comprehensive.integration.test.ts +1196 -0
  269. package/src/__tests__/delta.performance.test.ts +80 -0
  270. package/src/__tests__/performance.test.ts +725 -0
  271. package/src/__tests__/setup.ts +449 -0
  272. package/src/__tests__/testRunner.ts +444 -0
  273. package/src/__tests__/testUtils.ts +563 -0
  274. package/src/__tests__/workflows.e2e.test.ts +1675 -0
  275. package/src/index.ts +124 -0
  276. package/src/server/.gitkeep +1 -0
  277. package/src/server/YNABMCPServer.ts +1188 -0
  278. package/src/server/__tests__/YNABMCPServer.integration.test.ts +903 -0
  279. package/src/server/__tests__/YNABMCPServer.test.ts +894 -0
  280. package/src/server/__tests__/budgetResolver.test.ts +425 -0
  281. package/src/server/__tests__/cacheManager.test.ts +880 -0
  282. package/src/server/__tests__/config.test.ts +166 -0
  283. package/src/server/__tests__/deltaCache.merge.test.ts +724 -0
  284. package/src/server/__tests__/deltaCache.swr.test.ts +168 -0
  285. package/src/server/__tests__/deltaCache.test.ts +774 -0
  286. package/src/server/__tests__/diagnostics.test.ts +823 -0
  287. package/src/server/__tests__/errorHandler.integration.test.ts +466 -0
  288. package/src/server/__tests__/errorHandler.test.ts +416 -0
  289. package/src/server/__tests__/prompts.test.ts +354 -0
  290. package/src/server/__tests__/rateLimiter.test.ts +314 -0
  291. package/src/server/__tests__/requestLogger.test.ts +408 -0
  292. package/src/server/__tests__/resources.test.ts +299 -0
  293. package/src/server/__tests__/security.integration.test.ts +426 -0
  294. package/src/server/__tests__/securityMiddleware.test.ts +449 -0
  295. package/src/server/__tests__/server-startup.integration.test.ts +477 -0
  296. package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -0
  297. package/src/server/__tests__/toolRegistry.test.ts +855 -0
  298. package/src/server/budgetResolver.ts +235 -0
  299. package/src/server/cacheManager.ts +503 -0
  300. package/src/server/config.ts +41 -0
  301. package/src/server/deltaCache.merge.ts +149 -0
  302. package/src/server/deltaCache.ts +341 -0
  303. package/src/server/diagnostics.ts +338 -0
  304. package/src/server/errorHandler.ts +756 -0
  305. package/src/server/prompts.ts +291 -0
  306. package/src/server/rateLimiter.ts +156 -0
  307. package/src/server/requestLogger.ts +344 -0
  308. package/src/server/resources.ts +168 -0
  309. package/src/server/responseFormatter.ts +51 -0
  310. package/src/server/securityMiddleware.ts +236 -0
  311. package/src/server/serverKnowledgeStore.ts +91 -0
  312. package/src/server/toolRegistry.ts +489 -0
  313. package/src/tools/.gitkeep +1 -0
  314. package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -0
  315. package/src/tools/__tests__/accountTools.integration.test.ts +117 -0
  316. package/src/tools/__tests__/accountTools.test.ts +653 -0
  317. package/src/tools/__tests__/budgetTools.delta.integration.test.ts +90 -0
  318. package/src/tools/__tests__/budgetTools.integration.test.ts +134 -0
  319. package/src/tools/__tests__/budgetTools.test.ts +423 -0
  320. package/src/tools/__tests__/categoryTools.delta.integration.test.ts +80 -0
  321. package/src/tools/__tests__/categoryTools.integration.test.ts +295 -0
  322. package/src/tools/__tests__/categoryTools.test.ts +622 -0
  323. package/src/tools/__tests__/compareTransactions/formatter.test.ts +486 -0
  324. package/src/tools/__tests__/compareTransactions/index.test.ts +383 -0
  325. package/src/tools/__tests__/compareTransactions/matcher.test.ts +410 -0
  326. package/src/tools/__tests__/compareTransactions/parser.test.ts +764 -0
  327. package/src/tools/__tests__/compareTransactions.test.ts +342 -0
  328. package/src/tools/__tests__/compareTransactions.window.test.ts +147 -0
  329. package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +76 -0
  330. package/src/tools/__tests__/deltaFetcher.test.ts +270 -0
  331. package/src/tools/__tests__/deltaSupport.test.ts +188 -0
  332. package/src/tools/__tests__/deltaTestUtils.ts +46 -0
  333. package/src/tools/__tests__/exportTransactions.test.ts +213 -0
  334. package/src/tools/__tests__/monthTools.delta.integration.test.ts +80 -0
  335. package/src/tools/__tests__/monthTools.integration.test.ts +174 -0
  336. package/src/tools/__tests__/monthTools.test.ts +523 -0
  337. package/src/tools/__tests__/payeeTools.delta.integration.test.ts +80 -0
  338. package/src/tools/__tests__/payeeTools.integration.test.ts +150 -0
  339. package/src/tools/__tests__/payeeTools.test.ts +445 -0
  340. package/src/tools/__tests__/transactionTools.integration.test.ts +762 -0
  341. package/src/tools/__tests__/transactionTools.test.ts +3521 -0
  342. package/src/tools/__tests__/utilityTools.integration.test.ts +128 -0
  343. package/src/tools/__tests__/utilityTools.test.ts +205 -0
  344. package/src/tools/accountTools.ts +283 -0
  345. package/src/tools/budgetTools.ts +112 -0
  346. package/src/tools/categoryTools.ts +366 -0
  347. package/src/tools/compareTransactions/formatter.ts +163 -0
  348. package/src/tools/compareTransactions/index.ts +228 -0
  349. package/src/tools/compareTransactions/matcher.ts +240 -0
  350. package/src/tools/compareTransactions/parser.ts +557 -0
  351. package/src/tools/compareTransactions/types.ts +60 -0
  352. package/src/tools/compareTransactions.ts +3 -0
  353. package/src/tools/deltaFetcher.ts +278 -0
  354. package/src/tools/deltaSupport.ts +293 -0
  355. package/src/tools/exportTransactions.ts +273 -0
  356. package/src/tools/monthTools.ts +164 -0
  357. package/src/tools/payeeTools.ts +140 -0
  358. package/src/tools/reconcileAdapter.ts +312 -0
  359. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +122 -0
  360. package/src/tools/reconciliation/__tests__/adapter.test.ts +234 -0
  361. package/src/tools/reconciliation/__tests__/analyzer.test.ts +406 -0
  362. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +366 -0
  363. package/src/tools/reconciliation/__tests__/executor.test.ts +779 -0
  364. package/src/tools/reconciliation/__tests__/matcher.test.ts +650 -0
  365. package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +278 -0
  366. package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +658 -0
  367. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1000 -0
  368. package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +151 -0
  369. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +573 -0
  370. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +78 -0
  371. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +47 -0
  372. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +61 -0
  373. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +49 -0
  374. package/src/tools/reconciliation/analyzer.ts +824 -0
  375. package/src/tools/reconciliation/executor.ts +880 -0
  376. package/src/tools/reconciliation/index.ts +400 -0
  377. package/src/tools/reconciliation/matcher.ts +269 -0
  378. package/src/tools/reconciliation/payeeNormalizer.ts +167 -0
  379. package/src/tools/reconciliation/recommendationEngine.ts +506 -0
  380. package/src/tools/reconciliation/reportFormatter.ts +363 -0
  381. package/src/tools/reconciliation/types.ts +314 -0
  382. package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +424 -0
  383. package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +310 -0
  384. package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +448 -0
  385. package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +519 -0
  386. package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +155 -0
  387. package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +288 -0
  388. package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +478 -0
  389. package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +370 -0
  390. package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +401 -0
  391. package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +213 -0
  392. package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +474 -0
  393. package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +333 -0
  394. package/src/tools/schemas/outputs/accountOutputs.ts +137 -0
  395. package/src/tools/schemas/outputs/budgetOutputs.ts +86 -0
  396. package/src/tools/schemas/outputs/categoryOutputs.ts +194 -0
  397. package/src/tools/schemas/outputs/comparisonOutputs.ts +600 -0
  398. package/src/tools/schemas/outputs/index.ts +270 -0
  399. package/src/tools/schemas/outputs/monthOutputs.ts +243 -0
  400. package/src/tools/schemas/outputs/payeeOutputs.ts +105 -0
  401. package/src/tools/schemas/outputs/reconciliationOutputs.ts +796 -0
  402. package/src/tools/schemas/outputs/transactionMutationOutputs.ts +758 -0
  403. package/src/tools/schemas/outputs/transactionOutputs.ts +243 -0
  404. package/src/tools/schemas/outputs/utilityOutputs.ts +411 -0
  405. package/src/tools/schemas/shared/commonOutputs.ts +140 -0
  406. package/src/tools/toolCategories.ts +140 -0
  407. package/src/tools/transactionTools.ts +2509 -0
  408. package/src/tools/utilityTools.ts +90 -0
  409. package/src/types/.gitkeep +1 -0
  410. package/src/types/__tests__/index.test.ts +52 -0
  411. package/src/types/index.ts +67 -0
  412. package/src/types/integration-tests.d.ts +35 -0
  413. package/src/types/toolAnnotations.ts +44 -0
  414. package/src/utils/__tests__/dateUtils.test.ts +170 -0
  415. package/src/utils/__tests__/money.test.ts +189 -0
  416. package/src/utils/amountUtils.ts +32 -0
  417. package/src/utils/dateUtils.ts +108 -0
  418. package/src/utils/money.ts +123 -0
  419. package/test-csv-sample.csv +28 -0
  420. package/test-exports/sample_bank_statement.csv +7 -0
  421. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +23 -0
  422. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +23 -0
  423. package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +44 -0
  424. package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +58 -0
  425. package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +115 -0
  426. package/test-reconcile-autodetect.js +40 -0
  427. package/test-reconcile-tool.js +152 -0
  428. package/test-reconcile-with-csv.cjs +89 -0
  429. package/test-statement.csv +8 -0
  430. package/test_debug.js +47 -0
  431. package/test_simple.mjs +16 -0
  432. package/tsconfig.json +31 -0
  433. package/tsconfig.prod.json +18 -0
  434. package/vitest-reporters/split-json-reporter.ts +211 -0
  435. package/vitest.config.ts +96 -0
@@ -0,0 +1,796 @@
1
+ /**
2
+ * @fileoverview Reconciliation analysis output schemas for YNAB MCP server.
3
+ * Defines Zod validation schemas for account reconciliation including transaction matching,
4
+ * balance verification, insights, actionable recommendations, and optional execution results.
5
+ *
6
+ * @see src/tools/reconciliation/index.ts - Main reconciliation handler (lines 147-362)
7
+ * @see src/tools/reconciliation/types.ts - Type definitions for reconciliation entities
8
+ * @see src/tools/reconciliation/executor.ts - Execution engine for auto-applying recommendations
9
+ * @see src/tools/reconcileAdapter.ts - Adapter for building reconciliation payloads
10
+ * @see src/utils/money.ts - Money value formatting utilities
11
+ *
12
+ * @example
13
+ * // Human-readable narrative only (default)
14
+ * {
15
+ * human: "Successfully matched 45 of 50 bank transactions. Found 3 suggested matches..."
16
+ * }
17
+ *
18
+ * @example
19
+ * // With structured data (include_structured_data=true)
20
+ * {
21
+ * human: "Successfully matched 45 of 50 bank transactions...",
22
+ * structured: {
23
+ * success: true,
24
+ * phase: "analysis",
25
+ * summary: {
26
+ * statement_date_range: "2025-10-01 to 2025-10-31",
27
+ * bank_transactions_count: 50,
28
+ * ynab_transactions_count: 52,
29
+ * auto_matched: 45,
30
+ * suggested_matches: 3,
31
+ * unmatched_bank: 2,
32
+ * unmatched_ynab: 4,
33
+ * discrepancy: { amount: -25.50, currency: "USD", formatted: "-$25.50" }
34
+ * },
35
+ * balance_info: {
36
+ * current_cleared: { amount: 1250.00, currency: "USD", formatted: "$1,250.00" },
37
+ * target_statement: { amount: 1275.50, currency: "USD", formatted: "$1,275.50" },
38
+ * discrepancy: { amount: -25.50, currency: "USD", formatted: "-$25.50" },
39
+ * on_track: false
40
+ * },
41
+ * recommendations: [...],
42
+ * audit_metadata: { data_freshness: "real-time", ... }
43
+ * }
44
+ * }
45
+ */
46
+
47
+ import { z } from 'zod';
48
+
49
+ // ============================================================================
50
+ // NESTED SCHEMAS FOR COMPOSITION
51
+ // ============================================================================
52
+
53
+ /**
54
+ * Structured monetary value with formatting.
55
+ * Used throughout reconciliation for balances and discrepancies.
56
+ *
57
+ * @see src/utils/money.ts - MoneyValue type definition
58
+ */
59
+ export const MoneyValueSchema = z.object({
60
+ amount: z.number().finite(),
61
+ currency: z.string(),
62
+ formatted: z.string(),
63
+ memo: z.string().optional(),
64
+ });
65
+
66
+ export type MoneyValue = z.infer<typeof MoneyValueSchema>;
67
+
68
+ /**
69
+ * ISO 8601 date string with calendar validation.
70
+ * Ensures dates are in YYYY-MM-DD format and represent valid calendar dates.
71
+ *
72
+ * @remarks
73
+ * This schema validates both format (regex) and calendar validity (refinement).
74
+ * It catches invalid dates like "2025-02-31" which Date.parse might coerce to "2025-03-03".
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * IsoDateWithCalendarValidationSchema.parse("2025-01-15"); // ✓ Valid
79
+ * IsoDateWithCalendarValidationSchema.parse("2025-02-31"); // ✗ Invalid (no Feb 31st)
80
+ * IsoDateWithCalendarValidationSchema.parse("2025-13-01"); // ✗ Invalid (month > 12)
81
+ * IsoDateWithCalendarValidationSchema.parse("2025-1-15"); // ✗ Invalid (must be zero-padded)
82
+ * ```
83
+ */
84
+ export const IsoDateWithCalendarValidationSchema = z
85
+ .string()
86
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be in ISO format (YYYY-MM-DD)')
87
+ .refine(
88
+ (dateStr) => {
89
+ const parsed = Date.parse(dateStr);
90
+ if (isNaN(parsed)) {
91
+ return false;
92
+ }
93
+ // Verify that the parsed date components match the original string
94
+ // This catches cases like "2025-02-31" which Date.parse might coerce to "2025-03-03"
95
+ const date = new Date(parsed);
96
+ const year = date.getUTCFullYear();
97
+ const month = String(date.getUTCMonth() + 1).padStart(2, '0');
98
+ const day = String(date.getUTCDate()).padStart(2, '0');
99
+ const reconstructed = `${year}-${month}-${day}`;
100
+ return reconstructed === dateStr;
101
+ },
102
+ {
103
+ message: 'Invalid calendar date (e.g., month must be 01-12, day must be valid for the month)',
104
+ },
105
+ );
106
+
107
+ /**
108
+ * Bank transaction from CSV import (output format with money formatting).
109
+ * Represents a single transaction from the user's bank statement.
110
+ *
111
+ * @remarks
112
+ * The adapter adds `amount_money` field with formatted money value.
113
+ * Date field is always serialized as an ISO 8601 date string (YYYY-MM-DD) in tool output,
114
+ * even though the internal type may use Date objects during analysis.
115
+ *
116
+ * @see src/tools/reconciliation/types.ts - BankTransaction interface (internal type)
117
+ * @see src/tools/reconcileAdapter.ts:77-80 - toBankTransactionView function
118
+ */
119
+ export const BankTransactionSchema = z.object({
120
+ id: z.string().uuid(),
121
+ date: IsoDateWithCalendarValidationSchema,
122
+ amount: z.number(),
123
+ payee: z.string(),
124
+ memo: z.string().optional(),
125
+ original_csv_row: z.number(),
126
+ amount_money: MoneyValueSchema, // Added by adapter
127
+ });
128
+
129
+ export type BankTransaction = z.infer<typeof BankTransactionSchema>;
130
+
131
+ /**
132
+ * Simplified YNAB transaction for reconciliation matching (output format with money formatting).
133
+ * Contains essential fields for transaction comparison.
134
+ *
135
+ * @remarks
136
+ * The adapter adds `amount_money` field with formatted money value.
137
+ * Date field is always serialized as an ISO 8601 date string (YYYY-MM-DD) in tool output,
138
+ * even though the internal type may use Date objects during analysis.
139
+ *
140
+ * @see src/tools/reconciliation/types.ts - YNABTransaction interface (internal type)
141
+ * @see src/tools/reconcileAdapter.ts:82-85 - toYNABTransactionView function
142
+ */
143
+ export const YNABTransactionSimpleSchema = z.object({
144
+ id: z.string(),
145
+ date: IsoDateWithCalendarValidationSchema,
146
+ amount: z.number(),
147
+ payee_name: z.string().nullable(),
148
+ category_name: z.string().nullable(),
149
+ cleared: z.enum(['cleared', 'uncleared', 'reconciled']),
150
+ approved: z.boolean(),
151
+ memo: z.string().nullable().optional(),
152
+ amount_money: MoneyValueSchema, // Added by adapter
153
+ });
154
+
155
+ export type YNABTransactionSimple = z.infer<typeof YNABTransactionSimpleSchema>;
156
+
157
+ /**
158
+ * Potential match candidate with confidence scoring.
159
+ * Represents a possible match between bank and YNAB transactions.
160
+ *
161
+ * @see src/tools/reconciliation/types.ts - MatchCandidate interface
162
+ */
163
+ export const MatchCandidateSchema = z.object({
164
+ ynab_transaction: YNABTransactionSimpleSchema,
165
+ confidence: z.number().min(0).max(100),
166
+ match_reason: z.string(),
167
+ explanation: z.string(),
168
+ });
169
+
170
+ export type MatchCandidate = z.infer<typeof MatchCandidateSchema>;
171
+
172
+ /**
173
+ * Derives the confidence enum value from a numeric confidence score.
174
+ * Used to enforce consistency between confidence and confidence_score fields.
175
+ *
176
+ * @param score - Numeric confidence score (0-100)
177
+ * @returns Corresponding confidence level enum value
178
+ *
179
+ * @remarks
180
+ * Export this function to use in application logic when constructing
181
+ * TransactionMatch objects to ensure consistency between the two fields.
182
+ *
183
+ * Thresholds:
184
+ * - 'high': score >= 90
185
+ * - 'medium': score >= 60
186
+ * - 'low': score >= 1
187
+ * - 'none': score === 0
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * const confidenceScore = 85;
192
+ * const transactionMatch = {
193
+ * // ... other fields
194
+ * confidence: deriveConfidenceFromScore(confidenceScore),
195
+ * confidence_score: confidenceScore,
196
+ * };
197
+ * ```
198
+ */
199
+ export function deriveConfidenceFromScore(score: number): 'high' | 'medium' | 'low' | 'none' {
200
+ if (score >= 90) return 'high';
201
+ if (score >= 60) return 'medium';
202
+ if (score >= 1) return 'low';
203
+ return 'none';
204
+ }
205
+
206
+ /**
207
+ * Transaction match result with confidence and candidates.
208
+ * Links a bank transaction to a YNAB transaction or suggests candidates.
209
+ *
210
+ * @see src/tools/reconciliation/types.ts - TransactionMatch interface
211
+ *
212
+ * @remarks
213
+ * This schema contains both `confidence` (enum) and `confidence_score` (0-100)
214
+ * for backwards compatibility. A validation rule enforces consistency between
215
+ * the two fields by deriving the expected enum value from the numeric score
216
+ * and rejecting mismatches.
217
+ *
218
+ * Confidence thresholds:
219
+ * - 'high': confidence_score >= 90
220
+ * - 'medium': confidence_score >= 60
221
+ * - 'low': confidence_score >= 1
222
+ * - 'none': confidence_score === 0
223
+ */
224
+ export const TransactionMatchSchema = z
225
+ .object({
226
+ bank_transaction: BankTransactionSchema,
227
+ ynab_transaction: YNABTransactionSimpleSchema.optional(),
228
+ candidates: z.array(MatchCandidateSchema).optional(),
229
+ confidence: z.enum(['high', 'medium', 'low', 'none']),
230
+ confidence_score: z.number().min(0).max(100),
231
+ match_reason: z.string(),
232
+ top_confidence: z.number().optional(),
233
+ action_hint: z.string().optional(),
234
+ recommendation: z.string().optional(),
235
+ })
236
+ .refine(
237
+ (data) => {
238
+ const expectedConfidence = deriveConfidenceFromScore(data.confidence_score);
239
+ return data.confidence === expectedConfidence;
240
+ },
241
+ {
242
+ message: 'Confidence mismatch: confidence enum does not match confidence_score',
243
+ path: ['confidence'],
244
+ },
245
+ );
246
+
247
+ export type TransactionMatch = z.infer<typeof TransactionMatchSchema>;
248
+
249
+ /**
250
+ * Balance reconciliation status.
251
+ * Compares current account balances to target statement balance.
252
+ *
253
+ * @see src/tools/reconciliation/types.ts - BalanceInfo interface
254
+ */
255
+ export const BalanceInfoSchema = z.object({
256
+ current_cleared: MoneyValueSchema,
257
+ current_uncleared: MoneyValueSchema,
258
+ current_total: MoneyValueSchema,
259
+ target_statement: MoneyValueSchema,
260
+ discrepancy: MoneyValueSchema,
261
+ on_track: z.boolean(),
262
+ });
263
+
264
+ export type BalanceInfo = z.infer<typeof BalanceInfoSchema>;
265
+
266
+ /**
267
+ * Reconciliation summary statistics.
268
+ * High-level overview of matching results and balance status.
269
+ *
270
+ * @see src/tools/reconciliation/types.ts - ReconciliationSummary interface
271
+ */
272
+ export const ReconciliationSummarySchema = z.object({
273
+ statement_date_range: z.string(),
274
+ bank_transactions_count: z.number(),
275
+ ynab_transactions_count: z.number(),
276
+ auto_matched: z.number(),
277
+ suggested_matches: z.number(),
278
+ unmatched_bank: z.number(),
279
+ unmatched_ynab: z.number(),
280
+ current_cleared_balance: MoneyValueSchema,
281
+ target_statement_balance: MoneyValueSchema,
282
+ discrepancy: MoneyValueSchema,
283
+ discrepancy_explanation: z.string(),
284
+ });
285
+
286
+ export type ReconciliationSummary = z.infer<typeof ReconciliationSummarySchema>;
287
+
288
+ /**
289
+ * Reconciliation analysis insight.
290
+ * Highlights patterns, anomalies, or issues discovered during analysis.
291
+ *
292
+ * @see src/tools/reconciliation/types.ts - ReconciliationInsight interface
293
+ */
294
+ export const ReconciliationInsightSchema = z.object({
295
+ id: z.string(),
296
+ type: z.enum(['repeat_amount', 'near_match', 'anomaly']),
297
+ severity: z.enum(['info', 'warning', 'critical']),
298
+ title: z.string(),
299
+ description: z.string(),
300
+ evidence: z.record(z.string(), z.unknown()).optional(),
301
+ });
302
+
303
+ export type ReconciliationInsight = z.infer<typeof ReconciliationInsightSchema>;
304
+
305
+ /**
306
+ * Actionable recommendation discriminated union.
307
+ * Suggests specific actions to resolve discrepancies (create, update, review).
308
+ *
309
+ * @see src/tools/reconciliation/types.ts - ActionableRecommendation union type
310
+ */
311
+ export const ActionableRecommendationSchema = z.discriminatedUnion('action_type', [
312
+ // Create transaction recommendation
313
+ z.object({
314
+ id: z.string(),
315
+ action_type: z.literal('create_transaction'),
316
+ priority: z.enum(['high', 'medium', 'low']),
317
+ confidence: z.number().min(0).max(1),
318
+ message: z.string(),
319
+ reason: z.string(),
320
+ estimated_impact: MoneyValueSchema,
321
+ account_id: z.string(),
322
+ source_insight_id: z.string().optional(),
323
+ metadata: z.record(z.string(), z.unknown()).optional(),
324
+ parameters: z.object({
325
+ account_id: z.string(),
326
+ date: IsoDateWithCalendarValidationSchema,
327
+ amount: z.number(),
328
+ payee_name: z.string(),
329
+ memo: z.string().optional(),
330
+ cleared: z.enum(['cleared', 'uncleared']),
331
+ approved: z.boolean(),
332
+ category_id: z.string().optional(),
333
+ }),
334
+ }),
335
+ // Update cleared status recommendation
336
+ z.object({
337
+ id: z.string(),
338
+ action_type: z.literal('update_cleared'),
339
+ priority: z.enum(['high', 'medium', 'low']),
340
+ confidence: z.number().min(0).max(1),
341
+ message: z.string(),
342
+ reason: z.string(),
343
+ estimated_impact: MoneyValueSchema,
344
+ account_id: z.string(),
345
+ source_insight_id: z.string().optional(),
346
+ metadata: z.record(z.string(), z.unknown()).optional(),
347
+ parameters: z.object({
348
+ transaction_id: z.string(),
349
+ cleared: z.enum(['cleared', 'uncleared', 'reconciled']),
350
+ }),
351
+ }),
352
+ // Review duplicate recommendation
353
+ z.object({
354
+ id: z.string(),
355
+ action_type: z.literal('review_duplicate'),
356
+ priority: z.enum(['high', 'medium', 'low']),
357
+ confidence: z.number().min(0).max(1),
358
+ message: z.string(),
359
+ reason: z.string(),
360
+ estimated_impact: MoneyValueSchema,
361
+ account_id: z.string(),
362
+ source_insight_id: z.string().optional(),
363
+ metadata: z.record(z.string(), z.unknown()).optional(),
364
+ parameters: z.object({
365
+ candidate_ids: z.array(z.string()),
366
+ bank_transaction: BankTransactionSchema,
367
+ suggested_match_id: z.string().optional(),
368
+ }),
369
+ }),
370
+ // Manual review recommendation
371
+ z.object({
372
+ id: z.string(),
373
+ action_type: z.literal('manual_review'),
374
+ priority: z.enum(['high', 'medium', 'low']),
375
+ confidence: z.number().min(0).max(1),
376
+ message: z.string(),
377
+ reason: z.string(),
378
+ estimated_impact: MoneyValueSchema,
379
+ account_id: z.string(),
380
+ source_insight_id: z.string().optional(),
381
+ metadata: z.record(z.string(), z.unknown()).optional(),
382
+ parameters: z.object({
383
+ issue_type: z.string(),
384
+ related_transactions: z.array(z.string()),
385
+ }),
386
+ }),
387
+ ]);
388
+
389
+ export type ActionableRecommendation = z.infer<typeof ActionableRecommendationSchema>;
390
+
391
+ /**
392
+ * Account balance snapshot with money formatting.
393
+ * Used in execution result to show before/after balance states.
394
+ *
395
+ * @see src/tools/reconcileAdapter.ts:138-142 - convertAccountSnapshot function
396
+ */
397
+ export const AccountSnapshotSchema = z.object({
398
+ balance: MoneyValueSchema,
399
+ cleared_balance: MoneyValueSchema,
400
+ uncleared_balance: MoneyValueSchema,
401
+ });
402
+
403
+ export type AccountSnapshot = z.infer<typeof AccountSnapshotSchema>;
404
+
405
+ /**
406
+ * Transaction data shapes for different execution action types.
407
+ * Provides stronger typing than generic Record<string, unknown>.
408
+ */
409
+
410
+ /**
411
+ * Created transaction from YNAB API response.
412
+ * Used when a transaction is successfully created.
413
+ */
414
+ export const CreatedTransactionSchema = z
415
+ .object({
416
+ id: z.string(),
417
+ date: z.string(),
418
+ amount: z.number(),
419
+ memo: z.string().nullable().optional(),
420
+ cleared: z.enum(['cleared', 'uncleared', 'reconciled']).optional(),
421
+ approved: z.boolean().optional(),
422
+ payee_name: z.string().nullable().optional(),
423
+ category_name: z.string().nullable().optional(),
424
+ import_id: z.string().nullable().optional(),
425
+ })
426
+ .passthrough(); // Allow additional YNAB API fields
427
+
428
+ /**
429
+ * Transaction creation payload.
430
+ * Used when documenting what would be created (dry run) or what failed to create.
431
+ */
432
+ export const TransactionCreationPayloadSchema = z.object({
433
+ account_id: z.string(),
434
+ date: z.string(),
435
+ amount: z.number(),
436
+ payee_name: z.string().optional(),
437
+ memo: z.string().optional(),
438
+ cleared: z.enum(['cleared', 'uncleared']).optional(),
439
+ approved: z.boolean().optional(),
440
+ import_id: z.string().optional(),
441
+ });
442
+
443
+ /**
444
+ * Transaction update payload.
445
+ * Used when documenting status or date changes.
446
+ */
447
+ export const TransactionUpdatePayloadSchema = z.object({
448
+ transaction_id: z.string(),
449
+ new_date: z.string().optional(),
450
+ cleared: z.enum(['cleared', 'uncleared', 'reconciled']).optional(),
451
+ });
452
+
453
+ /**
454
+ * Duplicate detection payload.
455
+ * Used when a transaction creation is skipped due to duplicate detection.
456
+ */
457
+ export const DuplicateDetectionPayloadSchema = z.object({
458
+ transaction_id: z.string().nullable(),
459
+ import_id: z.string().optional(),
460
+ });
461
+
462
+ /**
463
+ * Execution action record with correlation tracking.
464
+ * Documents individual actions taken during reconciliation execution.
465
+ *
466
+ * Uses discriminated union based on action type for stronger type safety.
467
+ * Each action type has its own transaction payload schema.
468
+ *
469
+ * @see src/tools/reconciliation/executor.ts:29-36 - ExecutionActionRecord interface
470
+ * @see src/tools/reconciliation/executor.ts:226-238 - create_transaction action
471
+ * @see src/tools/reconciliation/executor.ts:278-290 - create_transaction_failed action
472
+ * @see src/tools/reconciliation/executor.ts:339-351 - create_transaction_duplicate action
473
+ * @see src/tools/reconciliation/executor.ts:472-480 - update_transaction action (dry run)
474
+ * @see src/tools/reconciliation/executor.ts:515-520 - update_transaction action (real)
475
+ */
476
+ export const ExecutionActionRecordSchema = z.discriminatedUnion('type', [
477
+ // Successful transaction creation
478
+ z.object({
479
+ type: z.literal('create_transaction'),
480
+ transaction: CreatedTransactionSchema.nullable(),
481
+ reason: z.string(),
482
+ bulk_chunk_index: z.number().optional(),
483
+ correlation_key: z.string().optional(),
484
+ }),
485
+ // Failed transaction creation
486
+ z.object({
487
+ type: z.literal('create_transaction_failed'),
488
+ transaction: TransactionCreationPayloadSchema,
489
+ reason: z.string(),
490
+ bulk_chunk_index: z.number().optional(),
491
+ correlation_key: z.string().optional(),
492
+ }),
493
+ // Duplicate transaction detected
494
+ z.object({
495
+ type: z.literal('create_transaction_duplicate'),
496
+ transaction: DuplicateDetectionPayloadSchema,
497
+ reason: z.string(),
498
+ bulk_chunk_index: z.number(),
499
+ correlation_key: z.string().optional(),
500
+ duplicate: z.literal(true),
501
+ }),
502
+ // Transaction update (status/date change)
503
+ z.object({
504
+ type: z.literal('update_transaction'),
505
+ transaction: z.union([
506
+ CreatedTransactionSchema.nullable(), // Real execution
507
+ TransactionUpdatePayloadSchema, // Dry run
508
+ ]),
509
+ reason: z.string(),
510
+ }),
511
+ // Balance alignment checkpoint
512
+ z.object({
513
+ type: z.literal('balance_checkpoint'),
514
+ transaction: z.null(),
515
+ reason: z.string(),
516
+ }),
517
+ // Bulk create fallback to sequential
518
+ z.object({
519
+ type: z.literal('bulk_create_fallback'),
520
+ transaction: z.null(),
521
+ reason: z.string(),
522
+ bulk_chunk_index: z.number(),
523
+ }),
524
+ ]);
525
+
526
+ export type ExecutionActionRecord = z.infer<typeof ExecutionActionRecordSchema>;
527
+
528
+ /**
529
+ * Execution summary statistics.
530
+ * High-level overview of reconciliation execution results.
531
+ *
532
+ * @see src/tools/reconciliation/executor.ts:38-48 - ExecutionSummary interface
533
+ */
534
+ export const ExecutionSummarySchema = z.object({
535
+ bank_transactions_count: z.number(),
536
+ ynab_transactions_count: z.number(),
537
+ matches_found: z.number(),
538
+ missing_in_ynab: z.number(),
539
+ missing_in_bank: z.number(),
540
+ transactions_created: z.number(),
541
+ transactions_updated: z.number(),
542
+ dates_adjusted: z.number(),
543
+ dry_run: z.boolean(),
544
+ });
545
+
546
+ export type ExecutionSummary = z.infer<typeof ExecutionSummarySchema>;
547
+
548
+ /**
549
+ * Bulk operation metrics for reconciliation transaction creation.
550
+ * Tracks performance and correlation behavior during bulk transaction operations.
551
+ *
552
+ * @remarks
553
+ * Note on failure counters:
554
+ * - `transaction_failures` is the canonical counter for per-transaction failures
555
+ * - `failed_transactions` is maintained for backward compatibility and should always
556
+ * mirror `transaction_failures` rather than represent an independent count
557
+ *
558
+ * @see src/tools/reconciliation/executor.ts:50-68 - BulkOperationDetails interface
559
+ */
560
+ export const BulkOperationDetailsSchema = z
561
+ .object({
562
+ chunks_processed: z.number(),
563
+ bulk_successes: z.number(),
564
+ sequential_fallbacks: z.number(),
565
+ duplicates_detected: z.number(),
566
+ failed_transactions: z.number(),
567
+ bulk_chunk_failures: z.number(),
568
+ transaction_failures: z.number(),
569
+ sequential_attempts: z.number().optional(),
570
+ })
571
+ .refine((data) => data.failed_transactions === data.transaction_failures, {
572
+ message: 'failed_transactions must equal transaction_failures',
573
+ });
574
+
575
+ export type BulkOperationDetails = z.infer<typeof BulkOperationDetailsSchema>;
576
+
577
+ /**
578
+ * Execution result (present if auto-execution enabled).
579
+ * Documents actions taken and resulting balance changes.
580
+ *
581
+ * @remarks
582
+ * This schema matches the actual payload shape built by convertExecution()
583
+ * in reconcileAdapter.ts, which differs from the legacy ExecutionResult interface.
584
+ * The adapter converts the ExecutionResult to a structured payload with formatted
585
+ * money values and nested balance snapshots.
586
+ *
587
+ * @see src/tools/reconcileAdapter.ts:199-232 - convertExecution function
588
+ * @see src/tools/reconciliation/executor.ts:69-79 - ExecutionResult interface
589
+ */
590
+ export const ExecutionResultSchema = z.object({
591
+ summary: ExecutionSummarySchema,
592
+ account_balance: z.object({
593
+ before: AccountSnapshotSchema,
594
+ after: AccountSnapshotSchema,
595
+ }),
596
+ actions_taken: z.array(ExecutionActionRecordSchema),
597
+ recommendations: z.array(z.string()),
598
+ balance_reconciliation: z.unknown().optional(),
599
+ bulk_operation_details: BulkOperationDetailsSchema.optional(),
600
+ });
601
+
602
+ export type ExecutionResult = z.infer<typeof ExecutionResultSchema>;
603
+
604
+ /**
605
+ * Data freshness audit metadata.
606
+ * Documents data sources, cache status, and staleness.
607
+ *
608
+ * @remarks
609
+ * This schema uses `.catchall(z.unknown())` to support forward-compatible extensions
610
+ * through the AdapterOptions.auditMetadata pattern. Known keys are typed explicitly,
611
+ * but additional fields can be passed through without validation errors.
612
+ *
613
+ * @see src/tools/reconciliation/index.ts:272-284 - Audit metadata construction
614
+ * @see src/tools/reconcileAdapter.ts:19-25 - AdapterOptions interface with extensible auditMetadata
615
+ */
616
+ export const AuditMetadataSchema = z
617
+ .object({
618
+ data_freshness: z.string(),
619
+ data_source: z.string(),
620
+ server_knowledge: z.number().optional(),
621
+ fetched_at: z.string(),
622
+ accounts_count: z.number().optional(),
623
+ transactions_count: z.number().optional(),
624
+ cache_status: z.object({
625
+ accounts_cached: z.boolean(),
626
+ transactions_cached: z.boolean(),
627
+ delta_merge_applied: z.boolean(),
628
+ }),
629
+ })
630
+ .catchall(z.unknown());
631
+
632
+ export type AuditMetadata = z.infer<typeof AuditMetadataSchema>;
633
+
634
+ // ============================================================================
635
+ // MAIN OUTPUT SCHEMA
636
+ // ============================================================================
637
+
638
+ /**
639
+ * Complete reconciliation analysis output.
640
+ * Discriminated union for human-only vs human+structured response modes.
641
+ *
642
+ * @see src/tools/reconciliation/index.ts:147-362 - Main handler
643
+ * @see src/tools/reconcileAdapter.ts - buildReconciliationPayload function
644
+ *
645
+ * @example
646
+ * // Human-readable narrative only (default)
647
+ * {
648
+ * human: "Successfully matched 45 of 50 bank transactions. Current cleared balance..."
649
+ * }
650
+ *
651
+ * @example
652
+ * // With structured data (include_structured_data=true)
653
+ * {
654
+ * human: "Successfully matched 45 of 50 bank transactions...",
655
+ * structured: {
656
+ * success: true,
657
+ * phase: "analysis",
658
+ * summary: {
659
+ * statement_date_range: "2025-10-01 to 2025-10-31",
660
+ * bank_transactions_count: 50,
661
+ * auto_matched: 45,
662
+ * suggested_matches: 3,
663
+ * unmatched_bank: 2,
664
+ * discrepancy: { amount: -25.50, currency: "USD", formatted: "-$25.50" }
665
+ * },
666
+ * auto_matches: [...],
667
+ * suggested_matches: [...],
668
+ * unmatched_bank: [...],
669
+ * unmatched_ynab: [...],
670
+ * balance_info: {
671
+ * current_cleared: { amount: 1250.00, currency: "USD", formatted: "$1,250.00" },
672
+ * target_statement: { amount: 1275.50, currency: "USD", formatted: "$1,275.50" },
673
+ * on_track: false
674
+ * },
675
+ * insights: [
676
+ * { id: "ins-1", type: "repeat_amount", severity: "warning", title: "Duplicate amount detected" }
677
+ * ],
678
+ * recommendations: [
679
+ * {
680
+ * id: "rec-1",
681
+ * action_type: "create_transaction",
682
+ * priority: "high",
683
+ * confidence: 0.95,
684
+ * message: "Create missing transaction for bank entry",
685
+ * parameters: { account_id: "acct-1", date: "2025-10-15", amount: -25500, ... }
686
+ * }
687
+ * ],
688
+ * execution_result: {
689
+ * executed: true,
690
+ * actions_taken: [{ action_type: "create_transaction", status: "success", transaction_id: "txn-new" }],
691
+ * reconciliation_complete: false,
692
+ * remaining_discrepancy: { amount: 0, currency: "USD", formatted: "$0.00" }
693
+ * },
694
+ * audit_metadata: {
695
+ * data_freshness: "real-time",
696
+ * data_source: "YNAB API + CSV",
697
+ * fetched_at: "2025-11-18T10:30:00Z",
698
+ * cache_status: { accounts_cached: false, transactions_cached: false }
699
+ * }
700
+ * }
701
+ * }
702
+ */
703
+ /**
704
+ * CSV format configuration metadata.
705
+ * Documents the CSV format detected or specified by the user.
706
+ *
707
+ * @see src/tools/reconciliation/index.ts:364-402 - mapCsvFormatForPayload function
708
+ */
709
+ export const CsvFormatMetadataSchema = z.object({
710
+ delimiter: z.string(),
711
+ decimal_separator: z.string(),
712
+ thousands_separator: z.string().nullable(),
713
+ date_format: z.string(),
714
+ header_row: z.boolean(),
715
+ date_column: z.string().nullable(),
716
+ amount_column: z.string().nullable(),
717
+ payee_column: z.string().nullable(),
718
+ });
719
+
720
+ export type CsvFormatMetadata = z.infer<typeof CsvFormatMetadataSchema>;
721
+
722
+ // Define the structured data schema without refinement first
723
+ const StructuredReconciliationDataBaseSchema = z.object({
724
+ version: z.string(),
725
+ schema_url: z.string(),
726
+ generated_at: z.string(),
727
+ account: z.object({
728
+ id: z.string().optional(),
729
+ name: z.string().optional(),
730
+ }),
731
+ summary: ReconciliationSummarySchema,
732
+ balance: BalanceInfoSchema.extend({
733
+ discrepancy_direction: z.enum(['balanced', 'ynab_higher', 'bank_higher']),
734
+ }),
735
+ insights: z.array(ReconciliationInsightSchema),
736
+ next_steps: z.array(z.string()),
737
+ matches: z.object({
738
+ auto: z.array(TransactionMatchSchema),
739
+ suggested: z.array(TransactionMatchSchema),
740
+ }),
741
+ unmatched: z.object({
742
+ bank: z.array(BankTransactionSchema),
743
+ ynab: z.array(YNABTransactionSimpleSchema),
744
+ }),
745
+ recommendations: z.array(ActionableRecommendationSchema).optional(),
746
+ csv_format: CsvFormatMetadataSchema.optional(),
747
+ execution: ExecutionResultSchema.optional(),
748
+ audit: AuditMetadataSchema.optional(),
749
+ });
750
+
751
+ export const ReconcileAccountOutputSchema = z
752
+ .union([
753
+ // Human + structured data (when include_structured_data=true) - check this FIRST
754
+ z.object({
755
+ human: z.string(),
756
+ structured: StructuredReconciliationDataBaseSchema,
757
+ }),
758
+ // Human narrative only (default mode) - check this SECOND
759
+ z.object({
760
+ human: z.string(),
761
+ }),
762
+ ])
763
+ .refine(
764
+ (data) => {
765
+ // Only validate if this is the structured variant (has 'structured' property)
766
+ if ('structured' in data && data.structured) {
767
+ const discrepancyAmount = data.structured.balance.discrepancy.amount;
768
+ const direction = data.structured.balance.discrepancy_direction;
769
+
770
+ // If absolute discrepancy < 0.01, direction must be 'balanced'
771
+ if (Math.abs(discrepancyAmount) < 0.01) {
772
+ return direction === 'balanced';
773
+ }
774
+
775
+ // If discrepancy > 0, direction must be 'ynab_higher'
776
+ if (discrepancyAmount > 0) {
777
+ return direction === 'ynab_higher';
778
+ }
779
+
780
+ // If discrepancy < 0, direction must be 'bank_higher'
781
+ if (discrepancyAmount < 0) {
782
+ return direction === 'bank_higher';
783
+ }
784
+ }
785
+
786
+ // Human-only variant always passes validation
787
+ return true;
788
+ },
789
+ {
790
+ message:
791
+ 'Discrepancy direction mismatch: direction must match the numeric discrepancy amount',
792
+ path: ['balance', 'discrepancy_direction'],
793
+ },
794
+ );
795
+
796
+ export type ReconcileAccountOutput = z.infer<typeof ReconcileAccountOutputSchema>;