import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import * as databaseService from '../services/database.js';
export function registerTransactionTools(server: McpServer): void {
// Tool 6: query_transactions
server.tool(
'query_transactions',
'Search and filter financial transactions from the database',
{
category: z.string().optional().describe('Filter by category (Software, Hardware, Travel, Marketing)'),
department: z.string().optional().describe('Filter by department (Engineering, Sales, Marketing)'),
minAmount: z.number().optional().describe('Minimum transaction amount'),
maxAmount: z.number().optional().describe('Maximum transaction amount'),
startDate: z.string().optional().describe('Start date (YYYY-MM-DD format)'),
endDate: z.string().optional().describe('End date (YYYY-MM-DD format)'),
vendor: z.string().optional().describe('Filter by vendor name (partial match)')
},
async (filters) => {
try {
const transactions = await databaseService.queryTransactions(filters);
const totalAmount = transactions.reduce((sum, t) => sum + t.amount, 0);
const result = {
count: transactions.length,
totalAmount: totalAmount.toFixed(2),
currency: 'EUR',
transactions,
...(transactions.length === 50 ? { note: 'Showing first 50 transactions. Add filters to narrow results.' } : {})
};
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
return {
content: [{ type: 'text', text: JSON.stringify({ error: `Failed to query transactions: ${errorMsg}` }, null, 2) }],
isError: true
};
}
}
);
// Tool 7: get_spending_by_category
server.tool(
'get_spending_by_category',
'Get spending breakdown by category with optional filters',
{
department: z.string().optional().describe('Filter by department'),
startDate: z.string().optional().describe('Start date (YYYY-MM-DD format)'),
endDate: z.string().optional().describe('End date (YYYY-MM-DD format)')
},
async ({ department, startDate, endDate }) => {
try {
const spending = await databaseService.getSpendingByCategory(department, startDate, endDate);
const totalSpending = spending.reduce((sum, s) => sum + s.total, 0);
const result = {
totalSpending: totalSpending.toFixed(2),
currency: 'EUR',
breakdown: spending.map(s => ({
category: s.category,
total: s.total.toFixed(2),
count: s.count,
averageTransaction: (s.total / s.count).toFixed(2)
}))
};
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
return {
content: [{ type: 'text', text: JSON.stringify({ error: `Failed to get spending by category: ${errorMsg}` }, null, 2) }],
isError: true
};
}
}
);
// Tool 8: get_spending_by_department
server.tool(
'get_spending_by_department',
'Get spending breakdown by department with optional date filters',
{
startDate: z.string().optional().describe('Start date (YYYY-MM-DD format)'),
endDate: z.string().optional().describe('End date (YYYY-MM-DD format)')
},
async ({ startDate, endDate }) => {
try {
const spending = await databaseService.getSpendingByDepartment(startDate, endDate);
const totalSpending = spending.reduce((sum, s) => sum + s.total, 0);
const result = {
totalSpending: totalSpending.toFixed(2),
currency: 'EUR',
breakdown: spending.map(s => ({
department: s.department,
total: s.total.toFixed(2),
count: s.count,
averageTransaction: (s.total / s.count).toFixed(2)
}))
};
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
return {
content: [{ type: 'text', text: JSON.stringify({ error: `Failed to get spending by department: ${errorMsg}` }, null, 2) }],
isError: true
};
}
}
);
// Tool 9: compare_to_budget
server.tool(
'compare_to_budget',
'Compare actual spending against budget for current month',
{
department: z.string().optional().describe('Filter by department')
},
async ({ department }) => {
try {
const analysis = await databaseService.compareToBudget(department);
const summary = {
overBudget: analysis.filter(a => a.status === 'OVER BUDGET').length,
warning: analysis.filter(a => a.status === 'WARNING').length,
ok: analysis.filter(a => a.status === 'OK').length
};
const result = {
budgetAnalysis: analysis.map(a => ({
department: a.department,
category: a.category,
budget: a.budget.toFixed(2),
actual: a.actual.toFixed(2),
remaining: a.remaining.toFixed(2),
percentUsed: a.percentUsed.toFixed(1),
status: a.status
})),
summary
};
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
return {
content: [{ type: 'text', text: JSON.stringify({ error: `Failed to compare to budget: ${errorMsg}` }, null, 2) }],
isError: true
};
}
}
);
// Tool 10: get_top_vendors
server.tool(
'get_top_vendors',
'Get top vendors by total spending amount',
{
limit: z.number().min(1).max(20).optional().default(10).describe('Number of vendors to return (1-20, default 10)'),
department: z.string().optional().describe('Filter by department')
},
async ({ limit, department }) => {
try {
const vendors = await databaseService.getTopVendors(limit, department);
const totalSpending = vendors.reduce((sum, v) => sum + v.total, 0);
const result = {
totalSpending: totalSpending.toFixed(2),
topVendors: vendors.map(v => ({
rank: v.rank,
vendor: v.vendor,
total: v.total.toFixed(2),
transactionCount: v.transactionCount,
averageTransaction: v.averageTransaction.toFixed(2)
}))
};
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
return {
content: [{ type: 'text', text: JSON.stringify({ error: `Failed to get top vendors: ${errorMsg}` }, null, 2) }],
isError: true
};
}
}
);
}