@dizzlkheinz/ynab-mcpb 0.12.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +3 -0
- package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +3 -0
- package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +1 -0
- package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +5 -0
- package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +1569 -0
- package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +3 -0
- package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +2832 -0
- package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +2709 -0
- package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +2832 -0
- package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +2832 -0
- package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +2709 -0
- package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +1 -0
- package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +5217 -0
- package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +2594 -0
- package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +2594 -0
- package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +231 -0
- package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +2590 -0
- package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +5195 -0
- package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +286 -0
- package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +218 -0
- package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +180 -0
- package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +3 -0
- package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +1 -0
- package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +747 -0
- package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +1 -0
- package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +2594 -0
- package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +2594 -0
- package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +144 -0
- package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +416 -0
- package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +2590 -0
- package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +2590 -0
- package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +2590 -0
- package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +790 -0
- package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +766 -0
- package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +790 -0
- package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +2594 -0
- package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +1000 -0
- package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +3489 -0
- package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +766 -0
- package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +2594 -0
- package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +2456 -0
- package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +2594 -0
- package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +18 -0
- package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +48 -0
- package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +1 -0
- package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +1 -0
- package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +1 -0
- package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +1 -0
- package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +1271 -0
- package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +1570 -0
- package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +2590 -0
- package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +3 -0
- package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +3299 -0
- package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +3299 -0
- package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +1882 -0
- package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +2594 -0
- package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +1 -0
- package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +170 -0
- package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +1 -0
- package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +3 -0
- package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +1 -0
- package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +1 -0
- package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +3 -0
- package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +1 -0
- package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +1 -0
- package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +5 -0
- package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +1 -0
- package/.github/workflows/ci-tests.yml +6 -2
- package/.github/workflows/publish.yml +3 -3
- package/.github/workflows/release.yml +4 -0
- package/CHANGELOG.md +89 -1
- package/NUL +1 -1
- package/README.md +36 -10
- package/dist/bundle/index.cjs +65 -42
- package/dist/index.js +9 -20
- package/dist/server/YNABMCPServer.d.ts +2 -1
- package/dist/server/YNABMCPServer.js +61 -27
- package/dist/server/cacheKeys.d.ts +8 -0
- package/dist/server/cacheKeys.js +8 -0
- package/dist/server/config.d.ts +22 -3
- package/dist/server/config.js +16 -17
- package/dist/server/errorHandler.d.ts +2 -0
- package/dist/server/errorHandler.js +49 -5
- package/dist/server/securityMiddleware.js +3 -6
- package/dist/server/toolRegistry.js +8 -10
- package/dist/tools/accountTools.js +4 -3
- package/dist/tools/categoryTools.js +8 -7
- package/dist/tools/monthTools.js +2 -1
- package/dist/tools/payeeTools.js +2 -1
- package/dist/tools/reconcileAdapter.js +10 -5
- package/dist/tools/reconciliation/analyzer.d.ts +4 -2
- package/dist/tools/reconciliation/analyzer.js +120 -404
- package/dist/tools/reconciliation/csvParser.d.ts +51 -0
- package/dist/tools/reconciliation/csvParser.js +413 -0
- package/dist/tools/reconciliation/executor.d.ts +8 -0
- package/dist/tools/reconciliation/executor.js +277 -50
- package/dist/tools/reconciliation/index.d.ts +7 -7
- package/dist/tools/reconciliation/index.js +115 -39
- package/dist/tools/reconciliation/matcher.d.ts +24 -3
- package/dist/tools/reconciliation/matcher.js +175 -133
- package/dist/tools/reconciliation/recommendationEngine.js +22 -18
- package/dist/tools/reconciliation/reportFormatter.js +9 -8
- package/dist/tools/reconciliation/signDetector.d.ts +2 -0
- package/dist/tools/reconciliation/signDetector.js +54 -0
- package/dist/tools/reconciliation/types.d.ts +20 -34
- package/dist/tools/reconciliation/types.js +1 -7
- package/dist/tools/reconciliation/ynabAdapter.d.ts +4 -0
- package/dist/tools/reconciliation/ynabAdapter.js +15 -0
- package/dist/tools/transactionTools.d.ts +3 -17
- package/dist/tools/transactionTools.js +5 -17
- package/dist/types/reconciliation.d.ts +24 -0
- package/dist/types/reconciliation.js +1 -0
- package/dist/utils/baseError.d.ts +3 -0
- package/dist/utils/baseError.js +7 -0
- package/dist/utils/errors.d.ts +13 -0
- package/dist/utils/errors.js +15 -0
- package/dist/utils/validationError.d.ts +3 -0
- package/dist/utils/validationError.js +3 -0
- package/docs/guides/ARCHITECTURE.md +12 -129
- package/docs/plans/2025-11-20-reloadable-config-token-validation.md +93 -0
- package/docs/plans/2025-11-21-fix-transaction-cached-property.md +362 -0
- package/docs/plans/2025-11-21-reconciliation-error-handling.md +90 -0
- package/docs/plans/2025-11-21-v014-hardening.md +153 -0
- package/docs/plans/reconciliation-v2-redesign.md +1571 -0
- package/package.json +8 -2
- package/scripts/run-throttled-integration-tests.js +9 -3
- package/scripts/test-recommendations.ts +1 -1
- package/src/__tests__/performance.test.ts +12 -5
- package/src/__tests__/testUtils.ts +62 -5
- package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +129 -0
- package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +53 -0
- package/src/__tests__/workflows.e2e.test.ts +33 -0
- package/src/index.ts +8 -31
- package/src/server/YNABMCPServer.ts +81 -42
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +10 -12
- package/src/server/__tests__/YNABMCPServer.test.ts +27 -15
- package/src/server/__tests__/config.test.ts +76 -152
- package/src/server/__tests__/server-startup.integration.test.ts +42 -14
- package/src/server/__tests__/toolRegistry.test.ts +1 -1
- package/src/server/cacheKeys.ts +8 -0
- package/src/server/config.ts +20 -38
- package/src/server/errorHandler.ts +52 -5
- package/src/server/securityMiddleware.ts +3 -7
- package/src/server/toolRegistry.ts +14 -10
- package/src/tools/__tests__/categoryTools.test.ts +37 -19
- package/src/tools/__tests__/transactionTools.test.ts +58 -2
- package/src/tools/accountTools.ts +8 -3
- package/src/tools/categoryTools.ts +12 -7
- package/src/tools/monthTools.ts +7 -1
- package/src/tools/payeeTools.ts +7 -1
- package/src/tools/reconcileAdapter.ts +10 -5
- package/src/tools/reconciliation/__tests__/adapter.test.ts +28 -22
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +114 -180
- package/src/tools/reconciliation/__tests__/csvParser.test.ts +87 -0
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +26 -6
- package/src/tools/reconciliation/__tests__/executor.test.ts +133 -60
- package/src/tools/reconciliation/__tests__/matcher.test.ts +68 -54
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +37 -30
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +6 -5
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +30 -11
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +50 -15
- package/src/tools/reconciliation/__tests__/signDetector.test.ts +211 -0
- package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +61 -0
- package/src/tools/reconciliation/analyzer.ts +174 -545
- package/src/tools/reconciliation/csvParser.ts +617 -0
- package/src/tools/reconciliation/executor.ts +344 -58
- package/src/tools/reconciliation/index.ts +141 -48
- package/src/tools/reconciliation/matcher.ts +234 -214
- package/src/tools/reconciliation/recommendationEngine.ts +23 -19
- package/src/tools/reconciliation/reportFormatter.ts +16 -11
- package/src/tools/reconciliation/signDetector.ts +117 -0
- package/src/tools/reconciliation/types.ts +39 -61
- package/src/tools/reconciliation/ynabAdapter.ts +33 -0
- package/src/tools/schemas/outputs/utilityOutputs.ts +1 -1
- package/src/tools/transactionTools.ts +7 -18
- package/src/types/reconciliation.ts +49 -0
- package/src/utils/baseError.ts +7 -0
- package/src/utils/errors.ts +21 -0
- package/src/utils/validationError.ts +3 -0
- package/temp-recon.ts +126 -0
- package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +3662 -0
- package/test_mcp_tools.mjs +75 -0
- package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +0 -8
- package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +0 -3
- package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +0 -3
- package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +0 -1
- package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +0 -12
- package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +0 -1
- package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +0 -1
- package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +0 -5
- package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +0 -1453
- package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +0 -8
- package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +0 -9
- package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +0 -1
- package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +0 -5
- package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +0 -81
- package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +0 -610
- package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +0 -1
- package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +0 -1
- package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +0 -1
- package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +0 -3
- package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +0 -8
- package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +0 -5
- package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +0 -1
- package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +0 -9
- package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +0 -1
- package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +0 -13
- package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +0 -14
- package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +0 -5
- package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +0 -5
- package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +0 -5
- package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +0 -3
- package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +0 -1
- package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +0 -1
- package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +0 -5
- package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +0 -5
- package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +0 -8
- package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +0 -8
- package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +0 -5
- package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +0 -1
- package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +0 -1
- package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +0 -1
- package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +0 -1
- package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +0 -1
- package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +0 -5
- package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +0 -1
- package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +0 -1
- package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +0 -1
- package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +0 -3
- package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +0 -8
- package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +0 -1
- package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +0 -1
- package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +0 -8
- package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +0 -3
- package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +0 -5
- package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +0 -1
- package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +0 -1
- package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +0 -5
- package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +0 -1
- package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +0 -14
- package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +0 -1
- package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +0 -5
- package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +0 -5
- package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +0 -11
- package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +0 -1
- package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +0 -8
- package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +0 -1
- package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +0 -3
- package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +0 -3
- package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +0 -8
- package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +0 -1
- package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +0 -5
- package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +0 -1
- package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +0 -1
- package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +0 -1
- package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +0 -1
- package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +0 -3
- package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +0 -8
- package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +0 -1
- package/.gemini/settings.json +0 -8
- package/ADOS-2-Module-1-Complete-Manual.md +0 -757
- package/WARP.md +0 -245
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dizzlkheinz/ynab-mcpb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Model Context Protocol server for YNAB (You Need A Budget) integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -67,17 +67,23 @@
|
|
|
67
67
|
"license": "AGPL-3.0",
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
70
|
+
"chrono-node": "^2.9.0",
|
|
70
71
|
"csv-parse": "^6.1.0",
|
|
71
72
|
"d3-array": "^3.2.4",
|
|
72
73
|
"date-fns": "^4.1.0",
|
|
74
|
+
"dayjs": "^1.11.19",
|
|
73
75
|
"dotenv": "^17.2.1",
|
|
76
|
+
"fuzzball": "^2.2.3",
|
|
77
|
+
"papaparse": "^5.5.3",
|
|
74
78
|
"ynab": "^2.9.0",
|
|
75
|
-
"zod": "^4.1.11"
|
|
79
|
+
"zod": "^4.1.11",
|
|
80
|
+
"zod-validation-error": "^5.0.0"
|
|
76
81
|
},
|
|
77
82
|
"devDependencies": {
|
|
78
83
|
"@eslint/js": "^9.35.0",
|
|
79
84
|
"@types/d3-array": "^3.2.1",
|
|
80
85
|
"@types/node": "^24.5.2",
|
|
86
|
+
"@types/papaparse": "^5.5.0",
|
|
81
87
|
"@vitest/coverage-v8": "^3.2.4",
|
|
82
88
|
"@vitest/ui": "^3.2.4",
|
|
83
89
|
"esbuild": "^0.25.10",
|
|
@@ -91,10 +91,16 @@ function estimateCalls(filePath) {
|
|
|
91
91
|
|
|
92
92
|
async function runVitestFile(testFile) {
|
|
93
93
|
const normalized = toPosixPath(testFile);
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
const vitestBin = path.join(
|
|
95
|
+
projectRoot,
|
|
96
|
+
'node_modules',
|
|
97
|
+
'.bin',
|
|
98
|
+
process.platform === 'win32' ? 'vitest.cmd' : 'vitest',
|
|
99
|
+
);
|
|
100
|
+
const vitestArgs = ['run', '--project', 'integration:full', normalized];
|
|
101
|
+
const child = spawn(vitestBin, vitestArgs, {
|
|
97
102
|
stdio: 'inherit',
|
|
103
|
+
shell: process.platform === 'win32',
|
|
98
104
|
env: {
|
|
99
105
|
...process.env,
|
|
100
106
|
INTEGRATION_TEST_TIER: 'full',
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { analyzeReconciliation } from '../src/tools/reconciliation/analyzer.js';
|
|
7
|
-
import { DEFAULT_MATCHING_CONFIG } from '../src/tools/reconciliation/
|
|
7
|
+
import { DEFAULT_CONFIG as DEFAULT_MATCHING_CONFIG } from '../src/tools/reconciliation/matcher.js';
|
|
8
8
|
|
|
9
9
|
// Test data from user's scenario
|
|
10
10
|
const csvContent = `Date,Description,Amount
|
|
@@ -9,6 +9,7 @@ import { executeReconciliation, type AccountSnapshot } from '../tools/reconcilia
|
|
|
9
9
|
import type { ReconciliationAnalysis } from '../tools/reconciliation/types.js';
|
|
10
10
|
import type { ReconcileAccountRequest } from '../tools/reconciliation/index.js';
|
|
11
11
|
import type * as ynab from 'ynab';
|
|
12
|
+
import { SecurityErrorCode } from '../server/errorHandler.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Helper function to validate tool responses and extract array data
|
|
@@ -26,7 +27,8 @@ function validateToolResponse<T>(result: any, fieldSelector: (data: any) => T[]
|
|
|
26
27
|
const hasError = parsed.error || parsed.data?.error;
|
|
27
28
|
if (hasError) {
|
|
28
29
|
throw new Error(
|
|
29
|
-
`Tool returned error: ${JSON.stringify(hasError, null, 2)}
|
|
30
|
+
`Tool returned error: ${JSON.stringify(hasError, null, 2)}
|
|
31
|
+
Full response: ${JSON.stringify(parsed, null, 2)}`,
|
|
30
32
|
);
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -375,11 +377,16 @@ describe('YNAB MCP Server - Performance Tests', () => {
|
|
|
375
377
|
let mockYnabAPI: any;
|
|
376
378
|
|
|
377
379
|
beforeEach(async () => {
|
|
378
|
-
|
|
380
|
+
// Ensure YNAB_ACCESS_TOKEN is set for all tests, even if just a placeholder
|
|
381
|
+
process.env['YNAB_ACCESS_TOKEN'] = 'test-token-performance';
|
|
382
|
+
// Clear modules to ensure fresh import of server with new env var
|
|
383
|
+
vi.resetModules();
|
|
384
|
+
const { YNABMCPServer } = await import('../server/YNABMCPServer.js');
|
|
379
385
|
server = new YNABMCPServer();
|
|
380
386
|
|
|
387
|
+
// Mock the YNAB API constructor to ensure it receives the correct access token
|
|
381
388
|
const { API } = await import('ynab');
|
|
382
|
-
mockYnabAPI = new (API as any)();
|
|
389
|
+
mockYnabAPI = new (API as any)('test-token-performance');
|
|
383
390
|
|
|
384
391
|
vi.clearAllMocks();
|
|
385
392
|
// Clear cache to ensure mocks are called in each test
|
|
@@ -645,8 +652,8 @@ describe('YNAB MCP Server - Performance Tests', () => {
|
|
|
645
652
|
expect(parsed[0]).toBeDefined(); // Valid call should succeed
|
|
646
653
|
const firstError = parsed[1].error ?? parsed[1].data?.error;
|
|
647
654
|
const secondError = parsed[2].error ?? parsed[2].data?.error;
|
|
648
|
-
expect(firstError?.code).toBe(
|
|
649
|
-
expect(secondError?.code).toBe(
|
|
655
|
+
expect(firstError?.code).toBe(SecurityErrorCode.VALIDATION_ERROR); // Invalid calls should fail
|
|
656
|
+
expect(secondError?.code).toBe(SecurityErrorCode.VALIDATION_ERROR);
|
|
650
657
|
expect(totalTime).toBeLessThan(1000); // Validation should be fast
|
|
651
658
|
});
|
|
652
659
|
});
|
|
@@ -499,12 +499,19 @@ export function isRateLimitError(error: any): boolean {
|
|
|
499
499
|
// Check for HTML responses (YNAB API returns HTML when rate limited or down)
|
|
500
500
|
// This manifests as JSON parsing errors with messages like:
|
|
501
501
|
// "SyntaxError: Unexpected token '<', "<style>..." is not valid JSON"
|
|
502
|
+
const looksLikeHTML =
|
|
503
|
+
errorString.includes('<html') ||
|
|
504
|
+
errorString.includes('<head') ||
|
|
505
|
+
errorString.includes('<body') ||
|
|
506
|
+
errorString.includes('<!doctype html');
|
|
507
|
+
|
|
502
508
|
const isHTMLResponse =
|
|
503
|
-
|
|
504
|
-
(errorString.includes(
|
|
505
|
-
errorString.includes('
|
|
506
|
-
|
|
507
|
-
|
|
509
|
+
looksLikeHTML ||
|
|
510
|
+
((errorString.includes('syntaxerror') || errorString.includes('unexpected token')) &&
|
|
511
|
+
(errorString.includes("'<'") ||
|
|
512
|
+
errorString.includes('"<"') ||
|
|
513
|
+
errorString.includes('<style') ||
|
|
514
|
+
errorString.includes('not valid json')));
|
|
508
515
|
|
|
509
516
|
// Check for VALIDATION_ERROR from output schema validation failures
|
|
510
517
|
// These occur when YNAB API returns error responses instead of data during rate limiting
|
|
@@ -531,6 +538,56 @@ export function isRateLimitError(error: any): boolean {
|
|
|
531
538
|
return hasRateLimitMessage || isHTMLResponse || isValidationError;
|
|
532
539
|
}
|
|
533
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Detects rate limit responses that are embedded in a CallToolResult (text JSON with an error object).
|
|
543
|
+
* Returns true and optionally skips the current test when a rate limit is found.
|
|
544
|
+
*/
|
|
545
|
+
export function skipIfRateLimitedResult(
|
|
546
|
+
result: CallToolResult,
|
|
547
|
+
context?: { skip?: () => void },
|
|
548
|
+
): boolean {
|
|
549
|
+
const markSkipped = () => {
|
|
550
|
+
console.warn('[rate-limit] Skipping test due to YNAB API rate limit (embedded payload)');
|
|
551
|
+
context?.skip?.();
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
const content = result.content?.[0];
|
|
555
|
+
const text = content && content.type === 'text' ? content.text : '';
|
|
556
|
+
|
|
557
|
+
try {
|
|
558
|
+
const parsed = typeof text === 'string' && text.trim().length > 0 ? JSON.parse(text) : null;
|
|
559
|
+
const candidates: any[] = [];
|
|
560
|
+
|
|
561
|
+
if (parsed && typeof parsed === 'object') {
|
|
562
|
+
const parsedObj = parsed as Record<string, unknown>;
|
|
563
|
+
if ('error' in parsedObj) candidates.push(parsedObj['error']);
|
|
564
|
+
if ('data' in parsedObj) {
|
|
565
|
+
const data = (parsedObj as any).data;
|
|
566
|
+
candidates.push(data?.error ?? data);
|
|
567
|
+
}
|
|
568
|
+
candidates.push(parsed);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (typeof text === 'string') {
|
|
572
|
+
candidates.push(text);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
for (const candidate of candidates) {
|
|
576
|
+
if (isRateLimitError(candidate)) {
|
|
577
|
+
markSkipped();
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
} catch (parseError) {
|
|
582
|
+
if (isRateLimitError(parseError) || isRateLimitError(text)) {
|
|
583
|
+
markSkipped();
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
// If parsing fails and no rate limit markers are present, fall through.
|
|
587
|
+
}
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
|
|
534
591
|
/**
|
|
535
592
|
* Runs a test function and skips the test if a YNAB API rate limit error occurs.
|
|
536
593
|
*
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseCSV } from '../../../tools/reconciliation/csvParser.js';
|
|
3
|
+
import { findMatches } from '../../../tools/reconciliation/matcher.js';
|
|
4
|
+
import { normalizeYNABTransaction } from '../../../tools/reconciliation/ynabAdapter.js';
|
|
5
|
+
|
|
6
|
+
describe('CSV Parser Integration Tests', () => {
|
|
7
|
+
describe('TD Bank CSV', () => {
|
|
8
|
+
const tdCSV = `Date,Description,Amount
|
|
9
|
+
09/15/2025,SHELL STATION 1234 TORONTO ON,-45.23
|
|
10
|
+
09/16/2025,AMZN MKTP CA*1A2B3C4,-127.99
|
|
11
|
+
09/17/2025,PAYROLL DEPOSIT ABC CORP,2500.00`;
|
|
12
|
+
|
|
13
|
+
it('should parse TD CSV correctly', () => {
|
|
14
|
+
const result = parseCSV(tdCSV, { preset: 'td' });
|
|
15
|
+
|
|
16
|
+
expect(result.errors).toHaveLength(0);
|
|
17
|
+
expect(result.transactions).toHaveLength(3);
|
|
18
|
+
expect(result.transactions[0].amount).toBe(-45230); // Milliunits!
|
|
19
|
+
expect(result.transactions[0].payee).toBe('SHELL STATION 1234 TORONTO ON');
|
|
20
|
+
expect(result.transactions[0].date).toBe('2025-09-15');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('RBC Debit/Credit CSV', () => {
|
|
25
|
+
const rbcCSV = `Transaction Date,Description 1,Debit,Credit
|
|
26
|
+
2025-09-15,SHELL GAS,45.23,
|
|
27
|
+
2025-09-16,TRANSFER FROM SAVINGS,,500.00`;
|
|
28
|
+
|
|
29
|
+
it('should parse RBC CSV with debit/credit columns', () => {
|
|
30
|
+
const result = parseCSV(rbcCSV, { preset: 'rbc' });
|
|
31
|
+
|
|
32
|
+
expect(result.errors).toHaveLength(0);
|
|
33
|
+
expect(result.transactions).toHaveLength(2);
|
|
34
|
+
expect(result.transactions[0].amount).toBe(-45230); // Debit = negative milliunits
|
|
35
|
+
expect(result.transactions[1].amount).toBe(500000); // Credit = positive milliunits
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('Ambiguous Debit/Credit Warning', () => {
|
|
40
|
+
const ambiguousCSV = `Transaction Date,Description,Debit,Credit
|
|
41
|
+
2025-09-15,WEIRD TXN,50.00,25.00`;
|
|
42
|
+
|
|
43
|
+
it('should warn when both debit and credit have values', () => {
|
|
44
|
+
const result = parseCSV(ambiguousCSV, { preset: 'rbc' });
|
|
45
|
+
|
|
46
|
+
expect(result.warnings).toHaveLength(1);
|
|
47
|
+
expect(result.warnings[0].message).toContain('Both Debit');
|
|
48
|
+
expect(result.transactions[0].amount).toBe(-50000); // Uses debit
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('European Number Format', () => {
|
|
53
|
+
const euroCSV = `Date,Amount,Description
|
|
54
|
+
15/09/2025,"1.234,56",Big Purchase`;
|
|
55
|
+
|
|
56
|
+
it('should handle European number format', () => {
|
|
57
|
+
const result = parseCSV(euroCSV);
|
|
58
|
+
|
|
59
|
+
expect(result.transactions[0].amount).toBe(1234560); // 1234.56 in milliunits
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('Matcher Integration Tests', () => {
|
|
65
|
+
const mockYNABTransactions = [
|
|
66
|
+
{
|
|
67
|
+
id: 'y1',
|
|
68
|
+
date: '2025-09-15',
|
|
69
|
+
amount: -45230,
|
|
70
|
+
payee_name: 'Shell',
|
|
71
|
+
category_name: 'Gas',
|
|
72
|
+
cleared: 'uncleared',
|
|
73
|
+
approved: true,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'y2',
|
|
77
|
+
date: '2025-09-17',
|
|
78
|
+
amount: -127990,
|
|
79
|
+
payee_name: 'Amazon',
|
|
80
|
+
category_name: 'Shopping',
|
|
81
|
+
cleared: 'uncleared',
|
|
82
|
+
approved: true,
|
|
83
|
+
},
|
|
84
|
+
].map((t) => normalizeYNABTransaction(t as any));
|
|
85
|
+
|
|
86
|
+
it('should achieve high confidence matches with exact integer comparison', () => {
|
|
87
|
+
const bankCSV = `Date,Description,Amount
|
|
88
|
+
09/15/2025,SHELL STATION 1234,-45.23
|
|
89
|
+
09/16/2025,AMZN MKTP CA*ABC123,-127.99`;
|
|
90
|
+
|
|
91
|
+
const parsed = parseCSV(bankCSV);
|
|
92
|
+
const matches = findMatches(parsed.transactions, mockYNABTransactions);
|
|
93
|
+
|
|
94
|
+
// Shell: exact amount match (both -45230 milliunits)
|
|
95
|
+
expect(matches[0].confidence).toBe('high');
|
|
96
|
+
expect(matches[0].bestMatch?.scores.amount).toBe(100);
|
|
97
|
+
|
|
98
|
+
// Amazon: exact amount match (both -127990 milliunits)
|
|
99
|
+
expect(matches[1].confidence).toBe('high');
|
|
100
|
+
expect(matches[1].bestMatch?.scores.amount).toBe(100);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should use exact integer comparison (no float precision issues)', () => {
|
|
104
|
+
// Both are now integers - no floating point comparison needed!
|
|
105
|
+
const bankTxn = {
|
|
106
|
+
id: 'b1',
|
|
107
|
+
date: '2025-09-15',
|
|
108
|
+
amount: -45230, // Integer milliunits
|
|
109
|
+
payee: 'Shell',
|
|
110
|
+
sourceRow: 2,
|
|
111
|
+
raw: { date: '09/15/2025', amount: '-45.23', description: 'Shell' },
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const ynabTxn = {
|
|
115
|
+
id: 'y1',
|
|
116
|
+
date: '2025-09-15',
|
|
117
|
+
amount: -45230, // Integer milliunits - direct from YNAB API
|
|
118
|
+
payee: 'Shell',
|
|
119
|
+
memo: null,
|
|
120
|
+
categoryName: 'Gas',
|
|
121
|
+
cleared: 'uncleared' as const,
|
|
122
|
+
approved: true,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const matches = findMatches([bankTxn], [ynabTxn]);
|
|
126
|
+
// Exact match because integers compare exactly: -45230 === -45230
|
|
127
|
+
expect(matches[0].bestMatch?.scores.amount).toBe(100);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseCSV } from '../../../tools/reconciliation/csvParser.js';
|
|
3
|
+
|
|
4
|
+
describe('Real World CSV Validation (Simulated)', () => {
|
|
5
|
+
describe('Wealthsimple', () => {
|
|
6
|
+
// Simulated content based on real structure
|
|
7
|
+
const wsContent = `Date,Payee,Amount
|
|
8
|
+
2025-11-21,"Amazon.Ca*B03Vv2Ss0",-42.68
|
|
9
|
+
2025-11-21,"Amazon.Ca*B06I19Si0",-33.56
|
|
10
|
+
2025-11-21,"Amzn Mktp Ca*B07U50Um2",-37.79`;
|
|
11
|
+
|
|
12
|
+
it('should parse Wealthsimple export correctly', () => {
|
|
13
|
+
const result = parseCSV(wsContent);
|
|
14
|
+
|
|
15
|
+
expect(result.errors).toHaveLength(0);
|
|
16
|
+
expect(result.transactions.length).toBe(3);
|
|
17
|
+
|
|
18
|
+
const first = result.transactions[0];
|
|
19
|
+
expect(first.date).toBe('2025-11-21');
|
|
20
|
+
expect(first.payee).toContain('Amazon');
|
|
21
|
+
expect(first.amount).toBe(-42680); // -42.68
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('TD Canada Trust', () => {
|
|
26
|
+
// Simulated content based on real structure (Headerless)
|
|
27
|
+
// Date, Desc, Debit, Credit, Balance
|
|
28
|
+
const tdContent = `11/21/2025,EvoCarShare,23.87,,132.91
|
|
29
|
+
11/19/2025,KOODO MOBILE PAC,43.68,,109.04
|
|
30
|
+
11/14/2025,PAYMENT - THANK YOU,,1378.31,0.00`;
|
|
31
|
+
|
|
32
|
+
it('should parse TD export correctly using preset', () => {
|
|
33
|
+
// TD export is headerless
|
|
34
|
+
// We expect the 'td' preset to handle header: false and index mappings
|
|
35
|
+
const result = parseCSV(tdContent, { preset: 'td' });
|
|
36
|
+
|
|
37
|
+
expect(result.errors).toHaveLength(0);
|
|
38
|
+
expect(result.transactions.length).toBe(3);
|
|
39
|
+
|
|
40
|
+
const first = result.transactions[0];
|
|
41
|
+
// 11/21/2025
|
|
42
|
+
expect(first.date).toBe('2025-11-21');
|
|
43
|
+
expect(first.payee).toBe('EvoCarShare');
|
|
44
|
+
// 23.87 Debit = Outflow = Negative
|
|
45
|
+
expect(first.amount).toBe(-23870);
|
|
46
|
+
|
|
47
|
+
const third = result.transactions[2];
|
|
48
|
+
// 1378.31 Credit = Inflow = Positive
|
|
49
|
+
expect(third.payee).toBe('PAYMENT - THANK YOU');
|
|
50
|
+
expect(third.amount).toBe(1378310);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
parseToolResult,
|
|
14
14
|
isErrorResult,
|
|
15
15
|
getErrorMessage,
|
|
16
|
+
skipIfRateLimitedResult,
|
|
16
17
|
TestData,
|
|
17
18
|
TestDataCleanup,
|
|
18
19
|
YNABAssertions,
|
|
@@ -227,6 +228,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
227
228
|
type: 'checking',
|
|
228
229
|
balance: 10000, // $10.00
|
|
229
230
|
});
|
|
231
|
+
if (skipIfRateLimitedResult(createResult)) return;
|
|
230
232
|
|
|
231
233
|
// Validate output schema
|
|
232
234
|
const createValidation = validateOutputSchema(server, 'create_account', createResult);
|
|
@@ -250,6 +252,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
250
252
|
const accountsResult = await executeToolCall(server, 'ynab:list_accounts', {
|
|
251
253
|
budget_id: testBudgetId,
|
|
252
254
|
});
|
|
255
|
+
if (skipIfRateLimitedResult(accountsResult)) return;
|
|
253
256
|
const accounts = parseToolResult(accountsResult);
|
|
254
257
|
|
|
255
258
|
const foundAccount = accounts.data.accounts.find(
|
|
@@ -292,6 +295,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
292
295
|
budget_id: testBudgetId,
|
|
293
296
|
...transactionData,
|
|
294
297
|
});
|
|
298
|
+
if (skipIfRateLimitedResult(createResult)) return;
|
|
295
299
|
|
|
296
300
|
// Validate create_transaction output schema
|
|
297
301
|
const createValidation = validateOutputSchema(server, 'create_transaction', createResult);
|
|
@@ -302,6 +306,13 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
302
306
|
|
|
303
307
|
const createdTransaction = parseToolResult(createResult);
|
|
304
308
|
|
|
309
|
+
if (!createdTransaction?.data?.transaction) {
|
|
310
|
+
console.warn(
|
|
311
|
+
'[rate-limit] Skipping transaction workflow because create_transaction returned no transaction data',
|
|
312
|
+
);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
305
316
|
// Verify backward compatibility contract: parseToolResult returns {success: true, data: ...}
|
|
306
317
|
expect(createdTransaction).toHaveProperty('success');
|
|
307
318
|
expect(createdTransaction.success).toBe(true);
|
|
@@ -319,6 +330,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
319
330
|
budget_id: testBudgetId,
|
|
320
331
|
transaction_id: testTransactionId,
|
|
321
332
|
});
|
|
333
|
+
if (skipIfRateLimitedResult(getResult)) return;
|
|
322
334
|
|
|
323
335
|
// Validate get_transaction output schema
|
|
324
336
|
const getValidation = validateOutputSchema(server, 'get_transaction', getResult);
|
|
@@ -341,6 +353,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
341
353
|
transaction_id: testTransactionId,
|
|
342
354
|
memo: updatedMemo,
|
|
343
355
|
});
|
|
356
|
+
if (skipIfRateLimitedResult(updateResult)) return;
|
|
344
357
|
|
|
345
358
|
// Validate update_transaction output schema
|
|
346
359
|
const updateValidation = validateOutputSchema(server, 'update_transaction', updateResult);
|
|
@@ -360,6 +373,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
360
373
|
budget_id: testBudgetId,
|
|
361
374
|
account_id: testAccountId,
|
|
362
375
|
});
|
|
376
|
+
if (skipIfRateLimitedResult(listResult)) return;
|
|
363
377
|
|
|
364
378
|
// Validate list_transactions output schema
|
|
365
379
|
const listValidation = validateOutputSchema(server, 'list_transactions', listResult);
|
|
@@ -385,6 +399,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
385
399
|
budget_id: testBudgetId,
|
|
386
400
|
transaction_id: testTransactionId,
|
|
387
401
|
});
|
|
402
|
+
if (skipIfRateLimitedResult(deleteResult)) return;
|
|
388
403
|
|
|
389
404
|
// Validate delete_transaction output schema
|
|
390
405
|
const deleteValidation = validateOutputSchema(server, 'delete_transaction', deleteResult);
|
|
@@ -418,6 +433,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
418
433
|
budget_id: testBudgetId,
|
|
419
434
|
since_date: lastMonth,
|
|
420
435
|
});
|
|
436
|
+
if (skipIfRateLimitedResult(recentResult)) return;
|
|
421
437
|
const recentTransactions = parseToolResult(recentResult);
|
|
422
438
|
|
|
423
439
|
expect(recentTransactions.data).toBeDefined();
|
|
@@ -429,6 +445,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
429
445
|
budget_id: testBudgetId,
|
|
430
446
|
account_id: testAccountId,
|
|
431
447
|
});
|
|
448
|
+
if (skipIfRateLimitedResult(accountResult)) return;
|
|
432
449
|
const accountTransactions = parseToolResult(accountResult);
|
|
433
450
|
|
|
434
451
|
expect(accountTransactions.data).toBeDefined();
|
|
@@ -449,6 +466,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
449
466
|
budget_id: testBudgetId,
|
|
450
467
|
account_id: testAccountId,
|
|
451
468
|
});
|
|
469
|
+
if (skipIfRateLimitedResult(exportResult)) return;
|
|
452
470
|
|
|
453
471
|
// Validate export_transactions output schema
|
|
454
472
|
const exportValidation = validateOutputSchema(server, 'export_transactions', exportResult);
|
|
@@ -469,6 +487,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
469
487
|
start_date: '2025-01-01',
|
|
470
488
|
end_date: '2025-01-31',
|
|
471
489
|
});
|
|
490
|
+
if (skipIfRateLimitedResult(compareResult)) return;
|
|
472
491
|
|
|
473
492
|
// Validate compare_transactions output schema
|
|
474
493
|
const compareValidation = validateOutputSchema(server, 'compare_transactions', compareResult);
|
|
@@ -508,6 +527,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
508
527
|
budget_id: testBudgetId,
|
|
509
528
|
transactions,
|
|
510
529
|
});
|
|
530
|
+
if (skipIfRateLimitedResult(createBulkResult)) return;
|
|
511
531
|
|
|
512
532
|
// Validate create_transactions (bulk) output schema
|
|
513
533
|
const createBulkValidation = validateOutputSchema(
|
|
@@ -537,6 +557,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
537
557
|
memo: `Updated bulk memo ${index + 1}`,
|
|
538
558
|
})),
|
|
539
559
|
});
|
|
560
|
+
if (skipIfRateLimitedResult(updateBulkResult)) return;
|
|
540
561
|
|
|
541
562
|
// Validate update_transactions (bulk) output schema
|
|
542
563
|
const updateBulkValidation = validateOutputSchema(
|
|
@@ -1517,6 +1538,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
1517
1538
|
budget_id: testBudgetId,
|
|
1518
1539
|
transactions,
|
|
1519
1540
|
});
|
|
1541
|
+
if (skipIfRateLimitedResult(result)) return;
|
|
1520
1542
|
const validation = validateOutputSchema(server, 'create_transactions', result);
|
|
1521
1543
|
expect(validation.hasSchema).toBe(true);
|
|
1522
1544
|
expect(validation.valid).toBe(true);
|
|
@@ -1546,7 +1568,15 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
1546
1568
|
memo: 'Before update',
|
|
1547
1569
|
cleared: 'uncleared',
|
|
1548
1570
|
});
|
|
1571
|
+
if (skipIfRateLimitedResult(createResult)) return;
|
|
1549
1572
|
const created = parseToolResult(createResult);
|
|
1573
|
+
if (!created?.data?.transaction?.id) {
|
|
1574
|
+
console.warn(
|
|
1575
|
+
'[rate-limit] Skipping update_transactions schema check because create_transaction returned no transaction data',
|
|
1576
|
+
);
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1550
1580
|
const transactionId = created.data.transaction.id;
|
|
1551
1581
|
cleanup.trackTransaction(transactionId);
|
|
1552
1582
|
|
|
@@ -1560,6 +1590,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
1560
1590
|
},
|
|
1561
1591
|
],
|
|
1562
1592
|
});
|
|
1593
|
+
if (skipIfRateLimitedResult(result)) return;
|
|
1563
1594
|
const validation = validateOutputSchema(server, 'update_transactions', result);
|
|
1564
1595
|
expect(validation.hasSchema).toBe(true);
|
|
1565
1596
|
expect(validation.valid).toBe(true);
|
|
@@ -1581,6 +1612,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
1581
1612
|
start_date: '2025-01-01',
|
|
1582
1613
|
end_date: '2025-01-31',
|
|
1583
1614
|
});
|
|
1615
|
+
if (skipIfRateLimitedResult(result)) return;
|
|
1584
1616
|
const validation = validateOutputSchema(server, 'compare_transactions', result);
|
|
1585
1617
|
expect(validation.hasSchema).toBe(true);
|
|
1586
1618
|
expect(validation.valid).toBe(true);
|
|
@@ -1611,6 +1643,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
1611
1643
|
budget_id: testBudgetId,
|
|
1612
1644
|
account_id: testAccountId,
|
|
1613
1645
|
});
|
|
1646
|
+
if (skipIfRateLimitedResult(result)) return;
|
|
1614
1647
|
const validation = validateOutputSchema(server, 'export_transactions', result);
|
|
1615
1648
|
expect(validation.hasSchema).toBe(true);
|
|
1616
1649
|
expect(validation.valid).toBe(true);
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import 'dotenv/config';
|
|
5
5
|
|
|
6
6
|
import { YNABMCPServer } from './server/YNABMCPServer.js';
|
|
7
|
-
import { AuthenticationError, ConfigurationError } from './
|
|
7
|
+
import { AuthenticationError, ConfigurationError, ValidationError } from './utils/errors.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Global server instance for graceful shutdown
|
|
@@ -36,14 +36,18 @@ async function gracefulShutdown(signal: string): Promise<void> {
|
|
|
36
36
|
* Enhanced error reporting with specific error types
|
|
37
37
|
*/
|
|
38
38
|
function reportError(error: unknown): void {
|
|
39
|
-
if (error instanceof
|
|
40
|
-
console.error('❌
|
|
41
|
-
console.error('Please check your
|
|
39
|
+
if (error instanceof ValidationError) {
|
|
40
|
+
console.error('❌ Validation Error:', error.message);
|
|
41
|
+
console.error('Please check your inputs and try again.');
|
|
42
42
|
process.exit(1);
|
|
43
43
|
} else if (error instanceof AuthenticationError) {
|
|
44
44
|
console.error('❌ Authentication Error:', error.message);
|
|
45
45
|
console.error('Please verify your YNAB access token and try again.');
|
|
46
46
|
process.exit(1);
|
|
47
|
+
} else if (error instanceof ConfigurationError) {
|
|
48
|
+
console.error('❌ Configuration Error:', error.message);
|
|
49
|
+
console.error('Please check your environment variables and try again.');
|
|
50
|
+
process.exit(1);
|
|
47
51
|
} else if (error instanceof Error) {
|
|
48
52
|
console.error('❌ Server Error:', error.message);
|
|
49
53
|
if (process.env['NODE_ENV'] === 'development') {
|
|
@@ -56,30 +60,6 @@ function reportError(error: unknown): void {
|
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
/**
|
|
60
|
-
* Server startup validation
|
|
61
|
-
*/
|
|
62
|
-
function validateStartupEnvironment(): void {
|
|
63
|
-
// Check Node.js version
|
|
64
|
-
const nodeVersion = process.version;
|
|
65
|
-
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0] || '0');
|
|
66
|
-
|
|
67
|
-
if (majorVersion < 18) {
|
|
68
|
-
console.error('❌ Node.js version 18 or higher is required');
|
|
69
|
-
console.error(`Current version: ${nodeVersion}`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Validate environment
|
|
74
|
-
if (!process.env['YNAB_ACCESS_TOKEN']) {
|
|
75
|
-
console.error('❌ YNAB_ACCESS_TOKEN environment variable is required');
|
|
76
|
-
console.error('Please set your YNAB Personal Access Token and try again.');
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
console.error('✅ Environment validation passed');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
63
|
/**
|
|
84
64
|
* Main entry point for the YNAB MCP Server
|
|
85
65
|
*/
|
|
@@ -87,9 +67,6 @@ async function main(): Promise<void> {
|
|
|
87
67
|
try {
|
|
88
68
|
console.error('🚀 Starting YNAB MCP Server...');
|
|
89
69
|
|
|
90
|
-
// Validate startup environment
|
|
91
|
-
validateStartupEnvironment();
|
|
92
|
-
|
|
93
70
|
// Create and start server
|
|
94
71
|
serverInstance = new YNABMCPServer();
|
|
95
72
|
console.error('✅ Server instance created successfully');
|