@dizzlkheinz/ynab-mcpb 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/.env.example +33 -33
  2. package/.github/workflows/ci-tests.yml +45 -45
  3. package/.github/workflows/claude-code-review.yml +57 -57
  4. package/.github/workflows/claude.yml +50 -50
  5. package/.github/workflows/full-integration.yml +22 -22
  6. package/.github/workflows/publish.yml +12 -3
  7. package/.github/workflows/release.yml +2 -2
  8. package/CHANGELOG.md +10 -1
  9. package/CLAUDE.md +16 -12
  10. package/README.md +6 -1
  11. package/dist/bundle/index.cjs +49 -49
  12. package/dist/server/YNABMCPServer.d.ts +125 -54
  13. package/dist/server/YNABMCPServer.js +42 -11
  14. package/dist/server/cacheManager.js +6 -5
  15. package/dist/server/completions.d.ts +25 -0
  16. package/dist/server/completions.js +160 -0
  17. package/dist/server/config.d.ts +2 -2
  18. package/dist/server/errorHandler.js +1 -0
  19. package/dist/server/rateLimiter.js +3 -1
  20. package/dist/server/resources.d.ts +1 -0
  21. package/dist/server/resources.js +33 -16
  22. package/dist/server/securityMiddleware.d.ts +38 -8
  23. package/dist/server/securityMiddleware.js +1 -0
  24. package/dist/server/toolRegistry.d.ts +9 -0
  25. package/dist/server/toolRegistry.js +11 -0
  26. package/dist/tools/adapters.d.ts +3 -1
  27. package/dist/tools/adapters.js +1 -0
  28. package/dist/tools/reconciliation/executor.d.ts +2 -0
  29. package/dist/tools/reconciliation/executor.js +26 -1
  30. package/dist/tools/reconciliation/index.d.ts +3 -2
  31. package/dist/tools/reconciliation/index.js +4 -3
  32. package/dist/tools/schemas/outputs/index.d.ts +2 -2
  33. package/dist/tools/schemas/outputs/index.js +2 -2
  34. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +0 -15
  35. package/dist/tools/schemas/outputs/utilityOutputs.js +0 -9
  36. package/dist/tools/utilityTools.d.ts +0 -7
  37. package/dist/tools/utilityTools.js +1 -50
  38. package/docs/maintainers/npm-publishing.md +27 -0
  39. package/docs/reference/API.md +83 -97
  40. package/docs/technical/reconciliation-system-architecture.md +2251 -2251
  41. package/package.json +6 -6
  42. package/scripts/analyze-bundle.mjs +41 -41
  43. package/scripts/generate-mcpb.ps1 +95 -95
  44. package/scripts/watch-and-restart.ps1 +49 -49
  45. package/src/__tests__/comprehensive.integration.test.ts +4 -32
  46. package/src/__tests__/performance.test.ts +5 -14
  47. package/src/__tests__/setup.ts +45 -14
  48. package/src/__tests__/smoke.e2e.test.ts +70 -0
  49. package/src/__tests__/testUtils.ts +2 -113
  50. package/src/server/YNABMCPServer.ts +64 -10
  51. package/src/server/__tests__/YNABMCPServer.test.ts +0 -1
  52. package/src/server/__tests__/completions.integration.test.ts +117 -0
  53. package/src/server/__tests__/completions.test.ts +319 -0
  54. package/src/server/__tests__/resources.template.test.ts +3 -3
  55. package/src/server/__tests__/resources.test.ts +3 -3
  56. package/src/server/__tests__/toolRegistration.test.ts +3 -3
  57. package/src/server/cacheManager.ts +7 -6
  58. package/src/server/completions.ts +279 -0
  59. package/src/server/errorHandler.ts +1 -0
  60. package/src/server/rateLimiter.ts +4 -1
  61. package/src/server/resources.ts +49 -13
  62. package/src/server/securityMiddleware.ts +1 -0
  63. package/src/server/toolRegistry.ts +42 -0
  64. package/src/tools/__tests__/transactionTools.integration.test.ts +63 -3
  65. package/src/tools/__tests__/utilityTools.integration.test.ts +1 -85
  66. package/src/tools/__tests__/utilityTools.test.ts +1 -123
  67. package/src/tools/adapters.ts +22 -1
  68. package/src/tools/reconciliation/__tests__/executor.progress.test.ts +462 -0
  69. package/src/tools/reconciliation/executor.ts +55 -1
  70. package/src/tools/reconciliation/index.ts +7 -3
  71. package/src/tools/schemas/outputs/index.ts +0 -3
  72. package/src/tools/schemas/outputs/utilityOutputs.ts +2 -43
  73. package/src/tools/toolCategories.ts +0 -1
  74. package/src/tools/utilityTools.ts +5 -76
  75. package/vitest.config.ts +4 -1
  76. package/.chunkhound.json +0 -11
  77. package/.code/agents/0098661e-0fa3-4990-beb9-c0cbf3f123aa/status.txt +0 -1
  78. package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +0 -3
  79. package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +0 -3
  80. package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +0 -1
  81. package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +0 -5
  82. package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +0 -1569
  83. package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +0 -3
  84. package/.code/agents/1324/exec-call_tIpx9uV1TpARbAMZonRQm8AO.txt +0 -757
  85. package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +0 -2832
  86. package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +0 -2709
  87. package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +0 -2832
  88. package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +0 -2832
  89. package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +0 -2709
  90. package/.code/agents/1572/exec-call_GjVFBFOWcY7lE0idc5nWlLNh.txt +0 -781
  91. package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +0 -1
  92. package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +0 -5217
  93. package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +0 -2594
  94. package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +0 -2594
  95. package/.code/agents/1846/exec-call_1YNAVD18RjrMN7JnfkkQhUP3.txt +0 -766
  96. package/.code/agents/1846/exec-call_lh3lDzE4WJAh1lFiomiiZ73D.txt +0 -766
  97. package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +0 -231
  98. package/.code/agents/2038/exec-call_DYwOukaYsL8VCONWmV2rUW5u.txt +0 -766
  99. package/.code/agents/2038/exec-call_c7fOQ7UrpVcTtvdfGBRM146V.txt +0 -652
  100. package/.code/agents/2038/exec-call_ySNyq9Mm55jWE480s54r5QcA.txt +0 -766
  101. package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +0 -2590
  102. package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +0 -5195
  103. package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +0 -286
  104. package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +0 -218
  105. package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +0 -180
  106. package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +0 -3
  107. package/.code/agents/2256/exec-call_AtPcRWPmFPMcmX6qOFm1fCEY.txt +0 -766
  108. package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +0 -1
  109. package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +0 -747
  110. package/.code/agents/2454/exec-call_aFJpupwjfZeOBm7ixI5Vc8z2.txt +0 -766
  111. package/.code/agents/2454/exec-call_wogZ4HfXTodTEXvdgXlVUBpv.txt +0 -766
  112. package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +0 -1
  113. package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +0 -2594
  114. package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +0 -2594
  115. package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +0 -144
  116. package/.code/agents/2e905864-aa07-4314-bcf9-c5b32277e4ac/result.txt +0 -36
  117. package/.code/agents/3073/exec-call_Peeagc9DxGYLgE6pNdMZhqIE.txt +0 -766
  118. package/.code/agents/3073/exec-call_d2YSE3hXF08KRSoUM3qd8Z3x.txt +0 -766
  119. package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +0 -416
  120. package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +0 -2590
  121. package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +0 -2590
  122. package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +0 -2590
  123. package/.code/agents/335aa031-466d-4fb7-925f-3cd864e264d0/result.txt +0 -191
  124. package/.code/agents/3364/exec-call_NbhIrsM5HhyDZDmJZG5CuCYL.txt +0 -766
  125. package/.code/agents/3364/exec-call_cKtJg0NrXiwXEFwlsE3uPZRA.txt +0 -766
  126. package/.code/agents/36d98414-5cde-4d9d-9a67-a240a18c1f07/result.txt +0 -189
  127. package/.code/agents/4604e866-b7b8-44f5-992f-2f683b0a523b/status.txt +0 -1
  128. package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +0 -790
  129. package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +0 -766
  130. package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +0 -790
  131. package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +0 -2594
  132. package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +0 -1000
  133. package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +0 -3489
  134. package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +0 -766
  135. package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +0 -2594
  136. package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +0 -2456
  137. package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +0 -2594
  138. package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +0 -18
  139. package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +0 -48
  140. package/.code/agents/5f8dc01c-47b3-4163-b0b3-aa31be89fcdc/status.txt +0 -1
  141. package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +0 -1
  142. package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +0 -1
  143. package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +0 -1
  144. package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +0 -1
  145. package/.code/agents/7/exec-call_HltHpkDox0Zm1vGEjdksUgpE.txt +0 -1120
  146. package/.code/agents/7/exec-call_LCATrOPPAgbxW9Q1z0XaVi2E.txt +0 -2646
  147. package/.code/agents/7/exec-call_W8DeRfNG9hvbgVFvf0clBf6R.txt +0 -2646
  148. package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +0 -1271
  149. package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +0 -1570
  150. package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +0 -2590
  151. package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +0 -3
  152. package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +0 -3299
  153. package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +0 -3299
  154. package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +0 -1882
  155. package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +0 -2594
  156. package/.code/agents/94a0ddf3-a304-4ec3-913e-3cceef509948/error.txt +0 -1
  157. package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +0 -1
  158. package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +0 -170
  159. package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +0 -1
  160. package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +0 -3
  161. package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +0 -1
  162. package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +0 -1
  163. package/.code/agents/e2c752b7-711d-423a-af57-f53c809deb84/result.txt +0 -160
  164. package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +0 -3
  165. package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +0 -1
  166. package/.code/agents/e6601719-c31f-4a0e-8c71-d70787d0ab71/status.txt +0 -1
  167. package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +0 -1
  168. package/.code/agents/f250b7ed-5bd5-4036-aa8c-ce63caee7d61/result.txt +0 -20
  169. package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +0 -5
  170. package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +0 -1
  171. package/AGENTS.md +0 -1
  172. package/NUL +0 -0
  173. package/package.json.tmp +0 -105
  174. package/src/__tests__/delta.performance.test.ts +0 -80
  175. package/src/__tests__/workflows.e2e.test.ts +0 -1702
  176. package/temp-recon.ts +0 -126
  177. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +0 -23
  178. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +0 -23
  179. package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +0 -44
  180. package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +0 -58
  181. package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +0 -3662
  182. package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +0 -115
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dizzlkheinz/ynab-mcpb",
3
- "version": "0.17.0",
3
+ "version": "0.18.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",
@@ -23,9 +23,9 @@
23
23
  "verify-build": "node scripts/verify-build.js",
