@dizzlkheinz/ynab-mcpb 0.16.1 → 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 (169) 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 +33 -47
  8. package/README.md +8 -10
  9. package/dist/bundle/index.cjs +54 -54
  10. package/dist/server/YNABMCPServer.d.ts +120 -54
  11. package/dist/server/YNABMCPServer.js +28 -381
  12. package/dist/server/config.d.ts +2 -0
  13. package/dist/server/config.js +1 -0
  14. package/dist/server/securityMiddleware.d.ts +37 -8
  15. package/dist/tools/accountTools.d.ts +2 -0
  16. package/dist/tools/accountTools.js +45 -0
  17. package/dist/tools/adapters.d.ts +12 -0
  18. package/dist/tools/adapters.js +25 -0
  19. package/dist/tools/budgetTools.d.ts +2 -0
  20. package/dist/tools/budgetTools.js +30 -0
  21. package/dist/tools/categoryTools.d.ts +2 -0
  22. package/dist/tools/categoryTools.js +45 -0
  23. package/dist/tools/monthTools.d.ts +2 -0
  24. package/dist/tools/monthTools.js +32 -0
  25. package/dist/tools/payeeTools.d.ts +2 -0
  26. package/dist/tools/payeeTools.js +32 -0
  27. package/dist/tools/reconciliation/index.d.ts +2 -0
  28. package/dist/tools/reconciliation/index.js +33 -0
  29. package/dist/tools/schemas/common.d.ts +3 -0
  30. package/dist/tools/schemas/common.js +3 -0
  31. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
  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/transactionTools.d.ts +2 -0
  37. package/dist/tools/transactionTools.js +124 -0
  38. package/dist/tools/utilityTools.d.ts +2 -7
  39. package/dist/tools/utilityTools.js +19 -38
  40. package/dist/types/index.d.ts +1 -0
  41. package/dist/types/toolRegistration.d.ts +27 -0
  42. package/dist/types/toolRegistration.js +1 -0
  43. package/docs/maintainers/npm-publishing.md +27 -0
  44. package/docs/reference/API.md +15 -70
  45. package/docs/technical/reconciliation-system-architecture.md +2251 -2251
  46. package/package.json +6 -6
  47. package/scripts/analyze-bundle.mjs +41 -41
  48. package/scripts/generate-mcpb.ps1 +95 -95
  49. package/scripts/run-domain-integration-tests.js +4 -1
  50. package/scripts/watch-and-restart.ps1 +49 -49
  51. package/src/__tests__/comprehensive.integration.test.ts +0 -28
  52. package/src/__tests__/performance.test.ts +4 -12
  53. package/src/__tests__/setup.ts +45 -14
  54. package/src/__tests__/workflows.e2e.test.ts +1 -51
  55. package/src/server/YNABMCPServer.ts +33 -519
  56. package/src/server/__tests__/YNABMCPServer.test.ts +0 -1
  57. package/src/server/__tests__/toolRegistration.test.ts +236 -0
  58. package/src/server/config.ts +1 -0
  59. package/src/tools/__tests__/adapters.test.ts +113 -0
  60. package/src/tools/__tests__/transactionTools.integration.test.ts +63 -3
  61. package/src/tools/__tests__/utilityTools.integration.test.ts +1 -85
  62. package/src/tools/__tests__/utilityTools.test.ts +1 -123
  63. package/src/tools/accountTools.ts +53 -0
  64. package/src/tools/adapters.ts +74 -0
  65. package/src/tools/budgetTools.ts +37 -0
  66. package/src/tools/categoryTools.ts +53 -0
  67. package/src/tools/monthTools.ts +39 -0
  68. package/src/tools/payeeTools.ts +39 -0
  69. package/src/tools/reconciliation/index.ts +45 -0
  70. package/src/tools/schemas/common.ts +18 -0
  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/transactionTools.ts +140 -0
  75. package/src/tools/utilityTools.ts +24 -55
  76. package/src/types/index.ts +3 -0
  77. package/src/types/toolRegistration.ts +88 -0
  78. package/vitest.config.ts +2 -1
  79. package/.chunkhound.json +0 -11
  80. package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +0 -3
  81. package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +0 -3
  82. package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +0 -1
  83. package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +0 -5
  84. package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +0 -1569
  85. package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +0 -3
  86. package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +0 -2832
  87. package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +0 -2709
  88. package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +0 -2832
  89. package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +0 -2832
  90. package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +0 -2709
  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/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +0 -231
  96. package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +0 -2590
  97. package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +0 -5195
  98. package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +0 -286
  99. package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +0 -218
  100. package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +0 -180
  101. package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +0 -3
  102. package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +0 -1
  103. package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +0 -747
  104. package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +0 -1
  105. package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +0 -2594
  106. package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +0 -2594
  107. package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +0 -144
  108. package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +0 -416
  109. package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +0 -2590
  110. package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +0 -2590
  111. package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +0 -2590
  112. package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +0 -790
  113. package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +0 -766
  114. package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +0 -790
  115. package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +0 -2594
  116. package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +0 -1000
  117. package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +0 -3489
  118. package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +0 -766
  119. package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +0 -2594
  120. package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +0 -2456
  121. package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +0 -2594
  122. package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +0 -18
  123. package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +0 -48
  124. package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +0 -1
  125. package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +0 -1
  126. package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +0 -1
  127. package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +0 -1
  128. package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +0 -1271
  129. package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +0 -1570
  130. package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +0 -2590
  131. package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +0 -3
  132. package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +0 -3299
  133. package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +0 -3299
  134. package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +0 -1882
  135. package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +0 -2594
  136. package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +0 -1
  137. package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +0 -170
  138. package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +0 -1
  139. package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +0 -3
  140. package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +0 -1
  141. package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +0 -1
  142. package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +0 -3
  143. package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +0 -1
  144. package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +0 -1
  145. package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +0 -5
  146. package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +0 -1
  147. package/.github/workflows/pr-description-check.yml +0 -88
  148. package/AGENTS.md +0 -36
  149. package/NUL +0 -1
  150. package/docs/README.md +0 -72
  151. package/docs/getting-started/CONFIGURATION.md +0 -175
  152. package/docs/getting-started/INSTALLATION.md +0 -333
  153. package/docs/getting-started/QUICKSTART.md +0 -282
  154. package/docs/guides/ARCHITECTURE.md +0 -533
  155. package/docs/guides/DEPLOYMENT.md +0 -189
  156. package/docs/guides/INTEGRATION_TESTING.md +0 -730
  157. package/docs/guides/TESTING.md +0 -591
  158. package/docs/reconciliation-flow.md +0 -83
  159. package/docs/reference/EXAMPLES.md +0 -946
  160. package/docs/reference/TOOLS.md +0 -348
  161. package/docs/reference/TROUBLESHOOTING.md +0 -481
  162. package/package.json.tmp +0 -105
  163. package/temp-recon.ts +0 -126
  164. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +0 -23
  165. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +0 -23
  166. package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +0 -44
  167. package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +0 -58
  168. package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +0 -3662
  169. package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +0 -115
