@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
@@ -11,19 +11,18 @@ import { loadConfig } from './config.js';
11
11
  import { createErrorHandler, ErrorHandler } from './errorHandler.js';
12
12
  import { BudgetResolver } from './budgetResolver.js';
13
13
  import { SecurityMiddleware, withSecurityWrapper } from './securityMiddleware.js';
14
- import { handleListBudgets, handleGetBudget, GetBudgetSchema } from '../tools/budgetTools.js';
15
- import { handleListAccounts, handleGetAccount, handleCreateAccount, ListAccountsSchema, GetAccountSchema, CreateAccountSchema, } from '../tools/accountTools.js';
16
- import { handleListTransactions, handleGetTransaction, handleCreateTransaction, handleCreateTransactions, handleCreateReceiptSplitTransaction, handleUpdateTransaction, handleUpdateTransactions, handleDeleteTransaction, ListTransactionsSchema, GetTransactionSchema, CreateTransactionSchema, CreateTransactionsSchema, CreateReceiptSplitTransactionSchema, UpdateTransactionSchema, UpdateTransactionsSchema, DeleteTransactionSchema, } from '../tools/transactionTools.js';
17
- import { handleExportTransactions, ExportTransactionsSchema } from '../tools/exportTransactions.js';
18
- import { handleCompareTransactions, CompareTransactionsSchema, } from '../tools/compareTransactions/index.js';
19
- import { handleReconcileAccount, ReconcileAccountSchema } from '../tools/reconciliation/index.js';
20
- import { handleListCategories, handleGetCategory, handleUpdateCategory, ListCategoriesSchema, GetCategorySchema, UpdateCategorySchema, } from '../tools/categoryTools.js';
21
- import { handleListPayees, handleGetPayee, ListPayeesSchema, GetPayeeSchema, } from '../tools/payeeTools.js';
22
- import { handleGetMonth, handleListMonths, GetMonthSchema, ListMonthsSchema, } from '../tools/monthTools.js';
23
- import { handleGetUser, handleConvertAmount, ConvertAmountSchema } from '../tools/utilityTools.js';
14
+ import { registerBudgetTools } from '../tools/budgetTools.js';
15
+ import { registerAccountTools } from '../tools/accountTools.js';
16
+ import { registerTransactionTools } from '../tools/transactionTools.js';
17
+ import { registerReconciliationTools } from '../tools/reconciliation/index.js';
18
+ import { registerCategoryTools } from '../tools/categoryTools.js';
19
+ import { registerPayeeTools } from '../tools/payeeTools.js';
20
+ import { registerMonthTools } from '../tools/monthTools.js';
21
+ import { registerUtilityTools } from '../tools/utilityTools.js';
22
+ import { emptyObjectSchema } from '../tools/schemas/common.js';
24
23
  import { cacheManager, CacheManager } from './cacheManager.js';
25
24
  import { responseFormatter } from './responseFormatter.js';
26
- import { ToolRegistry, DefaultArgumentResolutionError, } from './toolRegistry.js';
25
+ import { ToolRegistry } from './toolRegistry.js';
27
26
  import { ResourceManager } from './resources.js';
28
27
  import { PromptManager } from './prompts.js';
29
28
  import { DiagnosticManager } from './diagnostics.js';
@@ -31,12 +30,11 @@ import { ServerKnowledgeStore } from './serverKnowledgeStore.js';
31
30
  import { DeltaCache } from './deltaCache.js';
32
31
  import { DeltaFetcher } from '../tools/deltaFetcher.js';
33
32
  import { ToolAnnotationPresets } from '../tools/toolCategories.js';
