@dizzlkheinz/ynab-mcpb 0.17.0 → 0.17.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 (142) 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 +11 -2
  7. package/CLAUDE.md +7 -6
  8. package/dist/bundle/index.cjs +52 -52
  9. package/dist/server/YNABMCPServer.d.ts +120 -54
  10. package/dist/server/securityMiddleware.d.ts +37 -8
  11. package/dist/tools/schemas/outputs/index.d.ts +2 -2
  12. package/dist/tools/schemas/outputs/index.js +2 -2
  13. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +0 -15
  14. package/dist/tools/schemas/outputs/utilityOutputs.js +0 -9
  15. package/dist/tools/utilityTools.d.ts +0 -7
  16. package/dist/tools/utilityTools.js +1 -50
  17. package/docs/maintainers/npm-publishing.md +27 -0
  18. package/docs/reference/API.md +15 -70
  19. package/docs/technical/reconciliation-system-architecture.md +2251 -2251
  20. package/package.json +5 -5
  21. package/scripts/analyze-bundle.mjs +41 -41
  22. package/scripts/generate-mcpb.ps1 +95 -95
  23. package/scripts/watch-and-restart.ps1 +49 -49
  24. package/src/__tests__/comprehensive.integration.test.ts +0 -28
  25. package/src/__tests__/performance.test.ts +4 -12
  26. package/src/__tests__/setup.ts +45 -14
  27. package/src/__tests__/workflows.e2e.test.ts +0 -44
  28. package/src/server/__tests__/YNABMCPServer.test.ts +0 -1
  29. package/src/server/__tests__/toolRegistration.test.ts +2 -2
  30. package/src/tools/__tests__/transactionTools.integration.test.ts +63 -3
  31. package/src/tools/__tests__/utilityTools.integration.test.ts +1 -85
  32. package/src/tools/__tests__/utilityTools.test.ts +1 -123
  33. package/src/tools/schemas/outputs/index.ts +0 -3
  34. package/src/tools/schemas/outputs/utilityOutputs.ts +2 -43
  35. package/src/tools/toolCategories.ts +0 -1
  36. package/src/tools/utilityTools.ts +5 -76
  37. package/vitest.config.ts +2 -1
  38. package/.chunkhound.json +0 -11
  39. package/.code/agents/0098661e-0fa3-4990-beb9-c0cbf3f123aa/status.txt +0 -1
  40. package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +0 -3
  41. package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +0 -3
  42. package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +0 -1
  43. package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +0 -5
  44. package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +0 -1569
  45. package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +0 -3
  46. package/.code/agents/1324/exec-call_tIpx9uV1TpARbAMZonRQm8AO.txt +0 -757
  47. package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +0 -2832
  48. package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +0 -2709
  49. package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +0 -2832
  50. package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +0 -2832
  51. package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +0 -2709
  52. package/.code/agents/1572/exec-call_GjVFBFOWcY7lE0idc5nWlLNh.txt +0 -781
  53. package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +0 -1
  54. package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +0 -5217
  55. package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +0 -2594
  56. package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +0 -2594
  57. package/.code/agents/1846/exec-call_1YNAVD18RjrMN7JnfkkQhUP3.txt +0 -766
  58. package/.code/agents/1846/exec-call_lh3lDzE4WJAh1lFiomiiZ73D.txt +0 -766
  59. package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +0 -231
  60. package/.code/agents/2038/exec-call_DYwOukaYsL8VCONWmV2rUW5u.txt +0 -766
  61. package/.code/agents/2038/exec-call_c7fOQ7UrpVcTtvdfGBRM146V.txt +0 -652
  62. package/.code/agents/2038/exec-call_ySNyq9Mm55jWE480s54r5QcA.txt +0 -766
  63. package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +0 -2590
  64. package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +0 -5195
  65. package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +0 -286
  66. package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +0 -218
  67. package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +0 -180
  68. package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +0 -3
  69. package/.code/agents/2256/exec-call_AtPcRWPmFPMcmX6qOFm1fCEY.txt +0 -766
  70. package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +0 -1
  71. package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +0 -747
  72. package/.code/agents/2454/exec-call_aFJpupwjfZeOBm7ixI5Vc8z2.txt +0 -766
  73. package/.code/agents/2454/exec-call_wogZ4HfXTodTEXvdgXlVUBpv.txt +0 -766
  74. package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +0 -1
  75. package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +0 -2594
  76. package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +0 -2594
  77. package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +0 -144
  78. package/.code/agents/2e905864-aa07-4314-bcf9-c5b32277e4ac/result.txt +0 -36
  79. package/.code/agents/3073/exec-call_Peeagc9DxGYLgE6pNdMZhqIE.txt +0 -766
  80. package/.code/agents/3073/exec-call_d2YSE3hXF08KRSoUM3qd8Z3x.txt +0 -766
  81. package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +0 -416
  82. package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +0 -2590
  83. package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +0 -2590
  84. package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +0 -2590
  85. package/.code/agents/335aa031-466d-4fb7-925f-3cd864e264d0/result.txt +0 -191
  86. package/.code/agents/3364/exec-call_NbhIrsM5HhyDZDmJZG5CuCYL.txt +0 -766
  87. package/.code/agents/3364/exec-call_cKtJg0NrXiwXEFwlsE3uPZRA.txt +0 -766
  88. package/.code/agents/36d98414-5cde-4d9d-9a67-a240a18c1f07/result.txt +0 -189
  89. package/.code/agents/4604e866-b7b8-44f5-992f-2f683b0a523b/status.txt +0 -1
  90. package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +0 -790
  91. package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +0 -766
  92. package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +0 -790
  93. package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +0 -2594
  94. package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +0 -1000
  95. package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +0 -3489
  96. package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +0 -766
  97. package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +0 -2594
  98. package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +0 -2456
  99. package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +0 -2594
  100. package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +0 -18
  101. package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +0 -48
  102. package/.code/agents/5f8dc01c-47b3-4163-b0b3-aa31be89fcdc/status.txt +0 -1
  103. package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +0 -1
  104. package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +0 -1
  105. package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +0 -1
  106. package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +0 -1
  107. package/.code/agents/7/exec-call_HltHpkDox0Zm1vGEjdksUgpE.txt +0 -1120
  108. package/.code/agents/7/exec-call_LCATrOPPAgbxW9Q1z0XaVi2E.txt +0 -2646
  109. package/.code/agents/7/exec-call_W8DeRfNG9hvbgVFvf0clBf6R.txt +0 -2646
  110. package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +0 -1271
  111. package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +0 -1570
  112. package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +0 -2590
  113. package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +0 -3
  114. package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +0 -3299
  115. package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +0 -3299
  116. package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +0 -1882
  117. package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +0 -2594
  118. package/.code/agents/94a0ddf3-a304-4ec3-913e-3cceef509948/error.txt +0 -1
  119. package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +0 -1
  120. package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +0 -170
  121. package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +0 -1
  122. package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +0 -3
  123. package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +0 -1
  124. package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +0 -1
  125. package/.code/agents/e2c752b7-711d-423a-af57-f53c809deb84/result.txt +0 -160
  126. package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +0 -3
  127. package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +0 -1
  128. package/.code/agents/e6601719-c31f-4a0e-8c71-d70787d0ab71/status.txt +0 -1
  129. package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +0 -1
  130. package/.code/agents/f250b7ed-5bd5-4036-aa8c-ce63caee7d61/result.txt +0 -20
  131. package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +0 -5
  132. package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +0 -1
  133. package/AGENTS.md +0 -1
  134. package/NUL +0 -0
  135. package/package.json.tmp +0 -105
  136. package/temp-recon.ts +0 -126
  137. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +0 -23
  138. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +0 -23
  139. package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +0 -44
  140. package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +0 -58
  141. package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +0 -3662
  142. package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +0 -115
