@dizzlkheinz/ynab-mcpb 0.16.0 → 0.17.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 (114) hide show
  1. package/.code/agents/0098661e-0fa3-4990-beb9-c0cbf3f123aa/status.txt +1 -0
  2. package/.code/agents/1324/exec-call_tIpx9uV1TpARbAMZonRQm8AO.txt +757 -0
  3. package/.code/agents/1572/exec-call_GjVFBFOWcY7lE0idc5nWlLNh.txt +781 -0
  4. package/.code/agents/1846/exec-call_1YNAVD18RjrMN7JnfkkQhUP3.txt +766 -0
  5. package/.code/agents/1846/exec-call_lh3lDzE4WJAh1lFiomiiZ73D.txt +766 -0
  6. package/.code/agents/2038/exec-call_DYwOukaYsL8VCONWmV2rUW5u.txt +766 -0
  7. package/.code/agents/2038/exec-call_c7fOQ7UrpVcTtvdfGBRM146V.txt +652 -0
  8. package/.code/agents/2038/exec-call_ySNyq9Mm55jWE480s54r5QcA.txt +766 -0
  9. package/.code/agents/2256/exec-call_AtPcRWPmFPMcmX6qOFm1fCEY.txt +766 -0
  10. package/.code/agents/2454/exec-call_aFJpupwjfZeOBm7ixI5Vc8z2.txt +766 -0
  11. package/.code/agents/2454/exec-call_wogZ4HfXTodTEXvdgXlVUBpv.txt +766 -0
  12. package/.code/agents/2e905864-aa07-4314-bcf9-c5b32277e4ac/result.txt +36 -0
  13. package/.code/agents/3073/exec-call_Peeagc9DxGYLgE6pNdMZhqIE.txt +766 -0
  14. package/.code/agents/3073/exec-call_d2YSE3hXF08KRSoUM3qd8Z3x.txt +766 -0
  15. package/.code/agents/335aa031-466d-4fb7-925f-3cd864e264d0/result.txt +191 -0
  16. package/.code/agents/3364/exec-call_NbhIrsM5HhyDZDmJZG5CuCYL.txt +766 -0
  17. package/.code/agents/3364/exec-call_cKtJg0NrXiwXEFwlsE3uPZRA.txt +766 -0
  18. package/.code/agents/36d98414-5cde-4d9d-9a67-a240a18c1f07/result.txt +189 -0
  19. package/.code/agents/4604e866-b7b8-44f5-992f-2f683b0a523b/status.txt +1 -0
  20. package/.code/agents/5f8dc01c-47b3-4163-b0b3-aa31be89fcdc/status.txt +1 -0
  21. package/.code/agents/7/exec-call_HltHpkDox0Zm1vGEjdksUgpE.txt +1120 -0
  22. package/.code/agents/7/exec-call_LCATrOPPAgbxW9Q1z0XaVi2E.txt +2646 -0
  23. package/.code/agents/7/exec-call_W8DeRfNG9hvbgVFvf0clBf6R.txt +2646 -0
  24. package/.code/agents/94a0ddf3-a304-4ec3-913e-3cceef509948/error.txt +1 -0
  25. package/.code/agents/e2c752b7-711d-423a-af57-f53c809deb84/result.txt +160 -0
  26. package/.code/agents/e6601719-c31f-4a0e-8c71-d70787d0ab71/status.txt +1 -0
  27. package/.code/agents/f250b7ed-5bd5-4036-aa8c-ce63caee7d61/result.txt +20 -0
  28. package/AGENTS.md +1 -36
  29. package/CLAUDE.md +131 -51
  30. package/NUL +0 -1
  31. package/README.md +27 -14
  32. package/dist/bundle/index.cjs +41 -41
  33. package/dist/server/YNABMCPServer.js +28 -381
  34. package/dist/server/config.d.ts +2 -0
  35. package/dist/server/config.js +1 -0
  36. package/dist/tools/accountTools.d.ts +2 -0
  37. package/dist/tools/accountTools.js +45 -0
  38. package/dist/tools/adapters.d.ts +12 -0
  39. package/dist/tools/adapters.js +25 -0
  40. package/dist/tools/budgetTools.d.ts +2 -0
  41. package/dist/tools/budgetTools.js +30 -0
  42. package/dist/tools/categoryTools.d.ts +2 -0
  43. package/dist/tools/categoryTools.js +45 -0
  44. package/dist/tools/monthTools.d.ts +2 -0
  45. package/dist/tools/monthTools.js +32 -0
  46. package/dist/tools/payeeTools.d.ts +2 -0
  47. package/dist/tools/payeeTools.js +32 -0
  48. package/dist/tools/reconciliation/index.d.ts +2 -0
  49. package/dist/tools/reconciliation/index.js +33 -0
  50. package/dist/tools/schemas/common.d.ts +3 -0
  51. package/dist/tools/schemas/common.js +3 -0
  52. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
  53. package/dist/tools/transactionTools.d.ts +2 -0
  54. package/dist/tools/transactionTools.js +129 -0
  55. package/dist/tools/utilityTools.d.ts +3 -1
  56. package/dist/tools/utilityTools.js +32 -2
  57. package/dist/types/index.d.ts +1 -0
  58. package/dist/types/toolRegistration.d.ts +27 -0
  59. package/dist/types/toolRegistration.js +1 -0
  60. package/package.json +2 -2
  61. package/scripts/run-domain-integration-tests.js +4 -1
  62. package/src/__tests__/workflows.e2e.test.ts +1 -7
  63. package/src/server/YNABMCPServer.ts +33 -519
  64. package/src/server/__tests__/toolRegistration.test.ts +236 -0
  65. package/src/server/config.ts +1 -0
  66. package/src/tools/__tests__/adapters.test.ts +113 -0
  67. package/src/tools/__tests__/transactionTools.test.ts +90 -17
  68. package/src/tools/__tests__/utilityTools.test.ts +7 -7
  69. package/src/tools/accountTools.ts +53 -0
  70. package/src/tools/adapters.ts +74 -0
  71. package/src/tools/budgetTools.ts +37 -0
  72. package/src/tools/categoryTools.ts +53 -0
  73. package/src/tools/monthTools.ts +39 -0
  74. package/src/tools/payeeTools.ts +39 -0
  75. package/src/tools/reconciliation/index.ts +45 -0
  76. package/src/tools/schemas/common.ts +18 -0
  77. package/src/tools/transactionTools.ts +150 -0
  78. package/src/tools/utilityTools.ts +42 -2
  79. package/src/types/index.ts +3 -0
  80. package/src/types/toolRegistration.ts +88 -0
  81. package/.dxtignore +0 -57
  82. package/.github/workflows/pr-description-check.yml +0 -88
  83. package/CODEREVIEW_RESPONSE.md +0 -128
  84. package/SCHEMA_IMPROVEMENT_SUMMARY.md +0 -120
  85. package/TESTING_NOTES.md +0 -217
  86. package/accountactivity-merged.csv +0 -149
  87. package/bundle-analysis.html +0 -13110
  88. package/docs/README.md +0 -72
  89. package/docs/getting-started/CONFIGURATION.md +0 -175
  90. package/docs/getting-started/INSTALLATION.md +0 -333
  91. package/docs/getting-started/QUICKSTART.md +0 -282
  92. package/docs/guides/ARCHITECTURE.md +0 -533
  93. package/docs/guides/DEPLOYMENT.md +0 -189
  94. package/docs/guides/INTEGRATION_TESTING.md +0 -730
  95. package/docs/guides/TESTING.md +0 -591
  96. package/docs/plans/2025-11-20-reloadable-config-token-validation.md +0 -93
  97. package/docs/plans/2025-11-21-fix-transaction-cached-property.md +0 -362
  98. package/docs/plans/2025-11-21-reconciliation-error-handling.md +0 -90
  99. package/docs/plans/2025-11-21-v014-hardening.md +0 -153
  100. package/docs/plans/reconciliation-v2-redesign.md +0 -1571
  101. package/docs/reconciliation-flow.md +0 -83
  102. package/docs/reference/EXAMPLES.md +0 -946
  103. package/docs/reference/TOOLS.md +0 -348
  104. package/docs/reference/TROUBLESHOOTING.md +0 -481
  105. package/fix-types.sh +0 -17
  106. package/test-csv-sample.csv +0 -28
  107. package/test-exports/sample_bank_statement.csv +0 -7
  108. package/test-reconcile-autodetect.js +0 -40
  109. package/test-reconcile-tool.js +0 -152
  110. package/test-reconcile-with-csv.cjs +0 -89
  111. package/test-statement.csv +0 -8
  112. package/test_debug.js +0 -47
  113. package/test_mcp_tools.mjs +0 -75
  114. package/test_simple.mjs +0 -16