34
- import { GetUserOutputSchema, ConvertAmountOutputSchema, GetDefaultBudgetOutputSchema, SetDefaultBudgetOutputSchema, ClearCacheOutputSchema, SetOutputFormatOutputSchema, DiagnosticInfoOutputSchema, GetBudgetOutputSchema, ListBudgetsOutputSchema, ListAccountsOutputSchema, GetAccountOutputSchema, GetTransactionOutputSchema, ExportTransactionsOutputSchema, CompareTransactionsOutputSchema, ListCategoriesOutputSchema, GetCategoryOutputSchema, ListPayeesOutputSchema, GetPayeeOutputSchema, GetMonthOutputSchema, ListMonthsOutputSchema, } from '../tools/schemas/outputs/index.js';
35
33
  export class YNABMCPServer {
36
34
  constructor(exitOnError = true) {
37
35
  this.exitOnError = exitOnError;
38
36
  this.configInstance = loadConfig();
39
- this.defaultBudgetId = process.env['YNAB_DEFAULT_BUDGET_ID'];
37
+ this.defaultBudgetId = this.configInstance.YNAB_DEFAULT_BUDGET_ID;
40
38
  this.ynabAPI = new ynab.API(this.configInstance.YNAB_ACCESS_TOKEN);
41
39
  this.serverVersion = this.readPackageVersion() ?? '0.0.0';
42
40
  this.server = new Server({
@@ -211,23 +209,16 @@ export class YNABMCPServer {
211
209
  const register = (definition) => {
212
210
  this.toolRegistry.register(definition);
213
211
  };
214
- const adapt = (handler) => async ({ input }) => handler(this.ynabAPI, input);
215
- const adaptNoInput = (handler) => async (_payload) => handler(this.ynabAPI);
216
- const adaptWithDelta = (handler) => async ({ input }) => handler(this.ynabAPI, this.deltaFetcher, input);
217
- const adaptWrite = (handler) => async ({ input }) => handler(this.ynabAPI, this.deltaCache, this.serverKnowledgeStore, input);
218
- const resolveBudgetId = () => {
219
- return ({ rawArguments }) => {
220
- const provided = typeof rawArguments['budget_id'] === 'string' && rawArguments['budget_id'].length > 0
221
- ? rawArguments['budget_id']
222
- : undefined;
223
- const result = BudgetResolver.resolveBudgetId(provided, this.defaultBudgetId);
224
- if (typeof result === 'string') {
225
- return { budget_id: result };
226
- }
227
- throw new DefaultArgumentResolutionError(result);
228
- };
212
+ const toolContext = {
213
+ ynabAPI: this.ynabAPI,
214
+ deltaFetcher: this.deltaFetcher,
215
+ deltaCache: this.deltaCache,
216
+ serverKnowledgeStore: this.serverKnowledgeStore,
217
+ getDefaultBudgetId: () => this.defaultBudgetId,
218
+ setDefaultBudget: (budgetId) => this.setDefaultBudget(budgetId),
219
+ cacheManager,
220
+ diagnosticManager: this.diagnosticManager,
229
221
  };
230
- const emptyObjectSchema = z.object({}).strict();
231
222
  const setDefaultBudgetSchema = z.object({ budget_id: z.string().min(1) }).strict();
232
223
  const diagnosticInfoSchema = z
233
224
  .object({
@@ -245,38 +236,18 @@ export class YNABMCPServer {
245
236
  pretty_spaces: z.number().int().min(0).max(10).optional(),
246
237
  })
247
238
  .strict();
248
- const LooseObjectSchema = z.object({}).passthrough();
249
- register({
250
- name: 'list_budgets',
251
- description: "List all budgets associated with the user's account",
252
- inputSchema: emptyObjectSchema,
253
- outputSchema: ListBudgetsOutputSchema,
254
- handler: adaptWithDelta(handleListBudgets),
255
- metadata: {
256
- annotations: {
257
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
258
- title: 'YNAB: List Budgets',
259
- },
260
- },
261
- });
262
- register({
263
- name: 'get_budget',
264
- description: 'Get detailed information for a specific budget',
265
- inputSchema: GetBudgetSchema,
266
- outputSchema: GetBudgetOutputSchema,
267
- handler: adapt(handleGetBudget),
268
- metadata: {
269
- annotations: {
270
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
271
- title: 'YNAB: Get Budget Details',
272
- },
273
- },
274
- });
239
+ registerBudgetTools(this.toolRegistry, toolContext);
240
+ registerPayeeTools(this.toolRegistry, toolContext);
241
+ registerCategoryTools(this.toolRegistry, toolContext);
242
+ registerAccountTools(this.toolRegistry, toolContext);
243
+ registerMonthTools(this.toolRegistry, toolContext);
244
+ registerTransactionTools(this.toolRegistry, toolContext);
245
+ registerReconciliationTools(this.toolRegistry, toolContext);
246
+ registerUtilityTools(this.toolRegistry, toolContext);
275
247
  register({
276
248
  name: 'set_default_budget',
277
249
  description: 'Set the default budget for subsequent operations',
278
250
  inputSchema: setDefaultBudgetSchema,
279
- outputSchema: SetDefaultBudgetOutputSchema,
280
251
  handler: async ({ input }) => {
281
252
  const { budget_id } = input;
282
253
  await this.ynabAPI.budgets.getBudgetById(budget_id);
@@ -308,7 +279,6 @@ export class YNABMCPServer {
308
279
  name: 'get_default_budget',
309
280
  description: 'Get the currently set default budget',
310
281
  inputSchema: emptyObjectSchema,
311
- outputSchema: GetDefaultBudgetOutputSchema,
312
282
  handler: async () => {
313
283
  try {
314
284
  const defaultBudget = this.getDefaultBudget();
@@ -338,331 +308,10 @@ export class YNABMCPServer {
338
308
  },
339
309
  },
340
310
  });
341
- register({
342
- name: 'list_accounts',
343
- description: 'List all accounts for a specific budget (uses default budget if not specified)',
344
- inputSchema: ListAccountsSchema,
345
- outputSchema: ListAccountsOutputSchema,
346
- handler: adaptWithDelta(handleListAccounts),
347
- defaultArgumentResolver: resolveBudgetId(),
348
- metadata: {
349
- annotations: {
350
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
351
- title: 'YNAB: List Accounts',
352
- },
353
- },
354
- });
355
- register({
356
- name: 'get_account',
357
- description: 'Get detailed information for a specific account',
358
- inputSchema: GetAccountSchema,
359
- outputSchema: GetAccountOutputSchema,
360
- handler: adapt(handleGetAccount),
361
- defaultArgumentResolver: resolveBudgetId(),
362
- metadata: {
363
- annotations: {
364
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
365
- title: 'YNAB: Get Account Details',
366
- },
367
- },
368
- });
369
- register({
370
- name: 'create_account',
371
- description: 'Create a new account in the specified budget',
372
- inputSchema: CreateAccountSchema,
373
- outputSchema: LooseObjectSchema,
374
- handler: adaptWrite(handleCreateAccount),
375
- defaultArgumentResolver: resolveBudgetId(),
376
- metadata: {
377
- annotations: {
378
- ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
379
- title: 'YNAB: Create Account',
380
- },
381
- },
382
- });
383
- register({
384
- name: 'list_transactions',
385
- description: 'List transactions for a budget with optional filtering',
386
- inputSchema: ListTransactionsSchema,
387
- outputSchema: LooseObjectSchema,
388
- handler: adaptWithDelta(handleListTransactions),
389
- defaultArgumentResolver: resolveBudgetId(),
390
- metadata: {
391
- annotations: {
392
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
393
- title: 'YNAB: List Transactions',
394
- },
395
- },
396
- });
397
- register({
398
- name: 'export_transactions',
399
- description: 'Export all transactions to a JSON file with descriptive filename',
400
- inputSchema: ExportTransactionsSchema,
401
- outputSchema: ExportTransactionsOutputSchema,
402
- handler: adapt(handleExportTransactions),
403
- defaultArgumentResolver: resolveBudgetId(),
404
- metadata: {
405
- annotations: {
406
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
407
- title: 'YNAB: Export Transactions',
408
- },
409
- },
410
- });
411
- register({
412
- name: 'compare_transactions',
413
- description: 'Compare bank transactions from CSV with YNAB transactions to find missing entries',
414
- inputSchema: CompareTransactionsSchema,
415
- outputSchema: CompareTransactionsOutputSchema,
416
- handler: adapt(handleCompareTransactions),
417
- defaultArgumentResolver: resolveBudgetId(),
418
- metadata: {
419
- annotations: {
420
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
421
- title: 'YNAB: Compare Transactions',
422
- },
423
- },
424
- });
425
- register({
426
- name: 'reconcile_account',
427
- 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).',
428
- inputSchema: ReconcileAccountSchema,
429
- outputSchema: LooseObjectSchema,
430
- handler: adaptWithDelta(handleReconcileAccount),
431
- defaultArgumentResolver: resolveBudgetId(),
432
- metadata: {
433
- annotations: {
434
- ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
435
- title: 'YNAB: Reconcile Account',
436
- },
437
- },
438
- });
439
- register({
440
- name: 'get_transaction',
441
- description: 'Get detailed information for a specific transaction',
442
- inputSchema: GetTransactionSchema,
443
- outputSchema: GetTransactionOutputSchema,
444
- handler: adapt(handleGetTransaction),
445
- defaultArgumentResolver: resolveBudgetId(),
446
- metadata: {
447
- annotations: {
448
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
449
- title: 'YNAB: Get Transaction Details',
450
- },
451
- },
452
- });
453
- register({
454
- name: 'create_transaction',
455
- description: 'Create a new transaction in the specified budget and account',
456
- inputSchema: CreateTransactionSchema,
457
- outputSchema: LooseObjectSchema,
458
- handler: adaptWrite(handleCreateTransaction),
459
- defaultArgumentResolver: resolveBudgetId(),
460
- metadata: {
461
- annotations: {
462
- ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
463
- title: 'YNAB: Create Transaction',
464
- },
465
- },
466
- });
467
- register({
468
- name: 'create_transactions',
469
- 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.',
470
- inputSchema: CreateTransactionsSchema,
471
- outputSchema: LooseObjectSchema,
472
- handler: adaptWrite(handleCreateTransactions),
473
- defaultArgumentResolver: resolveBudgetId(),
474
- metadata: {
475
- annotations: {
476
- ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
477
- title: 'YNAB: Create Multiple Transactions',
478
- },
479
- },
480
- });
481
- register({
482
- name: 'update_transactions',
483
- 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.',
484
- inputSchema: UpdateTransactionsSchema,
485
- outputSchema: LooseObjectSchema,
486
- handler: adaptWrite(handleUpdateTransactions),
487
- defaultArgumentResolver: resolveBudgetId(),
488
- metadata: {
489
- annotations: {
490
- ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
491
- title: 'YNAB: Update Multiple Transactions',
492
- },
493
- },
494
- });
495
- register({
496
- name: 'create_receipt_split_transaction',
497
- description: 'Create a split transaction from receipt items with proportional tax allocation',
498
- inputSchema: CreateReceiptSplitTransactionSchema,
499
- outputSchema: LooseObjectSchema,
500
- handler: adaptWrite(handleCreateReceiptSplitTransaction),
501
- defaultArgumentResolver: resolveBudgetId(),
502
- metadata: {
503
- annotations: {
504
- ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
505
- title: 'YNAB: Create Split Transaction from Receipt',
506
- },
507
- },
508
- });
509
- register({
510
- name: 'update_transaction',
511
- description: 'Update an existing transaction',
512
- inputSchema: UpdateTransactionSchema,
513
- outputSchema: LooseObjectSchema,
514
- handler: adaptWrite(handleUpdateTransaction),
515
- defaultArgumentResolver: resolveBudgetId(),
516
- metadata: {
517
- annotations: {
518
- ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
519
- title: 'YNAB: Update Transaction',
520
- },
521
- },
522
- });
523
- register({
524
- name: 'delete_transaction',
525
- description: 'Delete a transaction from the specified budget',
526
- inputSchema: DeleteTransactionSchema,
527
- outputSchema: LooseObjectSchema,
528
- handler: adaptWrite(handleDeleteTransaction),
529
- defaultArgumentResolver: resolveBudgetId(),
530
- metadata: {
531
- annotations: {
532
- ...ToolAnnotationPresets.WRITE_EXTERNAL_DELETE,
533
- title: 'YNAB: Delete Transaction',
534
- },
535
- },
536
- });
537
- register({
538
- name: 'list_categories',
539
- description: 'List all categories for a specific budget',
540
- inputSchema: ListCategoriesSchema,
541
- outputSchema: ListCategoriesOutputSchema,
542
- handler: adaptWithDelta(handleListCategories),
543
- defaultArgumentResolver: resolveBudgetId(),
544
- metadata: {
545
- annotations: {
546
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
547
- title: 'YNAB: List Categories',
548
- },
549
- },
550
- });
551
- register({
552
- name: 'get_category',
553
- description: 'Get detailed information for a specific category',
554
- inputSchema: GetCategorySchema,
555
- outputSchema: GetCategoryOutputSchema,
556
- handler: adapt(handleGetCategory),
557
- defaultArgumentResolver: resolveBudgetId(),
558
- metadata: {
559
- annotations: {
560
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
561
- title: 'YNAB: Get Category Details',
562
- },
563
- },
564
- });
565
- register({
566
- name: 'update_category',
567
- description: 'Update the budgeted amount for a category in the current month',
568
- inputSchema: UpdateCategorySchema,
569
- outputSchema: LooseObjectSchema,
570
- handler: adaptWrite(handleUpdateCategory),
571
- defaultArgumentResolver: resolveBudgetId(),
572
- metadata: {
573
- annotations: {
574
- ...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
575
- title: 'YNAB: Update Category Budget',
576
- },
577
- },
578
- });
579
- register({
580
- name: 'list_payees',
581
- description: 'List all payees for a specific budget',
582
- inputSchema: ListPayeesSchema,
583
- outputSchema: ListPayeesOutputSchema,
584
- handler: adaptWithDelta(handleListPayees),
585
- defaultArgumentResolver: resolveBudgetId(),
586
- metadata: {
587
- annotations: {
588
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
589
- title: 'YNAB: List Payees',
590
- },
591
- },
592
- });
593
- register({
594
- name: 'get_payee',
595
- description: 'Get detailed information for a specific payee',
596
- inputSchema: GetPayeeSchema,
597
- outputSchema: GetPayeeOutputSchema,
598
- handler: adapt(handleGetPayee),
599
- defaultArgumentResolver: resolveBudgetId(),
600
- metadata: {
601
- annotations: {
602
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
603
- title: 'YNAB: Get Payee Details',
604
- },
605
- },
606
- });
607
- register({
608
- name: 'get_month',
609
- description: 'Get budget data for a specific month',
610
- inputSchema: GetMonthSchema,
611
- outputSchema: GetMonthOutputSchema,
612
- handler: adapt(handleGetMonth),
613
- defaultArgumentResolver: resolveBudgetId(),
614
- metadata: {
615
- annotations: {
616
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
617
- title: 'YNAB: Get Month Budget Data',
618
- },
619
- },
620
- });
621
- register({
622
- name: 'list_months',
623
- description: 'List all months summary data for a budget',
624
- inputSchema: ListMonthsSchema,
625
- outputSchema: ListMonthsOutputSchema,
626
- handler: adaptWithDelta(handleListMonths),
627
- defaultArgumentResolver: resolveBudgetId(),
628
- metadata: {
629
- annotations: {
630
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
631
- title: 'YNAB: List Months',
632
- },
633
- },
634
- });
635
- register({
636
- name: 'get_user',
637
- description: 'Get information about the authenticated user',
638
- inputSchema: emptyObjectSchema,
639
- outputSchema: GetUserOutputSchema,
640
- handler: adaptNoInput(handleGetUser),
641
- metadata: {
642
- annotations: {
643
- ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
644
- title: 'YNAB: Get User Information',
645
- },
646
- },
647
- });
648
- register({
649
- name: 'convert_amount',
650
- description: 'Convert between dollars and milliunits with integer arithmetic for precision',
651
- inputSchema: ConvertAmountSchema,
652
- outputSchema: ConvertAmountOutputSchema,
653
- handler: async ({ input }) => handleConvertAmount(input),
654
- metadata: {
655
- annotations: {
656
- ...ToolAnnotationPresets.UTILITY_LOCAL,
657
- title: 'YNAB: Convert Amount',
658
- },
659
- },
660
- });
661
311
  register({
662
312
  name: 'diagnostic_info',
663
313
  description: 'Get comprehensive diagnostic information about the MCP server',
664
314
  inputSchema: diagnosticInfoSchema,
665
- outputSchema: DiagnosticInfoOutputSchema,
666
315
  handler: async ({ input }) => {
667
316
  return this.diagnosticManager.collectDiagnostics(input);
668
317
  },
@@ -677,7 +326,6 @@ export class YNABMCPServer {
677
326
  name: 'clear_cache',
678
327
  description: 'Clear the in-memory cache (safe, no YNAB data is modified)',
679
328
  inputSchema: emptyObjectSchema,
680
- outputSchema: ClearCacheOutputSchema,
681
329
  handler: async () => {
682
330
  cacheManager.clear();
683
331
  return {
@@ -695,7 +343,6 @@ export class YNABMCPServer {
695
343
  name: 'set_output_format',
696
344
  description: 'Configure default JSON output formatting (minify or pretty spaces)',
697
345
  inputSchema: setOutputFormatSchema,
698
- outputSchema: SetOutputFormatOutputSchema,
699
346
  handler: async ({ input }) => {
700
347
  const options = {};
701
348
  if (typeof input.default_minify === 'boolean') {
@@ -2,6 +2,7 @@ import 'dotenv/config';
2
2
  import { z } from 'zod';
3
3
  declare const envSchema: z.ZodObject<{
4
4
  YNAB_ACCESS_TOKEN: z.ZodString;
5
+ YNAB_DEFAULT_BUDGET_ID: z.ZodOptional<z.ZodString>;
5
6
  MCP_PORT: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
6
7
  LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
7
8
  error: "error";
@@ -17,6 +18,7 @@ export declare function loadConfig(env?: NodeJS.ProcessEnv): AppConfig;
17
18
  export declare const config: {
18
19
  YNAB_ACCESS_TOKEN: string;
19
20
  LOG_LEVEL: "error" | "warn" | "info" | "debug" | "trace" | "fatal";
21
+ YNAB_DEFAULT_BUDGET_ID?: string | undefined;
20
22
  MCP_PORT?: number | undefined;
21
23
  };
22
24
  export {};
@@ -4,6 +4,7 @@ import { fromZodError } from 'zod-validation-error';
4
4
  import { ValidationError } from '../utils/errors.js';
5
5
  const envSchema = z.object({
6
6
  YNAB_ACCESS_TOKEN: z.string().trim().min(1, 'YNAB_ACCESS_TOKEN must be a non-empty string'),
7
+ YNAB_DEFAULT_BUDGET_ID: z.string().uuid('YNAB_DEFAULT_BUDGET_ID must be a valid UUID').optional(),
7
8
  MCP_PORT: z.coerce.number().int().positive().optional(),
8
9
  LOG_LEVEL: z.enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal']).default('info'),
9
10
  });
@@ -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 ListAccountsSchema: z.ZodObject<{
8
9
  budget_id: z.ZodString;
9
10
  limit: z.ZodOptional<z.ZodNumber>;
@@ -35,3 +36,4 @@ export declare function handleListAccounts(ynabAPI: ynab.API, params: ListAccoun
35
36
  export declare function handleGetAccount(ynabAPI: ynab.API, params: GetAccountParams): Promise<CallToolResult>;
36
37
  export declare function handleCreateAccount(ynabAPI: ynab.API, deltaCache: DeltaCache, knowledgeStore: ServerKnowledgeStore, params: CreateAccountParams): Promise<CallToolResult>;
37
38
  export declare function handleCreateAccount(ynabAPI: ynab.API, params: CreateAccountParams): Promise<CallToolResult>;
39
+ export declare const registerAccountTools: 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 ListAccountsSchema = z
9
11
  .object({
10
12
  budget_id: z.string().min(1, 'Budget ID is required'),
@@ -174,3 +176,46 @@ export async function handleCreateAccount(ynabAPI, deltaCacheOrParams, knowledge
174
176
  };
175
177
  }, 'ynab:create_account', 'creating account');
176
178
  }
179
+ export const registerAccountTools = (registry, context) => {
180
+ const { adapt, adaptWithDelta, adaptWrite } = createAdapters(context);
181
+ const budgetResolver = createBudgetResolver(context);
182
+ registry.register({
183
+ name: 'list_accounts',
184
+ description: 'List all accounts for a specific budget',
185
+ inputSchema: ListAccountsSchema,
186
+ handler: adaptWithDelta(handleListAccounts),
187
+ defaultArgumentResolver: budgetResolver(),
188
+ metadata: {
189
+ annotations: {
190
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
191
+ title: 'YNAB: List Accounts',
192
+ },
193
+ },
194
+ });
195
+ registry.register({
196
+ name: 'get_account',
197
+ description: 'Get detailed information for a specific account',
198
+ inputSchema: GetAccountSchema,
199
+ handler: adapt(handleGetAccount),
200
+ defaultArgumentResolver: budgetResolver(),
201
+ metadata: {
202
+ annotations: {
203
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
204
+ title: 'YNAB: Get Account Details',
205
+ },
206
+ },
207
+ });
208
+ registry.register({
209
+ name: 'create_account',
210
+ description: 'Create a new account in the specified budget',
211
+ inputSchema: CreateAccountSchema,
212
+ handler: adaptWrite(handleCreateAccount),
213
+ defaultArgumentResolver: budgetResolver(),
214
+ metadata: {
215
+ annotations: {
216
+ ...ToolAnnotationPresets.WRITE_EXTERNAL_CREATE,
217
+ title: 'YNAB: Create Account',
218
+ },
219
+ },
220
+ });
221
+ };
@@ -0,0 +1,12 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import type { ToolExecutionPayload, DefaultArgumentResolver } from '../server/toolRegistry.js';
3
+ import type { ToolContext, Handler, DeltaHandler, WriteHandler, NoInputHandler } from '../types/toolRegistration.js';
4
+ export declare function createAdapters(context: ToolContext): {
5
+ adapt: <TInput extends Record<string, unknown>>(handler: Handler<TInput>) => ({ input }: ToolExecutionPayload<TInput>) => Promise<CallToolResult>;
6
+ adaptNoInput: (handler: NoInputHandler) => (_payload: ToolExecutionPayload<Record<string, unknown>>) => Promise<CallToolResult>;
7
+ adaptWithDelta: <TInput extends Record<string, unknown>>(handler: DeltaHandler<TInput>) => ({ input }: ToolExecutionPayload<TInput>) => Promise<CallToolResult>;
8
+ adaptWrite: <TInput extends Record<string, unknown>>(handler: WriteHandler<TInput>) => ({ input }: ToolExecutionPayload<TInput>) => Promise<CallToolResult>;
9
+ };
10
+ export declare function createBudgetResolver(context: ToolContext): <TInput extends {
11
+ budget_id?: string | undefined;
12
+ }>() => DefaultArgumentResolver<TInput>;
@@ -0,0 +1,25 @@
1
+ import { BudgetResolver } from '../server/budgetResolver.js';
2
+ import { DefaultArgumentResolutionError } from '../server/toolRegistry.js';
3
+ export function createAdapters(context) {
4
+ const { ynabAPI, deltaFetcher, deltaCache, serverKnowledgeStore } = context;
5
+ return {
6
+ adapt: (handler) => async ({ input }) => handler(ynabAPI, input),
7
+ adaptNoInput: (handler) => async (_payload) => handler(ynabAPI),
8
+ adaptWithDelta: (handler) => async ({ input }) => handler(ynabAPI, deltaFetcher, input),
9
+ adaptWrite: (handler) => async ({ input }) => handler(ynabAPI, deltaCache, serverKnowledgeStore, input),
10
+ };
11
+ }
12
+ export function createBudgetResolver(context) {
13
+ return () => {
14
+ return ({ rawArguments }) => {
15
+ const provided = typeof rawArguments['budget_id'] === 'string' && rawArguments['budget_id'].length > 0
16
+ ? rawArguments['budget_id']
17
+ : undefined;
18
+ const result = BudgetResolver.resolveBudgetId(provided, context.getDefaultBudgetId());
19
+ if (typeof result === 'string') {
20
+ return { budget_id: result };
21
+ }
22
+ throw new DefaultArgumentResolutionError(result);
23
+ };
24
+ };
25
+ }
@@ -2,9 +2,11 @@ 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 GetBudgetSchema: z.ZodObject<{
6
7
  budget_id: z.ZodString;
7
8
  }, z.core.$strict>;
8
9
  export type GetBudgetParams = z.infer<typeof GetBudgetSchema>;
9
10
  export declare function handleListBudgets(ynabAPI: ynab.API, deltaFetcherOrParams?: DeltaFetcher | Record<string, unknown>, maybeParams?: Record<string, unknown>): Promise<CallToolResult>;
10
11
  export declare function handleGetBudget(ynabAPI: ynab.API, params: GetBudgetParams): Promise<CallToolResult>;
12
+ export declare const registerBudgetTools: ToolFactory;
@@ -2,6 +2,9 @@ import { z } from 'zod/v4';
2
2
  import { withToolErrorHandling } from '../types/index.js';
3
3
  import { responseFormatter } from '../server/responseFormatter.js';
4
4
  import { resolveDeltaFetcherArgs } from './deltaSupport.js';
5
+ import { createAdapters } from './adapters.js';
6
+ import { ToolAnnotationPresets } from './toolCategories.js';
7
+ import { emptyObjectSchema } from './schemas/common.js';
5
8
  export const GetBudgetSchema = z
6
9
  .object({
7
10
  budget_id: z.string().min(1, 'Budget ID is required'),
@@ -66,3 +69,30 @@ export async function handleGetBudget(ynabAPI, params) {
66
69
  };
67
70
  }, 'ynab:get_budget', 'getting budget details');
68
71
  }
72
+ export const registerBudgetTools = (registry, context) => {
73
+ const { adapt, adaptWithDelta } = createAdapters(context);
74
+ registry.register({
75
+ name: 'list_budgets',
76
+ description: "List all budgets associated with the user's account",
77
+ inputSchema: emptyObjectSchema,
78
+ handler: adaptWithDelta(handleListBudgets),
79
+ metadata: {
80
+ annotations: {
81
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
82
+ title: 'YNAB: List Budgets',
83
+ },
84
+ },
85
+ });
86
+ registry.register({
87
+ name: 'get_budget',
88
+ description: 'Get detailed information for a specific budget',
89
+ inputSchema: GetBudgetSchema,
90
+ handler: adapt(handleGetBudget),
91
+ metadata: {
92
+ annotations: {
93
+ ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
94
+ title: 'YNAB: Get Budget Details',
95
+ },
96
+ },
97
+ });
98
+ };