Skip to main content
Glama
boldcommerce

Magento 2 MCP Server

by boldcommerce

get_revenue_by_country

Retrieve revenue data for specific countries within defined date ranges to analyze sales performance by region. Supports filtering by order status and tax inclusion.

Instructions

Get revenue filtered by country for a given date range

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
date_rangeYesDate range expression (e.g., 'today', 'yesterday', 'last week', 'this month', 'YTD', or a specific date range like '2023-01-01 to 2023-01-31')
countryYesCountry code (e.g., 'US', 'NL', 'GB') or country name (e.g., 'United States', 'The Netherlands', 'United Kingdom')
statusNoFilter by order status (e.g., 'processing', 'complete', 'pending')
include_taxNoWhether to include tax in the revenue calculation (default: true)

Implementation Reference

  • The core handler function that implements the tool's business logic. Fetches orders from Magento API within the specified date range, filters them by billing or shipping country_id matching the normalized country input, calculates total revenue (grand_total) with optional tax exclusion, order count, average order value, and returns formatted JSON.
      async ({ date_range, country, status, include_tax = true }) => {
        try {
          // Parse the date range expression
          const dateRange = parseDateExpression(date_range);
          
          // Normalize country input (handle both country codes and names)
          const normalizedCountry = normalizeCountry(country);
          
          // Build the search criteria for the date range
          let searchCriteria = buildDateRangeFilter('created_at', dateRange.startDate, dateRange.endDate);
          
          // Add status filter if provided
          if (status) {
            searchCriteria += `&searchCriteria[filter_groups][2][filters][0][field]=status&` +
                              `searchCriteria[filter_groups][2][filters][0][value]=${encodeURIComponent(status)}&` +
                              `searchCriteria[filter_groups][2][filters][0][condition_type]=eq`;
          }
          
          // Fetch all orders using the helper function
          const allOrders = await fetchAllPages('/orders', searchCriteria);
          
          // Filter orders by country and calculate revenue
          let totalRevenue = 0;
          let totalTax = 0;
          let orderCount = 0;
          let filteredOrders = [];
          
          if (allOrders && Array.isArray(allOrders)) {
            // Filter orders by country
            filteredOrders = allOrders.filter(order => {
              // Check billing address country
              const billingCountry = order.billing_address?.country_id;
              
              // Check shipping address country
              const shippingCountry = order.extension_attributes?.shipping_assignments?.[0]?.shipping?.address?.country_id;
              
              // Match if either billing or shipping country matches
              return normalizedCountry.includes(billingCountry) || normalizedCountry.includes(shippingCountry);
            });
            
            orderCount = filteredOrders.length;
            
            // Calculate revenue for filtered orders
            filteredOrders.forEach(order => {
              // Use grand_total which includes tax, shipping, etc.
              totalRevenue += parseFloat(order.grand_total || 0);
              
              // Track tax separately
              totalTax += parseFloat(order.tax_amount || 0);
            });
          }
          
          // Adjust revenue if tax should be excluded
          const revenueWithoutTax = totalRevenue - totalTax;
          const finalRevenue = include_tax ? totalRevenue : revenueWithoutTax;
          
          // Format the response
          const result = {
            query: {
              date_range: dateRange.description,
              country: country,
              normalized_country: normalizedCountry.join(', '),
              status: status || 'All',
              include_tax: include_tax,
              period: {
                start_date: format(dateRange.startDate, 'yyyy-MM-dd'),
                end_date: format(dateRange.endDate, 'yyyy-MM-dd')
              }
            },
            result: {
              revenue: parseFloat(finalRevenue.toFixed(2)),
              currency: 'USD', // This should be dynamically determined from the store configuration
              order_count: orderCount,
              average_order_value: orderCount > 0 ? parseFloat((finalRevenue / orderCount).toFixed(2)) : 0,
              tax_amount: parseFloat(totalTax.toFixed(2))
            }
          };
          
          return {
            content: [
              {
                type: "text",
                text: JSON.stringify(result, null, 2)
              }
            ]
          };
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error fetching revenue by country: ${error.message}`
              }
            ],
            isError: true
          };
        }
      }
    );
  • Input schema using Zod for validation and descriptions of parameters: date_range (string, required), country (string, required), status (string, optional), include_tax (boolean, optional).
    {
      date_range: z.string().describe("Date range expression (e.g., 'today', 'yesterday', 'last week', 'this month', 'YTD', or a specific date range like '2023-01-01 to 2023-01-31')"),
      country: z.string().describe("Country code (e.g., 'US', 'NL', 'GB') or country name (e.g., 'United States', 'The Netherlands', 'United Kingdom')"),
      status: z.string().optional().describe("Filter by order status (e.g., 'processing', 'complete', 'pending')"),
      include_tax: z.boolean().optional().describe("Whether to include tax in the revenue calculation (default: true)")
    },
  • Registration of the tool with the MCP server using server.tool(), specifying name, description, input schema, and handler function.
    server.tool(
      "get_revenue_by_country",
      "Get revenue filtered by country for a given date range",
      {
        date_range: z.string().describe("Date range expression (e.g., 'today', 'yesterday', 'last week', 'this month', 'YTD', or a specific date range like '2023-01-01 to 2023-01-31')"),
        country: z.string().describe("Country code (e.g., 'US', 'NL', 'GB') or country name (e.g., 'United States', 'The Netherlands', 'United Kingdom')"),
        status: z.string().optional().describe("Filter by order status (e.g., 'processing', 'complete', 'pending')"),
        include_tax: z.boolean().optional().describe("Whether to include tax in the revenue calculation (default: true)")
      },
      async ({ date_range, country, status, include_tax = true }) => {
        try {
          // Parse the date range expression
          const dateRange = parseDateExpression(date_range);
          
          // Normalize country input (handle both country codes and names)
          const normalizedCountry = normalizeCountry(country);
          
          // Build the search criteria for the date range
          let searchCriteria = buildDateRangeFilter('created_at', dateRange.startDate, dateRange.endDate);
          
          // Add status filter if provided
          if (status) {
            searchCriteria += `&searchCriteria[filter_groups][2][filters][0][field]=status&` +
                              `searchCriteria[filter_groups][2][filters][0][value]=${encodeURIComponent(status)}&` +
                              `searchCriteria[filter_groups][2][filters][0][condition_type]=eq`;
          }
          
          // Fetch all orders using the helper function
          const allOrders = await fetchAllPages('/orders', searchCriteria);
          
          // Filter orders by country and calculate revenue
          let totalRevenue = 0;
          let totalTax = 0;
          let orderCount = 0;
          let filteredOrders = [];
          
          if (allOrders && Array.isArray(allOrders)) {
            // Filter orders by country
            filteredOrders = allOrders.filter(order => {
              // Check billing address country
              const billingCountry = order.billing_address?.country_id;
              
              // Check shipping address country
              const shippingCountry = order.extension_attributes?.shipping_assignments?.[0]?.shipping?.address?.country_id;
              
              // Match if either billing or shipping country matches
              return normalizedCountry.includes(billingCountry) || normalizedCountry.includes(shippingCountry);
            });
            
            orderCount = filteredOrders.length;
            
            // Calculate revenue for filtered orders
            filteredOrders.forEach(order => {
              // Use grand_total which includes tax, shipping, etc.
              totalRevenue += parseFloat(order.grand_total || 0);
              
              // Track tax separately
              totalTax += parseFloat(order.tax_amount || 0);
            });
          }
          
          // Adjust revenue if tax should be excluded
          const revenueWithoutTax = totalRevenue - totalTax;
          const finalRevenue = include_tax ? totalRevenue : revenueWithoutTax;
          
          // Format the response
          const result = {
            query: {
              date_range: dateRange.description,
              country: country,
              normalized_country: normalizedCountry.join(', '),
              status: status || 'All',
              include_tax: include_tax,
              period: {
                start_date: format(dateRange.startDate, 'yyyy-MM-dd'),
                end_date: format(dateRange.endDate, 'yyyy-MM-dd')
              }
            },
            result: {
              revenue: parseFloat(finalRevenue.toFixed(2)),
              currency: 'USD', // This should be dynamically determined from the store configuration
              order_count: orderCount,
              average_order_value: orderCount > 0 ? parseFloat((finalRevenue / orderCount).toFixed(2)) : 0,
              tax_amount: parseFloat(totalTax.toFixed(2))
            }
          };
          
          return {
            content: [
              {
                type: "text",
                text: JSON.stringify(result, null, 2)
              }
            ]
          };
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error fetching revenue by country: ${error.message}`
              }
            ],
            isError: true
          };
        }
      }
    );
  • Supporting helper function that normalizes flexible country input (names or codes) to standard ISO country codes array, used for filtering orders by billing_address.country_id or shipping address country_id.
    function normalizeCountry(country) {
      // Normalize the country input (handle both country codes and names)
      const countryInput = country.trim().toLowerCase();
      
      // Map of common country names to ISO country codes
      const countryMap = {
        // Common variations for The Netherlands
        'netherlands': 'NL',
        'the netherlands': 'NL',
        'holland': 'NL',
        'nl': 'NL',
        
        // Common variations for United States
        'united states': 'US',
        'usa': 'US',
        'us': 'US',
        'america': 'US',
        
        // Common variations for United Kingdom
        'united kingdom': 'GB',
        'uk': 'GB',
        'great britain': 'GB',
        'gb': 'GB',
        'england': 'GB',
        
        // Add more countries as needed
        'canada': 'CA',
        'ca': 'CA',
        
        'australia': 'AU',
        'au': 'AU',
        
        'germany': 'DE',
        'de': 'DE',
        
        'france': 'FR',
        'fr': 'FR',
        
        'italy': 'IT',
        'it': 'IT',
        
        'spain': 'ES',
        'es': 'ES',
        
        'belgium': 'BE',
        'be': 'BE',
        
        'sweden': 'SE',
        'se': 'SE',
        
        'norway': 'NO',
        'no': 'NO',
        
        'denmark': 'DK',
        'dk': 'DK',
        
        'finland': 'FI',
        'fi': 'FI',
        
        'ireland': 'IE',
        'ie': 'IE',
        
        'switzerland': 'CH',
        'ch': 'CH',
        
        'austria': 'AT',
        'at': 'AT',
        
        'portugal': 'PT',
        'pt': 'PT',
        
        'greece': 'GR',
        'gr': 'GR',
        
        'poland': 'PL',
        'pl': 'PL',
        
        'japan': 'JP',
        'jp': 'JP',
        
        'china': 'CN',
        'cn': 'CN',
        
        'india': 'IN',
        'in': 'IN',
        
        'brazil': 'BR',
        'br': 'BR',
        
        'mexico': 'MX',
        'mx': 'MX',
        
        'south africa': 'ZA',
        'za': 'ZA'
      };
      
      // Check if the input is in our map
      if (countryMap[countryInput]) {
        return [countryMap[countryInput]];
      }
      
      // If it's not in our map, assume it's a country code or name and return as is
      // For a more robust solution, we would validate against a complete list of country codes
      return [countryInput.toUpperCase()];
    }
  • Helper utility to parse human-readable date_range strings into precise startDate/endDate Date objects, supporting relative terms like 'ytd', 'last week', exact dates, and ranges.
    function parseDateExpression(dateExpression) {
      const now = new Date();
      const currentYear = now.getFullYear();
      const currentMonth = now.getMonth();
      const currentDay = now.getDate();
      
      // Normalize the date expression
      const normalizedExpression = dateExpression.toLowerCase().trim();
      
      // Handle relative date expressions
      switch (normalizedExpression) {
        case 'today':
          return {
            startDate: startOfDay(now),
            endDate: endOfDay(now),
            description: 'Today'
          };
        case 'yesterday':
          const yesterday = subDays(now, 1);
          return {
            startDate: startOfDay(yesterday),
            endDate: endOfDay(yesterday),
            description: 'Yesterday'
          };
        case 'this week':
          return {
            startDate: startOfWeek(now, { weekStartsOn: 1 }), // Week starts on Monday
            endDate: endOfDay(now),
            description: 'This week'
          };
        case 'last week':
          const lastWeekStart = subDays(startOfWeek(now, { weekStartsOn: 1 }), 7);
          const lastWeekEnd = subDays(endOfWeek(now, { weekStartsOn: 1 }), 7);
          return {
            startDate: lastWeekStart,
            endDate: lastWeekEnd,
            description: 'Last week'
          };
        case 'this month':
          return {
            startDate: startOfMonth(now),
            endDate: endOfDay(now),
            description: 'This month'
          };
        case 'last month':
          const lastMonth = new Date(currentYear, currentMonth - 1, 1);
          return {
            startDate: startOfMonth(lastMonth),
            endDate: endOfMonth(lastMonth),
            description: 'Last month'
          };
        case 'ytd':
        case 'this ytd':
        case 'this year to date':
        case 'year to date':
          return {
            startDate: startOfYear(now),
            endDate: endOfDay(now),
            description: 'Year to date'
          };
        case 'last year':
          const lastYear = new Date(currentYear - 1, 0, 1);
          return {
            startDate: startOfYear(lastYear),
            endDate: endOfYear(lastYear),
            description: 'Last year'
          };
        default:
          // Try to parse as ISO date or other common formats
          try {
            // Check if it's a single date (not a range)
            const parsedDate = parseISO(normalizedExpression);
            if (isValid(parsedDate)) {
              return {
                startDate: startOfDay(parsedDate),
                endDate: endOfDay(parsedDate),
                description: format(parsedDate, 'yyyy-MM-dd')
              };
            }
            
            // Check if it's a date range in format "YYYY-MM-DD to YYYY-MM-DD"
            const rangeParts = normalizedExpression.split(' to ');
            if (rangeParts.length === 2) {
              const startDate = parseISO(rangeParts[0]);
              const endDate = parseISO(rangeParts[1]);
              
              if (isValid(startDate) && isValid(endDate)) {
                return {
                  startDate: startOfDay(startDate),
                  endDate: endOfDay(endDate),
                  description: `${format(startDate, 'yyyy-MM-dd')} to ${format(endDate, 'yyyy-MM-dd')}`
                };
              }
            }
            
            // If we can't parse it, throw an error
            throw new Error(`Unable to parse date expression: ${dateExpression}`);
          } catch (error) {
            throw new Error(`Invalid date expression: ${dateExpression}. ${error.message}`);
          }
      }
    }

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/boldcommerce/magento2-mcp'

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