@dizzlkheinz/ynab-mcpb 0.12.2 → 0.15.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 (262) hide show
  1. package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +3 -0
  2. package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +3 -0
  3. package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +1 -0
  4. package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +5 -0
  5. package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +1569 -0
  6. package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +3 -0
  7. package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +2832 -0
  8. package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +2709 -0
  9. package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +2832 -0
  10. package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +2832 -0
  11. package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +2709 -0
  12. package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +1 -0
  13. package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +5217 -0
  14. package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +2594 -0
  15. package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +2594 -0
  16. package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +231 -0
  17. package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +2590 -0
  18. package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +5195 -0
  19. package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +286 -0
  20. package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +218 -0
  21. package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +180 -0
  22. package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +3 -0
  23. package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +1 -0
  24. package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +747 -0
  25. package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +1 -0
  26. package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +2594 -0
  27. package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +2594 -0
  28. package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +144 -0
  29. package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +416 -0
  30. package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +2590 -0
  31. package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +2590 -0
  32. package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +2590 -0
  33. package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +790 -0
  34. package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +766 -0
  35. package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +790 -0
  36. package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +2594 -0
  37. package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +1000 -0
  38. package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +3489 -0
  39. package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +766 -0
  40. package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +2594 -0
  41. package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +2456 -0
  42. package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +2594 -0
  43. package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +18 -0
  44. package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +48 -0
  45. package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +1 -0
  46. package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +1 -0
  47. package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +1 -0
  48. package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +1 -0
  49. package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +1271 -0
  50. package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +1570 -0
  51. package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +2590 -0
  52. package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +3 -0
  53. package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +3299 -0
  54. package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +3299 -0
  55. package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +1882 -0
  56. package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +2594 -0
  57. package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +1 -0
  58. package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +170 -0
  59. package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +1 -0
  60. package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +3 -0
  61. package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +1 -0
  62. package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +1 -0
  63. package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +3 -0
  64. package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +1 -0
  65. package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +1 -0
  66. package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +5 -0
  67. package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +1 -0
  68. package/.github/workflows/ci-tests.yml +6 -2
  69. package/.github/workflows/publish.yml +3 -3
  70. package/.github/workflows/release.yml +4 -0
  71. package/CHANGELOG.md +89 -1
  72. package/NUL +1 -1
  73. package/README.md +36 -10
  74. package/dist/bundle/index.cjs +65 -42
  75. package/dist/index.js +9 -20
  76. package/dist/server/YNABMCPServer.d.ts +2 -1
  77. package/dist/server/YNABMCPServer.js +61 -27
  78. package/dist/server/cacheKeys.d.ts +8 -0
  79. package/dist/server/cacheKeys.js +8 -0
  80. package/dist/server/config.d.ts +22 -3
  81. package/dist/server/config.js +16 -17
  82. package/dist/server/errorHandler.d.ts +2 -0
  83. package/dist/server/errorHandler.js +49 -5
  84. package/dist/server/securityMiddleware.js +3 -6
  85. package/dist/server/toolRegistry.js +8 -10
  86. package/dist/tools/accountTools.js +4 -3
  87. package/dist/tools/categoryTools.js +8 -7
  88. package/dist/tools/monthTools.js +2 -1
  89. package/dist/tools/payeeTools.js +2 -1
  90. package/dist/tools/reconcileAdapter.js +10 -5
  91. package/dist/tools/reconciliation/analyzer.d.ts +4 -2
  92. package/dist/tools/reconciliation/analyzer.js +120 -404
  93. package/dist/tools/reconciliation/csvParser.d.ts +51 -0
  94. package/dist/tools/reconciliation/csvParser.js +413 -0
  95. package/dist/tools/reconciliation/executor.d.ts +8 -0
  96. package/dist/tools/reconciliation/executor.js +277 -50
  97. package/dist/tools/reconciliation/index.d.ts +7 -7
  98. package/dist/tools/reconciliation/index.js +115 -39
  99. package/dist/tools/reconciliation/matcher.d.ts +24 -3
  100. package/dist/tools/reconciliation/matcher.js +175 -133
  101. package/dist/tools/reconciliation/recommendationEngine.js +22 -18
  102. package/dist/tools/reconciliation/reportFormatter.js +9 -8
  103. package/dist/tools/reconciliation/signDetector.d.ts +2 -0
  104. package/dist/tools/reconciliation/signDetector.js +54 -0
  105. package/dist/tools/reconciliation/types.d.ts +20 -34
  106. package/dist/tools/reconciliation/types.js +1 -7
  107. package/dist/tools/reconciliation/ynabAdapter.d.ts +4 -0
  108. package/dist/tools/reconciliation/ynabAdapter.js +15 -0
  109. package/dist/tools/transactionTools.d.ts +3 -17
  110. package/dist/tools/transactionTools.js +5 -17
  111. package/dist/types/reconciliation.d.ts +24 -0
  112. package/dist/types/reconciliation.js +1 -0
  113. package/dist/utils/baseError.d.ts +3 -0
  114. package/dist/utils/baseError.js +7 -0
  115. package/dist/utils/errors.d.ts +13 -0
  116. package/dist/utils/errors.js +15 -0
  117. package/dist/utils/validationError.d.ts +3 -0
  118. package/dist/utils/validationError.js +3 -0
  119. package/docs/guides/ARCHITECTURE.md +12 -129
  120. package/docs/plans/2025-11-20-reloadable-config-token-validation.md +93 -0
  121. package/docs/plans/2025-11-21-fix-transaction-cached-property.md +362 -0
  122. package/docs/plans/2025-11-21-reconciliation-error-handling.md +90 -0
  123. package/docs/plans/2025-11-21-v014-hardening.md +153 -0
  124. package/docs/plans/reconciliation-v2-redesign.md +1571 -0
  125. package/package.json +8 -2
  126. package/scripts/run-throttled-integration-tests.js +9 -3
  127. package/scripts/test-recommendations.ts +1 -1
  128. package/src/__tests__/performance.test.ts +12 -5
  129. package/src/__tests__/testUtils.ts +62 -5
  130. package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +129 -0
  131. package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +53 -0
  132. package/src/__tests__/workflows.e2e.test.ts +33 -0
  133. package/src/index.ts +8 -31
  134. package/src/server/YNABMCPServer.ts +81 -42
  135. package/src/server/__tests__/YNABMCPServer.integration.test.ts +10 -12
  136. package/src/server/__tests__/YNABMCPServer.test.ts +27 -15
  137. package/src/server/__tests__/config.test.ts +76 -152
  138. package/src/server/__tests__/server-startup.integration.test.ts +42 -14
  139. package/src/server/__tests__/toolRegistry.test.ts +1 -1
  140. package/src/server/cacheKeys.ts +8 -0
  141. package/src/server/config.ts +20 -38
  142. package/src/server/errorHandler.ts +52 -5
  143. package/src/server/securityMiddleware.ts +3 -7
  144. package/src/server/toolRegistry.ts +14 -10
  145. package/src/tools/__tests__/categoryTools.test.ts +37 -19
  146. package/src/tools/__tests__/transactionTools.test.ts +58 -2
  147. package/src/tools/accountTools.ts +8 -3
  148. package/src/tools/categoryTools.ts +12 -7
  149. package/src/tools/monthTools.ts +7 -1
  150. package/src/tools/payeeTools.ts +7 -1
  151. package/src/tools/reconcileAdapter.ts +10 -5
  152. package/src/tools/reconciliation/__tests__/adapter.test.ts +28 -22
  153. package/src/tools/reconciliation/__tests__/analyzer.test.ts +114 -180
  154. package/src/tools/reconciliation/__tests__/csvParser.test.ts +87 -0
  155. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +26 -6
  156. package/src/tools/reconciliation/__tests__/executor.test.ts +133 -60
  157. package/src/tools/reconciliation/__tests__/matcher.test.ts +68 -54
  158. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +37 -30
  159. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +6 -5
  160. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +30 -11
  161. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +50 -15
  162. package/src/tools/reconciliation/__tests__/signDetector.test.ts +211 -0
  163. package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +61 -0
  164. package/src/tools/reconciliation/analyzer.ts +174 -545
  165. package/src/tools/reconciliation/csvParser.ts +617 -0
  166. package/src/tools/reconciliation/executor.ts +344 -58
  167. package/src/tools/reconciliation/index.ts +141 -48
  168. package/src/tools/reconciliation/matcher.ts +234 -214
  169. package/src/tools/reconciliation/recommendationEngine.ts +23 -19
  170. package/src/tools/reconciliation/reportFormatter.ts +16 -11
  171. package/src/tools/reconciliation/signDetector.ts +117 -0
  172. package/src/tools/reconciliation/types.ts +39 -61
  173. package/src/tools/reconciliation/ynabAdapter.ts +33 -0
  174. package/src/tools/schemas/outputs/utilityOutputs.ts +1 -1
  175. package/src/tools/transactionTools.ts +7 -18
  176. package/src/types/reconciliation.ts +49 -0
  177. package/src/utils/baseError.ts +7 -0
  178. package/src/utils/errors.ts +21 -0
  179. package/src/utils/validationError.ts +3 -0
  180. package/temp-recon.ts +126 -0
  181. package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +3662 -0
  182. package/test_mcp_tools.mjs +75 -0
  183. package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +0 -8
  184. package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +0 -3
  185. package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +0 -3
  186. package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +0 -1
  187. package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +0 -12
  188. package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +0 -1
  189. package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +0 -1
  190. package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +0 -5
  191. package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +0 -1453
  192. package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +0 -8
  193. package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +0 -9
  194. package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +0 -1
  195. package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +0 -5
  196. package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +0 -81
  197. package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +0 -610
  198. package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +0 -1
  199. package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +0 -1
  200. package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +0 -1
  201. package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +0 -3
  202. package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +0 -8
  203. package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +0 -5
  204. package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +0 -1
  205. package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +0 -9
  206. package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +0 -1
  207. package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +0 -13
  208. package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +0 -14
  209. package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +0 -5
  210. package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +0 -5
  211. package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +0 -5
  212. package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +0 -3
  213. package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +0 -1
  214. package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +0 -1
  215. package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +0 -5
  216. package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +0 -5
  217. package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +0 -8
  218. package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +0 -8
  219. package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +0 -5
  220. package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +0 -1
  221. package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +0 -1
  222. package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +0 -1
  223. package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +0 -1
  224. package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +0 -1
  225. package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +0 -5
  226. package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +0 -1
  227. package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +0 -1
  228. package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +0 -1
  229. package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +0 -3
  230. package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +0 -8
  231. package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +0 -1
  232. package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +0 -1
  233. package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +0 -8
  234. package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +0 -3
  235. package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +0 -5
  236. package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +0 -1
  237. package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +0 -1
  238. package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +0 -5
  239. package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +0 -1
  240. package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +0 -14
  241. package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +0 -1
  242. package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +0 -5
  243. package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +0 -5
  244. package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +0 -11
  245. package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +0 -1
  246. package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +0 -8
  247. package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +0 -1
  248. package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +0 -3
  249. package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +0 -3
  250. package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +0 -8
  251. package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +0 -1
  252. package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +0 -5
  253. package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +0 -1
  254. package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +0 -1
  255. package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +0 -1
  256. package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +0 -1
  257. package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +0 -3
  258. package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +0 -8
  259. package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +0 -1
  260. package/.gemini/settings.json +0 -8
  261. package/ADOS-2-Module-1-Complete-Manual.md +0 -757
  262. package/WARP.md +0 -245