@@ -12,7 +12,7 @@ import {
12
12
  ReadResourceRequestSchema,
13
13
  GetPromptRequestSchema,
14
14
  } from '@modelcontextprotocol/sdk/types.js';
15
- import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js';
15
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
16
16
  import * as ynab from 'ynab';
17
17
  import {
18
18
  AuthenticationError,
@@ -20,73 +20,23 @@ import {
20
20
  ValidationError as ConfigValidationError,
21
21
  } from '../utils/errors.js';
22
22
  import { YNABErrorCode, ValidationError } from '../types/index.js';
23
+ import type { ToolContext } from '../types/toolRegistration.js';
23
24
  import { loadConfig, type AppConfig } from './config.js';
24
25
  import { createErrorHandler, ErrorHandler } from './errorHandler.js';
25
26
  import { BudgetResolver } from './budgetResolver.js';
26
27
  import { SecurityMiddleware, withSecurityWrapper } from './securityMiddleware.js';
27
- import { handleListBudgets, handleGetBudget, GetBudgetSchema } from '../tools/budgetTools.js';
28
- import {
29
- handleListAccounts,
30
- handleGetAccount,
31
- handleCreateAccount,
32
- ListAccountsSchema,
33
- GetAccountSchema,
34
- CreateAccountSchema,
35
- } from '../tools/accountTools.js';
36
- import {
37
- handleListTransactions,
38
- handleGetTransaction,
39
- handleCreateTransaction,
40
- handleCreateTransactions,
41
- handleCreateReceiptSplitTransaction,
42
- handleUpdateTransaction,
43
- handleUpdateTransactions,
44
- handleDeleteTransaction,
45
- ListTransactionsSchema,
46
- GetTransactionSchema,
47
- CreateTransactionSchema,
48
- CreateTransactionsSchema,
49
- CreateReceiptSplitTransactionSchema,
50
- UpdateTransactionSchema,
51
- UpdateTransactionsSchema,
52
- DeleteTransactionSchema,
53
- } from '../tools/transactionTools.js';
54
- import { handleExportTransactions, ExportTransactionsSchema } from '../tools/exportTransactions.js';
55
- import {
56
- handleCompareTransactions,
57
- CompareTransactionsSchema,
58
- } from '../tools/compareTransactions/index.js';
59
- import { handleReconcileAccount, ReconcileAccountSchema } from '../tools/reconciliation/index.js';
60
- import {
61
- handleListCategories,
62
- handleGetCategory,
63
- handleUpdateCategory,
64
- ListCategoriesSchema,
65
- GetCategorySchema,
66
- UpdateCategorySchema,
67
- } from '../tools/categoryTools.js';
68
- import {
69
- handleListPayees,
70
- handleGetPayee,
71
- ListPayeesSchema,
72
- GetPayeeSchema,
73
- } from '../tools/payeeTools.js';
74
- import {
75
- handleGetMonth,
76
- handleListMonths,
77
- GetMonthSchema,
78
- ListMonthsSchema,
79
- } from '../tools/monthTools.js';
80
- import { handleGetUser, handleConvertAmount, ConvertAmountSchema } from '../tools/utilityTools.js';
28
+ import { registerBudgetTools } from '../tools/budgetTools.js';
29
+ import { registerAccountTools } from '../tools/accountTools.js';
30
+ import { registerTransactionTools } from '../tools/transactionTools.js';
31
+ import { registerReconciliationTools } from '../tools/reconciliation/index.js';
32
+ import { registerCategoryTools } from '../tools/categoryTools.js';
33
+ import { registerPayeeTools } from '../tools/payeeTools.js';
34
+ import { registerMonthTools } from '../tools/monthTools.js';
35
+ import { registerUtilityTools } from '../tools/utilityTools.js';
36
+ import { emptyObjectSchema } from '../tools/schemas/common.js';
81
37
  import { cacheManager, CacheManager } from './cacheManager.js';
82
38
  import { responseFormatter } from './responseFormatter.js';
83
- import {
84
- ToolRegistry,
85
- DefaultArgumentResolutionError,
86
- type ToolDefinition,
87
- type DefaultArgumentResolver,
88
- type ToolExecutionPayload,
89
- } from './toolRegistry.js';
39
+ import { ToolRegistry, type ToolDefinition } from './toolRegistry.js';
90
40
  import { ResourceManager } from './resources.js';
91
41
  import { PromptManager } from './prompts.js';
92
42
  import { DiagnosticManager } from './diagnostics.js';
@@ -94,28 +44,6 @@ import { ServerKnowledgeStore } from './serverKnowledgeStore.js';
94
44
  import { DeltaCache } from './deltaCache.js';
95
45
  import { DeltaFetcher } from '../tools/deltaFetcher.js';
96
46
  import { ToolAnnotationPresets } from '../tools/toolCategories.js';
97
- import {
98
- GetUserOutputSchema,
99
- ConvertAmountOutputSchema,
100
- GetDefaultBudgetOutputSchema,
101
- SetDefaultBudgetOutputSchema,
102
- ClearCacheOutputSchema,
103
- SetOutputFormatOutputSchema,
104
- DiagnosticInfoOutputSchema,
105
- GetBudgetOutputSchema,
106
- ListBudgetsOutputSchema,
107
- ListAccountsOutputSchema,
108
- GetAccountOutputSchema,
109
- GetTransactionOutputSchema,
110
- ExportTransactionsOutputSchema,
111
- CompareTransactionsOutputSchema,
112
- ListCategoriesOutputSchema,
113
- GetCategoryOutputSchema,
114
- ListPayeesOutputSchema,
115
- GetPayeeOutputSchema,
116
- GetMonthOutputSchema,
117
- ListMonthsOutputSchema,
118
- } from '../tools/schemas/outputs/index.js';
119
47
 
120
48
  /**
121
49
  * YNAB MCP Server class that provides integration with You Need A Budget API
@@ -140,7 +68,7 @@ export class YNABMCPServer {
140
68
  this.exitOnError = exitOnError;
141
69
  this.configInstance = loadConfig();
142
70
  // Config is now imported and validated at startup
143
- this.defaultBudgetId = process.env['YNAB_DEFAULT_BUDGET_ID'];
71
+ this.defaultBudgetId = this.configInstance.YNAB_DEFAULT_BUDGET_ID;
144
72
 
145
73
  // Initialize YNAB API
146
74
  this.ynabAPI = new ynab.API(this.configInstance.YNAB_ACCESS_TOKEN);
@@ -392,58 +320,17 @@ export class YNABMCPServer {
392
320
  this.toolRegistry.register(definition);
393
321
  };
394
322
 
395
- const adapt =
396
- <TInput extends Record<string, unknown>>(
397
- handler: (ynabAPI: ynab.API, params: TInput) => Promise<CallToolResult>,
398
- ) =>
399
- async ({ input }: ToolExecutionPayload<TInput>): Promise<CallToolResult> =>
400
- handler(this.ynabAPI, input);
401
-
402
- const adaptNoInput =
403
- (handler: (ynabAPI: ynab.API) => Promise<CallToolResult>) =>
404
- async (_payload: ToolExecutionPayload<Record<string, unknown>>): Promise<CallToolResult> =>
405
- handler(this.ynabAPI);
406
-
407
- const adaptWithDelta =
408
- <TInput extends Record<string, unknown>>(
409
- handler: (
410
- ynabAPI: ynab.API,
411
- deltaFetcher: DeltaFetcher,
412
- params: TInput,
413
- ) => Promise<CallToolResult>,
414
- ) =>
415
- async ({ input }: ToolExecutionPayload<TInput>): Promise<CallToolResult> =>
416
- handler(this.ynabAPI, this.deltaFetcher, input);
417
-
418
- const adaptWrite =
419
- <TInput extends Record<string, unknown>>(
420
- handler: (
421
- ynabAPI: ynab.API,
422
- deltaCache: DeltaCache,
423
- knowledgeStore: ServerKnowledgeStore,
424
- params: TInput,
425
- ) => Promise<CallToolResult>,
426
- ) =>
427
- async ({ input }: ToolExecutionPayload<TInput>): Promise<CallToolResult> =>
428
- handler(this.ynabAPI, this.deltaCache, this.serverKnowledgeStore, input);
429
-
430
- const resolveBudgetId = <
431
- TInput extends { budget_id?: string | undefined },
432
- >(): DefaultArgumentResolver<TInput> => {
433
- return ({ rawArguments }) => {
434
- const provided =
435
- typeof rawArguments['budget_id'] === 'string' && rawArguments['budget_id'].length > 0
436
- ? (rawArguments['budget_id'] as string)
437
- : undefined;
438
- const result = BudgetResolver.resolveBudgetId(provided, this.defaultBudgetId);
439
- if (typeof result === 'string') {
440
- return { budget_id: result } as Partial<TInput>;
441
- }
442
- throw new DefaultArgumentResolutionError(result);
443
- };
323
+ const toolContext: ToolContext = {
324
+ ynabAPI: this.ynabAPI,
325
+ deltaFetcher: this.deltaFetcher,
326
+ deltaCache: this.deltaCache,
327
+ serverKnowledgeStore: this.serverKnowledgeStore,
328
+ getDefaultBudgetId: () => this.defaultBudgetId,
329
+ setDefaultBudget: (budgetId: string) => this.setDefaultBudget(budgetId),
330
+ cacheManager,
331
+ diagnosticManager: this.diagnosticManager,
444
332
  };
445
333
 
446
- const emptyObjectSchema = z.object({}).strict();
447
334
  const setDefaultBudgetSchema = z.object({ budget_id: z.string().min(1) }).strict();
448
335
  const diagnosticInfoSchema = z
449
336
  .object({
@@ -461,42 +348,21 @@ export class YNABMCPServer {
461
348
  pretty_spaces: z.number().int().min(0).max(10).optional(),
462
349
  })
463
350
  .strict();
464
- // Permissive object schema used where hosts require a top-level object type
465
- const LooseObjectSchema = z.object({}).passthrough();
466
-
467
- register({
468
- name: 'list_budgets',
469
- description: "List all budgets associated with the user's account",
470
- inputSchema: emptyObjectSchema,
471
- outputSchema: ListBudgetsOutputSchema,
472
- handler: adaptWithDelta(handleListBudgets),
473
- metadata: {
474
- annotations: {
475
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
476
- title: 'YNAB: List Budgets',
477
- },
478
- },
479
- });
480
-
481
- register({
482
- name: 'get_budget',
483
- description: 'Get detailed information for a specific budget',
484
- inputSchema: GetBudgetSchema,
485
- outputSchema: GetBudgetOutputSchema,
486
- handler: adapt(handleGetBudget),
487
- metadata: {
488
- annotations: {
489
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
490
- title: 'YNAB: Get Budget Details',
491
- },
492
- },
493
- });
494
-
351
+ registerBudgetTools(this.toolRegistry, toolContext);
352
+ registerPayeeTools(this.toolRegistry, toolContext);
353
+ registerCategoryTools(this.toolRegistry, toolContext);
354
+ registerAccountTools(this.toolRegistry, toolContext);
355
+ registerMonthTools(this.toolRegistry, toolContext);
356
+ registerTransactionTools(this.toolRegistry, toolContext);
357
+ registerReconciliationTools(this.toolRegistry, toolContext);
358
+ registerUtilityTools(this.toolRegistry, toolContext);
359
+
360
+ // Server-owned inline tools stay here because they depend on instance state (default budget,
361
+ // diagnostics manager, cache manager, response formatter) rather than the factory context.
495
362
  register({
496
363
  name: 'set_default_budget',
497
364
  description: 'Set the default budget for subsequent operations',
498
365
  inputSchema: setDefaultBudgetSchema,
499
- outputSchema: SetDefaultBudgetOutputSchema,
500
366
  handler: async ({ input }) => {
501
367
  const { budget_id } = input;
502
368
  await this.ynabAPI.budgets.getBudgetById(budget_id);
@@ -533,7 +399,6 @@ export class YNABMCPServer {
533
399
  name: 'get_default_budget',
534
400
  description: 'Get the currently set default budget',
535
401
  inputSchema: emptyObjectSchema,
536
- outputSchema: GetDefaultBudgetOutputSchema,
537
402
  handler: async () => {
538
403
  try {
539
404
  const defaultBudget = this.getDefaultBudget();
@@ -569,359 +434,10 @@ export class YNABMCPServer {
569
434
  },
570
435
  });
571
436
 
572
- register({
573
- name: 'list_accounts',
574
- description: 'List all accounts for a specific budget (uses default budget if not specified)',
575
- inputSchema: ListAccountsSchema,
576
- outputSchema: ListAccountsOutputSchema,
577
- handler: adaptWithDelta(handleListAccounts),
578
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof ListAccountsSchema>>(),
579
- metadata: {
580
- annotations: {
581
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
582
- title: 'YNAB: List Accounts',
583
- },
584
- },
585
- });
586
-
587
- register({
588
- name: 'get_account',
589
- description: 'Get detailed information for a specific account',
590
- inputSchema: GetAccountSchema,
591
- outputSchema: GetAccountOutputSchema,
592
- handler: adapt(handleGetAccount),
593
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof GetAccountSchema>>(),
594
- metadata: {
595
- annotations: {
596
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
597
- title: 'YNAB: Get Account Details',
598
- },
599
- },
600
- });
601
-
602
- register({
603
- name: 'create_account',
604
- description: 'Create a new account in the specified budget',
605
- inputSchema: CreateAccountSchema,
606
- outputSchema: LooseObjectSchema,
607
- handler: adaptWrite(handleCreateAccount),
608
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof CreateAccountSchema>>(),
609
- metadata: {
610
- annotations: {
611
- ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
612
- title: 'YNAB: Create Account',
613
- },
614
- },
615
- });
616
-
617
- register({
618
- name: 'list_transactions',
619
- description: 'List transactions for a budget with optional filtering',
620
- inputSchema: ListTransactionsSchema,
621
- outputSchema: LooseObjectSchema,
622
- handler: adaptWithDelta(handleListTransactions),
623
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof ListTransactionsSchema>>(),
624
- metadata: {
625
- annotations: {
626
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
627
- title: 'YNAB: List Transactions',
628
- },
629
- },
630
- });
631
-
632
- register({
633
- name: 'export_transactions',
634
- description: 'Export all transactions to a JSON file with descriptive filename',
635
- inputSchema: ExportTransactionsSchema,
636
- outputSchema: ExportTransactionsOutputSchema,
637
- handler: adapt(handleExportTransactions),
638
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof ExportTransactionsSchema>>(),
639
- metadata: {
640
- annotations: {
641
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
642
- title: 'YNAB: Export Transactions',
643
- },
644
- },
645
- });
646
-
647
- register({
648
- name: 'compare_transactions',
649
- description:
650
- 'Compare bank transactions from CSV with YNAB transactions to find missing entries',
651
- inputSchema: CompareTransactionsSchema,
652
- outputSchema: CompareTransactionsOutputSchema,
653
- handler: adapt(handleCompareTransactions),
654
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof CompareTransactionsSchema>>(),
655
- metadata: {
656
- annotations: {
657
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
658
- title: 'YNAB: Compare Transactions',
659
- },
660
- },
661
- });
662
-
663
- register({
664
- name: 'reconcile_account',
665
- description:
666
- 'Guided reconciliation workflow with human narrative, insight detection, and optional execution (create/update/unclear). Set include_structured_data=true to also get full JSON output (large).',
667
- inputSchema: ReconcileAccountSchema,
668
- outputSchema: LooseObjectSchema,
669
- handler: adaptWithDelta(handleReconcileAccount),
670
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof ReconcileAccountSchema>>(),
671
- metadata: {
672
- annotations: {
673
- ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
674
- title: 'YNAB: Reconcile Account',
675
- },
676
- },
677
- });
678
-
679
- register({
680
- name: 'get_transaction',
681
- description: 'Get detailed information for a specific transaction',
682
- inputSchema: GetTransactionSchema,
683
- outputSchema: GetTransactionOutputSchema,
684
- handler: adapt(handleGetTransaction),
685
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof GetTransactionSchema>>(),
686
- metadata: {
687
- annotations: {
688
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
689
- title: 'YNAB: Get Transaction Details',
690
- },
691
- },
692
- });
693
-
694
- register({
695
- name: 'create_transaction',
696
- description: 'Create a new transaction in the specified budget and account',
697
- inputSchema: CreateTransactionSchema,
698
- outputSchema: LooseObjectSchema,
699
- handler: adaptWrite(handleCreateTransaction),
700
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof CreateTransactionSchema>>(),
701
- metadata: {
702
- annotations: {
703
- ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
704
- title: 'YNAB: Create Transaction',
705
- },
706
- },
707
- });
708
-
709
- register({
710
- name: 'create_transactions',
711
- description:
712
- 'Create multiple transactions in a single batch (1-100 items) with duplicate detection, dry-run validation, and automatic response size management with correlation metadata.',
713
- inputSchema: CreateTransactionsSchema,
714
- outputSchema: LooseObjectSchema,
715
- handler: adaptWrite(handleCreateTransactions),
716
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof CreateTransactionsSchema>>(),
717
- metadata: {
718
- annotations: {
719
- ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
720
- title: 'YNAB: Create Multiple Transactions',
721
- },
722
- },
723
- });
724
-
725
- register({
726
- name: 'update_transactions',
727
- description:
728
- 'Update multiple transactions in a single batch (1-100 items) with dry-run validation, automatic cache invalidation, and response size management. Supports optional original_account_id and original_date metadata for efficient cache invalidation.',
729
- inputSchema: UpdateTransactionsSchema,
730
- outputSchema: LooseObjectSchema,
731
- handler: adaptWrite(handleUpdateTransactions),
732
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof UpdateTransactionsSchema>>(),
733
- metadata: {
734
- annotations: {
735
- ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
736
- title: 'YNAB: Update Multiple Transactions',
737
- },
738
- },
739
- });
740
-
741
- register({
742
- name: 'create_receipt_split_transaction',
743
- description: 'Create a split transaction from receipt items with proportional tax allocation',
744
- inputSchema: CreateReceiptSplitTransactionSchema,
745
- outputSchema: LooseObjectSchema,
746
- handler: adaptWrite(handleCreateReceiptSplitTransaction),
747
- defaultArgumentResolver:
748
- resolveBudgetId<z.infer<typeof CreateReceiptSplitTransactionSchema>>(),
749
- metadata: {
750
- annotations: {
751
- ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
752
- title: 'YNAB: Create Split Transaction from Receipt',
753
- },
754
- },
755
- });
756
-
757
- register({
758
- name: 'update_transaction',
759
- description: 'Update an existing transaction',
760
- inputSchema: UpdateTransactionSchema,
761
- outputSchema: LooseObjectSchema,
762
- handler: adaptWrite(handleUpdateTransaction),
763
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof UpdateTransactionSchema>>(),
764
- metadata: {
765
- annotations: {
766
- ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
767
- title: 'YNAB: Update Transaction',
768
- },
769
- },
770
- });
771
-
772
- register({
773
- name: 'delete_transaction',
774
- description: 'Delete a transaction from the specified budget',
775
- inputSchema: DeleteTransactionSchema,
776
- outputSchema: LooseObjectSchema,
777
- handler: adaptWrite(handleDeleteTransaction),
778
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof DeleteTransactionSchema>>(),
779
- metadata: {
780
- annotations: {
781
- ...ToolAnnotationPresets.WRITE_EXTERNAL_DELETE,
782
- title: 'YNAB: Delete Transaction',
783
- },
784
- },
785
- });
786
-
787
- register({
788
- name: 'list_categories',
789
- description: 'List all categories for a specific budget',
790
- inputSchema: ListCategoriesSchema,
791
- outputSchema: ListCategoriesOutputSchema,
792
- handler: adaptWithDelta(handleListCategories),
793
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof ListCategoriesSchema>>(),
794
- metadata: {
795
- annotations: {
796
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
797
- title: 'YNAB: List Categories',
798
- },
799
- },
800
- });
801
-
802
- register({
803
- name: 'get_category',
804
- description: 'Get detailed information for a specific category',
805
- inputSchema: GetCategorySchema,
806
- outputSchema: GetCategoryOutputSchema,
807
- handler: adapt(handleGetCategory),
808
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof GetCategorySchema>>(),
809
- metadata: {
810
- annotations: {
811
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
812
- title: 'YNAB: Get Category Details',
813
- },
814
- },
815
- });
816
-
817
- register({
818
- name: 'update_category',
819
- description: 'Update the budgeted amount for a category in the current month',
820
- inputSchema: UpdateCategorySchema,
821
- outputSchema: LooseObjectSchema,
822
- handler: adaptWrite(handleUpdateCategory),
823
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof UpdateCategorySchema>>(),
824
- metadata: {
825
- annotations: {
826
- ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
827
- title: 'YNAB: Update Category Budget',
828
- },
829
- },
830
- });
831
-
832
- register({
833
- name: 'list_payees',
834
- description: 'List all payees for a specific budget',
835
- inputSchema: ListPayeesSchema,
836
- outputSchema: ListPayeesOutputSchema,
837
- handler: adaptWithDelta(handleListPayees),
838
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof ListPayeesSchema>>(),
839
- metadata: {
840
- annotations: {
841
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
842
- title: 'YNAB: List Payees',
843
- },
844
- },
845
- });
846
-
847
- register({
848
- name: 'get_payee',
849
- description: 'Get detailed information for a specific payee',
850
- inputSchema: GetPayeeSchema,
851
- outputSchema: GetPayeeOutputSchema,
852
- handler: adapt(handleGetPayee),
853
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof GetPayeeSchema>>(),
854
- metadata: {
855
- annotations: {
856
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
857
- title: 'YNAB: Get Payee Details',
858
- },
859
- },
860
- });
861
-
862
- register({
863
- name: 'get_month',
864
- description: 'Get budget data for a specific month',
865
- inputSchema: GetMonthSchema,
866
- outputSchema: GetMonthOutputSchema,
867
- handler: adapt(handleGetMonth),
868
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof GetMonthSchema>>(),
869
- metadata: {
870
- annotations: {
871
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
872
- title: 'YNAB: Get Month Budget Data',
873
- },
874
- },
875
- });
876
-
877
- register({
878
- name: 'list_months',
879
- description: 'List all months summary data for a budget',
880
- inputSchema: ListMonthsSchema,
881
- outputSchema: ListMonthsOutputSchema,
882
- handler: adaptWithDelta(handleListMonths),
883
- defaultArgumentResolver: resolveBudgetId<z.infer<typeof ListMonthsSchema>>(),
884
- metadata: {
885
- annotations: {
886
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
887
- title: 'YNAB: List Months',
888
- },
889
- },
890
- });
891
-
892
- register({
893
- name: 'get_user',
894
- description: 'Get information about the authenticated user',
895
- inputSchema: emptyObjectSchema,
896
- outputSchema: GetUserOutputSchema,
897
- handler: adaptNoInput(handleGetUser),
898
- metadata: {
899
- annotations: {
900
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
901
- title: 'YNAB: Get User Information',
902
- },
903
- },
904
- });
905
-
906
- register({
907
- name: 'convert_amount',
908
- description: 'Convert between dollars and milliunits with integer arithmetic for precision',
909
- inputSchema: ConvertAmountSchema,
910
- outputSchema: ConvertAmountOutputSchema,
911
- handler: async ({ input }) => handleConvertAmount(input),
912
- metadata: {
913
- annotations: {
914
- ...ToolAnnotationPresets.UTILITY_LOCAL,
915
- title: 'YNAB: Convert Amount',
916
- },
917
- },
918
- });
919
-
920
437
  register({
921
438
  name: 'diagnostic_info',
922
439
  description: 'Get comprehensive diagnostic information about the MCP server',
923
440
  inputSchema: diagnosticInfoSchema,
924
- outputSchema: DiagnosticInfoOutputSchema,
925
441
  handler: async ({ input }) => {
926
442
  return this.diagnosticManager.collectDiagnostics(input);
927
443
  },
@@ -937,7 +453,6 @@ export class YNABMCPServer {
937
453
  name: 'clear_cache',
938
454
  description: 'Clear the in-memory cache (safe, no YNAB data is modified)',
939
455
  inputSchema: emptyObjectSchema,
940
- outputSchema: ClearCacheOutputSchema,
941
456
  handler: async () => {
942
457
  cacheManager.clear();
943
458
  return {
@@ -956,7 +471,6 @@ export class YNABMCPServer {
956
471
  name: 'set_output_format',
957
472
  description: 'Configure default JSON output formatting (minify or pretty spaces)',
958
473
  inputSchema: setOutputFormatSchema,
959
- outputSchema: SetOutputFormatOutputSchema,
960
474
  handler: async ({ input }) => {
961
475
  const options: { defaultMinify?: boolean; prettySpaces?: number } = {};
962
476
  if (typeof input.default_minify === 'boolean') {
@@ -45,7 +45,6 @@ describe('YNABMCPServer', () => {
45
45
  'get_month',
46
46
  'list_months',
47
47
  'get_user',
48
- 'convert_amount',
49
48
  'diagnostic_info',
50
49
  'clear_cache',
51
50
  'set_output_format',