@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,205 @@
1
+ const defaultPromptDefinitions = [
2
+ {
3
+ name: 'create-transaction',
4
+ description: 'Create a new transaction in YNAB',
5
+ arguments: [
6
+ {
7
+ name: 'budget_name',
8
+ description: 'Name of the budget (optional, uses first budget if not specified)',
9
+ required: false,
10
+ },
11
+ {
12
+ name: 'account_name',
13
+ description: 'Name of the account',
14
+ required: true,
15
+ },
16
+ {
17
+ name: 'amount',
18
+ description: 'Transaction amount (negative for expenses, positive for income)',
19
+ required: true,
20
+ },
21
+ {
22
+ name: 'payee',
23
+ description: 'Who you paid or received money from',
24
+ required: true,
25
+ },
26
+ {
27
+ name: 'category',
28
+ description: 'Budget category (optional)',
29
+ required: false,
30
+ },
31
+ {
32
+ name: 'memo',
33
+ description: 'Additional notes (optional)',
34
+ required: false,
35
+ },
36
+ ],
37
+ },
38
+ {
39
+ name: 'budget-summary',
40
+ description: 'Get a summary of your budget status',
41
+ arguments: [
42
+ {
43
+ name: 'budget_name',
44
+ description: 'Name of the budget (optional, uses first budget if not specified)',
45
+ required: false,
46
+ },
47
+ {
48
+ name: 'month',
49
+ description: 'Month to analyze (YYYY-MM format, optional, uses current month if not specified)',
50
+ required: false,
51
+ },
52
+ ],
53
+ },
54
+ {
55
+ name: 'account-balances',
56
+ description: 'Check balances across all accounts',
57
+ arguments: [
58
+ {
59
+ name: 'budget_name',
60
+ description: 'Name of the budget (optional, uses first budget if not specified)',
61
+ required: false,
62
+ },
63
+ {
64
+ name: 'account_type',
65
+ description: 'Filter by account type (checking, savings, creditCard, etc.)',
66
+ required: false,
67
+ },
68
+ ],
69
+ },
70
+ ];
71
+ const defaultPromptHandlers = {
72
+ 'create-transaction': async (_name, args) => {
73
+ const budgetName = args?.['budget_name'] || 'first available budget';
74
+ const accountName = args?.['account_name'] || '[ACCOUNT_NAME]';
75
+ const amount = args?.['amount'] || '[AMOUNT]';
76
+ const payee = args?.['payee'] || '[PAYEE]';
77
+ const category = args?.['category'] || '[CATEGORY]';
78
+ const memo = args?.['memo'] || '';
79
+ return {
80
+ description: `Create a transaction for ${payee} in ${accountName}`,
81
+ messages: [
82
+ {
83
+ role: 'user',
84
+ content: {
85
+ type: 'text',
86
+ text: `Please create a transaction with the following details:
87
+ - Budget: ${budgetName}
88
+ - Account: ${accountName}
89
+ - Amount: $${amount}
90
+ - Payee: ${payee}
91
+ - Category: ${category}
92
+ - Memo: ${memo}
93
+
94
+ Use the appropriate YNAB MCP tools to:
95
+ 1. First, list budgets to find the budget ID
96
+ 2. List accounts for that budget to find the account ID
97
+ 3. If a category is specified, list categories to find the category ID
98
+ 4. Create the transaction with the correct amount in milliunits (multiply by 1000)
99
+ 5. Confirm the transaction was created successfully`,
100
+ },
101
+ },
102
+ ],
103
+ };
104
+ },
105
+ 'budget-summary': async (_name, args) => {
106
+ const summaryBudget = args?.['budget_name'] || 'first available budget';
107
+ const month = args?.['month'] || 'current month';
108
+ return {
109
+ description: `Get budget summary for ${summaryBudget}`,
110
+ messages: [
111
+ {
112
+ role: 'user',
113
+ content: {
114
+ type: 'text',
115
+ text: `Please provide a comprehensive budget summary for ${summaryBudget} (${month}):
116
+
117
+ IMPORTANT: In YNAB, understand these key fields:
118
+ - budgeted: Amount assigned to the category this month
119
+ - activity: Spending/income in the category this month (negative = spending)
120
+ - balance: Available amount in the category = previous balance + budgeted + activity
121
+ - OVERSPENDING occurs when balance < 0 (Available goes negative), NOT when spending > budgeted for the month
122
+
123
+ SPENDING TRENDS: The analysis uses linear regression over multiple months to detect real spending patterns. Each trend includes:
124
+ - explanation: User-friendly description of what the trend means
125
+ - reliability_score: Confidence level (0-100%) indicating how reliable the trend is
126
+ - data_points: Number of months used in the analysis
127
+ Focus on trends with high reliability scores for actionable insights.
128
+
129
+ BUDGET OPTIMIZATION: The system provides three types of optimization insights:
130
+ 1. "Consistently Under-Spent Categories" - Based on multi-month historical trends (reliable patterns)
131
+ 2. "Categories Over Monthly Assignment" - Current month only (spending > budgeted but Available still positive)
132
+ 3. "Large Unused Category Balances" - Categories with substantial unused funds
133
+ Distinguish between current-month patterns vs historical trends when presenting insights.
134
+
135
+ 1. List all budgets and select the appropriate one
136
+ 2. Get monthly data for ${month}
137
+ 3. List categories to show budget vs actual spending
138
+ 4. Provide insights on:
139
+ - Total budgeted vs actual spending
140
+ - Categories where Available balance is negative (true overspending - when the category's balance field is < 0)
141
+ - Categories where spending exceeded this month's assignment (but still have positive Available balance)
142
+ - Available money to budget
143
+ - Any true overspending where categories went into the red (negative Available balance)
144
+
145
+ Format the response in a clear, easy-to-read summary.`,
146
+ },
147
+ },
148
+ ],
149
+ };
150
+ },
151
+ 'account-balances': async (_name, args) => {
152
+ const balanceBudget = args?.['budget_name'] || 'first available budget';
153
+ const accountType = args?.['account_type'] || 'all accounts';
154
+ return {
155
+ description: `Check account balances for ${accountType}`,
156
+ messages: [
157
+ {
158
+ role: 'user',
159
+ content: {
160
+ type: 'text',
161
+ text: `Please show account balances for ${balanceBudget}:
162
+
163
+ 1. List all budgets and select the appropriate one
164
+ 2. List accounts for that budget
165
+ 3. Filter by account type: ${accountType}
166
+ 4. Show balances in a clear format with:
167
+ - Account name and type
168
+ - Current balance
169
+ - Cleared vs uncleared amounts
170
+ - Total by account type
171
+ - Net worth summary (assets - liabilities)
172
+
173
+ Convert milliunits to dollars for easy reading.`,
174
+ },
175
+ },
176
+ ],
177
+ };
178
+ },
179
+ };
180
+ export class PromptManager {
181
+ constructor() {
182
+ this.promptDefinitions = [...defaultPromptDefinitions];
183
+ this.promptHandlers = { ...defaultPromptHandlers };
184
+ }
185
+ registerPrompt(definition, handler) {
186
+ this.promptDefinitions.push(definition);
187
+ this.promptHandlers[definition.name] = handler;
188
+ }
189
+ listPrompts() {
190
+ return {
191
+ prompts: this.promptDefinitions,
192
+ };
193
+ }
194
+ async getPrompt(name, args) {
195
+ const handler = this.promptHandlers[name];
196
+ if (!handler) {
197
+ throw new Error(`Unknown prompt: ${name}`);
198
+ }
199
+ const definition = this.promptDefinitions.find((p) => p.name === name);
200
+ if (!definition) {
201
+ throw new Error(`Prompt definition not found: ${name}`);
202
+ }
203
+ return await handler(name, args);
204
+ }
205
+ }
@@ -0,0 +1,27 @@
1
+ export interface RateLimitConfig {
2
+ maxRequests: number;
3
+ windowMs: number;
4
+ enableLogging?: boolean;
5
+ }
6
+ export interface RateLimitInfo {
7
+ remaining: number;
8
+ resetTime: Date;
9
+ isLimited: boolean;
10
+ }
11
+ export declare class RateLimiter {
12
+ private requests;
13
+ private config;
14
+ constructor(config?: Partial<RateLimitConfig>);
15
+ isAllowed(identifier: string): RateLimitInfo;
16
+ recordRequest(identifier: string): void;
17
+ getStatus(identifier: string): RateLimitInfo;
18
+ reset(identifier?: string): void;
19
+ cleanup(): void;
20
+ private hashIdentifier;
21
+ }
22
+ export declare class RateLimitError extends Error {
23
+ readonly resetTime: Date;
24
+ readonly remaining: number;
25
+ constructor(message: string, resetTime: Date, remaining?: number);
26
+ }
27
+ export declare const globalRateLimiter: RateLimiter;
@@ -0,0 +1,82 @@
1
+ export class RateLimiter {
2
+ constructor(config = {}) {
3
+ this.requests = new Map();
4
+ this.config = {
5
+ maxRequests: 200,
6
+ windowMs: 60 * 60 * 1000,
7
+ enableLogging: false,
8
+ ...config,
9
+ };
10
+ }
11
+ isAllowed(identifier) {
12
+ const now = Date.now();
13
+ const windowStart = now - this.config.windowMs;
14
+ let requests = this.requests.get(identifier) || [];
15
+ requests = requests.filter((timestamp) => timestamp > windowStart);
16
+ this.requests.set(identifier, requests);
17
+ const remaining = Math.max(0, this.config.maxRequests - requests.length);
18
+ const resetTime = new Date(now + this.config.windowMs);
19
+ const isLimited = requests.length >= this.config.maxRequests;
20
+ if (this.config.enableLogging) {
21
+ console.error(`Rate limit check for ${this.hashIdentifier(identifier)}: ${requests.length}/${this.config.maxRequests} requests, remaining: ${remaining}, limited: ${isLimited}`);
22
+ }
23
+ return {
24
+ remaining,
25
+ resetTime,
26
+ isLimited,
27
+ };
28
+ }
29
+ recordRequest(identifier) {
30
+ const now = Date.now();
31
+ const requests = this.requests.get(identifier) || [];
32
+ requests.push(now);
33
+ this.requests.set(identifier, requests);
34
+ if (this.config.enableLogging) {
35
+ console.error(`Recorded request for ${this.hashIdentifier(identifier)}: ${requests.length}/${this.config.maxRequests} requests`);
36
+ }
37
+ }
38
+ getStatus(identifier) {
39
+ return this.isAllowed(identifier);
40
+ }
41
+ reset(identifier) {
42
+ if (identifier) {
43
+ this.requests.delete(identifier);
44
+ }
45
+ else {
46
+ this.requests.clear();
47
+ }
48
+ }
49
+ cleanup() {
50
+ const now = Date.now();
51
+ const windowStart = now - this.config.windowMs;
52
+ for (const [identifier, requests] of this.requests.entries()) {
53
+ const validRequests = requests.filter((timestamp) => timestamp > windowStart);
54
+ if (validRequests.length === 0) {
55
+ this.requests.delete(identifier);
56
+ }
57
+ else {
58
+ this.requests.set(identifier, validRequests);
59
+ }
60
+ }
61
+ }
62
+ hashIdentifier(identifier) {
63
+ let hash = 0;
64
+ for (let i = 0; i < identifier.length; i++) {
65
+ const char = identifier.charCodeAt(i);
66
+ hash = (hash << 5) - hash + char;
67
+ hash = hash & hash;
68
+ }
69
+ return `token_${Math.abs(hash).toString(16)}`;
70
+ }
71
+ }
72
+ export class RateLimitError extends Error {
73
+ constructor(message, resetTime, remaining = 0) {
74
+ super(message);
75
+ this.resetTime = resetTime;
76
+ this.remaining = remaining;
77
+ this.name = 'RateLimitError';
78
+ }
79
+ }
80
+ export const globalRateLimiter = new RateLimiter({
81
+ enableLogging: process.env['NODE_ENV'] !== 'production',
82
+ });
@@ -0,0 +1,62 @@
1
+ export interface LogEntry {
2
+ timestamp: Date;
3
+ toolName: string;
4
+ operation: string;
5
+ parameters: Record<string, unknown>;
6
+ success: boolean;
7
+ duration?: number;
8
+ error?: string;
9
+ rateLimitInfo?: {
10
+ remaining: number;
11
+ isLimited: boolean;
12
+ };
13
+ }
14
+ export interface LoggerConfig {
15
+ enabled: boolean;
16
+ logLevel: 'error' | 'warn' | 'info' | 'debug';
17
+ maxLogEntries: number;
18
+ sanitizeParameters: boolean;
19
+ }
20
+ export declare class RequestLogger {
21
+ private logs;
22
+ private config;
23
+ constructor(config?: Partial<LoggerConfig>);
24
+ logRequest(toolName: string, operation: string, parameters: Record<string, unknown>, success: boolean, duration?: number, error?: string, rateLimitInfo?: {
25
+ remaining: number;
26
+ isLimited: boolean;
27
+ }): void;
28
+ logSuccess(toolName: string, operation: string, parameters: Record<string, unknown>, duration?: number, rateLimitInfo?: {
29
+ remaining: number;
30
+ isLimited: boolean;
31
+ }): void;
32
+ logError(toolName: string, operation: string, parameters: Record<string, unknown>, error: string, duration?: number, rateLimitInfo?: {
33
+ remaining: number;
34
+ isLimited: boolean;
35
+ }): void;
36
+ getRecentLogs(count?: number): LogEntry[];
37
+ getFilteredLogs(filter: {
38
+ toolName?: string;
39
+ success?: boolean;
40
+ since?: Date;
41
+ limit?: number;
42
+ }): LogEntry[];
43
+ clearLogs(): void;
44
+ getStats(): {
45
+ totalRequests: number;
46
+ successfulRequests: number;
47
+ failedRequests: number;
48
+ averageDuration: number;
49
+ rateLimitedRequests: number;
50
+ toolUsage: Record<string, number>;
51
+ };
52
+ private sanitizeParameters;
53
+ private isSensitiveParameter;
54
+ private sanitizeString;
55
+ private sanitizeObject;
56
+ private sanitizeError;
57
+ private outputLog;
58
+ private shouldLog;
59
+ private formatLogMessage;
60
+ }
61
+ export declare const globalRequestLogger: RequestLogger;
62
+ export default globalRequestLogger;
@@ -0,0 +1,190 @@
1
+ export class RequestLogger {
2
+ constructor(config = {}) {
3
+ this.logs = [];
4
+ this.config = {
5
+ enabled: process.env['NODE_ENV'] !== 'production',
6
+ logLevel: process.env['LOG_LEVEL'] || 'info',
7
+ maxLogEntries: 1000,
8
+ sanitizeParameters: true,
9
+ ...config,
10
+ };
11
+ }
12
+ logRequest(toolName, operation, parameters, success, duration, error, rateLimitInfo) {
13
+ if (!this.config.enabled)
14
+ return;
15
+ const logEntry = {
16
+ timestamp: new Date(),
17
+ toolName,
18
+ operation,
19
+ parameters: this.config.sanitizeParameters ? this.sanitizeParameters(parameters) : parameters,
20
+ success,
21
+ ...(duration !== undefined && { duration }),
22
+ ...(error && { error: this.sanitizeError(error) }),
23
+ ...(rateLimitInfo && { rateLimitInfo }),
24
+ };
25
+ this.logs.push(logEntry);
26
+ if (this.logs.length > this.config.maxLogEntries) {
27
+ this.logs.shift();
28
+ }
29
+ this.outputLog(logEntry);
30
+ }
31
+ logSuccess(toolName, operation, parameters, duration, rateLimitInfo) {
32
+ this.logRequest(toolName, operation, parameters, true, duration, undefined, rateLimitInfo);
33
+ }
34
+ logError(toolName, operation, parameters, error, duration, rateLimitInfo) {
35
+ this.logRequest(toolName, operation, parameters, false, duration, error, rateLimitInfo);
36
+ }
37
+ getRecentLogs(count = 50) {
38
+ return this.logs.slice(-count);
39
+ }
40
+ getFilteredLogs(filter) {
41
+ let filtered = this.logs;
42
+ if (filter.toolName) {
43
+ filtered = filtered.filter((log) => log.toolName === filter.toolName);
44
+ }
45
+ if (filter.success !== undefined) {
46
+ filtered = filtered.filter((log) => log.success === filter.success);
47
+ }
48
+ if (filter.since) {
49
+ filtered = filtered.filter((log) => log.timestamp >= filter.since);
50
+ }
51
+ if (filter.limit) {
52
+ filtered = filtered.slice(-filter.limit);
53
+ }
54
+ return filtered;
55
+ }
56
+ clearLogs() {
57
+ this.logs = [];
58
+ }
59
+ getStats() {
60
+ const totalRequests = this.logs.length;
61
+ const successfulRequests = this.logs.filter((log) => log.success).length;
62
+ const failedRequests = totalRequests - successfulRequests;
63
+ const durationsWithValues = this.logs
64
+ .filter((log) => log.duration !== undefined)
65
+ .map((log) => log.duration);
66
+ const averageDuration = durationsWithValues.length > 0
67
+ ? durationsWithValues.reduce((sum, duration) => sum + duration, 0) /
68
+ durationsWithValues.length
69
+ : 0;
70
+ const rateLimitedRequests = this.logs.filter((log) => log.rateLimitInfo?.isLimited).length;
71
+ const toolUsage = {};
72
+ this.logs.forEach((log) => {
73
+ toolUsage[log.toolName] = (toolUsage[log.toolName] || 0) + 1;
74
+ });
75
+ return {
76
+ totalRequests,
77
+ successfulRequests,
78
+ failedRequests,
79
+ averageDuration,
80
+ rateLimitedRequests,
81
+ toolUsage,
82
+ };
83
+ }
84
+ sanitizeParameters(parameters) {
85
+ const sanitized = {};
86
+ for (const [key, value] of Object.entries(parameters)) {
87
+ if (this.isSensitiveParameter(key)) {
88
+ sanitized[key] = '***';
89
+ }
90
+ else if (typeof value === 'string') {
91
+ sanitized[key] = this.sanitizeString(value);
92
+ }
93
+ else if (typeof value === 'object' && value !== null) {
94
+ sanitized[key] = this.sanitizeObject(value);
95
+ }
96
+ else {
97
+ sanitized[key] = value;
98
+ }
99
+ }
100
+ return sanitized;
101
+ }
102
+ isSensitiveParameter(key) {
103
+ const sensitiveKeys = [
104
+ 'token',
105
+ 'access_token',
106
+ 'api_key',
107
+ 'password',
108
+ 'secret',
109
+ 'authorization',
110
+ 'auth',
111
+ 'key',
112
+ 'credential',
113
+ ];
114
+ return sensitiveKeys.some((sensitiveKey) => key.toLowerCase().includes(sensitiveKey));
115
+ }
116
+ sanitizeString(value) {
117
+ return value
118
+ .replace(/[a-zA-Z0-9]{30,}/g, '***')
119
+ .replace(/Bearer\s+[a-zA-Z0-9_-]+/gi, 'Bearer ***')
120
+ .replace(/token[s]?[:\s=]+[a-zA-Z0-9_-]+/gi, 'token=***')
121
+ .replace(/key[s]?[:\s=]+[a-zA-Z0-9_-]+/gi, 'key=***');
122
+ }
123
+ sanitizeObject(obj) {
124
+ if (Array.isArray(obj)) {
125
+ return obj.map((item) => typeof item === 'object' && item !== null ? this.sanitizeObject(item) : item);
126
+ }
127
+ if (typeof obj === 'object' && obj !== null) {
128
+ const sanitized = {};
129
+ for (const [key, value] of Object.entries(obj)) {
130
+ if (this.isSensitiveParameter(key)) {
131
+ sanitized[key] = '***';
132
+ }
133
+ else if (typeof value === 'string') {
134
+ sanitized[key] = this.sanitizeString(value);
135
+ }
136
+ else if (typeof value === 'object' && value !== null) {
137
+ sanitized[key] = this.sanitizeObject(value);
138
+ }
139
+ else {
140
+ sanitized[key] = value;
141
+ }
142
+ }
143
+ return sanitized;
144
+ }
145
+ return obj;
146
+ }
147
+ sanitizeError(error) {
148
+ return this.sanitizeString(error);
149
+ }
150
+ outputLog(logEntry) {
151
+ const logMessage = this.formatLogMessage(logEntry);
152
+ if (logEntry.success) {
153
+ if (this.shouldLog('info')) {
154
+ console.error(`[INFO] ${logMessage}`);
155
+ }
156
+ }
157
+ else {
158
+ if (this.shouldLog('error')) {
159
+ console.error(`[ERROR] ${logMessage}`);
160
+ }
161
+ }
162
+ if (logEntry.rateLimitInfo?.isLimited && this.shouldLog('warn')) {
163
+ console.error(`[WARN] Rate limit exceeded for ${logEntry.toolName}`);
164
+ }
165
+ }
166
+ shouldLog(level) {
167
+ const levels = ['error', 'warn', 'info', 'debug'];
168
+ const currentLevelIndex = levels.indexOf(this.config.logLevel);
169
+ const requestedLevelIndex = levels.indexOf(level);
170
+ return requestedLevelIndex <= currentLevelIndex;
171
+ }
172
+ formatLogMessage(logEntry) {
173
+ const parts = [
174
+ `${logEntry.toolName}:${logEntry.operation}`,
175
+ logEntry.success ? 'SUCCESS' : 'FAILED',
176
+ ];
177
+ if (logEntry.duration !== undefined) {
178
+ parts.push(`${logEntry.duration}ms`);
179
+ }
180
+ if (logEntry.rateLimitInfo) {
181
+ parts.push(`rate_limit_remaining:${logEntry.rateLimitInfo.remaining}`);
182
+ }
183
+ if (logEntry.error) {
184
+ parts.push(`error:"${logEntry.error}"`);
185
+ }
186
+ return parts.join(' | ');
187
+ }
188
+ }
189
+ export const globalRequestLogger = new RequestLogger();
190
+ export default globalRequestLogger;
@@ -0,0 +1,39 @@
1
+ import type * as ynab from 'ynab';
2
+ interface ResponseFormatter {
3
+ format(data: unknown): string;
4
+ }
5
+ export type ResourceHandler = (uri: string, dependencies: ResourceDependencies) => Promise<{
6
+ contents: {
7
+ uri: string;
8
+ mimeType: string;
9
+ text: string;
10
+ }[];
11
+ }>;
12
+ export interface ResourceDefinition {
13
+ uri: string;
14
+ name: string;
15
+ description: string;
16
+ mimeType: string;
17
+ }
18
+ export interface ResourceDependencies {
19
+ ynabAPI: ynab.API;
20
+ responseFormatter: ResponseFormatter;
21
+ }
22
+ export declare class ResourceManager {
23
+ private dependencies;
24
+ private resourceHandlers;
25
+ private resourceDefinitions;
26
+ constructor(dependencies: ResourceDependencies);
27
+ registerResource(definition: ResourceDefinition, handler: ResourceHandler): void;
28
+ listResources(): {
29
+ resources: ResourceDefinition[];
30
+ };
31
+ readResource(uri: string): Promise<{
32
+ contents: {
33
+ uri: string;
34
+ mimeType: string;
35
+ text: string;
36
+ }[];
37
+ }>;
38
+ }
39
+ export {};
@@ -0,0 +1,85 @@
1
+ const defaultResourceHandlers = {
2
+ 'ynab://budgets': async (uri, { ynabAPI, responseFormatter }) => {
3
+ try {
4
+ const response = await ynabAPI.budgets.getBudgets();
5
+ const budgets = response.data.budgets.map((budget) => ({
6
+ id: budget.id,
7
+ name: budget.name,
8
+ last_modified_on: budget.last_modified_on,
9
+ first_month: budget.first_month,
10
+ last_month: budget.last_month,
11
+ currency_format: budget.currency_format,
12
+ }));
13
+ return {
14
+ contents: [
15
+ {
16
+ uri: uri,
17
+ mimeType: 'application/json',
18
+ text: responseFormatter.format({ budgets }),
19
+ },
20
+ ],
21
+ };
22
+ }
23
+ catch (error) {
24
+ throw new Error(`Failed to fetch budgets: ${error}`);
25
+ }
26
+ },
27
+ 'ynab://user': async (uri, { ynabAPI, responseFormatter }) => {
28
+ try {
29
+ const response = await ynabAPI.user.getUser();
30
+ const userInfo = response.data.user;
31
+ const user = {
32
+ id: userInfo.id,
33
+ };
34
+ return {
35
+ contents: [
36
+ {
37
+ uri: uri,
38
+ mimeType: 'application/json',
39
+ text: responseFormatter.format({ user }),
40
+ },
41
+ ],
42
+ };
43
+ }
44
+ catch (error) {
45
+ throw new Error(`Failed to fetch user info: ${error}`);
46
+ }
47
+ },
48
+ };
49
+ const defaultResourceDefinitions = [
50
+ {
51
+ uri: 'ynab://budgets',
52
+ name: 'YNAB Budgets',
53
+ description: 'List of all available budgets',
54
+ mimeType: 'application/json',
55
+ },
56
+ {
57
+ uri: 'ynab://user',
58
+ name: 'YNAB User Info',
59
+ description: 'Current user information including ID and email address',
60
+ mimeType: 'application/json',
61
+ },
62
+ ];
63
+ export class ResourceManager {
64
+ constructor(dependencies) {
65
+ this.dependencies = dependencies;
66
+ this.resourceHandlers = { ...defaultResourceHandlers };
67
+ this.resourceDefinitions = [...defaultResourceDefinitions];
68
+ }
69
+ registerResource(definition, handler) {
70
+ this.resourceDefinitions.push(definition);
71
+ this.resourceHandlers[definition.uri] = handler;
72
+ }
73
+ listResources() {
74
+ return {
75
+ resources: this.resourceDefinitions,
76
+ };
77
+ }
78
+ async readResource(uri) {
79
+ const handler = this.resourceHandlers[uri];
80
+ if (!handler) {
81
+ throw new Error(`Unknown resource: ${uri}`);
82
+ }
83
+ return await handler(uri, this.dependencies);
84
+ }
85
+ }