@@ -173,16 +173,17 @@ function formatBankTransactionLine(txn: BankTransaction): string {
173
173
  * Format a suggested match line
174
174
  */
175
175
  function formatSuggestedMatchLine(match: TransactionMatch): string {
176
- const bankTxn = match.bank_transaction;
176
+ const bankTxn = match.bankTransaction;
177
177
  const amountStr = formatAmount(bankTxn.amount);
178
- const confidenceStr = `${match.confidence_score}%`;
178
+ const confidenceStr = `${match.confidenceScore}%`;
179
179
  return ` ${bankTxn.date} - ${bankTxn.payee.substring(0, 35).padEnd(35)} ${amountStr} (${confidenceStr} confidence)`;
180
180
  }
181
181
 
182
182
  /**
183
- * Format an amount for display
183
+ * Format an amount for display (input in milliunits)
184
184
  */
185
- function formatAmount(amount: number): string {
185
+ function formatAmount(amountMilli: number): string {
186
+ const amount = amountMilli / 1000;
186
187
  const sign = amount >= 0 ? '+' : '-';
187
188
  const absAmount = Math.abs(amount);
188
189
  return `${sign}$${absAmount.toFixed(2)}`.padStart(10);
@@ -336,6 +337,8 @@ export function formatBalanceInfo(balance: BalanceInfo): string {
336
337
  /**
337
338
  * Format transaction list (helper for detailed reports)
338
339
  */
340
+ type FormattableYnabTransaction = YNABTransaction & { payee_name?: string | null };
341
+
339
342
  export function formatTransactionList(
340
343
  transactions: BankTransaction[] | YNABTransaction[],
341
344
  maxItems: number = 10,
@@ -344,14 +347,16 @@ export function formatTransactionList(
344
347
  const toShow = transactions.slice(0, maxItems);
345
348
 
346
349
  for (const txn of toShow) {
347
- if ('payee' in txn) {
348
- // Bank transaction
349
- lines.push(formatBankTransactionLine(txn));
350
+ if ('cleared' in txn) {
351
+ // YNAB transaction (normalized)
352
+ const ynabTxn = txn as FormattableYnabTransaction;
353
+ const payee = ynabTxn.payee_name ?? ynabTxn.payee ?? 'Unknown';
354
+ lines.push(
355
+ ` ${ynabTxn.date} - ${payee.substring(0, 40).padEnd(40)} ${formatAmount(ynabTxn.amount)}`,
356
+ );
350
357
  } else {
351
- // YNAB transaction
352
- const amount = txn.amount / 1000; // Convert milliunits to dollars
353
- const payee = txn.payee_name ?? 'Unknown';
354
- lines.push(` ${txn.date} - ${payee.substring(0, 40).padEnd(40)} ${formatAmount(amount)}`);
358
+ // Bank transaction
359
+ lines.push(formatBankTransactionLine(txn as BankTransaction));
355
360
  }
356
361
  }
357
362
 
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Smart sign detection for bank transaction imports
3
+ *
4
+ * Analyzes a sample of bank and YNAB transactions to determine if
5
+ * bank amounts need to be inverted to match YNAB's sign convention.
6
+ */
7
+
8
+ import type { BankTransaction, NormalizedYNABTransaction } from '../../types/reconciliation.js';
9
+
10
+ interface SignMatch {
11
+ bankAmount: number;
12
+ ynabAmount: number;
13
+ oppositeSign: boolean;
14
+ }
15
+
16
+ /**
17
+ * Detects whether bank transaction amounts need to be inverted
18
+ * to match YNAB's sign convention.
19
+ *
20
+ * Algorithm:
21
+ * 1. Find matching transactions based on date/amount proximity
22
+ * 2. For each match, check if signs are opposite
23
+ * 3. If >50% of matches have opposite signs, return true (needs inversion)
24
+ *
25
+ * @param bankTransactions - Raw bank transactions from CSV
26
+ * @param ynabTransactions - Normalized YNAB transactions
27
+ * @returns true if bank amounts should be inverted, false otherwise
28
+ */
29
+ export function detectSignInversion(
30
+ bankTransactions: BankTransaction[],
31
+ ynabTransactions: NormalizedYNABTransaction[],
32
+ ): boolean {
33
+ // Edge cases: empty lists
34
+ if (bankTransactions.length === 0 || ynabTransactions.length === 0) {
35
+ return false; // Conservative default: don't invert
36
+ }
37
+
38
+ // Sample up to 20 transactions for performance
39
+ const sampleSize = Math.min(20, bankTransactions.length);
40
+ const sample = bankTransactions.slice(0, sampleSize);
41
+
42
+ const matches: SignMatch[] = [];
43
+
44
+ // Try to find matches for each bank transaction
45
+ for (const bankTxn of sample) {
46
+ const match = findClosestMatch(bankTxn, ynabTransactions);
47
+ if (match) {
48
+ matches.push(match);
49
+ }
50
+ }
51
+
52
+ // Need at least 1 match to make a determination
53
+ if (matches.length === 0) {
54
+ return false; // Conservative default: don't invert
55
+ }
56
+
57
+ // Count how many matches have opposite signs
58
+ const oppositeSignCount = matches.filter((m) => m.oppositeSign).length;
59
+ const oppositeSignRatio = oppositeSignCount / matches.length;
60
+
61
+ // If more than 50% have opposite signs, inversion is needed
62
+ return oppositeSignRatio > 0.5;
63
+ }
64
+
65
+ /**
66
+ * Find the closest matching YNAB transaction for a bank transaction
67
+ */
68
+ function findClosestMatch(
69
+ bankTxn: BankTransaction,
70
+ ynabTransactions: NormalizedYNABTransaction[],
71
+ ): SignMatch | null {
72
+ const bankDate = new Date(bankTxn.date);
73
+ const bankAbsAmount = Math.abs(bankTxn.amount);
74
+
75
+ let bestMatch: SignMatch | null = null;
76
+ let bestScore = 0;
77
+
78
+ for (const ynabTxn of ynabTransactions) {
79
+ const ynabDate = new Date(ynabTxn.date);
80
+ const ynabAbsAmount = Math.abs(ynabTxn.amount);
81
+
82
+ // Check if amounts match (within tolerance)
83
+ const amountDiff = Math.abs(bankAbsAmount - ynabAbsAmount);
84
+ const amountTolerance = 100; // 10 cents in milliunits
85
+ if (amountDiff > amountTolerance) {
86
+ continue; // Amounts too different
87
+ }
88
+
89
+ // Check date proximity
90
+ const daysDiff = Math.abs(bankDate.getTime() - ynabDate.getTime()) / (1000 * 60 * 60 * 24);
91
+ if (daysDiff > 7) {
92
+ continue; // Dates too far apart
93
+ }
94
+
95
+ // Calculate match score (closer = higher score)
96
+ const amountScore = amountDiff === 0 ? 100 : Math.max(0, 100 - amountDiff / 10);
97
+ const dateScore = daysDiff === 0 ? 100 : Math.max(0, 100 - daysDiff * 10);
98
+ const score = amountScore * 0.7 + dateScore * 0.3;
99
+
100
+ if (score > bestScore) {
101
+ bestScore = score;
102
+
103
+ // Check if signs are opposite
104
+ const bankSign = Math.sign(bankTxn.amount);
105
+ const ynabSign = Math.sign(ynabTxn.amount);
106
+ const oppositeSign = bankSign !== 0 && ynabSign !== 0 && bankSign !== ynabSign;
107
+
108
+ bestMatch = {
109
+ bankAmount: bankTxn.amount,
110
+ ynabAmount: ynabTxn.amount,
111
+ oppositeSign,
112
+ };
113
+ }
114
+ }
115
+
116
+ return bestMatch;
117
+ }
@@ -1,48 +1,28 @@
1
1
  /**
2
2
  * Type definitions for the reconciliation tool
3
3
  * Based on the 2025-10-31 reconciliation redesign specification
4
+ *
5
+ * IMPORTANT UNIT CONVENTION:
6
+ * BankTransaction.amount is in MILLIUNITS (integers) in V2 architecture (formerly dollars).
7
+ * YNABTransaction.amount is in MILLIUNITS (integers).
8
+ * All internal calculations use milliunits to avoid floating-point errors.
4
9
  */
5
10
 
6
11
  import type { MoneyValue } from '../../utils/money.js';
12
+ import type {
13
+ BankTransaction as CanonicalBankTransaction,
14
+ NormalizedYNABTransaction as CanonicalYNABTransaction,
15
+ } from '../../types/reconciliation.js';
16
+
17
+ // Re-export canonical types as the standard types
18
+ export type BankTransaction = CanonicalBankTransaction;
19
+ export type YNABTransaction = CanonicalYNABTransaction;
7
20
 
8
21
  /**
9
22
  * Matching confidence levels
10
23
  */
11
24
  export type MatchConfidence = 'high' | 'medium' | 'low' | 'none';
12
25
 
13
- /**
14
- * Bank transaction parsed from CSV
15
- */
16
- export interface BankTransaction {
17
- /** Generated UUID for tracking */
18
- id: string;
19
- /** Transaction date in YYYY-MM-DD format */
20
- date: string;
21
- /** Amount in dollars */
22
- amount: number;
23
- /** Payee/merchant name */
24
- payee: string;
25
- /** Optional memo/description */
26
- memo?: string;
27
- /** Original CSV row number for debugging */
28
- original_csv_row: number;
29
- }
30
-
31
- /**
32
- * YNAB transaction (simplified from API)
33
- */
34
- export interface YNABTransaction {
35
- id: string;
36
- date: string;
37
- /** Amount in milliunits */
38
- amount: number;
39
- payee_name: string | null;
40
- category_name: string | null;
41
- cleared: 'cleared' | 'uncleared' | 'reconciled';
42
- approved: boolean;
43
- memo?: string | null;
44
- }
45
-
46
26
  /**
47
27
  * Match candidate with confidence score
48
28
  */
@@ -57,21 +37,21 @@ export interface MatchCandidate {
57
37
  * Transaction match result
58
38
  */
59
39
  export interface TransactionMatch {
60
- bank_transaction: BankTransaction;
40
+ bankTransaction: BankTransaction;
61
41
  /** Best matched YNAB transaction (if any) */
62
- ynab_transaction?: YNABTransaction;
42
+ ynabTransaction?: YNABTransaction;
63
43
  /** Alternative candidates for suggested matches */
64
44
  candidates?: MatchCandidate[];
65
45
  /** Confidence level */
66
46
  confidence: MatchConfidence;
67
47
  /** Confidence score 0-100 */
68
- confidence_score: number;
48
+ confidenceScore: number;
69
49
  /** Reason for the match */
70
- match_reason: string;
50
+ matchReason: string;
71
51
  /** Top confidence from candidates */
72
- top_confidence?: number;
52
+ topConfidence?: number;
73
53
  /** Action hint for user */
74
- action_hint?: string;
54
+ actionHint?: string;
75
55
  /** Recommendation text */
76
56
  recommendation?: string;
77
57
  }
@@ -163,31 +143,29 @@ export interface ReconciliationAction {
163
143
  }
164
144
 
165
145
  /**
166
- * Matching algorithm configuration
146
+ * Matching algorithm configuration (V2)
167
147
  */
168
148
  export interface MatchingConfig {
169
- /** Date tolerance in days */
170
- dateToleranceDays: number;
171
- /** Amount tolerance in cents */
172
- amountToleranceCents: number;
173
- /** Description similarity threshold (0-1) */
174
- descriptionSimilarityThreshold: number;
175
- /** Confidence threshold for auto-matching (0-100) */
176
- autoMatchThreshold: number;
177
- /** Confidence threshold for suggestions (0-100) */
178
- suggestionThreshold: number;
179
- }
149
+ weights: {
150
+ amount: number; // Recommended: 0.50
151
+ date: number; // Recommended: 0.15
152
+ payee: number; // Recommended: 0.35
153
+ };
180
154
 
181
- /**
182
- * Default matching configuration (not type-only for use in code)
183
- */
184
- export const DEFAULT_MATCHING_CONFIG = {
185
- dateToleranceDays: 2,
186
- amountToleranceCents: 1,
187
- descriptionSimilarityThreshold: 0.8,
188
- autoMatchThreshold: 90,
189
- suggestionThreshold: 60,
190
- };
155
+ // Tolerances (in MILLIUNITS for amount)
156
+ amountToleranceMilliunits: number; // Default: 10 (1 cent)
157
+ dateToleranceDays: number; // Default: 7
158
+
159
+ // Thresholds
160
+ autoMatchThreshold: number; // Default: 85
161
+ suggestedMatchThreshold: number; // Default: 60
162
+ minimumCandidateScore: number; // Default: 40
163
+
164
+ // Bonuses for perfect matches
165
+ exactAmountBonus: number; // Default: 10
166
+ exactDateBonus: number; // Default: 5
167
+ exactPayeeBonus: number; // Default: 10
168
+ }
191
169
 
192
170
  /**
193
171
  * Parsed CSV data from compareTransactions
@@ -0,0 +1,33 @@
1
+ import type * as ynab from 'ynab';
2
+ import type { NormalizedYNABTransaction } from '../../types/reconciliation.js';
3
+
4
+ /**
5
+ * Convert YNAB SDK transaction to normalized format for matching.
6
+ *
7
+ * This adapter keeps the YNAB SDK dependency isolated from the
8
+ * reconciliation core logic.
9
+ *
10
+ * NOTE: Amount stays in milliunits - no conversion needed since
11
+ * YNAB API already uses milliunits natively.
12
+ */
13
+ export function normalizeYNABTransaction(txn: ynab.TransactionDetail): NormalizedYNABTransaction {
14
+ return {
15
+ id: txn.id,
16
+ date: txn.date,
17
+ amount: txn.amount, // Already in milliunits - no conversion!
18
+ payee: txn.payee_name ?? null,
19
+ memo: txn.memo ?? null,
20
+ categoryName: txn.category_name ?? null,
21
+ cleared: txn.cleared,
22
+ approved: txn.approved,
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Batch convert YNAB transactions.
28
+ */
29
+ export function normalizeYNABTransactions(
30
+ txns: ynab.TransactionDetail[],
31
+ ): NormalizedYNABTransaction[] {
32
+ return txns.map(normalizeYNABTransaction);
33
+ }
@@ -293,7 +293,7 @@ export const DeltaInfoSchema = z.object({
293
293
  * timestamp: "2025-01-17T12:34:56.789Z",
294
294
  * server: {
295
295
  * name: "ynab-mcp-server",
296
- * version: "0.12.0",
296
+ * version: "0.13.0",
297
297
  * node_version: "v20.10.0",
298
298
  * // ... other server info
299
299
  * },
@@ -231,24 +231,9 @@ type BulkTransactionInput = Omit<
231
231
  'budget_id' | 'dry_run' | 'subtransactions'
232
232
  >;
233
233
 
234
- const BulkTransactionInputSchema = BulkTransactionInputSchemaBase.extend({
235
- subtransactions: z.any().optional(),
236
- })
237
- .strict()
238
- .superRefine((data, ctx) => {
239
- if (data.subtransactions !== undefined) {
240
- ctx.addIssue({
241
- code: z.ZodIssueCode.custom,
242
- message: 'Subtransactions are not supported in bulk transaction creation',
243
- path: ['subtransactions'],
244
- });
245
- }
246
- })
247
- .transform((data): BulkTransactionInput => {
248
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
249
- const { subtransactions, ...rest } = data;
250
- return rest;
251
- });
234
+ // Schema for bulk transaction creation - subtransactions are not supported
235
+ // The .strict() modifier automatically rejects any fields not in the schema
236
+ const BulkTransactionInputSchema = BulkTransactionInputSchemaBase.strict();
252
237
 
253
238
  export const CreateTransactionsSchema = z
254
239
  .object({
@@ -813,6 +798,10 @@ export async function handleListTransactions(
813
798
  showing: `First ${preview.length} transactions:`,
814
799
  total_count: transactions.length,
815
800
  estimated_size_kb: Math.round(estimatedSize / 1024),
801
+ cached: cacheHit,
802
+ cache_info: cacheHit
803
+ ? `Data retrieved from cache for improved performance${usedDelta ? ' (delta merge applied)' : ''}`
804
+ : 'Fresh data retrieved from YNAB API',
816
805
  preview_transactions: preview.map((transaction) => ({
817
806
  id: transaction.id,
818
807
  date: transaction.date,
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Canonical bank transaction type used throughout reconciliation.
3
+ *
4
+ * AMOUNTS ARE IN MILLIUNITS (integers, 1000 = $1.00).
5
+ * This matches YNAB's native format and allows exact integer comparison.
6
+ */
7
+ export interface BankTransaction {
8
+ /** UUID generated for tracking */
9
+ id: string;
10
+ /** ISO date string YYYY-MM-DD */
11
+ date: string;
12
+ /** Amount in MILLIUNITS (negative = outflow, positive = inflow) */
13
+ amount: number;
14
+ /** Merchant/payee name from CSV */
15
+ payee: string;
16
+ /** Optional memo/description */
17
+ memo?: string;
18
+ /** Original CSV row number (1-indexed, after header) */
19
+ sourceRow: number;
20
+ /** Raw values from CSV for debugging */
21
+ raw: {
22
+ date: string;
23
+ amount: string;
24
+ description: string;
25
+ };
26
+ /** Parser warnings (e.g., ambiguous debit/credit) */
27
+ warnings?: string[];
28
+ }
29
+
30
+ /**
31
+ * YNAB transaction normalized for comparison.
32
+ *
33
+ * This interface is intentionally SDK-agnostic. Use the adapter
34
+ * function in ynabAdapter.ts to convert from ynab.TransactionDetail.
35
+ *
36
+ * AMOUNTS ARE IN MILLIUNITS - same as YNAB API native format.
37
+ * No conversion needed from the SDK.
38
+ */
39
+ export interface NormalizedYNABTransaction {
40
+ id: string;
41
+ date: string; // YYYY-MM-DD
42
+ /** Amount in MILLIUNITS (same as YNAB API) */
43
+ amount: number;
44
+ payee: string | null;
45
+ memo: string | null;
46
+ categoryName: string | null;
47
+ cleared: 'cleared' | 'uncleared' | 'reconciled';
48
+ approved: boolean;
49
+ }
@@ -0,0 +1,7 @@
1
+ export class BaseError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = this.constructor.name;
5
+ Object.setPrototypeOf(this, new.target.prototype);
6
+ }
7
+ }
@@ -0,0 +1,21 @@
1
+ export { BaseError } from './baseError.js';
2
+ export { ValidationError } from './validationError.js';
3
+ import { BaseError } from './baseError.js';
4
+
5
+ export class ConfigurationError extends BaseError {}
6
+
7
+ export class AuthenticationError extends BaseError {}
8
+
9
+ export class YNABRequestError extends BaseError {
10
+ constructor(
11
+ public status: number,
12
+ public statusText: string,
13
+ public ynabErrorId?: string,
14
+ ) {
15
+ super(
16
+ `YNAB API request failed: ${status} ${statusText}${
17
+ ynabErrorId ? ` (Error ID: ${ynabErrorId})` : ''
18
+ }`,
19
+ );
20
+ }
21
+ }
@@ -0,0 +1,3 @@
1
+ import { BaseError } from './baseError.js';
2
+
3
+ export class ValidationError extends BaseError {}
package/temp-recon.ts ADDED
@@ -0,0 +1,126 @@
1
+ import 'dotenv/config';
2
+ import * as ynab from 'ynab';
3
+ import { executeReconciliation } from './src/tools/reconciliation/executor.js';
4
+
5
+ async function main() {
6
+ const token = process.env['YNAB_ACCESS_TOKEN'];
7
+ if (!token) throw new Error('No token');
8
+ const api = new ynab.API(token);
9
+ const budgets = await api.budgets.getBudgets();
10
+ const budgetId = budgets.data.budgets[0]?.id;
11
+ if (!budgetId) throw new Error('no budget');
12
+ const accounts = await api.accounts.getAccounts(budgetId);
13
+ const account = accounts.data.accounts.find((a) => !a.closed);
14
+ if (!account) throw new Error('no account');
15
+ const snapshot = {
16
+ balance: account.balance,
17
+ cleared_balance: account.cleared_balance ?? account.balance,
18
+ uncleared_balance: account.uncleared_balance ?? 0,
19
+ };
20
+ const count = 2;
21
+ const transactionAmount = 7;
22
+ const clearedDollars = snapshot.cleared_balance / 1000;
23
+ const totalDelta = transactionAmount * count;
24
+ const statementBalance = clearedDollars + totalDelta;
25
+ const today = new Date().toISOString().slice(0, 10);
26
+ const unmatchedBank = Array.from({ length: count }, (_, i) => {
27
+ const date = new Date(Date.now() + i * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
28
+ return {
29
+ id: `integration-bank-${i}`,
30
+ date,
31
+ amount: transactionAmount,
32
+ payee: `Integration Payee ${i}`,
33
+ memo: `Integration memo ${i}`,
34
+ original_csv_row: i + 1,
35
+ };
36
+ });
37
+ const analysis = {
38
+ success: true,
39
+ phase: 'analysis',
40
+ summary: {
41
+ statement_date_range: 'Integration test',
42
+ bank_transactions_count: count,
43
+ ynab_transactions_count: 0,
44
+ auto_matched: 0,
45
+ suggested_matches: 0,
46
+ unmatched_bank: count,
47
+ unmatched_ynab: 0,
48
+ current_cleared_balance: clearedDollars,
49
+ target_statement_balance: statementBalance,
50
+ discrepancy: totalDelta,
51
+ discrepancy_explanation: 'Synthetic integration delta',
52
+ },
53
+ auto_matches: [],
54
+ suggested_matches: [],
55
+ unmatched_bank: unmatchedBank,
56
+ unmatched_ynab: [],
57
+ balance_info: {
58
+ current_cleared: clearedDollars,
59
+ current_uncleared: snapshot.uncleared_balance / 1000,
60
+ current_total: snapshot.balance / 1000,
61
+ target_statement: statementBalance,
62
+ discrepancy: totalDelta,
63
+ on_track: false,
64
+ },
65
+ next_steps: [],
66
+ insights: [],
67
+ } as const;
68
+
69
+ const params = {
70
+ budget_id: budgetId,
71
+ account_id: account.id,
72
+ csv_data: 'Date,Description,Amount',
73
+ statement_balance: statementBalance,
74
+ statement_date: today,
75
+ date_tolerance_days: 1,
76
+ amount_tolerance_cents: 1,
77
+ auto_match_threshold: 90,
78
+ suggestion_threshold: 60,
79
+ auto_create_transactions: true,
80
+ auto_update_cleared_status: false,
81
+ auto_unclear_missing: false,
82
+ auto_adjust_dates: false,
83
+ dry_run: false,
84
+ require_exact_match: true,
85
+ confidence_threshold: 0.8,
86
+ max_resolution_attempts: 3,
87
+ include_structured_data: false,
88
+ } as const;
89
+
90
+ const result = await executeReconciliation({
91
+ ynabAPI: api,
92
+ analysis: analysis as any,
93
+ params: params as any,
94
+ budgetId,
95
+ accountId: account.id,
96
+ initialAccount: snapshot,
97
+ currencyCode: 'USD',
98
+ });
99
+
100
+ console.log(
101
+ JSON.stringify(
102
+ {
103
+ summary: result.summary,
104
+ bulk: result.bulk_operation_details,
105
+ actions: result.actions_taken.slice(0, 5),
106
+ },
107
+ null,
108
+ 2,
109
+ ),
110
+ );
111
+
112
+ const ids: string[] = [];
113
+ for (const action of result.actions_taken) {
114
+ if (action.type === 'create_transaction' && (action.transaction as any)?.id) {
115
+ ids.push((action.transaction as any).id);
116
+ }
117
+ }
118
+ for (const id of ids) {
119
+ await api.transactions.deleteTransaction(budgetId, id);
120
+ }
121
+ }
122
+
123
+ main().catch((err) => {
124
+ console.error(err);
125
+ process.exit(1);
126
+ });