@oliverames/ynab-mcp-server 1.0.0 → 1.1.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.
- package/index.js +188 -47
- package/package.json +20 -33
package/index.js
CHANGED
|
@@ -19,8 +19,7 @@ const DEFAULT_BUDGET_ID = process.env.YNAB_BUDGET_ID;
|
|
|
19
19
|
// --- Helpers ---
|
|
20
20
|
|
|
21
21
|
function resolveBudgetId(input) {
|
|
22
|
-
const id = input || DEFAULT_BUDGET_ID;
|
|
23
|
-
if (!id) throw new Error("budgetId is required (pass it or set YNAB_BUDGET_ID env var)");
|
|
22
|
+
const id = input || DEFAULT_BUDGET_ID || "last-used";
|
|
24
23
|
return id;
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -40,8 +39,12 @@ async function run(fn) {
|
|
|
40
39
|
try {
|
|
41
40
|
return await fn();
|
|
42
41
|
} catch (e) {
|
|
43
|
-
const
|
|
44
|
-
|
|
42
|
+
const detail = e?.error?.detail;
|
|
43
|
+
const name = e?.error?.name;
|
|
44
|
+
const msg = detail
|
|
45
|
+
? (name ? `${name}: ${detail}` : detail)
|
|
46
|
+
: (e?.message || String(e));
|
|
47
|
+
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
|
|
@@ -61,7 +64,7 @@ server.tool("get_user", "Get the authenticated user", {}, () =>
|
|
|
61
64
|
})
|
|
62
65
|
);
|
|
63
66
|
|
|
64
|
-
server.tool("list_budgets", "List all budgets", {}, () =>
|
|
67
|
+
server.tool("list_budgets", "List all budgets. Use a budget ID from the results in other tools, or omit budgetId to use the last-used budget.", {}, () =>
|
|
65
68
|
run(async () => {
|
|
66
69
|
const { data } = await api.budgets.getBudgets();
|
|
67
70
|
return ok(data.budgets.map((b) => ({ id: b.id, name: b.name, last_modified_on: b.last_modified_on })));
|
|
@@ -100,6 +103,26 @@ server.tool(
|
|
|
100
103
|
|
|
101
104
|
// ==================== Accounts ====================
|
|
102
105
|
|
|
106
|
+
function formatAccount(a) {
|
|
107
|
+
const out = {
|
|
108
|
+
id: a.id,
|
|
109
|
+
name: a.name,
|
|
110
|
+
type: a.type,
|
|
111
|
+
on_budget: a.on_budget,
|
|
112
|
+
closed: a.closed,
|
|
113
|
+
balance: dollars(a.balance),
|
|
114
|
+
cleared_balance: dollars(a.cleared_balance),
|
|
115
|
+
uncleared_balance: dollars(a.uncleared_balance),
|
|
116
|
+
transfer_payee_id: a.transfer_payee_id,
|
|
117
|
+
direct_import_linked: a.direct_import_linked,
|
|
118
|
+
direct_import_in_error: a.direct_import_in_error,
|
|
119
|
+
last_reconciled_at: a.last_reconciled_at,
|
|
120
|
+
deleted: a.deleted,
|
|
121
|
+
};
|
|
122
|
+
if ("note" in a) out.note = a.note;
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
|
|
103
126
|
server.tool(
|
|
104
127
|
"list_accounts",
|
|
105
128
|
"List all accounts in a budget",
|
|
@@ -107,18 +130,7 @@ server.tool(
|
|
|
107
130
|
({ budgetId }) =>
|
|
108
131
|
run(async () => {
|
|
109
132
|
const { data } = await api.accounts.getAccounts(resolveBudgetId(budgetId));
|
|
110
|
-
return ok(
|
|
111
|
-
data.accounts.map((a) => ({
|
|
112
|
-
id: a.id,
|
|
113
|
-
name: a.name,
|
|
114
|
-
type: a.type,
|
|
115
|
-
on_budget: a.on_budget,
|
|
116
|
-
closed: a.closed,
|
|
117
|
-
balance: dollars(a.balance),
|
|
118
|
-
cleared_balance: dollars(a.cleared_balance),
|
|
119
|
-
uncleared_balance: dollars(a.uncleared_balance),
|
|
120
|
-
}))
|
|
121
|
-
);
|
|
133
|
+
return ok(data.accounts.map(formatAccount));
|
|
122
134
|
})
|
|
123
135
|
);
|
|
124
136
|
|
|
@@ -132,8 +144,7 @@ server.tool(
|
|
|
132
144
|
({ budgetId, accountId }) =>
|
|
133
145
|
run(async () => {
|
|
134
146
|
const { data } = await api.accounts.getAccountById(resolveBudgetId(budgetId), accountId);
|
|
135
|
-
|
|
136
|
-
return ok({ ...a, balance: dollars(a.balance), cleared_balance: dollars(a.cleared_balance), uncleared_balance: dollars(a.uncleared_balance) });
|
|
147
|
+
return ok(formatAccount(data.account));
|
|
137
148
|
})
|
|
138
149
|
);
|
|
139
150
|
|
|
@@ -151,12 +162,35 @@ server.tool(
|
|
|
151
162
|
const { data } = await api.accounts.createAccount(resolveBudgetId(budgetId), {
|
|
152
163
|
account: { name, type, balance: milliunits(bal) },
|
|
153
164
|
});
|
|
154
|
-
return ok(data.account);
|
|
165
|
+
return ok(formatAccount(data.account));
|
|
155
166
|
})
|
|
156
167
|
);
|
|
157
168
|
|
|
158
169
|
// ==================== Categories ====================
|
|
159
170
|
|
|
171
|
+
function formatCategory(c) {
|
|
172
|
+
return {
|
|
173
|
+
id: c.id,
|
|
174
|
+
category_group_id: c.category_group_id,
|
|
175
|
+
category_group_name: c.category_group_name,
|
|
176
|
+
name: c.name,
|
|
177
|
+
hidden: c.hidden,
|
|
178
|
+
note: c.note,
|
|
179
|
+
budgeted: dollars(c.budgeted),
|
|
180
|
+
activity: dollars(c.activity),
|
|
181
|
+
balance: dollars(c.balance),
|
|
182
|
+
goal_type: c.goal_type,
|
|
183
|
+
goal_target: dollars(c.goal_target),
|
|
184
|
+
goal_target_date: c.goal_target_date,
|
|
185
|
+
goal_percentage_complete: c.goal_percentage_complete,
|
|
186
|
+
goal_months_to_budget: c.goal_months_to_budget,
|
|
187
|
+
goal_under_funded: dollars(c.goal_under_funded),
|
|
188
|
+
goal_overall_funded: dollars(c.goal_overall_funded),
|
|
189
|
+
goal_overall_left: dollars(c.goal_overall_left),
|
|
190
|
+
goal_needs_whole_amount: c.goal_needs_whole_amount,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
160
194
|
server.tool(
|
|
161
195
|
"list_categories",
|
|
162
196
|
"List all category groups and their categories",
|
|
@@ -176,6 +210,7 @@ server.tool(
|
|
|
176
210
|
budgeted: dollars(c.budgeted),
|
|
177
211
|
activity: dollars(c.activity),
|
|
178
212
|
balance: dollars(c.balance),
|
|
213
|
+
goal_type: c.goal_type,
|
|
179
214
|
})),
|
|
180
215
|
}))
|
|
181
216
|
);
|
|
@@ -192,8 +227,7 @@ server.tool(
|
|
|
192
227
|
({ budgetId, categoryId }) =>
|
|
193
228
|
run(async () => {
|
|
194
229
|
const { data } = await api.categories.getCategoryById(resolveBudgetId(budgetId), categoryId);
|
|
195
|
-
|
|
196
|
-
return ok({ ...c, budgeted: dollars(c.budgeted), activity: dollars(c.activity), balance: dollars(c.balance), goal_target: dollars(c.goal_target) });
|
|
230
|
+
return ok(formatCategory(data.category));
|
|
197
231
|
})
|
|
198
232
|
);
|
|
199
233
|
|
|
@@ -208,8 +242,7 @@ server.tool(
|
|
|
208
242
|
({ budgetId, month, categoryId }) =>
|
|
209
243
|
run(async () => {
|
|
210
244
|
const { data } = await api.categories.getMonthCategoryById(resolveBudgetId(budgetId), month, categoryId);
|
|
211
|
-
|
|
212
|
-
return ok({ ...c, budgeted: dollars(c.budgeted), activity: dollars(c.activity), balance: dollars(c.balance) });
|
|
245
|
+
return ok(formatCategory(data.category));
|
|
213
246
|
})
|
|
214
247
|
);
|
|
215
248
|
|
|
@@ -227,8 +260,33 @@ server.tool(
|
|
|
227
260
|
const { data } = await api.categories.updateMonthCategory(resolveBudgetId(budgetId), month, categoryId, {
|
|
228
261
|
category: { budgeted: milliunits(budgeted) },
|
|
229
262
|
});
|
|
230
|
-
|
|
231
|
-
|
|
263
|
+
return ok(formatCategory(data.category));
|
|
264
|
+
})
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
server.tool(
|
|
268
|
+
"update_category",
|
|
269
|
+
"Update a category's name, note, goal target, or move it to a different group",
|
|
270
|
+
{
|
|
271
|
+
budgetId: z.string().optional().describe("Budget ID (uses default if not provided)"),
|
|
272
|
+
categoryId: z.string().describe("Category ID"),
|
|
273
|
+
name: z.string().optional().describe("New category name"),
|
|
274
|
+
note: z.string().nullable().optional().describe("Category note (null to clear)"),
|
|
275
|
+
categoryGroupId: z.string().optional().describe("Move to a different category group"),
|
|
276
|
+
goalTarget: z.number().nullable().optional().describe("Goal target amount in dollars (only if category already has a goal)"),
|
|
277
|
+
},
|
|
278
|
+
({ budgetId, categoryId, name, note, categoryGroupId, goalTarget }) =>
|
|
279
|
+
run(async () => {
|
|
280
|
+
const cat = {};
|
|
281
|
+
if (name !== undefined) cat.name = name;
|
|
282
|
+
if (note !== undefined) cat.note = note;
|
|
283
|
+
if (categoryGroupId !== undefined) cat.category_group_id = categoryGroupId;
|
|
284
|
+
if (goalTarget !== undefined) cat.goal_target = goalTarget != null ? milliunits(goalTarget) : null;
|
|
285
|
+
|
|
286
|
+
const { data } = await api.categories.updateCategory(resolveBudgetId(budgetId), categoryId, {
|
|
287
|
+
category: cat,
|
|
288
|
+
});
|
|
289
|
+
return ok(formatCategory(data.category));
|
|
232
290
|
})
|
|
233
291
|
);
|
|
234
292
|
|
|
@@ -292,6 +350,7 @@ server.tool(
|
|
|
292
350
|
budgeted: dollars(m.budgeted),
|
|
293
351
|
activity: dollars(m.activity),
|
|
294
352
|
to_be_budgeted: dollars(m.to_be_budgeted),
|
|
353
|
+
age_of_money: m.age_of_money,
|
|
295
354
|
}))
|
|
296
355
|
);
|
|
297
356
|
})
|
|
@@ -314,12 +373,17 @@ server.tool(
|
|
|
314
373
|
budgeted: dollars(m.budgeted),
|
|
315
374
|
activity: dollars(m.activity),
|
|
316
375
|
to_be_budgeted: dollars(m.to_be_budgeted),
|
|
376
|
+
age_of_money: m.age_of_money,
|
|
317
377
|
categories: m.categories?.map((c) => ({
|
|
318
378
|
id: c.id,
|
|
319
379
|
name: c.name,
|
|
380
|
+
category_group_name: c.category_group_name,
|
|
320
381
|
budgeted: dollars(c.budgeted),
|
|
321
382
|
activity: dollars(c.activity),
|
|
322
383
|
balance: dollars(c.balance),
|
|
384
|
+
goal_type: c.goal_type,
|
|
385
|
+
goal_target: dollars(c.goal_target),
|
|
386
|
+
goal_under_funded: dollars(c.goal_under_funded),
|
|
323
387
|
})),
|
|
324
388
|
});
|
|
325
389
|
})
|
|
@@ -344,6 +408,9 @@ function formatTransaction(t) {
|
|
|
344
408
|
category_id: t.category_id,
|
|
345
409
|
category_name: t.category_name,
|
|
346
410
|
transfer_account_id: t.transfer_account_id,
|
|
411
|
+
import_id: t.import_id,
|
|
412
|
+
import_payee_name: t.import_payee_name,
|
|
413
|
+
debt_transaction_type: t.debt_transaction_type,
|
|
347
414
|
subtransactions: t.subtransactions?.map((s) => ({
|
|
348
415
|
id: s.id,
|
|
349
416
|
amount: dollars(s.amount),
|
|
@@ -383,12 +450,8 @@ server.tool(
|
|
|
383
450
|
const { data } = await api.transactions.getTransactionsByPayee(bid, payeeId, sinceDate, type);
|
|
384
451
|
transactions = data.transactions;
|
|
385
452
|
} else if (month) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const [y, m] = month.split("-").map(Number);
|
|
389
|
-
const endDate = new Date(y, m, 0).toISOString().slice(0, 10); // last day of month
|
|
390
|
-
const { data } = await api.transactions.getTransactions(bid, startDate, type);
|
|
391
|
-
transactions = data.transactions.filter((t) => t.date <= endDate);
|
|
453
|
+
const { data } = await api.transactions.getTransactionsByMonth(bid, month, sinceDate, type);
|
|
454
|
+
transactions = data.transactions;
|
|
392
455
|
} else {
|
|
393
456
|
const { data } = await api.transactions.getTransactions(bid, sinceDate, type);
|
|
394
457
|
transactions = data.transactions;
|
|
@@ -427,22 +490,41 @@ server.tool(
|
|
|
427
490
|
cleared: z.enum(["cleared", "uncleared", "reconciled"]).optional().describe("Cleared status"),
|
|
428
491
|
approved: z.boolean().optional().describe("Whether transaction is approved"),
|
|
429
492
|
flagColor: z.enum(["red", "orange", "yellow", "green", "blue", "purple"]).optional().describe("Flag color"),
|
|
493
|
+
importId: z.string().optional().describe("Unique import ID for deduplication (max 36 chars). If omitted and the transaction is later imported, duplicates may be created."),
|
|
494
|
+
subtransactions: z.array(z.object({
|
|
495
|
+
amount: z.number().describe("Subtransaction amount in dollars"),
|
|
496
|
+
categoryId: z.string().optional().describe("Category ID"),
|
|
497
|
+
payeeId: z.string().optional().describe("Payee ID"),
|
|
498
|
+
payeeName: z.string().optional().describe("Payee name"),
|
|
499
|
+
memo: z.string().optional().describe("Memo"),
|
|
500
|
+
})).optional().describe("Split transaction into subtransactions. The subtransaction amounts must sum to the total transaction amount."),
|
|
430
501
|
},
|
|
431
|
-
({ budgetId, accountId, date, amount, payeeId, payeeName, categoryId, memo, cleared, approved, flagColor }) =>
|
|
502
|
+
({ budgetId, accountId, date, amount, payeeId, payeeName, categoryId, memo, cleared, approved, flagColor, importId, subtransactions }) =>
|
|
432
503
|
run(async () => {
|
|
504
|
+
const txn = {
|
|
505
|
+
account_id: accountId,
|
|
506
|
+
date,
|
|
507
|
+
amount: milliunits(amount),
|
|
508
|
+
payee_id: payeeId,
|
|
509
|
+
payee_name: payeeName,
|
|
510
|
+
category_id: categoryId,
|
|
511
|
+
memo,
|
|
512
|
+
cleared,
|
|
513
|
+
approved,
|
|
514
|
+
flag_color: flagColor,
|
|
515
|
+
import_id: importId,
|
|
516
|
+
};
|
|
517
|
+
if (subtransactions) {
|
|
518
|
+
txn.subtransactions = subtransactions.map((s) => ({
|
|
519
|
+
amount: milliunits(s.amount),
|
|
520
|
+
category_id: s.categoryId,
|
|
521
|
+
payee_id: s.payeeId,
|
|
522
|
+
payee_name: s.payeeName,
|
|
523
|
+
memo: s.memo,
|
|
524
|
+
}));
|
|
525
|
+
}
|
|
433
526
|
const { data } = await api.transactions.createTransaction(resolveBudgetId(budgetId), {
|
|
434
|
-
transaction:
|
|
435
|
-
account_id: accountId,
|
|
436
|
-
date,
|
|
437
|
-
amount: milliunits(amount),
|
|
438
|
-
payee_id: payeeId,
|
|
439
|
-
payee_name: payeeName,
|
|
440
|
-
category_id: categoryId,
|
|
441
|
-
memo,
|
|
442
|
-
cleared,
|
|
443
|
-
approved,
|
|
444
|
-
flag_color: flagColor,
|
|
445
|
-
},
|
|
527
|
+
transaction: txn,
|
|
446
528
|
});
|
|
447
529
|
return ok(formatTransaction(data.transaction));
|
|
448
530
|
})
|
|
@@ -496,7 +578,7 @@ server.tool(
|
|
|
496
578
|
({ budgetId, transactionId }) =>
|
|
497
579
|
run(async () => {
|
|
498
580
|
const { data } = await api.transactions.deleteTransaction(resolveBudgetId(budgetId), transactionId);
|
|
499
|
-
return ok(data.transaction);
|
|
581
|
+
return ok(formatTransaction(data.transaction));
|
|
500
582
|
})
|
|
501
583
|
);
|
|
502
584
|
|
|
@@ -569,6 +651,16 @@ function formatScheduledTransaction(t) {
|
|
|
569
651
|
payee_name: t.payee_name,
|
|
570
652
|
category_id: t.category_id,
|
|
571
653
|
category_name: t.category_name,
|
|
654
|
+
transfer_account_id: t.transfer_account_id,
|
|
655
|
+
subtransactions: t.subtransactions?.map((s) => ({
|
|
656
|
+
id: s.id,
|
|
657
|
+
amount: dollars(s.amount),
|
|
658
|
+
memo: s.memo,
|
|
659
|
+
payee_id: s.payee_id,
|
|
660
|
+
payee_name: s.payee_name,
|
|
661
|
+
category_id: s.category_id,
|
|
662
|
+
category_name: s.category_name,
|
|
663
|
+
})),
|
|
572
664
|
};
|
|
573
665
|
}
|
|
574
666
|
|
|
@@ -631,6 +723,50 @@ server.tool(
|
|
|
631
723
|
})
|
|
632
724
|
);
|
|
633
725
|
|
|
726
|
+
server.tool(
|
|
727
|
+
"update_scheduled_transaction",
|
|
728
|
+
"Update an existing scheduled transaction. Only provided fields are changed. Amounts in dollars.",
|
|
729
|
+
{
|
|
730
|
+
budgetId: z.string().optional().describe("Budget ID (uses default if not provided)"),
|
|
731
|
+
scheduledTransactionId: z.string().describe("Scheduled transaction ID"),
|
|
732
|
+
accountId: z.string().optional().describe("Account ID"),
|
|
733
|
+
date: z.string().optional().describe("Next occurrence date (YYYY-MM-DD)"),
|
|
734
|
+
frequency: z.enum(["never", "daily", "weekly", "everyOtherWeek", "twiceAMonth", "every4Weeks", "monthly", "everyOtherMonth", "every3Months", "every4Months", "twiceAYear", "yearly", "everyOtherYear"]).optional().describe("Recurrence frequency"),
|
|
735
|
+
amount: z.number().optional().describe("Amount in dollars (negative for outflows)"),
|
|
736
|
+
payeeId: z.string().nullable().optional().describe("Payee ID"),
|
|
737
|
+
payeeName: z.string().nullable().optional().describe("Payee name"),
|
|
738
|
+
categoryId: z.string().nullable().optional().describe("Category ID"),
|
|
739
|
+
memo: z.string().nullable().optional().describe("Memo"),
|
|
740
|
+
flagColor: z.enum(["red", "orange", "yellow", "green", "blue", "purple"]).nullable().optional().describe("Flag color"),
|
|
741
|
+
},
|
|
742
|
+
({ budgetId, scheduledTransactionId, accountId, date, frequency, amount, payeeId, payeeName, categoryId, memo, flagColor }) =>
|
|
743
|
+
run(async () => {
|
|
744
|
+
const bid = resolveBudgetId(budgetId);
|
|
745
|
+
// PUT replaces the full resource — fetch current values to merge with updates
|
|
746
|
+
const { data: current } = await api.scheduledTransactions.getScheduledTransactionById(bid, scheduledTransactionId);
|
|
747
|
+
const existing = current.scheduled_transaction;
|
|
748
|
+
|
|
749
|
+
const st = {
|
|
750
|
+
account_id: accountId ?? existing.account_id,
|
|
751
|
+
date: date ?? existing.date_next,
|
|
752
|
+
frequency: frequency ?? existing.frequency,
|
|
753
|
+
amount: amount !== undefined ? milliunits(amount) : existing.amount,
|
|
754
|
+
payee_id: payeeId !== undefined ? payeeId : existing.payee_id,
|
|
755
|
+
payee_name: payeeName !== undefined ? payeeName : existing.payee_name,
|
|
756
|
+
category_id: categoryId !== undefined ? categoryId : existing.category_id,
|
|
757
|
+
memo: memo !== undefined ? memo : existing.memo,
|
|
758
|
+
flag_color: flagColor !== undefined ? flagColor : existing.flag_color,
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
const { data } = await api.scheduledTransactions.updateScheduledTransaction(
|
|
762
|
+
bid,
|
|
763
|
+
scheduledTransactionId,
|
|
764
|
+
{ scheduled_transaction: st }
|
|
765
|
+
);
|
|
766
|
+
return ok(formatScheduledTransaction(data.scheduled_transaction));
|
|
767
|
+
})
|
|
768
|
+
);
|
|
769
|
+
|
|
634
770
|
server.tool(
|
|
635
771
|
"delete_scheduled_transaction",
|
|
636
772
|
"Delete a scheduled transaction",
|
|
@@ -641,7 +777,7 @@ server.tool(
|
|
|
641
777
|
({ budgetId, scheduledTransactionId }) =>
|
|
642
778
|
run(async () => {
|
|
643
779
|
const { data } = await api.scheduledTransactions.deleteScheduledTransaction(resolveBudgetId(budgetId), scheduledTransactionId);
|
|
644
|
-
return ok(data.scheduled_transaction);
|
|
780
|
+
return ok(formatScheduledTransaction(data.scheduled_transaction));
|
|
645
781
|
})
|
|
646
782
|
);
|
|
647
783
|
|
|
@@ -726,5 +862,10 @@ server.tool(
|
|
|
726
862
|
|
|
727
863
|
// --- Start ---
|
|
728
864
|
|
|
865
|
+
process.on("uncaughtException", (err) => {
|
|
866
|
+
console.error("Uncaught exception:", err);
|
|
867
|
+
process.exit(1);
|
|
868
|
+
});
|
|
869
|
+
|
|
729
870
|
const transport = new StdioServerTransport();
|
|
730
871
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,33 +1,20 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@oliverames/ynab-mcp-server",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "YNAB MCP server with full API coverage",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"ynab-mcp-server": "index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"index.js"
|
|
12
|
-
],
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"repository": {
|
|
22
|
-
"type": "git",
|
|
23
|
-
"url": "https://github.com/oliverames/oliver-claude-marketplace",
|
|
24
|
-
"directory": "extensions/ynab-mcp-server"
|
|
25
|
-
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"start": "node index.js"
|
|
28
|
-
},
|
|
29
|
-
"dependencies": {
|
|
30
|
-
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
31
|
-
"ynab": "^2.5.0"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@oliverames/ynab-mcp-server",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "YNAB MCP server with full API coverage",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ynab-mcp-server": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node index.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
18
|
+
"ynab": "^2.5.0"
|
|
19
|
+
}
|
|
20
|
+
}
|