Skip to main content
Glama
dennisonbertram

Brex MCP Server

index.ts16.3 kB
/** * @file Resources Index * @version 1.0.0 * @description Exports all resource handlers for the Brex MCP server */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { ListResourcesRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { registerAccountsCapabilities, canHandleAccountsUri, readAccountsUri } from "./accounts.js"; import { registerBudgetsCapabilities, canHandleBudgetsUri, readBudgetsUri } from "./budgets.js"; import { registerSpendLimitsCapabilities, canHandleSpendLimitsUri, readSpendLimitsUri } from "./spendLimits.js"; import { registerBudgetProgramsCapabilities, canHandleBudgetProgramsUri, readBudgetProgramsUri } from "./budgetPrograms.js"; import { registerCardAccountsCapabilities, canHandleCardAccountsUri, readCardAccountsUri } from "./cardAccounts.js"; import { registerCashAccountsCapabilities, canHandleCashAccountsUri, readCashAccountsUri } from "./cashAccounts.js"; import { registerTransactionsCapabilities, canHandleTransactionsUri, readTransactionsUri } from "./transactions.js"; import { registerExpensesCapabilities, canHandleExpensesUri, readExpensesUri } from "./expenses.js"; import { registerCardExpensesCapabilities, canHandleCardExpensesUri, readCardExpensesUri } from "./cardExpenses.js"; import { canHandleUsageUri, readUsageUri } from "./usage.js"; import { logInfo, logDebug, logError } from "../utils/logger.js"; /** * Registers all resource handlers with the server * @param server The MCP server instance */ export function registerResources(server: Server): void { // Enable chained ReadResource handler so multiple modules can coexist enableChainedReadResource(server); // Central dispatcher (incremental): ordered routes, most specific first server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const uri = request.params.uri; if (canHandleUsageUri(uri)) return await readUsageUri(); if (canHandleCardExpensesUri(uri)) return await readCardExpensesUri(uri); if (canHandleExpensesUri(uri)) return await readExpensesUri(uri); if (canHandleCardAccountsUri(uri)) return await readCardAccountsUri(uri); if (canHandleAccountsUri(uri)) return await readAccountsUri(uri); if (canHandleCashAccountsUri(uri)) return await readCashAccountsUri(uri); if (canHandleTransactionsUri(uri)) return await readTransactionsUri(uri); if (canHandleBudgetsUri(uri)) return await readBudgetsUri(uri); if (canHandleSpendLimitsUri(uri)) return await readSpendLimitsUri(uri); if (canHandleBudgetProgramsUri(uri)) return await readBudgetProgramsUri(uri); return { handled: false } as any; }); // Register capabilities only (no per-module handlers) registerAccountsCapabilities(server); registerCardAccountsCapabilities(server); registerCashAccountsCapabilities(server); registerTransactionsCapabilities(server); registerExpensesCapabilities(server); registerCardExpensesCapabilities(server); registerBudgetsCapabilities(server); registerSpendLimitsCapabilities(server); registerBudgetProgramsCapabilities(server); // Register the list resources handler registerListResourcesHandler(server); } /** * Registers the handler for listing available resources * @param server The MCP server instance */ function registerListResourcesHandler(server: Server): void { server.setRequestHandler(ListResourcesRequestSchema, async () => { try { logInfo("===== LIST RESOURCES START ====="); logDebug("Request to list available Brex resources received"); // Define our resources statically - no API calls // Use simple URIs (not templates) for listing const resources = [ { uri: "brex://accounts", mimeType: "application/json", name: "Brex Accounts", description: "List of all Brex accounts (card and cash) with balances and status. Supports query parameters: ?summary_only=true (condensed data), &fields=field1,field2 (select specific fields using dot notation, e.g., 'id,status,current_balance.amount')" }, { uri: "brex://accounts/card", mimeType: "application/json", name: "Brex Card Accounts", description: "List of all card accounts with balances and status. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://accounts/cash", mimeType: "application/json", name: "Brex Cash Accounts", description: "List of all cash accounts with balances and status. Requires cash account scopes. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://accounts/cash/primary", mimeType: "application/json", name: "Brex Primary Cash Account", description: "Information for the primary cash account. Requires cash account scopes. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://transactions/card/primary", mimeType: "application/json", name: "Brex Card Transactions", description: "List of settled transactions for all card accounts. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://expenses", mimeType: "application/json", name: "Brex Expenses", description: "List of all Brex expenses with automatic expansion of merchant and budget details. Supports query parameters: ?summary_only=true (condensed data), &fields=field1,field2 (select specific fields using dot notation, e.g., 'id,status,purchased_amount.amount')" }, { uri: "brex://expenses/card", mimeType: "application/json", name: "Brex Card Expenses", description: "List of all card expenses with automatic expansion of merchant and budget details. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://budgets", mimeType: "application/json", name: "Brex Budgets", description: "List of all budget allocations with spending limits and status. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://spend_limits", mimeType: "application/json", name: "Brex Spend Limits", description: "List of all spend limit policies with authorization settings. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://budget_programs", mimeType: "application/json", name: "Brex Budget Programs", description: "List of all budget programs with employee filter configurations. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://docs/usage", mimeType: "application/json", name: "Brex MCP Usage Guide", description: "Guidelines and examples for using the Brex MCP server safely and efficiently. Includes parameter reference, tool selection guide, and best practices." } ]; logDebug(`Responding with ${resources.length} available resources: ${resources.map(r => r.uri).join(', ')}`); logInfo("===== LIST RESOURCES END ====="); // Also advertise resource templates so clients can construct parameterized URIs const resourceTemplates = [ { uriTemplate: "brex://accounts/{id}", name: "brex-account", title: "Brex Account by ID", description: "Detailed information for a specific account by ID. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" }, { uriTemplate: "brex://accounts/card/{id}", name: "brex-card-account", title: "Brex Card Account by ID", description: "Detailed information for a specific card account by ID. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" }, { uriTemplate: "brex://accounts/cash/{id}", name: "brex-cash-account", title: "Brex Cash Account by ID", description: "Detailed information for a specific cash account by ID. Requires cash account scopes. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" }, { uriTemplate: "brex://transactions/cash/{id}", name: "brex-cash-transactions", title: "Cash Account Transactions", description: "List of settled transactions for a specific cash account by ID. Requires cash account scopes. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" }, { uriTemplate: "brex://expenses/{id}", name: "brex-expense", title: "Expense by ID", description: "Detailed information for a specific expense by ID. Automatically expands merchant and budget details. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" }, { uriTemplate: "brex://expenses/card/{id}", name: "brex-card-expense", title: "Card Expense by ID", description: "Detailed information for a specific card expense by ID. Automatically expands merchant and budget details. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" }, { uriTemplate: "brex://budgets/{id}", name: "brex-budget", title: "Budget by ID", description: "Detailed information for a specific budget by ID. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" }, { uriTemplate: "brex://spend_limits/{id}", name: "brex-spend-limit", title: "Spend Limit by ID", description: "Detailed information for a specific spend limit by ID. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" }, { uriTemplate: "brex://budget_programs/{id}", name: "brex-budget-program", title: "Budget Program by ID", description: "Detailed information for a specific budget program by ID. Supports query parameters: ?summary_only=true, &fields=field1,field2", mimeType: "application/json" } ]; // Return immediately without any async operations return { resources, resourceTemplates } as any; } catch (error) { logError(`Error in ListResourcesRequestSchema handler: ${error instanceof Error ? error.message : String(error)}`); logError("Stack trace: " + (error instanceof Error ? error.stack : "Not available")); // Still need to return resources even if logging fails const resourcesFallback = [ { uri: "brex://accounts", mimeType: "application/json", name: "Brex Accounts", description: "List of all Brex accounts (card and cash) with balances and status. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://accounts/card", mimeType: "application/json", name: "Brex Card Accounts", description: "List of all card accounts with balances and status. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://accounts/cash", mimeType: "application/json", name: "Brex Cash Accounts", description: "List of all cash accounts with balances and status. Requires cash account scopes. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://accounts/cash/primary", mimeType: "application/json", name: "Brex Primary Cash Account", description: "Information for the primary cash account. Requires cash account scopes. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://transactions/card/primary", mimeType: "application/json", name: "Brex Card Transactions", description: "List of settled transactions for all card accounts. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://expenses", mimeType: "application/json", name: "Brex Expenses", description: "List of all Brex expenses with automatic expansion of merchant and budget details. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://expenses/card", mimeType: "application/json", name: "Brex Card Expenses", description: "List of all card expenses with automatic expansion of merchant and budget details. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://budgets", mimeType: "application/json", name: "Brex Budgets", description: "List of all budget allocations with spending limits and status. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://spend_limits", mimeType: "application/json", name: "Brex Spend Limits", description: "List of all spend limit policies with authorization settings. Supports query parameters: ?summary_only=true, &fields=field1,field2" }, { uri: "brex://budget_programs", mimeType: "application/json", name: "Brex Budget Programs", description: "List of all budget programs with employee filter configurations. Supports query parameters: ?summary_only=true, &fields=field1,field2" } ]; return { resources: resourcesFallback }; } }); } /** * Wraps server.setRequestHandler to collect multiple ReadResource handlers * and install a single dispatcher that tries each until one returns a proper * MCP response (contents or error). Handlers may return { handled: false } to skip. */ function enableChainedReadResource(server: Server): void { const originalSetHandler = server.setRequestHandler.bind(server); const readHandlers: Array<(request: any, extra?: any) => Promise<any>> = []; let installed = false; // @ts-ignore override method at runtime server.setRequestHandler = ((schema: any, handler: any) => { if (schema === ReadResourceRequestSchema) { readHandlers.push(handler); if (!installed) { installed = true; originalSetHandler(ReadResourceRequestSchema, async (request: any, extra?: any) => { for (const h of readHandlers) { // Each handler should either return a valid MCP response or { handled: false } const result = await h(request, extra); const r = result as any; if (r && (Array.isArray(r.contents) || r.error)) { return r; // handled } if (r && r.handled === false) { continue; // try next } } // Fallback guidance when no handler handled the URI const uri = request?.params?.uri ?? "unknown://"; return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify({ error: "Unsupported resource URI", guidance: "Use resources listed by list_resources or call tools for dynamic data", }, null, 2) }] } as any; }); } return; // do not register individual read handlers directly } // Non-ReadResource schemas pass-through return originalSetHandler(schema, handler); }) as any; }

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/dennisonbertram/mcp-brex'

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