Skip to main content
Glama
crazyrabbitLTC

Brex MCP Server

get_expenses

Retrieve and filter expense data from Brex, including status, payment details, and merchant information for financial tracking and analysis.

Instructions

LIST (single page): Expenses with optional filters. Returns complete expense objects. Example: {"limit":5,"status":"APPROVED","expand":["merchant"]}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
expense_typeNoType of expenses to retrieve
statusNoStatus filter for expenses
payment_statusNoPayment status filter for expenses
limitNoMaximum number of expenses to return (default: 50)
expandNoFields to expand (e.g., merchant, receipts)

Implementation Reference

  • The core execution handler for the 'get_expenses' MCP tool. Validates parameters, fetches expenses from Brex API, filters valid items, and returns JSON-formatted response.
    registerToolHandler("get_expenses", async (request: ToolCallRequest) => {
      try {
        // Validate parameters
        const params = validateParams(request.params.arguments);
        logDebug(`Getting expenses with params: ${JSON.stringify(params)}`);
        
        // Get Brex client
        const brexClient = getBrexClient();
        
        // Set default limit if not provided
        const limit = params.limit || 50;
        
        try {
          // Prepare API parameters - use user-provided expand or default to empty
          const apiParams: ListExpensesParams = {
            limit,
            expand: params.expand || [] // Use provided expand array or default to empty for minimal response size
          };
          
          // Add filters if provided, ensuring expense_type is an array
          if (params.expense_type) {
            apiParams.expense_type = [params.expense_type];
          }
          
          if (params.status) {
            apiParams.status = [params.status];
          }
          
          if (params.payment_status) {
            apiParams.payment_status = [params.payment_status];
          }
          
          // Call Brex API to get expenses
          const expenses = await brexClient.getExpenses(apiParams);
          
          // Validate expenses data
          if (!expenses || !Array.isArray(expenses.items)) {
            throw new Error("Invalid response format from Brex API");
          }
          
          // Filter valid expenses
          const validExpenses = expenses.items.filter(isExpense);
          logDebug(`Found ${validExpenses.length} valid expenses`);
          
          const result = {
            expenses: validExpenses,
            meta: {
              total_count: validExpenses.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_expenses tool: ${error instanceof Error ? error.message : String(error)}`);
        throw error;
      }
    });
  • JSON input schema definition for the 'get_expenses' tool, registered in the listTools response.
      name: "get_expenses",
      description: "LIST (single page): Expenses with optional filters. Returns complete expense objects. Example: {\"limit\":5,\"status\":\"APPROVED\",\"expand\":[\"merchant\"]}",
      inputSchema: {
        type: "object",
        properties: {
          expense_type: {
            type: "string",
            enum: Object.values(ExpenseType),
            description: "Type of expenses to retrieve"
          },
          status: {
            type: "string",
            enum: Object.values(ExpenseStatus),
            description: "Status filter for expenses"
          },
          payment_status: {
            type: "string",
            enum: Object.values(ExpensePaymentStatus),
            description: "Payment status filter for expenses"
          },
          limit: {
            type: "number",
            description: "Maximum number of expenses to return (default: 50)"
          },
          expand: {
            type: "array",
            items: { type: "string" },
            description: "Fields to expand (e.g., merchant, receipts)"
          }
        }
      }
    },
  • Invocation of registerGetExpenses to register the 'get_expenses' tool handler during server setup.
    registerGetExpenses(server);
  • Function that registers the 'get_expenses' tool handler with the internal toolHandlers map.
    export function registerGetExpenses(_server: Server): void {
      registerToolHandler("get_expenses", async (request: ToolCallRequest) => {
        try {
          // Validate parameters
          const params = validateParams(request.params.arguments);
          logDebug(`Getting expenses with params: ${JSON.stringify(params)}`);
          
          // Get Brex client
          const brexClient = getBrexClient();
          
          // Set default limit if not provided
          const limit = params.limit || 50;
          
          try {
            // Prepare API parameters - use user-provided expand or default to empty
            const apiParams: ListExpensesParams = {
              limit,
              expand: params.expand || [] // Use provided expand array or default to empty for minimal response size
            };
            
            // Add filters if provided, ensuring expense_type is an array
            if (params.expense_type) {
              apiParams.expense_type = [params.expense_type];
            }
            
            if (params.status) {
              apiParams.status = [params.status];
            }
            
            if (params.payment_status) {
              apiParams.payment_status = [params.payment_status];
            }
            
            // Call Brex API to get expenses
            const expenses = await brexClient.getExpenses(apiParams);
            
            // Validate expenses data
            if (!expenses || !Array.isArray(expenses.items)) {
              throw new Error("Invalid response format from Brex API");
            }
            
            // Filter valid expenses
            const validExpenses = expenses.items.filter(isExpense);
            logDebug(`Found ${validExpenses.length} valid expenses`);
            
            const result = {
              expenses: validExpenses,
              meta: {
                total_count: validExpenses.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_expenses tool: ${error instanceof Error ? error.message : String(error)}`);
          throw error;
        }
      });
    } 
  • Helper function to validate and sanitize input parameters according to the tool schema.
    function validateParams(input: any): GetExpensesParams {
      if (!input) {
        return {}; // All parameters are optional
      }
      
      const params: GetExpensesParams = {};
      
      // Validate expense_type if provided
      if (input.expense_type !== undefined) {
        if (!Object.values(ExpenseType).includes(input.expense_type)) {
          throw new Error(`Invalid expense_type: ${input.expense_type}`);
        }
        params.expense_type = input.expense_type;
      }
      
      // Validate status if provided
      if (input.status !== undefined) {
        if (!Object.values(ExpenseStatus).includes(input.status)) {
          throw new Error(`Invalid status: ${input.status}`);
        }
        params.status = input.status;
      }
      
      // Validate payment_status if provided
      if (input.payment_status !== undefined) {
        if (!Object.values(ExpensePaymentStatus).includes(input.payment_status)) {
          throw new Error(`Invalid payment_status: ${input.payment_status}`);
        }
        params.payment_status = input.payment_status;
      }
      
      // Validate limit if provided
      if (input.limit !== undefined) {
        const limit = parseInt(input.limit.toString(), 10);
        if (isNaN(limit) || limit <= 0) {
          throw new Error("Invalid limit: must be a positive number");
        }
        params.limit = limit;
      }
      
      // Validate expand if provided
      if (input.expand !== undefined) {
        if (Array.isArray(input.expand)) {
          params.expand = input.expand.map(String).filter((e: string) => e.trim().length > 0);
        } else {
          throw new Error("Invalid expand: must be an array of strings");
        }
      }
      
      return params;
    }
Behavior2/5

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

No annotations are provided, so the description carries the full burden. It mentions 'single page' and 'Returns complete expense objects,' which gives some behavioral context about output format and pagination. However, it lacks details on permissions, rate limits, error handling, or whether this is a read-only operation, which is important for a tool with no annotations.

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 concise and front-loaded with key information: action (LIST), scope (single page), and output (complete expense objects). The example is helpful but could be slightly more integrated. Overall, it's efficient with little waste.

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

Completeness3/5

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

Given the complexity (5 parameters, no output schema, no annotations), the description is moderately complete. It covers purpose and provides an example, but lacks details on behavioral aspects like authentication, error cases, or output structure, which would be beneficial for an agent to use this tool effectively.

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?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly with enums and descriptions. The description adds minimal value by providing an example usage with specific parameters (limit, status, expand), but doesn't explain parameter semantics beyond what the schema provides, aligning with the baseline score.

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: listing expenses with optional filters and returning complete expense objects. It specifies 'LIST (single page)' which distinguishes it from potential paginated alternatives, though it doesn't explicitly differentiate from sibling tools like 'get_all_expenses' or 'get_expense'.

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

Usage Guidelines3/5

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

The description implies usage through the phrase 'LIST (single page)' suggesting this is for retrieving a limited set of expenses rather than all expenses, but it doesn't provide explicit guidance on when to use this tool versus alternatives like 'get_all_expenses' or 'get_expense' from the sibling list.

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