@@ -4,6 +4,7 @@ import { z } from 'zod/v4';
4
4
  import type { DeltaFetcher } from './deltaFetcher.js';
5
5
  import type { DeltaCache } from '../server/deltaCache.js';
6
6
  import type { ServerKnowledgeStore } from '../server/serverKnowledgeStore.js';
7
+ import type { ToolFactory } from '../types/toolRegistration.js';
7
8
  export declare const ListCategoriesSchema: z.ZodObject<{
8
9
  budget_id: z.ZodString;
9
10
  }, z.core.$strict>;
@@ -25,3 +26,4 @@ export declare function handleListCategories(ynabAPI: ynab.API, params: ListCate
25
26
  export declare function handleGetCategory(ynabAPI: ynab.API, params: GetCategoryParams): Promise<CallToolResult>;
26
27
  export declare function handleUpdateCategory(ynabAPI: ynab.API, deltaCache: DeltaCache, knowledgeStore: ServerKnowledgeStore, params: UpdateCategoryParams): Promise<CallToolResult>;
27
28
  export declare function handleUpdateCategory(ynabAPI: ynab.API, params: UpdateCategoryParams): Promise<CallToolResult>;
29
+ export declare const registerCategoryTools: ToolFactory;
@@ -5,6 +5,8 @@ import { milliunitsToAmount } from '../utils/amountUtils.js';
5
5
  import { cacheManager, CACHE_TTLS, CacheManager } from '../server/cacheManager.js';
6
6
  import { CacheKeys } from '../server/cacheKeys.js';
7
7
  import { resolveDeltaFetcherArgs, resolveDeltaWriteArgs } from './deltaSupport.js';
8
+ import { createAdapters, createBudgetResolver } from './adapters.js';
9
+ import { ToolAnnotationPresets } from './toolCategories.js';
8
10
  export const ListCategoriesSchema = z
9
11
  .object({
10
12
  budget_id: z.string().min(1, 'Budget ID is required'),
@@ -199,6 +201,49 @@ export async function handleUpdateCategory(ynabAPI, deltaCacheOrParams, knowledg
199
201
  return handleCategoryError(error, 'Failed to update category');
200
202
  }
201
203
  }
204
+ export const registerCategoryTools = (registry, context) => {
205
+ const { adapt, adaptWithDelta, adaptWrite } = createAdapters(context);
206
+ const budgetResolver = createBudgetResolver(context);
207
+ registry.register({
208
+ name: 'list_categories',
209
+ description: 'List all categories for a specific budget',
210
+ inputSchema: ListCategoriesSchema,
211
+ handler: adaptWithDelta(handleListCategories),
212
+ defaultArgumentResolver: budgetResolver(),
213
+ metadata: {
214
+ annotations: {
215
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
216
+ title: 'YNAB: List Categories',
217
+ },
218
+ },
219
+ });
220
+ registry.register({
221
+ name: 'get_category',
222
+ description: 'Get detailed information for a specific category',
223
+ inputSchema: GetCategorySchema,
224
+ handler: adapt(handleGetCategory),
225
+ defaultArgumentResolver: budgetResolver(),
226
+ metadata: {
227
+ annotations: {
228
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
229
+ title: 'YNAB: Get Category Details',
230
+ },
231
+ },
232
+ });
233
+ registry.register({
234
+ name: 'update_category',
235
+ description: 'Update the budgeted amount for a category in the current month',
236
+ inputSchema: UpdateCategorySchema,
237
+ handler: adaptWrite(handleUpdateCategory),
238
+ defaultArgumentResolver: budgetResolver(),
239
+ metadata: {
240
+ annotations: {
241
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
242
+ title: 'YNAB: Update Category Budget',
243
+ },
244
+ },
245
+ });
246
+ };
202
247
  function handleCategoryError(error, defaultMessage) {
203
248
  let errorMessage = defaultMessage;
204
249
  if (error instanceof Error) {
@@ -2,6 +2,7 @@ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
2
  import * as ynab from 'ynab';
3
3
  import { z } from 'zod/v4';
4
4
  import type { DeltaFetcher } from './deltaFetcher.js';
5
+ import type { ToolFactory } from '../types/toolRegistration.js';
5
6
  export declare const GetMonthSchema: z.ZodObject<{
6
7
  budget_id: z.ZodString;
7
8
  month: z.ZodString;
@@ -14,3 +15,4 @@ export type ListMonthsParams = z.infer<typeof ListMonthsSchema>;
14
15
  export declare function handleGetMonth(ynabAPI: ynab.API, params: GetMonthParams): Promise<CallToolResult>;
15
16
  export declare function handleListMonths(ynabAPI: ynab.API, deltaFetcher: DeltaFetcher, params: ListMonthsParams): Promise<CallToolResult>;
16
17
  export declare function handleListMonths(ynabAPI: ynab.API, params: ListMonthsParams): Promise<CallToolResult>;
18
+ export declare const registerMonthTools: ToolFactory;
@@ -5,6 +5,8 @@ import { milliunitsToAmount } from '../utils/amountUtils.js';
5
5
  import { cacheManager, CACHE_TTLS, CacheManager } from '../server/cacheManager.js';
6
6
  import { CacheKeys } from '../server/cacheKeys.js';
7
7
  import { resolveDeltaFetcherArgs } from './deltaSupport.js';
8
+ import { createAdapters, createBudgetResolver } from './adapters.js';
9
+ import { ToolAnnotationPresets } from './toolCategories.js';
8
10
  export const GetMonthSchema = z
9
11
  .object({
10
12
  budget_id: z.string().min(1, 'Budget ID is required'),
@@ -106,3 +108,33 @@ export async function handleListMonths(ynabAPI, deltaFetcherOrParams, maybeParam
106
108
  };
107
109
  }, 'ynab:list_months', 'listing months');
108
110
  }
111
+ export const registerMonthTools = (registry, context) => {
112
+ const { adapt, adaptWithDelta } = createAdapters(context);
113
+ const budgetResolver = createBudgetResolver(context);
114
+ registry.register({
115
+ name: 'get_month',
116
+ description: 'Get budget data for a specific month',
117
+ inputSchema: GetMonthSchema,
118
+ handler: adapt(handleGetMonth),
119
+ defaultArgumentResolver: budgetResolver(),
120
+ metadata: {
121
+ annotations: {
122
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
123
+ title: 'YNAB: Get Month Details',
124
+ },
125
+ },
126
+ });
127
+ registry.register({
128
+ name: 'list_months',
129
+ description: 'List all months summary data for a budget',
130
+ inputSchema: ListMonthsSchema,
131
+ handler: adaptWithDelta(handleListMonths),
132
+ defaultArgumentResolver: budgetResolver(),
133
+ metadata: {
134
+ annotations: {
135
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
136
+ title: 'YNAB: List Months',
137
+ },
138
+ },
139
+ });
140
+ };
@@ -2,6 +2,7 @@ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
2
  import * as ynab from 'ynab';
3
3
  import { z } from 'zod/v4';
4
4
  import type { DeltaFetcher } from './deltaFetcher.js';
5
+ import type { ToolFactory } from '../types/toolRegistration.js';
5
6
  export declare const ListPayeesSchema: z.ZodObject<{
6
7
  budget_id: z.ZodString;
7
8
  limit: z.ZodOptional<z.ZodNumber>;
@@ -15,3 +16,4 @@ export type GetPayeeParams = z.infer<typeof GetPayeeSchema>;
15
16
  export declare function handleListPayees(ynabAPI: ynab.API, deltaFetcher: DeltaFetcher, params: ListPayeesParams): Promise<CallToolResult>;
16
17
  export declare function handleListPayees(ynabAPI: ynab.API, params: ListPayeesParams): Promise<CallToolResult>;
17
18
  export declare function handleGetPayee(ynabAPI: ynab.API, params: GetPayeeParams): Promise<CallToolResult>;
19
+ export declare const registerPayeeTools: ToolFactory;
@@ -4,6 +4,8 @@ import { responseFormatter } from '../server/responseFormatter.js';
4
4
  import { cacheManager, CACHE_TTLS, CacheManager } from '../server/cacheManager.js';
5
5
  import { CacheKeys } from '../server/cacheKeys.js';
6
6
  import { resolveDeltaFetcherArgs } from './deltaSupport.js';
7
+ import { createAdapters, createBudgetResolver } from './adapters.js';
8
+ import { ToolAnnotationPresets } from './toolCategories.js';
7
9
  export const ListPayeesSchema = z
8
10
  .object({
9
11
  budget_id: z.string().min(1, 'Budget ID is required'),
@@ -81,3 +83,33 @@ export async function handleGetPayee(ynabAPI, params) {
81
83
  };
82
84
  }, 'ynab:get_payee', 'getting payee details');
83
85
  }
86
+ export const registerPayeeTools = (registry, context) => {
87
+ const { adapt, adaptWithDelta } = createAdapters(context);
88
+ const budgetResolver = createBudgetResolver(context);
89
+ registry.register({
90
+ name: 'list_payees',
91
+ description: 'List all payees for a specific budget',
92
+ inputSchema: ListPayeesSchema,
93
+ handler: adaptWithDelta(handleListPayees),
94
+ defaultArgumentResolver: budgetResolver(),
95
+ metadata: {
96
+ annotations: {
97
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
98
+ title: 'YNAB: List Payees',
99
+ },
100
+ },
101
+ });
102
+ registry.register({
103
+ name: 'get_payee',
104
+ description: 'Get detailed information for a specific payee',
105
+ inputSchema: GetPayeeSchema,
106
+ handler: adapt(handleGetPayee),
107
+ defaultArgumentResolver: budgetResolver(),
108
+ metadata: {
109
+ annotations: {
110
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
111
+ title: 'YNAB: Get Payee Details',
112
+ },
113
+ },
114
+ });
115
+ };
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod/v4';
2
2
  import type * as ynab from 'ynab';
3
3
  import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
4
+ import type { ToolFactory } from '../../types/toolRegistration.js';
4
5
  import type { DeltaFetcher } from '../deltaFetcher.js';
5
6
  export type * from './types.js';
6
7
  export { analyzeReconciliation } from './analyzer.js';
@@ -52,3 +53,4 @@ export declare const ReconcileAccountSchema: z.ZodObject<{
52
53
  export type ReconcileAccountRequest = z.infer<typeof ReconcileAccountSchema>;
53
54
  export declare function handleReconcileAccount(ynabAPI: ynab.API, deltaFetcher: DeltaFetcher, params: ReconcileAccountRequest): Promise<CallToolResult>;
54
55
  export declare function handleReconcileAccount(ynabAPI: ynab.API, params: ReconcileAccountRequest): Promise<CallToolResult>;
56
+ export declare const registerReconciliationTools: ToolFactory;
@@ -1,6 +1,9 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import { z } from 'zod/v4';
3
3
  import { withToolErrorHandling } from '../../types/index.js';
4
+ import { createAdapters, createBudgetResolver } from '../adapters.js';
5
+ import { ToolAnnotationPresets } from '../toolCategories.js';
6
+ import { CompareTransactionsSchema, handleCompareTransactions, } from '../compareTransactions/index.js';
4
7
  import { analyzeReconciliation } from './analyzer.js';
5
8
  import { buildReconciliationPayload } from '../reconcileAdapter.js';
6
9
  import { executeReconciliation, } from './executor.js';
@@ -303,6 +306,36 @@ export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, mayb
303
306
  };
304
307
  }, 'ynab:reconcile_account', 'analyzing account reconciliation');
305
308
  }
309
+ export const registerReconciliationTools = (registry, context) => {
310
+ const { adapt, adaptWithDelta } = createAdapters(context);
311
+ const budgetResolver = createBudgetResolver(context);
312
+ registry.register({
313
+ name: 'compare_transactions',
314
+ description: 'Compare bank transactions from CSV with YNAB transactions to find missing entries',
315
+ inputSchema: CompareTransactionsSchema,
316
+ handler: adapt(handleCompareTransactions),
317
+ defaultArgumentResolver: budgetResolver(),
318
+ metadata: {
319
+ annotations: {
320
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
321
+ title: 'YNAB: Compare Transactions',
322
+ },
323
+ },
324
+ });
325
+ registry.register({
326
+ name: 'reconcile_account',
327
+ description: '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).',
328
+ inputSchema: ReconcileAccountSchema,
329
+ handler: adaptWithDelta(handleReconcileAccount),
330
+ defaultArgumentResolver: budgetResolver(),
331
+ metadata: {
332
+ annotations: {
333
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
334
+ title: 'YNAB: Reconcile Account',
335
+ },
336
+ },
337
+ });
338
+ };
306
339
  function mapCsvDateFormatToHint(format) {
307
340
  if (!format) {
308
341
  return undefined;
@@ -0,0 +1,3 @@
1
+ import { z } from 'zod/v4';
2
+ export declare const emptyObjectSchema: z.ZodObject<{}, z.core.$strict>;
3
+ export declare const looseObjectSchema: z.ZodObject<{}, z.core.$loose>;
@@ -0,0 +1,3 @@
1
+ import { z } from 'zod/v4';
2
+ export const emptyObjectSchema = z.object({}).strict();
3
+ export const looseObjectSchema = z.object({}).passthrough();
@@ -250,8 +250,8 @@ export declare const ExportTransactionsOutputSchema: z.ZodObject<{
250
250
  full_path: z.ZodString;
251
251
  export_directory: z.ZodString;
252
252
  export_mode: z.ZodEnum<{
253
- full: "full";
254
253
  minimal: "minimal";
254
+ full: "full";
255
255
  }>;
256
256
  minimal_fields: z.ZodNullable<z.ZodString>;
257
257
  filename_explanation: z.ZodString;
@@ -2,6 +2,7 @@ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
2
  import * as ynab from 'ynab';
3
3
  import type { SaveTransactionsResponseData } from 'ynab/dist/models/SaveTransactionsResponseData.js';
4
4
  import { z } from 'zod/v4';
5
+ import type { ToolFactory } from '../types/toolRegistration.js';
5
6
  import type { DeltaFetcher } from './deltaFetcher.js';
6
7
  import type { DeltaCache } from '../server/deltaCache.js';
7
8
  import type { ServerKnowledgeStore } from '../server/serverKnowledgeStore.js';
@@ -298,4 +299,5 @@ export declare function handleCreateTransactions(ynabAPI: ynab.API, deltaCache:
298
299
  export declare function handleCreateTransactions(ynabAPI: ynab.API, params: CreateTransactionsParams): Promise<CallToolResult>;
299
300
  export declare function handleUpdateTransactions(ynabAPI: ynab.API, deltaCache: DeltaCache, knowledgeStore: ServerKnowledgeStore, params: UpdateTransactionsParams): Promise<CallToolResult>;
300
301
  export declare function handleUpdateTransactions(ynabAPI: ynab.API, params: UpdateTransactionsParams): Promise<CallToolResult>;
302
+ export declare const registerTransactionTools: ToolFactory;
301
303
  export {};
@@ -1,11 +1,14 @@
1
1
  import { z } from 'zod/v4';
2
2
  import { createHash } from 'crypto';
3
3
  import { ValidationError, withToolErrorHandling } from '../types/index.js';
4
+ import { createAdapters, createBudgetResolver } from './adapters.js';
5
+ import { ToolAnnotationPresets } from './toolCategories.js';
4
6
  import { responseFormatter } from '../server/responseFormatter.js';
5
7
  import { amountToMilliunits, milliunitsToAmount } from '../utils/amountUtils.js';
6
8
  import { cacheManager, CACHE_TTLS, CacheManager } from '../server/cacheManager.js';
7
9
  import { globalRequestLogger } from '../server/requestLogger.js';
8
10
  import { resolveDeltaFetcherArgs, resolveDeltaWriteArgs } from './deltaSupport.js';
11
+ import { handleExportTransactions, ExportTransactionsSchema } from './exportTransactions.js';
9
12
  function ensureTransaction(transaction, errorMessage) {
10
13
  if (!transaction) {
11
14
  throw new Error(errorMessage);
@@ -496,6 +499,11 @@ export async function handleListTransactions(ynabAPI, deltaFetcherOrParams, mayb
496
499
  let cacheHit = false;
497
500
  let usedDelta = false;
498
501
  if (params.account_id) {
502
+ const accountsResult = await deltaFetcher.fetchAccounts(params.budget_id);
503
+ const accountExists = accountsResult.data.some((account) => account.id === params.account_id);
504
+ if (!accountExists) {
505
+ throw new Error(`Account ${params.account_id} not found in budget ${params.budget_id}`);
506
+ }
499
507
  const result = await deltaFetcher.fetchTransactionsByAccount(params.budget_id, params.account_id, params.since_date);
500
508
  transactions = result.data;
501
509
  cacheHit = result.wasCached;
@@ -1708,3 +1716,124 @@ function handleTransactionError(error, defaultMessage) {
1708
1716
  ],
1709
1717
  };
1710
1718
  }
1719
+ export const registerTransactionTools = (registry, context) => {
1720
+ const { adapt, adaptWithDelta, adaptWrite } = createAdapters(context);
1721
+ const budgetResolver = createBudgetResolver(context);
1722
+ registry.register({
1723
+ name: 'list_transactions',
1724
+ description: 'List transactions for a budget with optional filtering',
1725
+ inputSchema: ListTransactionsSchema,
1726
+ handler: adaptWithDelta(handleListTransactions),
1727
+ defaultArgumentResolver: budgetResolver(),
1728
+ metadata: {
1729
+ annotations: {
1730
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
1731
+ title: 'YNAB: List Transactions',
1732
+ },
1733
+ },
1734
+ });
1735
+ registry.register({
1736
+ name: 'export_transactions',
1737
+ description: 'Export all transactions to a JSON file with descriptive filename',
1738
+ inputSchema: ExportTransactionsSchema,
1739
+ handler: adapt(handleExportTransactions),
1740
+ defaultArgumentResolver: budgetResolver(),
1741
+ metadata: {
1742
+ annotations: {
1743
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
1744
+ title: 'YNAB: Export Transactions',
1745
+ },
1746
+ },
1747
+ });
1748
+ registry.register({
1749
+ name: 'get_transaction',
1750
+ description: 'Get detailed information for a specific transaction',
1751
+ inputSchema: GetTransactionSchema,
1752
+ handler: adapt(handleGetTransaction),
1753
+ defaultArgumentResolver: budgetResolver(),
1754
+ metadata: {
1755
+ annotations: {
1756
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
1757
+ title: 'YNAB: Get Transaction Details',
1758
+ },
1759
+ },
1760
+ });
1761
+ registry.register({
1762
+ name: 'create_transaction',
1763
+ description: 'Create a new transaction in the specified budget and account',
1764
+ inputSchema: CreateTransactionSchema,
1765
+ handler: adaptWrite(handleCreateTransaction),
1766
+ defaultArgumentResolver: budgetResolver(),
1767
+ metadata: {
1768
+ annotations: {
1769
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
1770
+ title: 'YNAB: Create Transaction',
1771
+ },
1772
+ },
1773
+ });
1774
+ registry.register({
1775
+ name: 'create_transactions',
1776
+ description: 'Create multiple transactions in a single batch (1-100 items) with duplicate detection, dry-run validation, and automatic response size management with correlation metadata.',
1777
+ inputSchema: CreateTransactionsSchema,
1778
+ handler: adaptWrite(handleCreateTransactions),
1779
+ defaultArgumentResolver: budgetResolver(),
1780
+ metadata: {
1781
+ annotations: {
1782
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
1783
+ title: 'YNAB: Create Multiple Transactions',
1784
+ },
1785
+ },
1786
+ });
1787
+ registry.register({
1788
+ name: 'create_receipt_split_transaction',
1789
+ description: 'Create a split transaction from receipt items with proportional tax allocation',
1790
+ inputSchema: CreateReceiptSplitTransactionSchema,
1791
+ handler: adaptWrite(handleCreateReceiptSplitTransaction),
1792
+ defaultArgumentResolver: budgetResolver(),
1793
+ metadata: {
1794
+ annotations: {
1795
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
1796
+ title: 'YNAB: Create Split Transaction from Receipt',
1797
+ },
1798
+ },
1799
+ });
1800
+ registry.register({
1801
+ name: 'update_transaction',
1802
+ description: 'Update an existing transaction',
1803
+ inputSchema: UpdateTransactionSchema,
1804
+ handler: adaptWrite(handleUpdateTransaction),
1805
+ defaultArgumentResolver: budgetResolver(),
1806
+ metadata: {
1807
+ annotations: {
1808
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
1809
+ title: 'YNAB: Update Transaction',
1810
+ },
1811
+ },
1812
+ });
1813
+ registry.register({
1814
+ name: 'update_transactions',
1815
+ description: '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.',
1816
+ inputSchema: UpdateTransactionsSchema,
1817
+ handler: adaptWrite(handleUpdateTransactions),
1818
+ defaultArgumentResolver: budgetResolver(),
1819
+ metadata: {
1820
+ annotations: {
1821
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
1822
+ title: 'YNAB: Update Multiple Transactions',
1823
+ },
1824
+ },
1825
+ });
1826
+ registry.register({
1827
+ name: 'delete_transaction',
1828
+ description: 'Delete a transaction from the specified budget',
1829
+ inputSchema: DeleteTransactionSchema,
1830
+ handler: adaptWrite(handleDeleteTransaction),
1831
+ defaultArgumentResolver: budgetResolver(),
1832
+ metadata: {
1833
+ annotations: {
1834
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_DELETE,
1835
+ title: 'YNAB: Delete Transaction',
1836
+ },
1837
+ },
1838
+ });
1839
+ };
@@ -1,10 +1,12 @@
1
1
  import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
2
  import * as ynab from 'ynab';
3
3
  import { z } from 'zod/v4';
4
+ import type { ToolFactory } from '../types/toolRegistration.js';
4
5
  export declare const ConvertAmountSchema: z.ZodObject<{
5
6
  amount: z.ZodNumber;
6
7
  to_milliunits: z.ZodBoolean;
7
8
  }, z.core.$strict>;
8
9
  export type ConvertAmountParams = z.infer<typeof ConvertAmountSchema>;
9
10
  export declare function handleGetUser(ynabAPI: ynab.API): Promise<CallToolResult>;
10
- export declare function handleConvertAmount(params: ConvertAmountParams): Promise<CallToolResult>;
11
+ export declare function handleConvertAmount(_ynabAPI: ynab.API, params: ConvertAmountParams): Promise<CallToolResult>;
12
+ export declare const registerUtilityTools: ToolFactory;
@@ -1,6 +1,9 @@
1
1
  import { z } from 'zod/v4';
2
- import { withToolErrorHandling } from '../types/index.js';
3
2
  import { responseFormatter } from '../server/responseFormatter.js';
3
+ import { withToolErrorHandling } from '../types/index.js';
4
+ import { createAdapters } from './adapters.js';
5
+ import { emptyObjectSchema } from './schemas/common.js';
6
+ import { ToolAnnotationPresets } from './toolCategories.js';
4
7
  export const ConvertAmountSchema = z
5
8
  .object({
6
9
  amount: z.number().finite(),
@@ -24,7 +27,7 @@ export async function handleGetUser(ynabAPI) {
24
27
  };
25
28
  }, 'ynab:get_user', 'getting user information');
26
29
  }
27
- export async function handleConvertAmount(params) {
30
+ export async function handleConvertAmount(_ynabAPI, params) {
28
31
  return await withToolErrorHandling(async () => {
29
32
  const { amount, to_milliunits } = params;
30
33
  let result;
@@ -54,3 +57,30 @@ export async function handleConvertAmount(params) {
54
57
  };
55
58
  }, 'ynab:convert_amount', 'converting amount');
56
59
  }
60
+ export const registerUtilityTools = (registry, context) => {
61
+ const { adapt, adaptNoInput } = createAdapters(context);
62
+ registry.register({
63
+ name: 'get_user',
64
+ description: 'Get information about the authenticated user',
65
+ inputSchema: emptyObjectSchema,
66
+ handler: adaptNoInput(handleGetUser),
67
+ metadata: {
68
+ annotations: {
69
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
70
+ title: 'YNAB: Get User Information',
71
+ },
72
+ },
73
+ });
74
+ registry.register({
75
+ name: 'convert_amount',
76
+ description: 'Convert between dollars and milliunits with integer arithmetic for precision',
77
+ inputSchema: ConvertAmountSchema,
78
+ handler: adapt(handleConvertAmount),
79
+ metadata: {
80
+ annotations: {
81
+ ...ToolAnnotationPresets.UTILITY_LOCAL,
82
+ title: 'YNAB: Convert Amount',
83
+ },
84
+ },
85
+ });
86
+ };
@@ -18,3 +18,4 @@ export { RequestLogger, globalRequestLogger, type LogEntry, type LoggerConfig, }
18
18
  export { SecurityMiddleware, withSecurityWrapper, type SecurityContext, } from '../server/securityMiddleware.js';
19
19
  export type { MCPToolAnnotations } from './toolAnnotations.js';
20
20
  export type { ToolDefinition } from '../server/toolRegistry.js';
21
+ export type { ToolContext, ToolFactory, BudgetIdResolverFactory } from './toolRegistration.js';
@@ -0,0 +1,27 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import * as ynab from 'ynab';
3
+ import type { ToolRegistry, ToolExecutionPayload, DefaultArgumentResolver } from '../server/toolRegistry.js';
4
+ import type { DeltaFetcher } from '../tools/deltaFetcher.js';
5
+ import type { DeltaCache } from '../server/deltaCache.js';
6
+ import type { ServerKnowledgeStore } from '../server/serverKnowledgeStore.js';
7
+ import type { DiagnosticManager } from '../server/diagnostics.js';
8
+ import type { CacheManager } from '../server/cacheManager.js';
9
+ export interface ToolContext {
10
+ ynabAPI: ynab.API;
11
+ deltaFetcher: DeltaFetcher;
12
+ deltaCache: DeltaCache;
13
+ serverKnowledgeStore: ServerKnowledgeStore;
14
+ getDefaultBudgetId: () => string | undefined;
15
+ setDefaultBudget: (budgetId: string) => void;
16
+ cacheManager: CacheManager;
17
+ diagnosticManager?: DiagnosticManager;
18
+ }
19
+ export type ToolFactory = (registry: ToolRegistry, context: ToolContext) => void;
20
+ export type Adapter<TInput extends Record<string, unknown>> = (payload: ToolExecutionPayload<TInput>) => Promise<CallToolResult>;
21
+ export type Handler<TInput extends Record<string, unknown>> = (api: ynab.API, params: TInput) => Promise<CallToolResult>;
22
+ export type DeltaHandler<TInput extends Record<string, unknown>> = (api: ynab.API, deltaFetcher: DeltaFetcher, params: TInput) => Promise<CallToolResult>;
23
+ export type WriteHandler<TInput extends Record<string, unknown>> = (api: ynab.API, deltaCache: DeltaCache, serverKnowledgeStore: ServerKnowledgeStore, params: TInput) => Promise<CallToolResult>;
24
+ export type NoInputHandler = (api: ynab.API) => Promise<CallToolResult>;
25
+ export type BudgetIdResolverFactory = <TInput extends {
26
+ budget_id?: string | undefined;
27
+ }>() => DefaultArgumentResolver<TInput>;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dizzlkheinz/ynab-mcpb",
3
- "version": "0.16.0",
3
+ "version": "0.17.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",
@@ -66,7 +66,7 @@
66
66
  "author": "",
67
67
  "license": "AGPL-3.0",
68
68
  "dependencies": {
69
- "@modelcontextprotocol/sdk": "^1.22.0",
69
+ "@modelcontextprotocol/sdk": "^1.24.3",
70
70
  "chrono-node": "^2.9.0",
71
71
  "csv-parse": "^6.1.0",
72
72
  "d3-array": "^3.2.4",
@@ -22,10 +22,13 @@ const env = {
22
22
  };
23
23
 
24
24
  const vitestArgs = ['vitest', 'run', '--project', 'integration:domain', ...passthroughArgs];
25
- const runner = process.platform === 'win32' ? 'npx.cmd' : 'npx';
25
+ const runner = 'npx';
26
+ const useShell = process.platform === 'win32';
27
+
26
28
  const child = spawn(runner, vitestArgs, {
27
29
  stdio: 'inherit',
28
30
  env,
31
+ shell: useShell,
29
32
  });
30
33
 
31
34
  child.on('close', (code) => {
@@ -1065,13 +1065,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
1065
1065
  expect(tool.description).toBeDefined();
1066
1066
  expect(tool.inputSchema).toBeDefined();
1067
1067
 
1068
- // Verify that all tools define outputSchema (as guaranteed by CHANGELOG.md and docs/reference/TOOLS.md)
1069
- // Note: Some utility tools like diagnostic_info or clear_cache may not define structured outputs,
1070
- // but most data-retrieval and CRUD tools should have output schemas.
1071
- expect(
1072
- tool.outputSchema,
1073
- `Tool '${tool.name}' should define an outputSchema`,
1074
- ).toBeDefined();
1068
+ // Output schemas are optional; tools may omit them.
1075
1069
  }
1076
1070
  });
1077
1071
  });