@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,845 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { z } from 'zod';
6
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema, ReadResourceRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
7
+ import * as ynab from 'ynab';
8
+ import { AuthenticationError, ConfigurationError, ErrorHandler, ValidationError, } from '../types/index.js';
9
+ import { createErrorHandler } from './errorHandler.js';
10
+ import { BudgetResolver } from './budgetResolver.js';
11
+ import { SecurityMiddleware, withSecurityWrapper } from './securityMiddleware.js';
12
+ import { handleListBudgets, handleGetBudget, GetBudgetSchema } from '../tools/budgetTools.js';
13
+ import { handleListAccounts, handleGetAccount, handleCreateAccount, ListAccountsSchema, GetAccountSchema, CreateAccountSchema, } from '../tools/accountTools.js';
14
+ import { handleListTransactions, handleGetTransaction, handleCreateTransaction, handleCreateTransactions, handleCreateReceiptSplitTransaction, handleUpdateTransaction, handleUpdateTransactions, handleDeleteTransaction, ListTransactionsSchema, GetTransactionSchema, CreateTransactionSchema, CreateTransactionsSchema, CreateReceiptSplitTransactionSchema, UpdateTransactionSchema, UpdateTransactionsSchema, DeleteTransactionSchema, } from '../tools/transactionTools.js';
15
+ import { handleExportTransactions, ExportTransactionsSchema } from '../tools/exportTransactions.js';
16
+ import { handleCompareTransactions, CompareTransactionsSchema, } from '../tools/compareTransactions/index.js';
17
+ import { handleReconcileAccount, ReconcileAccountSchema } from '../tools/reconciliation/index.js';
18
+ import { handleListCategories, handleGetCategory, handleUpdateCategory, ListCategoriesSchema, GetCategorySchema, UpdateCategorySchema, } from '../tools/categoryTools.js';
19
+ import { handleListPayees, handleGetPayee, ListPayeesSchema, GetPayeeSchema, } from '../tools/payeeTools.js';
20
+ import { handleGetMonth, handleListMonths, GetMonthSchema, ListMonthsSchema, } from '../tools/monthTools.js';
21
+ import { handleGetUser, handleConvertAmount, ConvertAmountSchema } from '../tools/utilityTools.js';
22
+ import { cacheManager, CacheManager } from './cacheManager.js';
23
+ import { responseFormatter } from './responseFormatter.js';
24
+ import { ToolRegistry, DefaultArgumentResolutionError, } from './toolRegistry.js';
25
+ import { validateEnvironment } from './config.js';
26
+ import { ResourceManager } from './resources.js';
27
+ import { PromptManager } from './prompts.js';
28
+ import { DiagnosticManager } from './diagnostics.js';
29
+ import { ServerKnowledgeStore } from './serverKnowledgeStore.js';
30
+ import { DeltaCache } from './deltaCache.js';
31
+ import { DeltaFetcher } from '../tools/deltaFetcher.js';
32
+ import { ToolAnnotationPresets } from '../tools/toolCategories.js';
33
+ import { GetUserOutputSchema, ConvertAmountOutputSchema, GetDefaultBudgetOutputSchema, SetDefaultBudgetOutputSchema, ClearCacheOutputSchema, SetOutputFormatOutputSchema, DiagnosticInfoOutputSchema, GetBudgetOutputSchema, ListBudgetsOutputSchema, ListAccountsOutputSchema, GetAccountOutputSchema, CreateAccountOutputSchema, ListTransactionsOutputSchema, GetTransactionOutputSchema, ExportTransactionsOutputSchema, CompareTransactionsOutputSchema, CreateTransactionOutputSchema, CreateTransactionsOutputSchema, UpdateTransactionOutputSchema, UpdateTransactionsOutputSchema, DeleteTransactionOutputSchema, CreateReceiptSplitTransactionOutputSchema, ListCategoriesOutputSchema, GetCategoryOutputSchema, UpdateCategoryOutputSchema, ListPayeesOutputSchema, GetPayeeOutputSchema, GetMonthOutputSchema, ListMonthsOutputSchema, ReconcileAccountOutputSchema, } from '../tools/schemas/outputs/index.js';
34
+ export class YNABMCPServer {
35
+ constructor(exitOnError = true) {
36
+ this.exitOnError = exitOnError;
37
+ this.config = validateEnvironment();
38
+ if (this.config.defaultBudgetId !== undefined) {
39
+ this.defaultBudgetId = this.config.defaultBudgetId;
40
+ }
41
+ this.ynabAPI = new ynab.API(this.config.accessToken);
42
+ this.serverVersion = this.readPackageVersion() ?? '0.0.0';
43
+ this.server = new Server({
44
+ name: 'ynab-mcp-server',
45
+ version: this.serverVersion,
46
+ }, {
47
+ capabilities: {
48
+ tools: {},
49
+ resources: {},
50
+ prompts: {},
51
+ },
52
+ });
53
+ this.errorHandler = createErrorHandler(responseFormatter);
54
+ ErrorHandler.setFormatter(responseFormatter);
55
+ this.toolRegistry = new ToolRegistry({
56
+ withSecurityWrapper,
57
+ errorHandler: this.errorHandler,
58
+ responseFormatter,
59
+ cacheHelpers: {
60
+ generateKey: (...segments) => {
61
+ const normalized = segments.map((segment) => {
62
+ if (typeof segment === 'string' ||
63
+ typeof segment === 'number' ||
64
+ typeof segment === 'boolean' ||
65
+ segment === undefined) {
66
+ return segment;
67
+ }
68
+ return JSON.stringify(segment);
69
+ });
70
+ return CacheManager.generateKey('tool', ...normalized);
71
+ },
72
+ invalidate: (key) => {
73
+ try {
74
+ cacheManager.delete(key);
75
+ }
76
+ catch (error) {
77
+ console.error(`Failed to invalidate cache key "${key}":`, error);
78
+ }
79
+ },
80
+ clear: () => {
81
+ try {
82
+ cacheManager.clear();
83
+ }
84
+ catch (error) {
85
+ console.error('Failed to clear cache:', error);
86
+ }
87
+ },
88
+ },
89
+ validateAccessToken: (token) => {
90
+ const expected = this.config.accessToken.trim();
91
+ const provided = typeof token === 'string' ? token.trim() : '';
92
+ if (!provided) {
93
+ throw this.errorHandler.createYNABError(401, 'validating access token', new Error('Missing access token'));
94
+ }
95
+ if (provided !== expected) {
96
+ throw this.errorHandler.createYNABError(401, 'validating access token', new Error('Access token mismatch'));
97
+ }
98
+ },
99
+ });
100
+ this.resourceManager = new ResourceManager({
101
+ ynabAPI: this.ynabAPI,
102
+ responseFormatter,
103
+ });
104
+ this.promptManager = new PromptManager();
105
+ this.serverKnowledgeStore = new ServerKnowledgeStore();
106
+ this.deltaCache = new DeltaCache(cacheManager, this.serverKnowledgeStore);
107
+ this.deltaFetcher = new DeltaFetcher(this.ynabAPI, this.deltaCache);
108
+ this.diagnosticManager = new DiagnosticManager({
109
+ securityMiddleware: SecurityMiddleware,
110
+ cacheManager,
111
+ responseFormatter,
112
+ serverVersion: this.serverVersion,
113
+ serverKnowledgeStore: this.serverKnowledgeStore,
114
+ deltaCache: this.deltaCache,
115
+ });
116
+ this.setupToolRegistry();
117
+ this.setupHandlers();
118
+ }
119
+ async validateToken() {
120
+ try {
121
+ await this.ynabAPI.user.getUser();
122
+ return true;
123
+ }
124
+ catch (error) {
125
+ if (error instanceof Error) {
126
+ if (error.message.includes('401') || error.message.includes('Unauthorized')) {
127
+ throw new AuthenticationError('Invalid or expired YNAB access token');
128
+ }
129
+ if (error.message.includes('403') || error.message.includes('Forbidden')) {
130
+ throw new AuthenticationError('YNAB access token has insufficient permissions');
131
+ }
132
+ }
133
+ throw new AuthenticationError(`Token validation failed: ${error}`);
134
+ }
135
+ }
136
+ setupHandlers() {
137
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
138
+ return this.resourceManager.listResources();
139
+ });
140
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
141
+ const { uri } = request.params;
142
+ try {
143
+ return await this.resourceManager.readResource(uri);
144
+ }
145
+ catch (error) {
146
+ return this.errorHandler.handleError(error, `reading resource: ${uri}`);
147
+ }
148
+ });
149
+ this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
150
+ return this.promptManager.listPrompts();
151
+ });
152
+ this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
153
+ const { name, arguments: args } = request.params;
154
+ const result = await this.promptManager.getPrompt(name, args);
155
+ return result;
156
+ });
157
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
158
+ return {
159
+ tools: this.toolRegistry.listTools(),
160
+ };
161
+ });
162
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
163
+ const rawArgs = (request.params.arguments ?? undefined);
164
+ const minifyOverride = this.extractMinifyOverride(rawArgs);
165
+ const sanitizedArgs = rawArgs
166
+ ? (() => {
167
+ const clone = { ...rawArgs };
168
+ delete clone['minify'];
169
+ delete clone['_minify'];
170
+ delete clone['__minify'];
171
+ return clone;
172
+ })()
173
+ : undefined;
174
+ const executionOptions = {
175
+ name: request.params.name,
176
+ accessToken: this.config.accessToken,
177
+ arguments: sanitizedArgs ?? {},
178
+ };
179
+ if (minifyOverride !== undefined) {
180
+ executionOptions.minifyOverride = minifyOverride;
181
+ }
182
+ return await this.toolRegistry.executeTool(executionOptions);
183
+ });
184
+ }
185
+ setupToolRegistry() {
186
+ const register = (definition) => {
187
+ this.toolRegistry.register(definition);
188
+ };
189
+ const adapt = (handler) => async ({ input }) => handler(this.ynabAPI, input);
190
+ const adaptNoInput = (handler) => async (_payload) => handler(this.ynabAPI);
191
+ const adaptWithDelta = (handler) => async ({ input }) => handler(this.ynabAPI, this.deltaFetcher, input);
192
+ const adaptWrite = (handler) => async ({ input }) => handler(this.ynabAPI, this.deltaCache, this.serverKnowledgeStore, input);
193
+ const resolveBudgetId = () => {
194
+ return ({ rawArguments }) => {
195
+ const provided = typeof rawArguments['budget_id'] === 'string' && rawArguments['budget_id'].length > 0
196
+ ? rawArguments['budget_id']
197
+ : undefined;
198
+ const result = BudgetResolver.resolveBudgetId(provided, this.defaultBudgetId);
199
+ if (typeof result === 'string') {
200
+ return { budget_id: result };
201
+ }
202
+ throw new DefaultArgumentResolutionError(result);
203
+ };
204
+ };
205
+ const emptyObjectSchema = z.object({}).strict();
206
+ const setDefaultBudgetSchema = z.object({ budget_id: z.string().min(1) }).strict();
207
+ const diagnosticInfoSchema = z
208
+ .object({
209
+ include_memory: z.boolean().default(true),
210
+ include_environment: z.boolean().default(true),
211
+ include_server: z.boolean().default(true),
212
+ include_security: z.boolean().default(true),
213
+ include_cache: z.boolean().default(true),
214
+ include_delta: z.boolean().default(true),
215
+ })
216
+ .strict();
217
+ const setOutputFormatSchema = z
218
+ .object({
219
+ default_minify: z.boolean().optional(),
220
+ pretty_spaces: z.number().int().min(0).max(10).optional(),
221
+ })
222
+ .strict();
223
+ register({
224
+ name: 'list_budgets',
225
+ description: "List all budgets associated with the user's account",
226
+ inputSchema: emptyObjectSchema,
227
+ outputSchema: ListBudgetsOutputSchema,
228
+ handler: adaptWithDelta(handleListBudgets),
229
+ metadata: {
230
+ annotations: {
231
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
232
+ title: 'YNAB: List Budgets',
233
+ },
234
+ },
235
+ });
236
+ register({
237
+ name: 'get_budget',
238
+ description: 'Get detailed information for a specific budget',
239
+ inputSchema: GetBudgetSchema,
240
+ outputSchema: GetBudgetOutputSchema,
241
+ handler: adapt(handleGetBudget),
242
+ metadata: {
243
+ annotations: {
244
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
245
+ title: 'YNAB: Get Budget Details',
246
+ },
247
+ },
248
+ });
249
+ register({
250
+ name: 'set_default_budget',
251
+ description: 'Set the default budget for subsequent operations',
252
+ inputSchema: setDefaultBudgetSchema,
253
+ outputSchema: SetDefaultBudgetOutputSchema,
254
+ handler: async ({ input }) => {
255
+ const { budget_id } = input;
256
+ await this.ynabAPI.budgets.getBudgetById(budget_id);
257
+ this.setDefaultBudget(budget_id);
258
+ this.warmCacheForBudget(budget_id).catch(() => {
259
+ });
260
+ return {
261
+ content: [
262
+ {
263
+ type: 'text',
264
+ text: responseFormatter.format({
265
+ success: true,
266
+ message: `Default budget set to: ${budget_id}`,
267
+ default_budget_id: budget_id,
268
+ cache_warm_started: true,
269
+ }),
270
+ },
271
+ ],
272
+ };
273
+ },
274
+ metadata: {
275
+ annotations: {
276
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
277
+ title: 'YNAB: Set Default Budget',
278
+ },
279
+ },
280
+ });
281
+ register({
282
+ name: 'get_default_budget',
283
+ description: 'Get the currently set default budget',
284
+ inputSchema: emptyObjectSchema,
285
+ outputSchema: GetDefaultBudgetOutputSchema,
286
+ handler: async () => {
287
+ try {
288
+ const defaultBudget = this.getDefaultBudget();
289
+ return {
290
+ content: [
291
+ {
292
+ type: 'text',
293
+ text: responseFormatter.format({
294
+ default_budget_id: defaultBudget ?? null,
295
+ has_default: !!defaultBudget,
296
+ message: defaultBudget
297
+ ? `Default budget is set to: ${defaultBudget}`
298
+ : 'No default budget is currently set',
299
+ }),
300
+ },
301
+ ],
302
+ };
303
+ }
304
+ catch (error) {
305
+ return this.errorHandler.createValidationError('Error getting default budget', error instanceof Error ? error.message : 'Unknown error');
306
+ }
307
+ },
308
+ metadata: {
309
+ annotations: {
310
+ ...ToolAnnotationPresets.UTILITY_LOCAL,
311
+ title: 'YNAB: Get Default Budget',
312
+ },
313
+ },
314
+ });
315
+ register({
316
+ name: 'list_accounts',
317
+ description: 'List all accounts for a specific budget (uses default budget if not specified)',
318
+ inputSchema: ListAccountsSchema,
319
+ outputSchema: ListAccountsOutputSchema,
320
+ handler: adaptWithDelta(handleListAccounts),
321
+ defaultArgumentResolver: resolveBudgetId(),
322
+ metadata: {
323
+ annotations: {
324
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
325
+ title: 'YNAB: List Accounts',
326
+ },
327
+ },
328
+ });
329
+ register({
330
+ name: 'get_account',
331
+ description: 'Get detailed information for a specific account',
332
+ inputSchema: GetAccountSchema,
333
+ outputSchema: GetAccountOutputSchema,
334
+ handler: adapt(handleGetAccount),
335
+ defaultArgumentResolver: resolveBudgetId(),
336
+ metadata: {
337
+ annotations: {
338
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
339
+ title: 'YNAB: Get Account Details',
340
+ },
341
+ },
342
+ });
343
+ register({
344
+ name: 'create_account',
345
+ description: 'Create a new account in the specified budget',
346
+ inputSchema: CreateAccountSchema,
347
+ outputSchema: CreateAccountOutputSchema,
348
+ handler: adaptWrite(handleCreateAccount),
349
+ defaultArgumentResolver: resolveBudgetId(),
350
+ metadata: {
351
+ annotations: {
352
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
353
+ title: 'YNAB: Create Account',
354
+ },
355
+ },
356
+ });
357
+ register({
358
+ name: 'list_transactions',
359
+ description: 'List transactions for a budget with optional filtering',
360
+ inputSchema: ListTransactionsSchema,
361
+ outputSchema: ListTransactionsOutputSchema,
362
+ handler: adaptWithDelta(handleListTransactions),
363
+ defaultArgumentResolver: resolveBudgetId(),
364
+ metadata: {
365
+ annotations: {
366
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
367
+ title: 'YNAB: List Transactions',
368
+ },
369
+ },
370
+ });
371
+ register({
372
+ name: 'export_transactions',
373
+ description: 'Export all transactions to a JSON file with descriptive filename',
374
+ inputSchema: ExportTransactionsSchema,
375
+ outputSchema: ExportTransactionsOutputSchema,
376
+ handler: adapt(handleExportTransactions),
377
+ defaultArgumentResolver: resolveBudgetId(),
378
+ metadata: {
379
+ annotations: {
380
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
381
+ title: 'YNAB: Export Transactions',
382
+ },
383
+ },
384
+ });
385
+ register({
386
+ name: 'compare_transactions',
387
+ description: 'Compare bank transactions from CSV with YNAB transactions to find missing entries',
388
+ inputSchema: CompareTransactionsSchema,
389
+ outputSchema: CompareTransactionsOutputSchema,
390
+ handler: adapt(handleCompareTransactions),
391
+ defaultArgumentResolver: resolveBudgetId(),
392
+ metadata: {
393
+ annotations: {
394
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
395
+ title: 'YNAB: Compare Transactions',
396
+ },
397
+ },
398
+ });
399
+ register({
400
+ name: 'reconcile_account',
401
+ description: 'Guided reconciliation workflow with human narrative, insight detection, and optional execution (create/update/unclear). Set include_structured_data=true to also get full JSON output (large).',
402
+ inputSchema: ReconcileAccountSchema,
403
+ outputSchema: ReconcileAccountOutputSchema,
404
+ handler: adaptWithDelta(handleReconcileAccount),
405
+ defaultArgumentResolver: resolveBudgetId(),
406
+ metadata: {
407
+ annotations: {
408
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
409
+ title: 'YNAB: Reconcile Account',
410
+ },
411
+ },
412
+ });
413
+ register({
414
+ name: 'get_transaction',
415
+ description: 'Get detailed information for a specific transaction',
416
+ inputSchema: GetTransactionSchema,
417
+ outputSchema: GetTransactionOutputSchema,
418
+ handler: adapt(handleGetTransaction),
419
+ defaultArgumentResolver: resolveBudgetId(),
420
+ metadata: {
421
+ annotations: {
422
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
423
+ title: 'YNAB: Get Transaction Details',
424
+ },
425
+ },
426
+ });
427
+ register({
428
+ name: 'create_transaction',
429
+ description: 'Create a new transaction in the specified budget and account',
430
+ inputSchema: CreateTransactionSchema,
431
+ outputSchema: CreateTransactionOutputSchema,
432
+ handler: adaptWrite(handleCreateTransaction),
433
+ defaultArgumentResolver: resolveBudgetId(),
434
+ metadata: {
435
+ annotations: {
436
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
437
+ title: 'YNAB: Create Transaction',
438
+ },
439
+ },
440
+ });
441
+ register({
442
+ name: 'create_transactions',
443
+ description: 'Create multiple transactions in a single batch (1-100 items) with duplicate detection, dry-run validation, and automatic response size management with correlation metadata.',
444
+ inputSchema: CreateTransactionsSchema,
445
+ outputSchema: CreateTransactionsOutputSchema,
446
+ handler: adaptWrite(handleCreateTransactions),
447
+ defaultArgumentResolver: resolveBudgetId(),
448
+ metadata: {
449
+ annotations: {
450
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
451
+ title: 'YNAB: Create Multiple Transactions',
452
+ },
453
+ },
454
+ });
455
+ register({
456
+ name: 'update_transactions',
457
+ description: 'Update multiple transactions in a single batch (1-100 items) with dry-run validation, automatic cache invalidation, and response size management. Supports optional original_account_id and original_date metadata for efficient cache invalidation.',
458
+ inputSchema: UpdateTransactionsSchema,
459
+ outputSchema: UpdateTransactionsOutputSchema,
460
+ handler: adaptWrite(handleUpdateTransactions),
461
+ defaultArgumentResolver: resolveBudgetId(),
462
+ metadata: {
463
+ annotations: {
464
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
465
+ title: 'YNAB: Update Multiple Transactions',
466
+ },
467
+ },
468
+ });
469
+ register({
470
+ name: 'create_receipt_split_transaction',
471
+ description: 'Create a split transaction from receipt items with proportional tax allocation',
472
+ inputSchema: CreateReceiptSplitTransactionSchema,
473
+ outputSchema: CreateReceiptSplitTransactionOutputSchema,
474
+ handler: adaptWrite(handleCreateReceiptSplitTransaction),
475
+ defaultArgumentResolver: resolveBudgetId(),
476
+ metadata: {
477
+ annotations: {
478
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
479
+ title: 'YNAB: Create Split Transaction from Receipt',
480
+ },
481
+ },
482
+ });
483
+ register({
484
+ name: 'update_transaction',
485
+ description: 'Update an existing transaction',
486
+ inputSchema: UpdateTransactionSchema,
487
+ outputSchema: UpdateTransactionOutputSchema,
488
+ handler: adaptWrite(handleUpdateTransaction),
489
+ defaultArgumentResolver: resolveBudgetId(),
490
+ metadata: {
491
+ annotations: {
492
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
493
+ title: 'YNAB: Update Transaction',
494
+ },
495
+ },
496
+ });
497
+ register({
498
+ name: 'delete_transaction',
499
+ description: 'Delete a transaction from the specified budget',
500
+ inputSchema: DeleteTransactionSchema,
501
+ outputSchema: DeleteTransactionOutputSchema,
502
+ handler: adaptWrite(handleDeleteTransaction),
503
+ defaultArgumentResolver: resolveBudgetId(),
504
+ metadata: {
505
+ annotations: {
506
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_DELETE,
507
+ title: 'YNAB: Delete Transaction',
508
+ },
509
+ },
510
+ });
511
+ register({
512
+ name: 'list_categories',
513
+ description: 'List all categories for a specific budget',
514
+ inputSchema: ListCategoriesSchema,
515
+ outputSchema: ListCategoriesOutputSchema,
516
+ handler: adaptWithDelta(handleListCategories),
517
+ defaultArgumentResolver: resolveBudgetId(),
518
+ metadata: {
519
+ annotations: {
520
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
521
+ title: 'YNAB: List Categories',
522
+ },
523
+ },
524
+ });
525
+ register({
526
+ name: 'get_category',
527
+ description: 'Get detailed information for a specific category',
528
+ inputSchema: GetCategorySchema,
529
+ outputSchema: GetCategoryOutputSchema,
530
+ handler: adapt(handleGetCategory),
531
+ defaultArgumentResolver: resolveBudgetId(),
532
+ metadata: {
533
+ annotations: {
534
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
535
+ title: 'YNAB: Get Category Details',
536
+ },
537
+ },
538
+ });
539
+ register({
540
+ name: 'update_category',
541
+ description: 'Update the budgeted amount for a category in the current month',
542
+ inputSchema: UpdateCategorySchema,
543
+ outputSchema: UpdateCategoryOutputSchema,
544
+ handler: adaptWrite(handleUpdateCategory),
545
+ defaultArgumentResolver: resolveBudgetId(),
546
+ metadata: {
547
+ annotations: {
548
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
549
+ title: 'YNAB: Update Category Budget',
550
+ },
551
+ },
552
+ });
553
+ register({
554
+ name: 'list_payees',
555
+ description: 'List all payees for a specific budget',
556
+ inputSchema: ListPayeesSchema,
557
+ outputSchema: ListPayeesOutputSchema,
558
+ handler: adaptWithDelta(handleListPayees),
559
+ defaultArgumentResolver: resolveBudgetId(),
560
+ metadata: {
561
+ annotations: {
562
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
563
+ title: 'YNAB: List Payees',
564
+ },
565
+ },
566
+ });
567
+ register({
568
+ name: 'get_payee',
569
+ description: 'Get detailed information for a specific payee',
570
+ inputSchema: GetPayeeSchema,
571
+ outputSchema: GetPayeeOutputSchema,
572
+ handler: adapt(handleGetPayee),
573
+ defaultArgumentResolver: resolveBudgetId(),
574
+ metadata: {
575
+ annotations: {
576
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
577
+ title: 'YNAB: Get Payee Details',
578
+ },
579
+ },
580
+ });
581
+ register({
582
+ name: 'get_month',
583
+ description: 'Get budget data for a specific month',
584
+ inputSchema: GetMonthSchema,
585
+ outputSchema: GetMonthOutputSchema,
586
+ handler: adapt(handleGetMonth),
587
+ defaultArgumentResolver: resolveBudgetId(),
588
+ metadata: {
589
+ annotations: {
590
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
591
+ title: 'YNAB: Get Month Budget Data',
592
+ },
593
+ },
594
+ });
595
+ register({
596
+ name: 'list_months',
597
+ description: 'List all months summary data for a budget',
598
+ inputSchema: ListMonthsSchema,
599
+ outputSchema: ListMonthsOutputSchema,
600
+ handler: adaptWithDelta(handleListMonths),
601
+ defaultArgumentResolver: resolveBudgetId(),
602
+ metadata: {
603
+ annotations: {
604
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
605
+ title: 'YNAB: List Months',
606
+ },
607
+ },
608
+ });
609
+ register({
610
+ name: 'get_user',
611
+ description: 'Get information about the authenticated user',
612
+ inputSchema: emptyObjectSchema,
613
+ outputSchema: GetUserOutputSchema,
614
+ handler: adaptNoInput(handleGetUser),
615
+ metadata: {
616
+ annotations: {
617
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
618
+ title: 'YNAB: Get User Information',
619
+ },
620
+ },
621
+ });
622
+ register({
623
+ name: 'convert_amount',
624
+ description: 'Convert between dollars and milliunits with integer arithmetic for precision',
625
+ inputSchema: ConvertAmountSchema,
626
+ outputSchema: ConvertAmountOutputSchema,
627
+ handler: async ({ input }) => handleConvertAmount(input),
628
+ metadata: {
629
+ annotations: {
630
+ ...ToolAnnotationPresets.UTILITY_LOCAL,
631
+ title: 'YNAB: Convert Amount',
632
+ },
633
+ },
634
+ });
635
+ register({
636
+ name: 'diagnostic_info',
637
+ description: 'Get comprehensive diagnostic information about the MCP server',
638
+ inputSchema: diagnosticInfoSchema,
639
+ outputSchema: DiagnosticInfoOutputSchema,
640
+ handler: async ({ input }) => {
641
+ return this.diagnosticManager.collectDiagnostics(input);
642
+ },
643
+ metadata: {
644
+ annotations: {
645
+ ...ToolAnnotationPresets.UTILITY_LOCAL,
646
+ title: 'YNAB: Diagnostic Information',
647
+ },
648
+ },
649
+ });
650
+ register({
651
+ name: 'clear_cache',
652
+ description: 'Clear the in-memory cache (safe, no YNAB data is modified)',
653
+ inputSchema: emptyObjectSchema,
654
+ outputSchema: ClearCacheOutputSchema,
655
+ handler: async () => {
656
+ cacheManager.clear();
657
+ return {
658
+ content: [{ type: 'text', text: responseFormatter.format({ success: true }) }],
659
+ };
660
+ },
661
+ metadata: {
662
+ annotations: {
663
+ ...ToolAnnotationPresets.UTILITY_LOCAL,
664
+ title: 'YNAB: Clear Cache',
665
+ },
666
+ },
667
+ });
668
+ register({
669
+ name: 'set_output_format',
670
+ description: 'Configure default JSON output formatting (minify or pretty spaces)',
671
+ inputSchema: setOutputFormatSchema,
672
+ outputSchema: SetOutputFormatOutputSchema,
673
+ handler: async ({ input }) => {
674
+ const options = {};
675
+ if (typeof input.default_minify === 'boolean') {
676
+ options.defaultMinify = input.default_minify;
677
+ }
678
+ if (typeof input.pretty_spaces === 'number') {
679
+ options.prettySpaces = Math.max(0, Math.min(10, Math.floor(input.pretty_spaces)));
680
+ }
681
+ responseFormatter.configure(options);
682
+ const parts = [];
683
+ if (options.defaultMinify !== undefined) {
684
+ parts.push(`minify=${options.defaultMinify}`);
685
+ }
686
+ if (options.prettySpaces !== undefined) {
687
+ parts.push(`spaces=${options.prettySpaces}`);
688
+ }
689
+ const message = parts.length > 0
690
+ ? `Output format configured: ${parts.join(', ')}`
691
+ : 'Output format configured';
692
+ return {
693
+ content: [
694
+ {
695
+ type: 'text',
696
+ text: responseFormatter.format({ success: true, message, options }),
697
+ },
698
+ ],
699
+ };
700
+ },
701
+ metadata: {
702
+ annotations: {
703
+ ...ToolAnnotationPresets.UTILITY_LOCAL,
704
+ title: 'YNAB: Set Output Format',
705
+ },
706
+ },
707
+ });
708
+ }
709
+ extractMinifyOverride(args) {
710
+ if (!args) {
711
+ return undefined;
712
+ }
713
+ for (const key of ['minify', '_minify', '__minify']) {
714
+ const value = args[key];
715
+ if (typeof value === 'boolean') {
716
+ return value;
717
+ }
718
+ }
719
+ return undefined;
720
+ }
721
+ async run() {
722
+ try {
723
+ await this.validateToken();
724
+ const transport = new StdioServerTransport();
725
+ await this.server.connect(transport);
726
+ console.error('YNAB MCP Server started successfully');
727
+ }
728
+ catch (error) {
729
+ if (error instanceof AuthenticationError || error instanceof ConfigurationError) {
730
+ console.error(`Server startup failed: ${error.message}`);
731
+ if (this.exitOnError) {
732
+ process.exit(1);
733
+ }
734
+ else {
735
+ throw error;
736
+ }
737
+ }
738
+ throw error;
739
+ }
740
+ }
741
+ getYNABAPI() {
742
+ return this.ynabAPI;
743
+ }
744
+ getServer() {
745
+ return this.server;
746
+ }
747
+ setDefaultBudget(budgetId) {
748
+ this.defaultBudgetId = budgetId;
749
+ }
750
+ getDefaultBudget() {
751
+ return this.defaultBudgetId;
752
+ }
753
+ clearDefaultBudget() {
754
+ this.defaultBudgetId = undefined;
755
+ }
756
+ getToolRegistry() {
757
+ return this.toolRegistry;
758
+ }
759
+ getBudgetId(providedBudgetId) {
760
+ const result = BudgetResolver.resolveBudgetId(providedBudgetId, this.defaultBudgetId);
761
+ if (typeof result === 'string') {
762
+ return result;
763
+ }
764
+ const errorText = result.content?.[0]?.type === 'text' ? result.content[0].text : 'Budget resolution failed';
765
+ const parsedError = (() => {
766
+ try {
767
+ return JSON.parse(errorText);
768
+ }
769
+ catch {
770
+ return { error: { message: errorText } };
771
+ }
772
+ })();
773
+ const message = parsedError.error?.message || 'Budget resolution failed';
774
+ throw new ValidationError(message);
775
+ }
776
+ async warmCacheForBudget(budgetId) {
777
+ try {
778
+ await Promise.all([
779
+ this.deltaFetcher.fetchAccounts(budgetId, { forceFullRefresh: true }),
780
+ this.deltaFetcher.fetchCategories(budgetId, { forceFullRefresh: true }),
781
+ this.deltaFetcher.fetchPayees(budgetId, { forceFullRefresh: true }),
782
+ ]);
783
+ }
784
+ catch {
785
+ }
786
+ }
787
+ async handleListTools() {
788
+ return {
789
+ tools: this.toolRegistry.listTools(),
790
+ };
791
+ }
792
+ async handleListResources() {
793
+ return this.resourceManager.listResources();
794
+ }
795
+ async handleReadResource(params) {
796
+ const { uri } = params;
797
+ try {
798
+ return await this.resourceManager.readResource(uri);
799
+ }
800
+ catch (error) {
801
+ return this.errorHandler.handleError(error, `reading resource: ${uri}`);
802
+ }
803
+ }
804
+ async handleListPrompts() {
805
+ return this.promptManager.listPrompts();
806
+ }
807
+ async handleGetPrompt(params) {
808
+ const { name, arguments: args } = params;
809
+ try {
810
+ const prompt = await this.promptManager.getPrompt(name, args);
811
+ const tools = Array.isArray(prompt.tools)
812
+ ? prompt.tools
813
+ : undefined;
814
+ return tools ? { ...prompt, tools } : prompt;
815
+ }
816
+ catch (error) {
817
+ return this.errorHandler.handleError(error, `getting prompt: ${name}`);
818
+ }
819
+ }
820
+ readPackageVersion() {
821
+ const candidates = [path.resolve(process.cwd(), 'package.json')];
822
+ try {
823
+ const metaUrl = import.meta?.url;
824
+ if (metaUrl) {
825
+ const maybe = path.resolve(path.dirname(new URL(metaUrl).pathname), '../../package.json');
826
+ candidates.push(maybe);
827
+ }
828
+ }
829
+ catch {
830
+ }
831
+ for (const p of candidates) {
832
+ try {
833
+ if (fs.existsSync(p)) {
834
+ const raw = fs.readFileSync(p, 'utf8');
835
+ const pkg = JSON.parse(raw);
836
+ if (pkg.version && typeof pkg.version === 'string')
837
+ return pkg.version;
838
+ }
839
+ }
840
+ catch {
841
+ }
842
+ }
843
+ return null;
844
+ }
845
+ }