24
24
  "lint": "npm run lint:eslint && npm run format:check",
25
25
  "lint:eslint": "eslint src --ext .ts",
26
- "lint:fix": "eslint src --ext .ts --fix && prettier --write .",
27
- "format": "prettier --write .",
28
- "format:check": "prettier --check .",
26
+ "lint:fix": "eslint src --ext .ts --fix && prettier --log-level silent --write .",
27
+ "format": "prettier --log-level silent --write .",
28
+ "format:check": "prettier --log-level silent --check .",
29
29
  "type-check": "tsc --noEmit",
30
30
  "test": "vitest run --project unit && npm run filter-test-results",
31
31
  "test:watch": "vitest",
@@ -66,7 +66,7 @@
66
66
  "author": "",
67
67
  "license": "AGPL-3.0",
68
68
  "dependencies": {
69
- "@modelcontextprotocol/sdk": "^1.24.3",
69
+ "@modelcontextprotocol/sdk": "^1.25.1",
70
70
  "chrono-node": "^2.9.0",
71
71
  "csv-parse": "^6.1.0",
72
72
  "d3-array": "^3.2.4",
@@ -90,7 +90,7 @@
90
90
  "eslint": "^9.35.0",
91
91
  "eslint-config-prettier": "^10.1.8",
92
92
  "prettier": "^3.3.3",
93
- "rimraf": "^6.0.1",
93
+ "rimraf": "^6.1.2",
94
94
  "tsx": "^4.20.6",
95
95
  "typescript": "^5.9.2",
96
96
  "typescript-eslint": "^8.42.0",
@@ -1,41 +1,41 @@
1
- import fs from 'fs';
2
-
3
- let meta;
4
- try {
5
- meta = JSON.parse(fs.readFileSync('meta.json', 'utf-8'));
6
- } catch (error) {
7
- console.error('❌ Error reading meta.json:', error.message);
8
- process.exit(1);
9
- }
10
- if (!meta.inputs || typeof meta.inputs !== 'object') {
11
- console.error('❌ Error: meta.inputs is missing or invalid in meta.json');
12
- process.exit(1);
13
- }
14
-
15
- const inputs = Object.entries(meta.inputs)
16
- .map(([path, data]) => ({
17
- path,
18
- bytes: data.bytes,
19
- }))
20
- .sort((a, b) => b.bytes - a.bytes)
21
- .slice(0, 20);
22
-
23
- const totalBytes = Object.values(meta.inputs).reduce((sum, item) => sum + item.bytes, 0);
24
- console.log(`\n📦 Total input size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
25
- const outputKey = 'dist/bundle/index.cjs';
26
- if (!meta.outputs || !meta.outputs[outputKey]) {
27
- console.error(`❌ Error: meta.outputs['${outputKey}'] is missing in meta.json`);
28
- process.exit(1);
29
- }
30
- console.log(`📦 Output size: ${(meta.outputs[outputKey].bytes / 1024 / 1024).toFixed(2)} MB`);
31
- console.log('\n📊 Top 20 largest inputs in bundle:\n');
32
- inputs.forEach((item) => {
33
- const kb = (item.bytes / 1024).toFixed(1).padStart(8);
34
- console.log(`${kb} KB ${item.path}`);
35
- }); const kb = (item.bytes / 1024).toFixed(1).padStart(8);
36
- console.log(`${kb} KB ${item.path}`);
37
- });
38
-
39
- const totalBytes = Object.values(meta.inputs).reduce((sum, item) => sum + item.bytes, 0);
40
- console.log(`\n📦 Total input size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
41
- console.log(`📦 Output size: ${(meta.outputs['dist/bundle/index.cjs'].bytes / 1024 / 1024).toFixed(2)} MB`);
1
+ import fs from 'fs';
2
+
3
+ let meta;
4
+ try {
5
+ meta = JSON.parse(fs.readFileSync('meta.json', 'utf-8'));
6
+ } catch (error) {
7
+ console.error('❌ Error reading meta.json:', error.message);
8
+ process.exit(1);
9
+ }
10
+ if (!meta.inputs || typeof meta.inputs !== 'object') {
11
+ console.error('❌ Error: meta.inputs is missing or invalid in meta.json');
12
+ process.exit(1);
13
+ }
14
+
15
+ const inputs = Object.entries(meta.inputs)
16
+ .map(([path, data]) => ({
17
+ path,
18
+ bytes: data.bytes,
19
+ }))
20
+ .sort((a, b) => b.bytes - a.bytes)
21
+ .slice(0, 20);
22
+
23
+ const totalBytes = Object.values(meta.inputs).reduce((sum, item) => sum + item.bytes, 0);
24
+ console.log(`\n📦 Total input size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
25
+ const outputKey = 'dist/bundle/index.cjs';
26
+ if (!meta.outputs || !meta.outputs[outputKey]) {
27
+ console.error(`❌ Error: meta.outputs['${outputKey}'] is missing in meta.json`);
28
+ process.exit(1);
29
+ }
30
+ console.log(`📦 Output size: ${(meta.outputs[outputKey].bytes / 1024 / 1024).toFixed(2)} MB`);
31
+ console.log('\n📊 Top 20 largest inputs in bundle:\n');
32
+ inputs.forEach((item) => {
33
+ const kb = (item.bytes / 1024).toFixed(1).padStart(8);
34
+ console.log(`${kb} KB ${item.path}`);
35
+ }); const kb = (item.bytes / 1024).toFixed(1).padStart(8);
36
+ console.log(`${kb} KB ${item.path}`);
37
+ });
38
+
39
+ const totalBytes = Object.values(meta.inputs).reduce((sum, item) => sum + item.bytes, 0);
40
+ console.log(`\n📦 Total input size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
41
+ console.log(`📦 Output size: ${(meta.outputs['dist/bundle/index.cjs'].bytes / 1024 / 1024).toFixed(2)} MB`);
@@ -1,96 +1,96 @@
1
- # PowerShell script to generate a .mcpb file using the official Anthropic MCPB CLI
2
-
3
- param(
4
- [string]$OutputDir = "dist"
5
- )
6
-
7
- Write-Host "Generating YNAB MCP Server .mcpb package using official CLI..." -ForegroundColor Green
8
-
9
- # Configuration
10
- $PackageJson = Get-Content "package.json" | ConvertFrom-Json
11
- $PackageName = $PackageJson.name
12
- $Version = $PackageJson.version
13
-
14
- # Ensure we have a built version
15
- if (-not (Test-Path "dist/index.js")) {
16
- Write-Host "Build not found. Running build first..." -ForegroundColor Red
17
- npm run build
18
- if ($LASTEXITCODE -ne 0) {
19
- Write-Host "Build failed!" -ForegroundColor Red
20
- exit 1
21
- }
22
- }
23
-
24
- # Check if official MCPB CLI is installed
25
- try {
26
- $mcpbVersion = & mcpb --version
27
- Write-Host "Using MCPB CLI version: $mcpbVersion" -ForegroundColor Green
28
- } catch {
29
- Write-Host "MCPB CLI not found. Installing..." -ForegroundColor Yellow
30
- npm install -g @anthropic-ai/mcpb
31
- if ($LASTEXITCODE -ne 0) {
32
- Write-Host "Failed to install MCPB CLI!" -ForegroundColor Red
33
- exit 1
34
- }
35
- }
36
-
37
- # Ensure manifest.json exists and is valid
38
- if (-not (Test-Path "manifest.json")) {
39
- Write-Host "manifest.json not found. Creating one..." -ForegroundColor Yellow
40
- & mcpb init -y
41
- if ($LASTEXITCODE -ne 0) {
42
- Write-Host "Failed to create manifest!" -ForegroundColor Red
43
- exit 1
44
- }
45
- }
46
-
47
- # Sync version from package.json to manifest.json
48
- Write-Host "Syncing version from package.json to manifest.json..." -ForegroundColor Yellow
49
- $ManifestPath = "manifest.json"
50
- $ManifestContent = Get-Content $ManifestPath -Raw
51
- $Manifest = $ManifestContent | ConvertFrom-Json
52
-
53
- if ($Manifest.version -ne $Version) {
54
- Write-Host "Updating manifest version from $($Manifest.version) to $Version" -ForegroundColor Cyan
55
- # Use regex to update version while preserving formatting
56
- $UpdatedContent = $ManifestContent -replace '("version"\s*:\s*)"[^"]*"', "`$1`"$Version`""
57
- $UpdatedContent | Set-Content $ManifestPath -NoNewline
58
- }
59
-
60
- # Validate the manifest
61
- Write-Host "Validating manifest..." -ForegroundColor Yellow
62
- & mcpb validate manifest.json
63
- if ($LASTEXITCODE -ne 0) {
64
- Write-Host "Manifest validation failed!" -ForegroundColor Red
65
- exit 1
66
- }
67
-
68
- # Pack the MCPB using official CLI
69
- Write-Host "Packing MCPB file..." -ForegroundColor Yellow
70
- $DxtFile = "$PackageName-$Version.mcpb"
71
- $OutputPath = Join-Path $OutputDir $DxtFile
72
-
73
- & mcpb pack . $OutputPath
74
- if ($LASTEXITCODE -ne 0) {
75
- Write-Host "MCPB packing failed!" -ForegroundColor Red
76
- exit 1
77
- }
78
-
79
- # Get file size
80
- if (Test-Path $OutputPath) {
81
- $FileSize = (Get-Item $OutputPath).Length
82
- $FileSizeKB = [math]::Round($FileSize / 1KB, 1)
83
- $FileSizeMB = [math]::Round($FileSize / 1MB, 1)
84
-
85
- Write-Host "Created $OutputPath" -ForegroundColor Green
86
- Write-Host "Size: $FileSizeKB KB ($FileSizeMB MB)" -ForegroundColor Cyan
87
- } else {
88
- Write-Host "MCPB file was not created!" -ForegroundColor Red
89
- exit 1
90
- }
91
-
92
- Write-Host ""
93
- Write-Host "Installation Instructions:" -ForegroundColor Yellow
94
- Write-Host "1. Drag and drop the .mcpb file into Claude Desktop" -ForegroundColor White
95
- Write-Host "2. Set YNAB_ACCESS_TOKEN environment variable" -ForegroundColor White
1
+ # PowerShell script to generate a .mcpb file using the official Anthropic MCPB CLI
2
+
3
+ param(
4
+ [string]$OutputDir = "dist"
5
+ )
6
+
7
+ Write-Host "Generating YNAB MCP Server .mcpb package using official CLI..." -ForegroundColor Green
8
+
9
+ # Configuration
10
+ $PackageJson = Get-Content "package.json" | ConvertFrom-Json
11
+ $PackageName = $PackageJson.name
12
+ $Version = $PackageJson.version
13
+
14
+ # Ensure we have a built version
15
+ if (-not (Test-Path "dist/index.js")) {
16
+ Write-Host "Build not found. Running build first..." -ForegroundColor Red
17
+ npm run build
18
+ if ($LASTEXITCODE -ne 0) {
19
+ Write-Host "Build failed!" -ForegroundColor Red
20
+ exit 1
21
+ }
22
+ }
23
+
24
+ # Check if official MCPB CLI is installed
25
+ try {
26
+ $mcpbVersion = & mcpb --version
27
+ Write-Host "Using MCPB CLI version: $mcpbVersion" -ForegroundColor Green
28
+ } catch {
29
+ Write-Host "MCPB CLI not found. Installing..." -ForegroundColor Yellow
30
+ npm install -g @anthropic-ai/mcpb
31
+ if ($LASTEXITCODE -ne 0) {
32
+ Write-Host "Failed to install MCPB CLI!" -ForegroundColor Red
33
+ exit 1
34
+ }
35
+ }
36
+
37
+ # Ensure manifest.json exists and is valid
38
+ if (-not (Test-Path "manifest.json")) {
39
+ Write-Host "manifest.json not found. Creating one..." -ForegroundColor Yellow
40
+ & mcpb init -y
41
+ if ($LASTEXITCODE -ne 0) {
42
+ Write-Host "Failed to create manifest!" -ForegroundColor Red
43
+ exit 1
44
+ }
45
+ }
46
+
47
+ # Sync version from package.json to manifest.json
48
+ Write-Host "Syncing version from package.json to manifest.json..." -ForegroundColor Yellow
49
+ $ManifestPath = "manifest.json"
50
+ $ManifestContent = Get-Content $ManifestPath -Raw
51
+ $Manifest = $ManifestContent | ConvertFrom-Json
52
+
53
+ if ($Manifest.version -ne $Version) {
54
+ Write-Host "Updating manifest version from $($Manifest.version) to $Version" -ForegroundColor Cyan
55
+ # Use regex to update version while preserving formatting
56
+ $UpdatedContent = $ManifestContent -replace '("version"\s*:\s*)"[^"]*"', "`$1`"$Version`""
57
+ $UpdatedContent | Set-Content $ManifestPath -NoNewline
58
+ }
59
+
60
+ # Validate the manifest
61
+ Write-Host "Validating manifest..." -ForegroundColor Yellow
62
+ & mcpb validate manifest.json
63
+ if ($LASTEXITCODE -ne 0) {
64
+ Write-Host "Manifest validation failed!" -ForegroundColor Red
65
+ exit 1
66
+ }
67
+
68
+ # Pack the MCPB using official CLI
69
+ Write-Host "Packing MCPB file..." -ForegroundColor Yellow
70
+ $DxtFile = "$PackageName-$Version.mcpb"
71
+ $OutputPath = Join-Path $OutputDir $DxtFile
72
+
73
+ & mcpb pack . $OutputPath
74
+ if ($LASTEXITCODE -ne 0) {
75
+ Write-Host "MCPB packing failed!" -ForegroundColor Red
76
+ exit 1
77
+ }
78
+
79
+ # Get file size
80
+ if (Test-Path $OutputPath) {
81
+ $FileSize = (Get-Item $OutputPath).Length
82
+ $FileSizeKB = [math]::Round($FileSize / 1KB, 1)
83
+ $FileSizeMB = [math]::Round($FileSize / 1MB, 1)
84
+
85
+ Write-Host "Created $OutputPath" -ForegroundColor Green
86
+ Write-Host "Size: $FileSizeKB KB ($FileSizeMB MB)" -ForegroundColor Cyan
87
+ } else {
88
+ Write-Host "MCPB file was not created!" -ForegroundColor Red
89
+ exit 1
90
+ }
91
+
92
+ Write-Host ""
93
+ Write-Host "Installation Instructions:" -ForegroundColor Yellow
94
+ Write-Host "1. Drag and drop the .mcpb file into Claude Desktop" -ForegroundColor White
95
+ Write-Host "2. Set YNAB_ACCESS_TOKEN environment variable" -ForegroundColor White
96
96
  Write-Host "3. Restart Claude Desktop" -ForegroundColor White
@@ -1,50 +1,50 @@
1
- # PowerShell script to watch for file changes and restart MCP
2
- param(
3
- [string]$Path = ".\src",
4
- [string]$Filter = "*.ts",
5
- [int]$RestartDelay = 3
6
- )
7
-
8
- Write-Host "🔍 Watching for changes in: $Path"
9
- Write-Host "📁 Filter: $Filter"
10
-
11
- $watcher = New-Object System.IO.FileSystemWatcher
12
- $watcher.Path = Resolve-Path $Path
13
- $watcher.Filter = $Filter
14
- $watcher.EnableRaisingEvents = $true
15
- $watcher.IncludeSubdirectories = $true
16
-
17
- $action = {
18
- $path = $Event.SourceEventArgs.FullPath
19
- $changeType = $Event.SourceEventArgs.ChangeType
20
- Write-Host "🔄 File $changeType`: $path"
21
-
22
- # Build the project
23
- Write-Host "🏗️ Building project..."
24
- npm run build
25
-
26
- if ($LASTEXITCODE -eq 0) {
27
- Write-Host "✅ Build successful"
28
- Start-Sleep -Seconds $RestartDelay
29
-
30
- Write-Host "🔄 Reconnecting to YNAB MCP server..."
31
- /mcp reconnect ynab-mcp-server
32
- Start-Sleep -Seconds 1
33
- /mcp reconnect ynab-mcp-server
34
- Write-Host "✅ MCP server reconnected"
35
- } else {
36
- Write-Host "❌ Build failed, skipping MCP restart"
37
- }
38
- }
39
-
40
- Register-ObjectEvent -InputObject $watcher -EventName "Changed" -Action $action
41
- Register-ObjectEvent -InputObject $watcher -EventName "Created" -Action $action
42
-
43
- try {
44
- Write-Host "✅ File watcher started. Press Ctrl+C to stop."
45
- while ($true) { Start-Sleep 1 }
46
- }
47
- finally {
48
- $watcher.Dispose()
49
- Write-Host "👋 File watcher stopped"
1
+ # PowerShell script to watch for file changes and restart MCP
2
+ param(
3
+ [string]$Path = ".\src",
4
+ [string]$Filter = "*.ts",
5
+ [int]$RestartDelay = 3
6
+ )
7
+
8
+ Write-Host "🔍 Watching for changes in: $Path"
9
+ Write-Host "📁 Filter: $Filter"
10
+
11
+ $watcher = New-Object System.IO.FileSystemWatcher
12
+ $watcher.Path = Resolve-Path $Path
13
+ $watcher.Filter = $Filter
14
+ $watcher.EnableRaisingEvents = $true
15
+ $watcher.IncludeSubdirectories = $true
16
+
17
+ $action = {
18
+ $path = $Event.SourceEventArgs.FullPath
19
+ $changeType = $Event.SourceEventArgs.ChangeType
20
+ Write-Host "🔄 File $changeType`: $path"
21
+
22
+ # Build the project
23
+ Write-Host "🏗️ Building project..."
24
+ npm run build
25
+
26
+ if ($LASTEXITCODE -eq 0) {
27
+ Write-Host "✅ Build successful"
28
+ Start-Sleep -Seconds $RestartDelay
29
+
30
+ Write-Host "🔄 Reconnecting to YNAB MCP server..."
31
+ /mcp reconnect ynab-mcp-server
32
+ Start-Sleep -Seconds 1
33
+ /mcp reconnect ynab-mcp-server
34
+ Write-Host "✅ MCP server reconnected"
35
+ } else {
36
+ Write-Host "❌ Build failed, skipping MCP restart"
37
+ }
38
+ }
39
+
40
+ Register-ObjectEvent -InputObject $watcher -EventName "Changed" -Action $action
41
+ Register-ObjectEvent -InputObject $watcher -EventName "Created" -Action $action
42
+
43
+ try {
44
+ Write-Host "✅ File watcher started. Press Ctrl+C to stop."
45
+ while ($true) { Start-Sleep 1 }
46
+ }
47
+ finally {
48
+ $watcher.Dispose()
49
+ Write-Host "👋 File watcher stopped"
50
50
  }
@@ -351,7 +351,7 @@ describe('YNAB MCP Server - Comprehensive Integration Tests', () => {
351
351
  });
352
352
 
353
353
  describe('Complete Transaction Management Integration', () => {
354
- // TODO: Re-enable after DeltaFetcher cache integration alignment (see docs/plans/2025-11-15-cache-test-alignment.md)
354
+ // TODO: Re-enable after DeltaFetcher cache integration alignment.
355
355
  it.skip(
356
356
  'should handle complete transaction workflow',
357
357
  { meta: { tier: 'domain', domain: 'workflows' } },
@@ -748,34 +748,6 @@ describe('YNAB MCP Server - Comprehensive Integration Tests', () => {
748
748
  expect(mockYnabAPI.user.getUser).toHaveBeenCalledTimes(1);
749
749
  },
750
750
  );
751
-
752
- it(
753
- 'should handle amount conversion',
754
- { meta: { tier: 'domain', domain: 'workflows' } },
755
- async () => {
756
- // Test dollar to milliunits conversion
757
- const toMilliunitsResult = await executeToolCall(server, 'ynab:convert_amount', {
758
- amount: 25.75,
759
- to_milliunits: true,
760
- });
761
- validateToolResult(toMilliunitsResult);
762
-
763
- const toMilli = parseToolResult(toMilliunitsResult);
764
- expect(toMilli.data.conversion.converted_amount).toBe(25750);
765
- expect(toMilli.data.conversion.description).toBe('$25.75 = 25750 milliunits');
766
-
767
- // Test milliunits to dollar conversion
768
- const toDollarsResult = await executeToolCall(server, 'ynab:convert_amount', {
769
- amount: 25750,
770
- to_milliunits: false,
771
- });
772
- validateToolResult(toDollarsResult);
773
-
774
- const dollars = parseToolResult(toDollarsResult);
775
- expect(dollars.data.conversion.converted_amount).toBe(25.75);
776
- expect(dollars.data.conversion.description).toBe('25750 milliunits = $25.75');
777
- },
778
- );
779
751
  });
780
752
 
781
753
  describe('Error Handling Integration', () => {
@@ -879,7 +851,7 @@ describe('YNAB MCP Server - Comprehensive Integration Tests', () => {
879
851
  }
880
852
  });
881
853
 
882
- // TODO: Re-enable after DeltaFetcher cache integration alignment (see docs/plans/2025-11-15-cache-test-alignment.md)
854
+ // TODO: Re-enable after DeltaFetcher cache integration alignment.
883
855
  it.skip(
884
856
  'should cache budget list requests and improve performance on subsequent calls',
885
857
  { meta: { tier: 'domain', domain: 'workflows' } },
@@ -1010,7 +982,7 @@ describe('YNAB MCP Server - Comprehensive Integration Tests', () => {
1010
982
  },
1011
983
  );
1012
984
 
1013
- // TODO: Re-enable after DeltaFetcher cache integration alignment (see docs/plans/2025-11-15-cache-test-alignment.md)
985
+ // TODO: Re-enable after DeltaFetcher cache integration alignment.
1014
986
  it.skip(
1015
987
  'should not cache filtered transaction requests',
1016
988
  { meta: { tier: 'domain', domain: 'workflows' } },
@@ -1143,7 +1115,7 @@ describe('YNAB MCP Server - Comprehensive Integration Tests', () => {
1143
1115
  },
1144
1116
  );
1145
1117
 
1146
- // TODO: Re-enable after DeltaFetcher cache integration alignment (see docs/plans/2025-11-15-cache-test-alignment.md)
1118
+ // TODO: Re-enable after DeltaFetcher cache integration alignment.
1147
1119
  it.skip(
1148
1120
  'should respect cache TTL and return fresh data after expiration',
1149
1121
  { meta: { tier: 'domain', domain: 'workflows' } },
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  import { describe, it, expect, beforeEach, vi } from 'vitest';
6
- import { YNABMCPServer } from '../server/YNABMCPServer.js';
7
6
  import { executeToolCall, parseToolResult } from './testUtils.js';
8
7
  import { executeReconciliation, type AccountSnapshot } from '../tools/reconciliation/executor.js';
9
8
  import type { ReconciliationAnalysis } from '../tools/reconciliation/types.js';
@@ -373,7 +372,7 @@ async function measurePerformanceScenario(options: {
373
372
  }
374
373
 
375
374
  describe('YNAB MCP Server - Performance Tests', () => {
376
- let server: YNABMCPServer;
375
+ let server: InstanceType<typeof import('../server/YNABMCPServer.js').YNABMCPServer>;
377
376
  let mockYnabAPI: any;
378
377
 
379
378
  beforeEach(async () => {
@@ -623,12 +622,6 @@ describe('YNAB MCP Server - Performance Tests', () => {
623
622
 
624
623
  // Test multiple validation scenarios
625
624
  const validationTests = [
626
- // Valid parameters
627
- executeToolCall(server, 'ynab:convert_amount', {
628
- amount: 25.5,
629
- to_milliunits: true,
630
- }),
631
-
632
625
  // Invalid parameters (should fail quickly)
633
626
  executeToolCall(server, 'ynab:get_budget', {
634
627
  budget_id: '', // Empty string should fail validation
@@ -648,10 +641,9 @@ describe('YNAB MCP Server - Performance Tests', () => {
648
641
 
649
642
  const totalTime = endTime - startTime;
650
643
 
651
- expect(parsed).toHaveLength(3);
652
- expect(parsed[0]).toBeDefined(); // Valid call should succeed
653
- const firstError = parsed[1].error ?? parsed[1].data?.error;
654
- const secondError = parsed[2].error ?? parsed[2].data?.error;
644
+ expect(parsed).toHaveLength(2);
645
+ const firstError = parsed[0].error ?? parsed[0].data?.error;
646
+ const secondError = parsed[1].error ?? parsed[1].data?.error;
655
647
  expect(firstError?.code).toBe(SecurityErrorCode.VALIDATION_ERROR); // Invalid calls should fail
656
648
  expect(secondError?.code).toBe(SecurityErrorCode.VALIDATION_ERROR);
657
649
  expect(totalTime).toBeLessThan(1000); // Validation should be fast
@@ -715,7 +707,6 @@ describe('YNAB MCP Server - Performance Tests', () => {
715
707
  executeToolCall(server, 'ynab:list_accounts', { budget_id: 'test' }),
716
708
  executeToolCall(server, 'ynab:list_transactions', { budget_id: 'test' }),
717
709
  executeToolCall(server, 'ynab:list_categories', { budget_id: 'test' }),
718
- executeToolCall(server, 'ynab:convert_amount', { amount: i * 10, to_milliunits: true }),
719
710
  );
720
711
  }
721
712
 
@@ -724,7 +715,7 @@ describe('YNAB MCP Server - Performance Tests', () => {
724
715
 
725
716
  const totalTime = endTime - startTime;
726
717
 
727
- expect(results).toHaveLength(100); // 20 iterations × 5 tools
718
+ expect(results).toHaveLength(80); // 20 iterations × 4 tools
728
719
  results.forEach((result) => expect(result).toBeDefined());
729
720
  expect(totalTime).toBeLessThan(10000); // Should complete within 10 seconds
730
721
  });
@@ -12,6 +12,49 @@ const hasAccessToken = !!process.env['YNAB_ACCESS_TOKEN'];
12
12
  if (!process.env['SKIP_E2E_TESTS']) {
13
13
  process.env['SKIP_E2E_TESTS'] = hasAccessToken ? 'false' : 'true';
14
14
  }
15
+ if (!process.env['YNAB_ACCESS_TOKEN']) {
16
+ process.env['YNAB_ACCESS_TOKEN'] = 'test-token-for-mocked-tests';
17
+ }
18
+
19
+ // Set test environment variables immediately
20
+ process.env['NODE_ENV'] = 'test';
21
+ if (!process.env['LOG_LEVEL']) {
22
+ process.env['LOG_LEVEL'] = 'error';
23
+ }
24
+
25
+ // Disable console output for cleaner test output unless VERBOSE_TESTS is set
26
+ if (!process.env['VERBOSE_TESTS']) {
27
+ const originalConsoleError = console.error;
28
+
29
+ console.error = (...args: any[]) => {
30
+ const firstArg = args[0];
31
+ const isString = typeof firstArg === 'string';
32
+ // Only show errors that are part of test assertions, actual errors, or explicitly marked [ERROR]
33
+ if (
34
+ (isString &&
35
+ (firstArg.includes('❌') || firstArg.includes('Test') || firstArg.includes('[ERROR]'))) ||
36
+ firstArg instanceof Error
37
+ ) {
38
+ originalConsoleError(...args);
39
+ }
40
+ };
41
+
42
+ console.warn = () => {
43
+ // Suppress warnings by default
44
+ };
45
+
46
+ console.log = () => {
47
+ // Suppress logs by default
48
+ };
49
+
50
+ console.info = () => {
51
+ // Suppress info logs by default
52
+ };
53
+
54
+ console.debug = () => {
55
+ // Suppress debug logs by default
56
+ };
57
+ }
15
58
 
16
59
  type TierFilter = 'core' | 'domain' | 'full';
17
60
  interface TestMeta {
@@ -47,26 +90,14 @@ const shouldRunDomain = (domain?: string): boolean => {
47
90
  * Global test setup
48
91
  */
49
92
  beforeAll(async () => {
50
- // Set test environment variables
51
- process.env['NODE_ENV'] = 'test';
52
-
53
93
  // Set default test token if not provided
54
94
  if (!process.env['YNAB_ACCESS_TOKEN']) {
55
95
  process.env['YNAB_ACCESS_TOKEN'] = 'test-token-for-mocked-tests';
56
96
  }
57
97
 
58
- // Disable console.error for cleaner test output (except for specific tests)
59
- if (!process.env['VERBOSE_TESTS']) {
60
- const originalConsoleError = console.error;
61
- console.error = (...args: any[]) => {
62
- // Only show errors that are part of test assertions
63
- if (args[0]?.includes?.('❌') || args[0]?.includes?.('Test')) {
64
- originalConsoleError(...args);
65
- }
66
- };
98
+ if (process.env['VERBOSE_TESTS']) {
99
+ console.warn('🧪 Test environment initialized');
67
100
  }
68
-
69
- console.warn('🧪 Test environment initialized');
70
101
  });
71
102
 
72
103
  /**