UNPKG

mcp-ynab

Version:

Model Context Protocol server for YNAB integration

89 lines (76 loc) 2.97 kB
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: 'list_budgets', description: 'List all available YNAB budgets with optional account details', inputSchema: { type: 'object', properties: { include_accounts: { type: 'boolean', description: 'Include account details for each budget (optional, defaults to false)', default: false } }, additionalProperties: false } }; const handler = async (params) => { try { const ynab = new YnabClient(); const { include_accounts = false } = params; // Generate cache key const cacheKey = cache.generateKey('list_budgets', { include_accounts }); return await cache.getOrSet(cacheKey, async () => { // Get all budgets const budgets = await ynab.getBudgets(); // Transform budgets data for response const budgetsData = await Promise.all(budgets.map(async (budget) => { let accounts = null; // Include accounts if requested if (include_accounts) { try { accounts = await ynab.getAccounts(budget.id); // Transform accounts data accounts = accounts.map(account => ({ id: account.id, name: account.name, type: account.type, balance: ynab.milliunitsToAmount(account.balance), on_budget: account.on_budget, closed: account.closed })); } catch (error) { console.error(`Failed to get accounts for budget ${budget.id}:`, error.message); accounts = null; } } return { id: budget.id, name: budget.name, last_modified_on: budget.last_modified_on, first_month: budget.first_month, last_month: budget.last_month, date_format: budget.date_format || null, currency_format: budget.currency_format || null, accounts: accounts }; })); // Sort by name for consistent output budgetsData.sort((a, b) => a.name.localeCompare(b.name)); return { budgets: budgetsData, total_budgets: budgetsData.length, default_budget_id: budgets.length > 0 ? (budgets.find(b => !b.name.toLowerCase().includes('demo'))?.id || budgets[0].id) : null, includes_accounts: include_accounts }; }, 10); // Cache for 10 minutes since budgets don't change often } catch (error) { console.error('Error in list_budgets:', error); return errorHandler.formatForMCP(error); } }; export { toolDefinition, handler };