mcp-ynab
Version:
Model Context Protocol server for YNAB integration
89 lines (76 loc) • 2.97 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: '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 };