Skip to main content
Glama
crazyrabbitLTC

Brex MCP Server

get_all_card_expenses

Retrieve paginated card expense data from Brex with filtering options for status, date range, amount, and merchant. Returns complete expense objects for financial analysis.

Instructions

LIST: Paginated card expenses (no expense_type needed). Returns complete card expense objects. Example: {"page_size":5,"max_items":5,"window_days":7,"min_amount":100}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
page_sizeNoNumber of items per page (default: 50, max: 100)
max_itemsNoMaximum total number of items to retrieve across all pages
statusNoFilter card expenses by status
payment_statusNoFilter card expenses by payment status
start_dateNoFilter card expenses created on or after this date (ISO format)
end_dateNoFilter card expenses created on or before this date (ISO format)
merchant_nameNoFilter card expenses by merchant name (partial match)
window_daysNoOptional batching window in days to split large date ranges
min_amountNoClient-side minimum purchased_amount.amount filter
max_amountNoClient-side maximum purchased_amount.amount filter
expandNoFields to expand (e.g., merchant, receipts)

Implementation Reference

  • Primary execution handler for the get_all_card_expenses tool. Validates parameters, fetches paginated card expenses via Brex API, applies filters, and returns JSON-formatted results with metadata.
    registerToolHandler("get_all_card_expenses", async (request: ToolCallRequest) => {
      try {
        // Validate parameters
        const params = validateParams(request.params.arguments);
        logDebug(`Getting all card expenses with params: ${JSON.stringify(params)}`);
        
        // Get Brex client
        const brexClient = getBrexClient();
        
        try {
          // Fetch all card expenses with pagination
          const allCardExpenses = await fetchAllCardExpenses(brexClient, params);
          
          logDebug(`Successfully fetched ${allCardExpenses.length} total card expenses`);
          
          // Return raw results with pagination metadata
          const result = {
            card_expenses: allCardExpenses,
            meta: {
              total_count: allCardExpenses.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 card expenses: ${apiError instanceof Error ? apiError.message : String(apiError)}`);
        }
      } catch (error) {
        logError(`Error in get_all_card_expenses tool: ${error instanceof Error ? error.message : String(error)}`);
        throw error;
      }
    });
  • Core helper function implementing pagination, date window batching, API calls to Brex for card expenses, client-side filtering by merchant and amount.
    async function fetchAllCardExpenses(client: BrexClient, params: GetAllCardExpensesParams): Promise<unknown[]> {
      const pageSize = params.page_size || 50;
      const maxItems = params.max_items || 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() });
          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,
              expense_type: [ExpenseType.CARD],
              expand: params.expand || [] // Use provided expand array or default to empty
            };
            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 card expenses page (window ${w.start || 'none'}..${w.end || 'none'}) cursor: ${cursor || 'initial'}`);
            const response = await client.getCardExpenses(requestParams);
            let validExpenses = response.items.filter(isExpense);
            if (params.merchant_name) {
              const merchantNameLower = params.merchant_name.toLowerCase();
              validExpenses = validExpenses.filter(expense => (expense.merchant?.raw_descriptor || '').toLowerCase().includes(merchantNameLower));
            }
            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} card expenses (total: ${allExpenses.length})`);
            cursor = response.nextCursor;
            hasMore = !!cursor;
          } catch (error) {
            logError(`Error fetching card expenses page: ${error instanceof Error ? error.message : String(error)}`);
            throw error;
          }
        }
        if (allExpenses.length >= maxItems) break;
      }
      return allExpenses;
    }
  • TypeScript interface defining the input parameters for the get_all_card_expenses tool, including pagination, filters, and expand options.
    interface GetAllCardExpensesParams {
      page_size?: number;
      max_items?: number;
      status?: ExpenseStatus[];
      payment_status?: ExpensePaymentStatus[];
      start_date?: string;
      end_date?: string;
      merchant_name?: string;
      min_amount?: number;
      max_amount?: number;
      window_days?: number;
      expand?: string[];
    }
  • Comprehensive input parameter validation function for the tool, handling type checks, range validation, enum validation, and date parsing.
    function validateParams(input: unknown): GetAllCardExpensesParams {
      if (!input) {
        return {}; // All parameters are optional
      }
      
      const raw = input as Record<string, unknown>;
      const params: GetAllCardExpensesParams = {};
      
      // Validate page_size if provided
      if (raw.page_size !== undefined) {
        const pageSize = parseInt(String(raw.page_size), 10);
        if (isNaN(pageSize) || pageSize <= 0 || pageSize > 100) {
          throw new Error("Invalid page_size: must be a positive number between 1 and 100");
        }
        params.page_size = pageSize;
      }
      
      // Validate max_items if provided
      if (raw.max_items !== undefined) {
        const maxItems = parseInt(String(raw.max_items), 10);
        if (isNaN(maxItems) || maxItems <= 0) {
          throw new Error("Invalid max_items: must be a positive number");
        }
        params.max_items = maxItems;
      }
      
      // Validate status if provided
      if (raw.status !== undefined) {
        const statuses = Array.isArray(raw.status) 
          ? raw.status 
          : [raw.status];
          
        statuses.forEach((status: string) => {
          // Type assertion to ensure status is treated as the correct enum type
          if (!Object.values(ExpenseStatus).includes(status as ExpenseStatus)) {
            throw new Error(`Invalid status: ${status}`);
          }
        });
        
        params.status = statuses as ExpenseStatus[];
      }
      
      // Validate payment_status if provided
      if (raw.payment_status !== undefined) {
        const paymentStatuses = Array.isArray(raw.payment_status) 
          ? raw.payment_status 
          : [raw.payment_status];
          
        paymentStatuses.forEach((status: string) => {
          // Type assertion to ensure status is treated as the correct enum type
          if (!Object.values(ExpensePaymentStatus).includes(status as ExpensePaymentStatus)) {
            throw new Error(`Invalid payment_status: ${status}`);
          }
        });
        
        params.payment_status = paymentStatuses as ExpensePaymentStatus[];
      }
      
      // Validate start_date if provided
      if (raw.start_date !== undefined) {
        try {
          const date = new Date(String(raw.start_date));
          if (isNaN(date.getTime())) {
            throw new Error("Invalid date format");
          }
          params.start_date = date.toISOString();
        } catch (error) {
          throw new Error("Invalid start_date: must be a valid ISO date string");
        }
      }
      
      // Validate end_date if provided
      if (raw.end_date !== undefined) {
        try {
          const date = new Date(String(raw.end_date));
          if (isNaN(date.getTime())) {
            throw new Error("Invalid date format");
          }
          params.end_date = date.toISOString();
        } catch (error) {
          throw new Error("Invalid end_date: must be a valid ISO date string");
        }
      }
      
      // Add merchant name filter if provided
      if (raw.merchant_name !== undefined) {
        if (typeof raw.merchant_name !== 'string' || String(raw.merchant_name).trim() === '') {
          throw new Error("Invalid merchant_name: must be a non-empty string");
        }
        params.merchant_name = String(raw.merchant_name).trim();
      }
      // Amount thresholds
      if (raw.min_amount !== undefined) {
        const n = parseFloat(String(raw.min_amount));
        if (isNaN(n) || n < 0) throw new Error("Invalid min_amount: must be non-negative");
        params.min_amount = n;
      }
      if (raw.max_amount !== undefined) {
        const n = parseFloat(String(raw.max_amount));
        if (isNaN(n) || n < 0) throw new Error("Invalid max_amount: must be non-negative");
        params.max_amount = n;
      }
      if (params.min_amount !== undefined && params.max_amount !== undefined && params.min_amount > params.max_amount) {
        throw new Error("min_amount cannot be greater than max_amount");
      }
      // Optional batching window size (days)
      if (raw.window_days !== undefined) {
        const d = parseInt(String(raw.window_days), 10);
        if (isNaN(d) || d <= 0) throw new Error("Invalid window_days: must be a positive integer");
        params.window_days = d;
      }
      
      // Validate expand if provided
      if (raw.expand !== undefined) {
        if (Array.isArray(raw.expand)) {
          params.expand = raw.expand.map(String).filter((e: string) => e.trim().length > 0);
        } else {
          throw new Error("Invalid expand: must be an array of strings");
        }
      }
      
      return params;
    }
  • Registration of the get_all_card_expenses tool via import and call to registerGetAllCardExpenses in the main tools index file.
    import { registerGetAllCardExpenses } from "./getAllCardExpenses.js";
    import { registerGetCardTransactions } from "./getCardTransactions.js";
    import { registerGetCashAccountStatements } from "./getCashAccountStatements.js";
    import { registerGetExpenseById } from "./getExpenseById.js";
    import { registerGetCardExpenseById } from "./getCardExpenseById.js";
    import { registerGetCardStatementsPrimary } from "./getCardStatementsPrimary.js";
    import { registerGetCashTransactions } from "./getCashTransactions.js";
    import { registerGetBudgets } from "./getBudgets.js";
    import { registerGetBudgetById } from "./getBudgetById.js";
    import { registerGetSpendLimits } from "./getSpendLimits.js";
    import { registerGetSpendLimitById } from "./getSpendLimitById.js";
    import { registerGetBudgetPrograms } from "./getBudgetPrograms.js";
    import { registerGetBudgetProgramById } from "./getBudgetProgramById.js";
    import { logError } from "../utils/logger.js";
    import { ExpenseType, ExpenseStatus, ExpensePaymentStatus } from "../services/brex/expenses-types.js";
    
    // Minimal request shape passed to individual tool handlers
    export type ToolCallRequest = { params: { name?: string; arguments?: Record<string, unknown> } };
    
    // Store tool handlers
    export const toolHandlers = new Map<string, (request: ToolCallRequest) => Promise<unknown>>();
    
    /**
     * Registers all tools with the server
     * @param server The MCP server instance
     */
    export function registerTools(server: Server): void {
      // Register tool handlers
      registerGetTransactions(server);
      registerGetExpenses(server);
      registerGetAccountDetails(server);
      registerUploadReceipt(server);
      registerMatchReceipt(server);
      registerUpdateExpense(server);
      
      // Register pagination-aware tool handlers
      registerGetAllAccounts(server);
      registerGetAllExpenses(server);
      registerGetAllCardExpenses(server);
  • JSON input schema definition for the get_all_card_expenses tool used in MCP listTools response.
      name: "get_all_card_expenses",
      description: "LIST: Paginated card expenses (no expense_type needed). Returns complete card expense objects. Example: {\"page_size\":5,\"max_items\":5,\"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"
          },
          status: {
            type: "array",
            items: {
              type: "string",
              enum: Object.values(ExpenseStatus)
            },
            description: "Filter card expenses by status"
          },
          payment_status: {
            type: "array",
            items: {
              type: "string",
              enum: Object.values(ExpensePaymentStatus)
            },
            description: "Filter card expenses by payment status"
          },
          start_date: {
            type: "string",
            description: "Filter card expenses created on or after this date (ISO format)"
          },
          end_date: {
            type: "string",
            description: "Filter card expenses created on or before this date (ISO format)"
          },
          merchant_name: {
            type: "string",
            description: "Filter card expenses by merchant name (partial match)"
          },
          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)"
          }
        }
      }
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/crazyrabbitLTC/mcp-brex-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server