@@ -1 +0,0 @@
1
- Failed to spawn sandboxed agent: batch file arguments are invalid
@@ -1 +0,0 @@
1
- Failed to execute code-gpt-5.1-codex-mini: program not found
@@ -1,170 +0,0 @@
1
- I'm using the writing-plans skill to create the implementation plan.
2
-
3
- # MCP Resilience Implementation Plan
4
-
5
- > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
6
-
7
- **Goal:** Harden the MCP server by validating every tool’s metadata schema and making `clear_cache` fully reset delta/caching state so knowledge doesn’t drift while keeping diagnostics accurate.
8
-
9
- **Architecture:** Validate metadata against generated `zod` schemas inside `ToolRegistry`, expose mismatches immediately, and delegate cache clearing to `DeltaCache.forceFullRefresh()` so both the LRU cache and `ServerKnowledgeStore` reset before diagnostics observe the new state.
10
-
11
- **Tech Stack:** TypeScript 5/Node 18+, `zod`/`zod-validation-error`, MCP SDK server wiring in `src/server`, Vitest for fast unit/integration suites, and Markdown docs in `docs/reference/TOOLS.md`.
12
-
13
- ### Task 1: Tighten tool metadata schema validation
14
-
15
- **Files:**
16
- - Modify: `src/server/toolRegistry.ts`
17
- - Modify: `src/server/__tests__/toolRegistry.test.ts`
18
-
19
- **Step 1: Write the failing test**
20
-
21
- ```ts
22
- it('rejects registration when metadata inputJsonSchema diverges from the handler schema', () => {
23
- const registry = new ToolRegistry({
24
- withSecurityWrapper: vi.fn().mockReturnValue(() => async (handler) => handler({})),
25
- errorHandler: mockedErrorHandler,
26
- responseFormatter: responseFormatter,
27
- });
28
- const mismatchedMetadata = { type: 'object', properties: { bogus: { type: 'string' } } };
29
- expect(() =>
30
- registry.register({
31
- name: 'budget-sync',
32
- description: 'Sync budget data',
33
- inputSchema: z.object({ budget_id: z.string().min(1) }).strict(),
34
- handler: async () => ({ content: [{ type: 'text', text: responseFormatter.format({}) }] }),
35
- metadata: { inputJsonSchema: mismatchedMetadata },
36
- }),
37
- ).toThrow('metadata input schema must match generated schema for tool "budget-sync"');
38
- });
39
- ```
40
-
41
- **Step 2: Run the test to verify it fails**
42
-
43
- Run: `npm run test:unit -- --runInBand src/server/__tests__/toolRegistry.test.ts --testNamePattern "metadata input schema"`
44
- Expected: FAIL because the registry currently accepts mismatched metadata.
45
-
46
- **Step 3: Implement the minimal enforcement**
47
-
48
- ```ts
49
- private ensureMetadataSchemaMatches(definition: ToolDefinition): void {
50
- const generatedSchema = this.generateJsonSchema(definition.inputSchema, 'input');
51
- if (definition.metadata?.inputJsonSchema) {
52
- if (JSON.stringify(generatedSchema) !== JSON.stringify(definition.metadata.inputJsonSchema)) {
53
- throw new Error(
54
- `metadata input schema must match generated schema for tool "${definition.name}"`,
55
- );
56
- }
57
- } else {
58
- definition.metadata = {
59
- ...definition.metadata,
60
- inputJsonSchema: generatedSchema,
61
- };
62
- }
63
- }
64
- ```
65
-
66
- Call `this.ensureMetadataSchemaMatches(definition);` near the top of `register` before storing the tool so `metadata.inputJsonSchema` is always populated (and validated) for `listTools()`. This also means `ToolRegistry.listTools()` no longer needs to fall back to `generateJsonSchema` when metadata is present.
67
-
68
- **Step 4: Run the test to confirm it now passes**
69
-
70
- Run: `npm run test:unit -- --runInBand src/server/__tests__/toolRegistry.test.ts --testNamePattern "metadata input schema"`
71
- Expected: PASS.
72
-
73
- **Step 5: Commit**
74
-
75
- ```bash
76
- git add src/server/toolRegistry.ts src/server/__tests__/toolRegistry.test.ts
77
- git commit -m "fix: validate tool metadata schemas at registration"
78
- ```
79
-
80
- ### Task 2: Reset delta knowledge when clearing the cache
81
-
82
- **Files:**
83
- - Modify: `src/server/YNABMCPServer.ts`
84
- - Modify: `src/server/__tests__/YNABMCPServer.integration.test.ts`
85
- - Modify: `docs/reference/TOOLS.md`
86
-
87
- **Step 1: Write the failing integration test**
88
-
89
- ```ts
90
- it(
91
- 'resets delta knowledge entries when clear_cache runs',
92
- { meta: { tier: 'domain', domain: 'server' } },
93
- async (ctx) => {
94
- await skipOnRateLimit(async () => {
95
- const token = accessToken();
96
- await registry.executeTool({
97
- name: 'list_accounts',
98
- accessToken: token,
99
- arguments: { budget_id: budgetsResult?.budgets?.[0]?.id },
100
- });
101
-
102
- const before = await registry.executeTool({
103
- name: 'diagnostic_info',
104
- accessToken: token,
105
- arguments: { include_delta: true },
106
- });
107
- const beforeData = JSON.parse(before.content?.[0]?.text ?? '{}');
108
- expect(beforeData.delta?.knowledge_entries).toBeGreaterThan(0);
109
-
110
- await registry.executeTool({ name: 'clear_cache', accessToken: token, arguments: {} });
111
-
112
- const after = await registry.executeTool({
113
- name: 'diagnostic_info',
114
- accessToken: token,
115
- arguments: { include_delta: true },
116
- });
117
- const afterData = JSON.parse(after.content?.[0]?.text ?? '{}');
118
- expect(afterData.delta?.knowledge_entries).toBe(0);
119
- }, ctx);
120
- },
121
- );
122
- ```
123
-
124
- **Step 2: Run the integration test to verify it fails**
125
-
126
- Run: `npm run test:integration:domain -- --testNamePattern "clear cache runs"`
127
- Expected: FAIL because `clear_cache` currently only clears `cacheManager`, leaving `ServerKnowledgeStore` populated.
128
-
129
- **Step 3: Implement the minimal fix**
130
-
131
- - Update the `clear_cache` handler in `setupToolRegistry` to call `this.deltaCache.forceFullRefresh()` instead of directly `cacheManager.clear()`, ensuring both the cache and knowledge store reset:
132
-
133
- ```ts
134
- handler: async () => {
135
- this.deltaCache.forceFullRefresh();
136
- return {
137
- content: [{ type: 'text', text: responseFormatter.format({ success: true }) }],
138
- };
139
- },
140
- ```
141
-
142
- - Update `docs/reference/TOOLS.md` entry to explain the stronger guarantee:
143
-
144
- ```md
145
- | `clear_cache` | Clear the cache and reset delta knowledge so clients can start fresh | None |
146
- ```
147
-
148
- **Step 4: Run the integration test to confirm it passes**
149
-
150
- Run: `npm run test:integration:domain -- --testNamePattern "clear cache runs"`
151
- Expected: PASS.
152
-
153
- **Step 5: Commit**
154
-
155
- ```bash
156
- git add src/server/YNABMCPServer.ts src/server/__tests__/YNABMCPServer.integration.test.ts docs/reference/TOOLS.md
157
- git commit -m "feat: reset delta knowledge when cache is cleared"
158
- ```
159
-
160
- ---
161
-
162
- Because the sandbox is read-only, I couldn’t save this plan to `docs/plans/2025-11-21-mcp-resilience.md`; please copy the above Markdown there in a workspace-write session before executing the tasks.
163
-
164
- - **Plan scope:** Adds schema validation in `src/server/toolRegistry.ts` plus a new regression test in `src/server/__tests__/toolRegistry.test.ts`, and modifies `clear_cache` in `src/server/YNABMCPServer.ts` with an integration test addition and doc update in `docs/reference/TOOLS.md`.
165
- - **Testing:** Run the targeted unit test via `npm run test:unit -- --runInBand src/server/__tests__/toolRegistry.test.ts --testNamePattern "metadata input schema"` and the integration slice via `npm run test:integration:domain -- --testNamePattern "clear cache runs"` after making the code changes.
166
-
167
- Next steps:
168
- 1. Execute the described plan in a writable worktree (create `docs/plans/2025-11-21-mcp-resilience.md` with the above content first) and implement Task 1’s schema validation.
169
- 2. Run the targeted unit test from Task 1 to confirm failure, then fix the code, rerun to confirm success.
170
- 3. Implement Task 2, add the integration test, update `clear_cache`, refresh `docs/reference/TOOLS.md`, and run `npm run test:integration:domain -- --testNamePattern "clear cache runs"`.
@@ -1 +0,0 @@
1
- Required agent 'gemini' is not installed or not in PATH
@@ -1,3 +0,0 @@
1
- Command failed: Error loading configuration: config profile `Smoke test: from the current repo, run `pwd` and `git status -sb`, then list top-level files/directories and print a short summary. No file writes or edits.
2
-
3
- [Running in read-only mode - no modifications allowed]` not found
@@ -1 +0,0 @@
1
- Agent cancelled
@@ -1 +0,0 @@
1
- Agent cancelled
@@ -1,160 +0,0 @@
1
- Here’s a concise blueprint for Phase 1 that matches the factory pattern doc and repo conventions.
2
-
3
- **Module layout (production-ready, minimal)**
4
- - `src/types/toolRegistration.ts`: canonical types/interfaces for tool definitions and adapters.
5
- - `src/tools/adapters/index.ts`: adapter factory functions for the five categories (adapt, adaptNoInput, adaptWithDelta, adaptWrite, inline); export typed helpers.
6
- - `src/tools/schemas/common.ts`: shared zod schemas for inputs/outputs/memo/delta, reusable across tools.
7
- - `src/tools/index.ts`: re-export adapters and shared schemas (and future tool registrations).
8
- - Tests: `src/__tests__/tools/adapters.test.ts` (unit, Vitest).
9
-
10
- **Core types (toolRegistration.ts)**
11
- ```ts
12
- import { z } from 'zod';
13
-
14
- export type ToolKind =
15
- | 'adapt' // input schema -> handler(args) -> output
16
- | 'adaptNoInput' // no input -> handler() -> output
17
- | 'adaptWithDelta' // input + delta/metadata -> output + delta?
18
- | 'adaptWrite' // input triggers mutation; output includes status
19
- | 'inline'; // small inline tools (no registration factory)
20
-
21
- export interface ToolContext {
22
- logger?: { debug?: (...args: any[]) => void; info?: (...args: any[]) => void; error?: (...args: any[]) => void };
23
- now?: () => Date;
24
- // extend later with cache, env, config
25
- }
26
-
27
- export type ToolHandler<Input, Output> = (args: {
28
- input: Input;
29
- context?: ToolContext;
30
- }) => Promise<Output> | Output;
31
-
32
- export type ToolHandlerNoInput<Output> = (args?: { context?: ToolContext }) => Promise<Output> | Output;
33
-
34
- export interface ToolRegistration<Input, Output> {
35
- id: string; // kebab-case tool id
36
- description: string;
37
- inputSchema: z.ZodType<Input>;
38
- outputSchema?: z.ZodType<Output>;
39
- handler: ToolHandler<Input, Output>;
40
- kind?: ToolKind; // optional hint for tooling/metrics
41
- }
42
-
43
- export interface ToolRegistrationNoInput<Output>
44
- extends Omit<ToolRegistration<undefined, Output>, 'inputSchema' | 'handler'> {
45
- handler: ToolHandlerNoInput<Output>;
46
- }
47
- ```
48
-
49
- **Shared schemas (schemas/common.ts)**
50
- ```ts
51
- import { z } from 'zod';
52
-
53
- export const memoSchema = z.string().max(500).optional();
54
- export const baseInputSchema = z.object({ memo: memoSchema }).strip();
55
- export const deltaSchema = z.object({
56
- deltaToken: z.string(),
57
- revision: z.number().int().nonnegative(),
58
- });
59
- export const statusSchema = z.object({
60
- ok: z.boolean(),
61
- message: z.string().optional(),
62
- });
63
- ```
64
-
65
- **Adapters (tools/adapters/index.ts)**
66
- ```ts
67
- import { z } from 'zod';
68
- import {
69
- ToolRegistration,
70
- ToolRegistrationNoInput,
71
- ToolHandler,
72
- ToolHandlerNoInput,
73
- } from '../../types/toolRegistration';
74
-
75
- type AdapterResult<Input, Output> = ToolRegistration<Input, Output>;
76
-
77
- export function adapt<Input, Output>(
78
- config: Omit<ToolRegistration<Input, Output>, 'kind'>,
79
- ): AdapterResult<Input, Output> {
80
- return { ...config, kind: 'adapt' };
81
- }
82
-
83
- export function adaptNoInput<Output>(
84
- config: Omit<ToolRegistrationNoInput<Output>, 'kind' | 'inputSchema'>,
85
- ): AdapterResult<undefined, Output> {
86
- return {
87
- ...config,
88
- inputSchema: z.void(),
89
- kind: 'adaptNoInput',
90
- };
91
- }
92
-
93
- export function adaptWithDelta<Input, Output>(
94
- config: Omit<ToolRegistration<Input, Output>, 'kind'>,
95
- ): AdapterResult<Input, Output> {
96
- return { ...config, kind: 'adaptWithDelta' };
97
- }
98
-
99
- export function adaptWrite<Input, Output>(
100
- config: Omit<ToolRegistration<Input, Output>, 'kind'>,
101
- ): AdapterResult<Input, Output> {
102
- return { ...config, kind: 'adaptWrite' };
103
- }
104
-
105
- // inline: kept simple; bypasses factory if needed, but helper for consistency
106
- export function inline<Input, Output>(
107
- config: ToolRegistration<Input, Output>,
108
- ): AdapterResult<Input, Output> {
109
- return { ...config, kind: 'inline' };
110
- }
111
-
112
- export type {
113
- ToolRegistration,
114
- ToolRegistrationNoInput,
115
- ToolHandler,
116
- ToolHandlerNoInput,
117
- };
118
- ```
119
-
120
- **Exports (src/tools/index.ts)**
121
- ```ts
122
- export * from './adapters';
123
- export * as commonSchemas from './schemas/common';
124
- ```
125
-
126
- **Unit test plan (src/__tests__/tools/adapters.test.ts)**
127
- - `adapt`: returns config with `kind: 'adapt'`, preserves handler and schema identity, and is type-safe for input/output.
128
- - `adaptNoInput`: sets `inputSchema` to `z.void()`, `kind: 'adaptNoInput'`, handler invoked without input, rejects passing unexpected input at runtime (zod parse).
129
- - `adaptWithDelta`: attaches `kind: 'adaptWithDelta'`, works with a schema that includes delta fields (e.g., merged input + `deltaSchema`).
130
- - `adaptWrite`: sets `kind: 'adaptWrite'`, supports output schema with status; ensure no mutation to original config.
131
- - `inline`: passes through config and sets `kind: 'inline'`.
132
- - Shared behaviors: returned objects are new instances (no mutation), required fields (`id`, `description`, `handler`) are preserved.
133
- - Validation snapshots: optional quick zod parse test per adapter (when schema is provided) to prove wiring.
134
-
135
- **Test scaffolding snippet**
136
- ```ts
137
- import { describe, it, expect } from 'vitest';
138
- import { z } from 'zod';
139
- import {
140
- adapt,
141
- adaptNoInput,
142
- adaptWithDelta,
143
- adaptWrite,
144
- inline,
145
- } from '../../tools/adapters';
146
- import { deltaSchema, statusSchema } from '../../tools/schemas/common';
147
-
148
- describe('tool adapters', () => {
149
- it('adapt sets kind and preserves config', async () => { /* ... */ });
150
- it('adaptNoInput sets z.void and kind', async () => { /* ... */ });
151
- it('adaptWithDelta supports delta schema', async () => { /* ... */ });
152
- it('adaptWrite marks kind adaptWrite', async () => { /* ... */ });
153
- it('inline marks kind inline', async () => { /* ... */ });
154
- });
155
- ```
156
-
157
- **Notes**
158
- - Keep adapters thin; no side effects. `kind` supports metrics/routing; callers can branch on it.
159
- - Shared schemas stay minimal; expand as new tools land.
160
- - Maintain kebab-case `id` per repo guidance; single export surface via `src/tools/index.ts`.
@@ -1,3 +0,0 @@
1
- Command failed: Error loading configuration: config profile `You are helping craft an implementation plan for the YNAB MCP Server repo (Model Context Protocol server for YNAB). Repo root at C:/Users/ksutk/projects/ynab-mcpb. Key info: TypeScript/Node 18+, ts source in src/, server wiring in src/server, tools in src/tools, utils in src/utils, entry at src/index.ts. Build: npm run build (lint+tsc+bundle) outputs dist/. Tests use Vitest with projects (unit, integration, e2e). package.json scripts include build:prod, lint:fix, test:unit, test:integration, test:e2e, package:mcpb. Docs in docs/. Current version 0.13.4. Goal: propose a concrete step-by-step implementation plan for the next iteration to improve the project. Include where to change code (paths), suggested tests to add/run, and rationale. Assume current tests passing (test-results show success). Focus on actionable tasks (2-5 min steps) using TDD mindset. Deliver plan structure we can hand to engineer. Do not assume write access.
2
-
3
- [Running in read-only mode - no modifications allowed]` not found
@@ -1 +0,0 @@
1
- Agent cancelled
@@ -1 +0,0 @@
1
- Agent cancelled
@@ -1 +0,0 @@
1
- Agent cancelled
@@ -1,20 +0,0 @@
1
- **MCP Tool Catalogue**
2
- - **Budgets** (`src/server/YNABMCPServer.ts:467-540`): `list_budgets` (delta adapter; input inline `emptyObjectSchema`; output `ListBudgetsOutputSchema`), `get_budget` (read adapter; input `GetBudgetSchema` from `src/tools/budgetTools.ts`; output `GetBudgetOutputSchema`), `set_default_budget` (inline server write; input inline `setDefaultBudgetSchema`; output `SetDefaultBudgetOutputSchema`), `get_default_budget` (inline server read; input inline `emptyObjectSchema`; output `GetDefaultBudgetOutputSchema`).
3
- - **Accounts** (`src/server/YNABMCPServer.ts:573-615`): `list_accounts` (delta; input `ListAccountsSchema`; output `ListAccountsOutputSchema`), `get_account` (read; input `GetAccountSchema`; output `GetAccountOutputSchema`), `create_account` (write; input `CreateAccountSchema`; output inline `LooseObjectSchema`).
4
- - **Transactions – read/delta** (`src/server/YNABMCPServer.ts:618-692`): `list_transactions` (delta; input `ListTransactionsSchema`; output `LooseObjectSchema`), `export_transactions` (read; input `ExportTransactionsSchema`; output `ExportTransactionsOutputSchema`), `compare_transactions` (read; input `CompareTransactionsSchema`; output `CompareTransactionsOutputSchema`), `reconcile_account` (delta; input `ReconcileAccountSchema`; output `LooseObjectSchema`), `get_transaction` (read; input `GetTransactionSchema`; output `GetTransactionOutputSchema`).
5
- - **Transactions – write** (`src/server/YNABMCPServer.ts:694-785`): `create_transaction`, `create_transactions`, `create_receipt_split_transaction` (all write adapters; inputs `CreateTransactionSchema`/`CreateTransactionsSchema`/`CreateReceiptSplitTransactionSchema`; outputs `LooseObjectSchema`), `update_transaction`, `update_transactions` (write; inputs `UpdateTransactionSchema`/`UpdateTransactionsSchema`; outputs `LooseObjectSchema`), `delete_transaction` (write; input `DeleteTransactionSchema`; output `LooseObjectSchema`).
6
- - **Categories** (`src/server/YNABMCPServer.ts:787-830`): `list_categories` (delta; input `ListCategoriesSchema`; output `ListCategoriesOutputSchema`), `get_category` (read; input `GetCategorySchema`; output `GetCategoryOutputSchema`), `update_category` (write; input `UpdateCategorySchema`; output `LooseObjectSchema`).
7
- - **Payees** (`src/server/YNABMCPServer.ts:832-860`): `list_payees` (delta; input `ListPayeesSchema`; output `ListPayeesOutputSchema`), `get_payee` (read; input `GetPayeeSchema`; output `GetPayeeOutputSchema`).
8
- - **Months** (`src/server/YNABMCPServer.ts:862-889`): `get_month` (read; input `GetMonthSchema`; output `GetMonthOutputSchema`), `list_months` (delta; input `ListMonthsSchema`; output `ListMonthsOutputSchema`).
9
- - **Utilities/Local** (`src/server/YNABMCPServer.ts:892-998`): `get_user` (no-input adapter; input inline `emptyObjectSchema`; output `GetUserOutputSchema`), `convert_amount` (inline local handler; input `ConvertAmountSchema` from `src/tools/utilityTools.ts`; output `ConvertAmountOutputSchema`), `diagnostic_info` (inline server; input inline `diagnosticInfoSchema`; output `DiagnosticInfoOutputSchema`), `clear_cache` (inline server; input inline `emptyObjectSchema`; output `ClearCacheOutputSchema`), `set_output_format` (inline server; input inline `setOutputFormatSchema`; output `SetOutputFormatOutputSchema`).
10
-
11
- **Inline Schema Observations**
12
- - Inline definitions in `src/server/YNABMCPServer.ts:446-465` (`emptyObjectSchema`, `LooseObjectSchema`, `setDefaultBudgetSchema`, `diagnosticInfoSchema`, `setOutputFormatSchema`) are reused across tools and could move to a shared schema module (e.g., `src/tools/schemas/common.ts`) to reduce duplication and keep registrations declarative.
13
- - Most tools already pull input/output schemas from feature modules (`src/tools/*` and `src/tools/schemas/outputs/index.ts`); only the inline schemas above break that pattern.
14
-
15
- **Server-Owned Inline Tools to Keep Inline**
16
- - `set_default_budget`, `get_default_budget`, `diagnostic_info`, `clear_cache`, and `set_output_format` rely on server state (default budget cache warming, diagnostic manager, cache manager, response formatter) and likely stay in `YNABMCPServer` even if their schemas are centralized.
17
-
18
- **Notes for Migration**
19
- - Budget-aware tools share `defaultArgumentResolver` to inject `budget_id`; keep this resolver available wherever registrations move.
20
- - `LooseObjectSchema` is the permissive output for many write/delta tools—consider replacing with explicit output schemas in `src/tools/schemas/outputs` if stability is desired.
@@ -1,5 +0,0 @@
1
- Command failed: Error loading configuration: config profile `Context: cwd: C:\Users\ksutk\projects\ynab-mcpb. This is only to verify whether a gemini-3-pro backed agent can successfully start and complete.
2
-
3
- Agent: Minimal Gemini smoke test: from the ynab-mcpb repo, run `git status -sb` and list top-level directories, then print a short summary and exit. No file writes required.
4
-
5
- [Running in read-only mode - no modifications allowed]` not found
@@ -1 +0,0 @@
1
- Agent cancelled
package/AGENTS.md DELETED
@@ -1 +0,0 @@
1
- CLAUDE.md
package/NUL DELETED
File without changes
package/package.json.tmp DELETED
@@ -1,105 +0,0 @@
1
- {
2
- "name": "@dizzlkheinz/ynab-mcp-server",
3
- "version": "0.12.0",
4
- "description": "Model Context Protocol server for YNAB (You Need A Budget) integration",
5
- "main": "dist/index.js",
6
- "type": "module",
7
- "bin": {
8
- "ynab-mcp-server": "bin/ynab-mcp-server.cjs"
9
- },
10
- "scripts": {
11
- "prebuild": "npm run clean",
12
- "build": "npm run build:prod",
13
- "postbuild": "node scripts/run-generate-mcpb.js",
14
- "build:dev": "npm run clean && tsc && npm run bundle",
15
- "build:no-lint": "npm run clean && tsc && npm run bundle:prod",
16
- "build:prod": "npm run lint:fix && npm run prebuild && tsc --project tsconfig.prod.json && npm run bundle:prod && npm run verify-build",
17
- "clean": "rimraf dist",
18
- "dev": "tsc --watch",
19
- "start": "node -r dotenv/config dist/index.js",
20
- "start:prod": "NODE_ENV=production node dist/index.js",
21
- "start:mcp": "node dist/index.js",
22
- "validate-env": "node scripts/validate-env.js",
23
- "verify-build": "node scripts/verify-build.js",
24
- "lint": "npm run lint:eslint && npm run format:check",
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 .",
29
- "type-check": "tsc --noEmit",
30
- "test": "vitest run --project unit && npm run filter-test-results",
31
- "test:watch": "vitest",
32
- "test:unit": "vitest run --project unit",
33
- "test:integration": "npm run test:integration:core",
34
- "test:integration:core": "vitest run --project integration:core",
35
- "test:integration:domain": "vitest run --project integration:domain",
36
- "test:integration:full": "node scripts/run-throttled-integration-tests.js",
37
- "test:integration:budgets": "node scripts/run-domain-integration-tests.js budgets",
38
- "test:integration:accounts": "node scripts/run-domain-integration-tests.js accounts",
39
- "test:integration:transactions": "node scripts/run-domain-integration-tests.js transactions",
40
- "test:integration:categories": "node scripts/run-domain-integration-tests.js categories",
41
- "test:integration:payees": "node scripts/run-domain-integration-tests.js payees",
42
- "test:integration:months": "node scripts/run-domain-integration-tests.js months",
43
- "test:integration:delta": "node scripts/run-domain-integration-tests.js delta",
44
- "test:integration:reconciliation": "node scripts/run-domain-integration-tests.js reconciliation",
45
- "test:e2e": "vitest run --project e2e",
46
- "test:coverage": "vitest run --coverage --project unit",
47
- "test:performance": "vitest run src/__tests__/performance.test.ts",
48
- "test:comprehensive": "tsx src/__tests__/testRunner.ts",
49
- "test:all": "npm run test:unit && npm run test:integration:core && npm run test:e2e && npm run test:performance",
50
- "filter-test-results": "node -e \"const fs = require('fs'); try { const results = JSON.parse(fs.readFileSync('test-results.json', 'utf-8')); results.testResults = results.testResults.filter(r => r.status === 'fail'); fs.writeFileSync('test-results.json', JSON.stringify(results, null, 2)); } catch (e) { console.error('Failed to filter test results:', e); process.exit(1); }\"",
51
- "generate:mcpb": "node scripts/run-generate-mcpb.js",
52
- "bundle": "node esbuild.config.mjs",
53
- "bundle:prod": "node esbuild.config.mjs --production",
54
- "package:mcpb": "npm run build:prod && npm run generate:mcpb",
55
- "pr:description": "node scripts/create-pr-description.js",
56
- "pr:create": "npm run pr:description && gh pr create --body-file .pr-description.md",
57
- "prepare": "npm run build:prod",
58
- "prepublishOnly": "npm run test:unit && npm run build:prod"
59
- },
60
- "keywords": [
61
- "mcp",
62
- "ynab",
63
- "budget",
64
- "finance"
65
- ],
66
- "author": "",
67
- "license": "AGPL-3.0",
68
- "dependencies": {
69
- "@modelcontextprotocol/sdk": "^1.22.0",
70
- "csv-parse": "^6.1.0",
71
- "d3-array": "^3.2.4",
72
- "date-fns": "^4.1.0",
73
- "dotenv": "^17.2.1",
74
- "ynab": "^2.9.0",
75
- "zod": "^4.1.11"
76
- },
77
- "devDependencies": {
78
- "@eslint/js": "^9.35.0",
79
- "@types/d3-array": "^3.2.1",
80
- "@types/node": "^24.5.2",
81
- "@vitest/coverage-v8": "^3.2.4",
82
- "@vitest/ui": "^3.2.4",
83
- "esbuild": "^0.25.10",
84
- "eslint": "^9.35.0",
85
- "eslint-config-prettier": "^10.1.8",
86
- "prettier": "^3.3.3",
87
- "rimraf": "^6.0.1",
88
- "tsx": "^4.20.6",
89
- "typescript": "^5.9.2",
90
- "typescript-eslint": "^8.42.0",
91
- "vitest": "^3.2.4"
92
- },
93
- "directories": {
94
- "doc": "docs"
95
- },
96
- "repository": {
97
- "type": "git",
98
- "url": "git+https://github.com/dizzlkheinz/ynab-mcp-dxt.git"
99
- },
100
- "types": "./dist/index.d.ts",
101
- "bugs": {
102
- "url": "https://github.com/dizzlkheinz/ynab-mcp-dxt/issues"
103
- },
104
- "homepage": "https://github.com/dizzlkheinz/ynab-mcp-dxt#readme"
105
- }
package/temp-recon.ts DELETED
@@ -1,126 +0,0 @@
1
- import 'dotenv/config';
2
- import * as ynab from 'ynab';
3
- import { executeReconciliation } from './src/tools/reconciliation/executor.js';
4
-
5
- async function main() {
6
- const token = process.env['YNAB_ACCESS_TOKEN'];
7
- if (!token) throw new Error('No token');
8
- const api = new ynab.API(token);
9
- const budgets = await api.budgets.getBudgets();
10
- const budgetId = budgets.data.budgets[0]?.id;
11
- if (!budgetId) throw new Error('no budget');
12
- const accounts = await api.accounts.getAccounts(budgetId);
13
- const account = accounts.data.accounts.find((a) => !a.closed);
14
- if (!account) throw new Error('no account');
15
- const snapshot = {
16
- balance: account.balance,
17
- cleared_balance: account.cleared_balance ?? account.balance,
18
- uncleared_balance: account.uncleared_balance ?? 0,
19
- };
20
- const count = 2;
21
- const transactionAmount = 7;
22
- const clearedDollars = snapshot.cleared_balance / 1000;
23
- const totalDelta = transactionAmount * count;
24
- const statementBalance = clearedDollars + totalDelta;
25
- const today = new Date().toISOString().slice(0, 10);
26
- const unmatchedBank = Array.from({ length: count }, (_, i) => {
27
- const date = new Date(Date.now() + i * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
28
- return {
29
- id: `integration-bank-${i}`,
30
- date,
31
- amount: transactionAmount,
32
- payee: `Integration Payee ${i}`,
33
- memo: `Integration memo ${i}`,
34
- original_csv_row: i + 1,
35
- };
36
- });
37
- const analysis = {
38
- success: true,
39
- phase: 'analysis',
40
- summary: {
41
- statement_date_range: 'Integration test',
42
- bank_transactions_count: count,
43
- ynab_transactions_count: 0,
44
- auto_matched: 0,
45
- suggested_matches: 0,
46
- unmatched_bank: count,
47
- unmatched_ynab: 0,
48
- current_cleared_balance: clearedDollars,
49
- target_statement_balance: statementBalance,
50
- discrepancy: totalDelta,
51
- discrepancy_explanation: 'Synthetic integration delta',
52
- },
53
- auto_matches: [],
54
- suggested_matches: [],
55
- unmatched_bank: unmatchedBank,
56
- unmatched_ynab: [],
57
- balance_info: {
58
- current_cleared: clearedDollars,
59
- current_uncleared: snapshot.uncleared_balance / 1000,
60
- current_total: snapshot.balance / 1000,
61
- target_statement: statementBalance,
62
- discrepancy: totalDelta,
63
- on_track: false,
64
- },
65
- next_steps: [],
66
- insights: [],
67
- } as const;
68
-
69
- const params = {
70
- budget_id: budgetId,
71
- account_id: account.id,
72
- csv_data: 'Date,Description,Amount',
73
- statement_balance: statementBalance,
74
- statement_date: today,
75
- date_tolerance_days: 1,
76
- amount_tolerance_cents: 1,
77
- auto_match_threshold: 90,
78
- suggestion_threshold: 60,
79
- auto_create_transactions: true,
80
- auto_update_cleared_status: false,
81
- auto_unclear_missing: false,
82
- auto_adjust_dates: false,
83
- dry_run: false,
84
- require_exact_match: true,
85
- confidence_threshold: 0.8,
86
- max_resolution_attempts: 3,
87
- include_structured_data: false,
88
- } as const;
89
-
90
- const result = await executeReconciliation({
91
- ynabAPI: api,
92
- analysis: analysis as any,
93
- params: params as any,
94
- budgetId,
95
- accountId: account.id,
96
- initialAccount: snapshot,
97
- currencyCode: 'USD',
98
- });
99
-
100
- console.log(
101
- JSON.stringify(
102
- {
103
- summary: result.summary,
104
- bulk: result.bulk_operation_details,
105
- actions: result.actions_taken.slice(0, 5),
106
- },
107
- null,
108
- 2,
109
- ),
110
- );
111
-
112
- const ids: string[] = [];
113
- for (const action of result.actions_taken) {
114
- if (action.type === 'create_transaction' && (action.transaction as any)?.id) {
115
- ids.push((action.transaction as any).id);
116
- }
117
- }
118
- for (const id of ids) {
119
- await api.transactions.deleteTransaction(budgetId, id);
120
- }
121
- }
122
-
123
- main().catch((err) => {
124
- console.error(err);
125
- process.exit(1);
126
- });
@@ -1,23 +0,0 @@
1
- {
2
- "export_info": {
3
- "exported_at": "2025-11-19T17:04:53.492Z",
4
- "total_transactions": 1,
5
- "minimal": true,
6
- "filters": {
7
- "budget_id": "7b47d8bb-ce4c-40c0-a9eb-c6d715af9a76",
8
- "account_id": "e9ddc2a6-f4ae-4f95-b45a-831cf1d7b90c",
9
- "category_id": null,
10
- "since_date": null,
11
- "type": null
12
- }
13
- },
14
- "transactions": [
15
- {
16
- "id": "93e4355b-6c45-4f73-899d-53b2c4ef8c7d",
17
- "date": "2025-11-17",
18
- "amount": 10000000,
19
- "payee_name": "Starting Balance",
20
- "cleared": "cleared"
21
- }
22
- ]
23
- }