search-transactions
Search your Lunch Money transactions by keyword to find specific purchases or payments within a customizable date range.
Instructions
Search transactions by keyword
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| keyword | Yes | Search term to look for | |
| days | No | Number of days to look back | |
| limit | No | Maximum number of transactions to return |
Implementation Reference
- src/index.ts:182-208 (handler)The handler function for the 'search-transactions' tool. It calculates the date range, fetches transactions from the Lunchmoney API, filters them by keyword in payee or notes, limits the results, and formats them for output.async ({ keyword, days, limit }) => { const endDate = new Date().toISOString().split('T')[0]; const startDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000) .toISOString() .split('T')[0]; const transactions = await this.fetchTransactions({ start_date: startDate, end_date: endDate, limit: 1000, }); const matchingTransactions = transactions.filter( (tx: Transaction) => tx.payee.toLowerCase().includes(keyword.toLowerCase()) || (tx.notes && tx.notes.toLowerCase().includes(keyword.toLowerCase())), ); return { content: [ { type: "text", text: this.formatTransactions(matchingTransactions.slice(0, limit)), }, ], }; },
- src/index.ts:177-181 (schema)Zod input schema defining parameters for the 'search-transactions' tool: keyword (required string), days (default 90), limit (default 10).{ keyword: z.string().describe("Search term to look for"), days: z.number().default(90).describe("Number of days to look back"), limit: z.number().default(10).describe("Maximum number of transactions to return"), },
- src/index.ts:174-209 (registration)Registration of the 'search-transactions' tool on the MCP server, including name, description, input schema, and inline handler function.this.server.tool( "search-transactions", "Search transactions by keyword", { keyword: z.string().describe("Search term to look for"), days: z.number().default(90).describe("Number of days to look back"), limit: z.number().default(10).describe("Maximum number of transactions to return"), }, async ({ keyword, days, limit }) => { const endDate = new Date().toISOString().split('T')[0]; const startDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000) .toISOString() .split('T')[0]; const transactions = await this.fetchTransactions({ start_date: startDate, end_date: endDate, limit: 1000, }); const matchingTransactions = transactions.filter( (tx: Transaction) => tx.payee.toLowerCase().includes(keyword.toLowerCase()) || (tx.notes && tx.notes.toLowerCase().includes(keyword.toLowerCase())), ); return { content: [ { type: "text", text: this.formatTransactions(matchingTransactions.slice(0, limit)), }, ], }; }, );
- src/index.ts:315-334 (helper)Helper method used by search-transactions to fetch transactions from the Lunchmoney API given query parameters.private async fetchTransactions(params: Record<string, any>): Promise<Transaction[]> { const queryParams = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { queryParams.append(key, value.toString()); } const response = await fetch(`${API_BASE}/transactions?${queryParams}`, { headers: { Authorization: `Bearer ${this.token}`, Accept: "application/json", } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json() as TransactionResponse; return data.transactions || []; }
- src/index.ts:336-359 (helper)Helper method used by search-transactions to format the list of matching transactions into a readable text string.private formatTransactions(transactions: Transaction[]): string { return transactions .map(tx => { let summary = [ `Date: ${tx.date}`, `Amount: ${tx.amount} ${tx.currency.toUpperCase()}`, `Payee: ${tx.payee}`, `Category: ${tx.category_name} (${tx.category_group_name})`, `Account: ${tx.account_display_name || "N/A"}`, `Status: ${tx.status}`, ]; if (tx.tags && tx.tags.length > 0) { summary.push(`Tags: ${tx.tags.map((t: Tag) => t.name).join(", ")}`); } if (tx.notes) { summary.push(`Notes: ${tx.notes}`); } return summary.join("\n"); }) .join("\n\n---\n\n"); }