Skip to main content
Glama
crazyrabbitLTC

Brex MCP Server

get_all_expenses

Retrieve and filter expense data from Brex with pagination, supporting parameters like status, date ranges, amount thresholds, and expense types for financial analysis.

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
NameRequiredDescriptionDefault
page_sizeNoNumber of items per page (default: 50, max: 100)
max_itemsNoMaximum total number of items to retrieve across all pages
expense_typeNoFilter expenses by type
statusNoFilter expenses by status
payment_statusNoFilter expenses by payment status
start_dateNoFilter expenses created on or after this date (ISO format)
end_dateNoFilter expenses created on or before this date (ISO format)
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

  • The primary tool handler for 'get_all_expenses'. Validates parameters, fetches expenses using pagination and filters via Brex API, handles errors, and returns formatted JSON response 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;
      }
    });
  • JSON Schema definition for the 'get_all_expenses' tool input parameters, provided in the MCP listTools response.
    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)"
        }
      }
    }
  • Registration function that sets up the 'get_all_expenses' tool handler using the shared registerToolHandler mechanism.
    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;
        }
      });
    } 
  • Invocation of the registration function during overall tools setup in registerTools.
    registerGetAllExpenses(server);
  • Helper function implementing the core logic for fetching all expenses with pagination, date window batching, API filtering, and client-side amount filtering.
    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;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions pagination and filtering capabilities, but doesn't address important behavioral aspects like authentication requirements, rate limits, error conditions, or what 'complete expense objects' actually contain. For a tool with 11 parameters and no annotations, this leaves significant gaps in understanding how the tool behaves in practice.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with three concise parts: a clear purpose statement, a specification of what's returned, and a practical example. Every sentence serves a purpose, and the example is directly relevant. The front-loading of 'LIST: Paginated expenses with filters' immediately communicates the core functionality.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 11 parameters, no annotations, and no output schema, the description is insufficiently complete. While it covers basic purpose and provides an example, it doesn't address the tool's behavioral characteristics, doesn't explain the relationship to sibling expense tools, and doesn't describe the structure of the 'complete expense objects' it returns. The absence of output schema means the description should ideally provide more guidance about return values.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so all parameters are documented in the schema. The description provides an example showing several parameters in use (page_size, max_items, status, window_days, min_amount), which adds practical context beyond the schema's technical descriptions. However, it doesn't explain parameter interactions or provide additional semantic meaning that isn't already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'LIST: Paginated expenses with filters. Returns complete expense objects.' This specifies the verb (list), resource (expenses), and key characteristics (paginated, filtered, returns complete objects). However, it doesn't explicitly differentiate from sibling tools like 'get_expenses' or 'get_all_card_expenses' which appear to be related expense retrieval tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With multiple sibling tools for expense retrieval (get_expenses, get_all_card_expenses, get_card_expense, get_expense), there's no indication of when this comprehensive filtered listing tool is preferred over more specific expense retrieval tools. The example shows usage but doesn't provide contextual guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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