get_all_expenses
Retrieve paginated expense data from Brex with filters for status, amount, date range, and type to analyze spending patterns and track financial transactions.
Instructions
LIST: Paginated expenses with filters. Returns complete expense objects. Example: {"page_size":5,"max_items":5,"status":["APPROVED"],"window_days":7,"min_amount":100}
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| end_date | No | Filter expenses created on or before this date (ISO format) | |
| expand | No | Fields to expand (e.g., merchant, receipts) | |
| expense_type | No | Filter expenses by type | |
| max_amount | No | Client-side maximum purchased_amount.amount filter | |
| max_items | No | Maximum total number of items to retrieve across all pages | |
| min_amount | No | Client-side minimum purchased_amount.amount filter | |
| page_size | No | Number of items per page (default: 50, max: 100) | |
| payment_status | No | Filter expenses by payment status | |
| start_date | No | Filter expenses created on or after this date (ISO format) | |
| status | No | Filter expenses by status | |
| window_days | No | Optional batching window in days to split large date ranges |
Implementation Reference
- src/tools/getAllExpenses.ts:265-303 (handler)Core execution handler for the get_all_expenses tool: validates parameters, fetches paginated and filtered expenses from Brex API, handles errors, and returns JSON-formatted results with metadata.registerToolHandler("get_all_expenses", async (request: ToolCallRequest) => { try { // Validate parameters const params = validateParams(request.params.arguments); logDebug(`Getting all expenses with params: ${JSON.stringify(params)}`); // Get Brex client const brexClient = getBrexClient(); try { // Fetch all expenses with pagination const allExpenses = await fetchAllExpenses(brexClient, params); logDebug(`Successfully fetched ${allExpenses.length} total expenses`); // Return raw results with pagination metadata const result = { expenses: allExpenses, meta: { total_count: allExpenses.length, requested_parameters: params } }; return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (apiError) { logError(`Error calling Brex API: ${apiError instanceof Error ? apiError.message : String(apiError)}`); throw new Error(`Failed to get expenses: ${apiError instanceof Error ? apiError.message : String(apiError)}`); } } catch (error) { logError(`Error in get_all_expenses tool: ${error instanceof Error ? error.message : String(error)}`); throw error; } });
- src/tools/index.ts:417-482 (schema)Input JSON Schema for get_all_expenses tool, defining all parameters for pagination, filtering by type/status/payment/date/amount, expansion fields, with descriptions and types.{ name: "get_all_expenses", description: "LIST: Paginated expenses with filters. Returns complete expense objects. Example: {\"page_size\":5,\"max_items\":5,\"status\":[\"APPROVED\"],\"window_days\":7,\"min_amount\":100}", inputSchema: { type: "object", properties: { page_size: { type: "number", description: "Number of items per page (default: 50, max: 100)" }, max_items: { type: "number", description: "Maximum total number of items to retrieve across all pages" }, expense_type: { type: "array", items: { type: "string", enum: Object.values(ExpenseType) }, description: "Filter expenses by type" }, status: { type: "array", items: { type: "string", enum: Object.values(ExpenseStatus) }, description: "Filter expenses by status" }, payment_status: { type: "array", items: { type: "string", enum: Object.values(ExpensePaymentStatus) }, description: "Filter expenses by payment status" }, start_date: { type: "string", description: "Filter expenses created on or after this date (ISO format)" }, end_date: { type: "string", description: "Filter expenses created on or before this date (ISO format)" }, window_days: { type: "number", description: "Optional batching window in days to split large date ranges" }, min_amount: { type: "number", description: "Client-side minimum purchased_amount.amount filter" }, max_amount: { type: "number", description: "Client-side maximum purchased_amount.amount filter" }, expand: { type: "array", items: { type: "string" }, description: "Fields to expand (e.g., merchant, receipts)" } } } },
- src/tools/getAllExpenses.ts:264-304 (registration)Local registration function registerGetAllExpenses that uses registerToolHandler to associate the 'get_all_expenses' name with its execution handler.export function registerGetAllExpenses(_server: Server): void { registerToolHandler("get_all_expenses", async (request: ToolCallRequest) => { try { // Validate parameters const params = validateParams(request.params.arguments); logDebug(`Getting all expenses with params: ${JSON.stringify(params)}`); // Get Brex client const brexClient = getBrexClient(); try { // Fetch all expenses with pagination const allExpenses = await fetchAllExpenses(brexClient, params); logDebug(`Successfully fetched ${allExpenses.length} total expenses`); // Return raw results with pagination metadata const result = { expenses: allExpenses, meta: { total_count: allExpenses.length, requested_parameters: params } }; return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (apiError) { logError(`Error calling Brex API: ${apiError instanceof Error ? apiError.message : String(apiError)}`); throw new Error(`Failed to get expenses: ${apiError instanceof Error ? apiError.message : String(apiError)}`); } } catch (error) { logError(`Error in get_all_expenses tool: ${error instanceof Error ? error.message : String(error)}`); throw error; } }); }
- src/tools/index.ts:53-54 (registration)Top-level registration call to registerGetAllExpenses(server) within the main registerTools function, integrating the tool into the MCP server.registerGetAllAccounts(server); registerGetAllExpenses(server);
- src/tools/getAllExpenses.ts:186-258 (helper)Helper function implementing pagination across API pages, date window batching, filter application, and client-side amount filtering to fetch all matching expenses.async function fetchAllExpenses(client: BrexClient, params: GetAllExpensesParams): Promise<unknown[]> { const pageSize = params.page_size || 50; const maxItems = params.max_items || 100; // Default to reasonable limit instead of Infinity let allExpenses: unknown[] = []; const start = params.start_date ? new Date(params.start_date) : undefined; const end = params.end_date ? new Date(params.end_date) : undefined; const windowDays = params.window_days && start && end ? params.window_days : undefined; const windows: Array<{ start?: string; end?: string }> = []; if (windowDays && start && end) { const cursorStart = new Date(start); while (cursorStart <= end && allExpenses.length < maxItems) { const cursorEnd = new Date(cursorStart); cursorEnd.setUTCDate(cursorEnd.getUTCDate() + windowDays); if (cursorEnd > end) cursorEnd.setTime(end.getTime()); windows.push({ start: cursorStart.toISOString(), end: cursorEnd.toISOString() }); // advance by windowDays cursorStart.setUTCDate(cursorStart.getUTCDate() + windowDays); } } else { windows.push({ start: params.start_date, end: params.end_date }); } for (const w of windows) { let cursor: string | undefined = undefined; let hasMore = true; while (hasMore && allExpenses.length < maxItems) { try { const limit = Math.min(pageSize, maxItems - allExpenses.length); const requestParams: ListExpensesParams = { limit, cursor, expand: params.expand || [] // Use provided expand array or default to empty }; if (params.expense_type) requestParams.expense_type = params.expense_type; if (params.status) requestParams.status = params.status; if (params.payment_status) requestParams.payment_status = params.payment_status; if (w.start) requestParams.updated_at_start = w.start; if (w.end) requestParams.updated_at_end = w.end; logDebug(`Fetching expenses page (window ${w.start || 'none'}..${w.end || 'none'}) cursor: ${cursor || 'initial'}`); const response = await client.getExpenses(requestParams); let validExpenses = response.items.filter(isExpense); // Client-side end_date filtering if needed if (w.end && !('updated_at_end' in requestParams)) { const endDate = new Date(w.end).getTime(); validExpenses = validExpenses.filter(expense => new Date(expense.updated_at).getTime() <= endDate); } // Client-side amount thresholds if (params.min_amount !== undefined) { validExpenses = validExpenses.filter(e => typeof e.purchased_amount?.amount === 'number' && e.purchased_amount.amount >= (params.min_amount as number)); } if (params.max_amount !== undefined) { validExpenses = validExpenses.filter(e => typeof e.purchased_amount?.amount === 'number' && e.purchased_amount.amount <= (params.max_amount as number)); } allExpenses = allExpenses.concat(validExpenses); logDebug(`Retrieved ${validExpenses.length} expenses (total: ${allExpenses.length})`); cursor = response.nextCursor; hasMore = !!cursor; } catch (error) { logError(`Error fetching expenses page: ${error instanceof Error ? error.message : String(error)}`); throw error; } } if (allExpenses.length >= maxItems) break; } return allExpenses; }