mcp-ynab
Version:
Model Context Protocol server for YNAB integration
112 lines (97 loc) • 4.32 kB
JavaScript
import { YnabClient } from '../../shared/ynab-client.js';
import { CacheManager } from '../../shared/cache-manager.js';
import { ErrorHandler } from '../../shared/error-handler.js';
const cache = new CacheManager();
const errorHandler = new ErrorHandler();
const toolDefinition = {
name: 'get_accounts',
description: 'Get all budget accounts with current balances and account details',
inputSchema: {
type: 'object',
properties: {
budget_id: {
type: 'string',
description: 'Specific budget ID (optional, defaults to default budget)'
},
include_closed: {
type: 'boolean',
description: 'Include closed accounts in results (optional, defaults to false)',
default: false
}
},
additionalProperties: false
}
};
const handler = async (params) => {
try {
const ynab = new YnabClient();
const { budget_id, include_closed = false } = params;
// Generate cache key
const cacheKey = cache.generateKey('accounts', { budget_id, include_closed });
return await cache.getOrSet(cacheKey, async () => {
const budgetId = budget_id || await ynab.getDefaultBudgetId();
// Get all accounts
const accounts = await ynab.getAccounts(budgetId);
// Filter accounts based on include_closed parameter
let filteredAccounts = accounts;
if (!include_closed) {
filteredAccounts = accounts.filter(account => !account.closed);
}
// Transform accounts data for response
const accountsData = filteredAccounts.map(account => ({
id: account.id,
name: account.name,
type: account.type,
balance: ynab.milliunitsToAmount(account.balance),
cleared_balance: ynab.milliunitsToAmount(account.cleared_balance),
uncleared_balance: ynab.milliunitsToAmount(account.uncleared_balance),
on_budget: account.on_budget,
closed: account.closed,
note: account.note || null,
last_reconciled_at: account.last_reconciled_at || null,
debt_original_balance: account.debt_original_balance ? ynab.milliunitsToAmount(account.debt_original_balance) : null,
debt_interest_rates: account.debt_interest_rates || null,
debt_minimum_payments: Array.isArray(account.debt_minimum_payments) ? account.debt_minimum_payments.map(payment => ({
...payment,
amount: ynab.milliunitsToAmount(payment.amount)
})) : null
}));
// Calculate summary statistics
const onBudgetAccounts = accountsData.filter(account => account.on_budget && !account.closed);
const offBudgetAccounts = accountsData.filter(account => !account.on_budget && !account.closed);
const closedAccounts = accountsData.filter(account => account.closed);
const totalOnBudgetBalance = onBudgetAccounts.reduce((sum, account) => sum + account.balance, 0);
const totalOffBudgetBalance = offBudgetAccounts.reduce((sum, account) => sum + account.balance, 0);
const totalBalance = totalOnBudgetBalance + totalOffBudgetBalance;
// Categorize by account type
const byType = accountsData.reduce((acc, account) => {
if (!acc[account.type]) {
acc[account.type] = [];
}
acc[account.type].push(account);
return acc;
}, {});
return {
accounts: accountsData,
summary: {
total_accounts: accountsData.length,
on_budget_accounts: onBudgetAccounts.length,
off_budget_accounts: offBudgetAccounts.length,
closed_accounts: closedAccounts.length,
total_balance: totalBalance,
total_on_budget_balance: totalOnBudgetBalance,
total_off_budget_balance: totalOffBudgetBalance
},
by_type: Object.keys(byType).map(type => ({
type: type,
count: byType[type].length,
accounts: byType[type],
total_balance: byType[type].reduce((sum, account) => sum + account.balance, 0)
}))
};
}, 10); // Cache for 10 minutes
} catch (error) {
return errorHandler.formatForMCP(error);
}
};
export { toolDefinition, handler };