Skip to main content
Glama
wonyoungseong

GA4 MCP Server

ga4_run_report

Generate Google Analytics 4 reports by specifying dimensions, metrics, and date ranges to retrieve website or app performance data for analysis.

Instructions

Runs a Google Analytics Data API report. Returns analytics data based on the specified dimensions, metrics, and date ranges.

Hints for arguments

Hints for dimensions

The dimensions list must consist solely of either:

  1. Standard dimensions from https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema#dimensions

  2. Custom dimensions for the property. Use ga4_custom_dimensions_metrics to retrieve custom dimensions.

Hints for metrics

The metrics list must consist solely of either:

  1. Standard metrics from https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema#metrics

  2. Custom metrics for the property. Use ga4_custom_dimensions_metrics to retrieve custom metrics.

Hints for dateRanges

Examples:

  • Single range: [{"startDate": "2025-01-01", "endDate": "2025-01-31"}]

  • Relative: [{"startDate": "30daysAgo", "endDate": "yesterday"}]

  • Multiple: [{"startDate": "2025-01-01", "endDate": "2025-01-31", "name": "Jan"}, {"startDate": "2025-02-01", "endDate": "2025-02-28", "name": "Feb"}]

Hints for dimensionFilter

Example: {"filter": {"fieldName": "eventName", "stringFilter": {"matchType": "BEGINS_WITH", "value": "page"}}}

Hints for orderBys

Example: [{"dimension": {"dimensionName": "eventName"}, "desc": false}] or [{"metric": {"metricName": "eventCount"}, "desc": true}]

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
propertyIdYesThe Google Analytics property ID. Accepted formats: '123456789' or 'properties/123456789'
dateRangesYesList of date ranges for the report
dimensionsYesList of dimension names (e.g., 'eventName', 'country', 'deviceCategory')
metricsYesList of metric names (e.g., 'activeUsers', 'eventCount', 'sessions')
dimensionFilterNoFilter expression for dimensions
metricFilterNoFilter expression for metrics
orderBysNoList of order by specifications
limitNoMaximum number of rows to return (default: 10000, max: 250000)
offsetNoRow offset for pagination (0-indexed)
currencyCodeNoISO4217 currency code (e.g., 'USD', 'EUR', 'JPY')
returnPropertyQuotaNoWhether to return property quota information

Implementation Reference

  • The main handler function runReport that executes the Google Analytics Data API report. It constructs the request, calls the API with all parameters (dimensions, metrics, date ranges, filters, orderBys, limit, offset, currencyCode, returnPropertyQuota), and returns the response.
    export async function runReport(params: RunReportParams): Promise<ToolResponse> {
      try {
        const client = await getAnalyticsDataClient();
        const propertyName = constructPropertyResourceName(params.propertyId);
    
        // Build the request body
        const requestBody: Record<string, unknown> = {
          property: propertyName,
          dateRanges: params.dateRanges,
          dimensions: params.dimensions.map(name => ({ name })),
          metrics: params.metrics.map(name => ({ name })),
        };
    
        if (params.dimensionFilter) {
          requestBody.dimensionFilter = params.dimensionFilter;
        }
    
        if (params.metricFilter) {
          requestBody.metricFilter = params.metricFilter;
        }
    
        if (params.orderBys && params.orderBys.length > 0) {
          requestBody.orderBys = params.orderBys;
        }
    
        if (params.limit !== undefined) {
          requestBody.limit = params.limit;
        }
    
        if (params.offset !== undefined) {
          requestBody.offset = params.offset;
        }
    
        if (params.currencyCode) {
          requestBody.currencyCode = params.currencyCode;
        }
    
        if (params.returnPropertyQuota !== undefined) {
          requestBody.returnPropertyQuota = params.returnPropertyQuota;
        }
    
        const response = await client.properties.runReport({
          property: propertyName,
          requestBody: requestBody,
        });
    
        return createSuccessResponse(response.data);
      } catch (error) {
        return createErrorResponse(`Failed to run report for ${params.propertyId}`, error);
      }
    }
  • TypeScript interface definitions for report parameters including DateRange, FilterExpression, OrderBy, and RunReportParams that define the input schema structure for the ga4_run_report tool.
    interface DateRange {
      startDate: string;
      endDate: string;
      name?: string;
    }
    
    interface FilterExpression {
      filter?: {
        fieldName: string;
        stringFilter?: {
          matchType: string;
          value: string;
          caseSensitive?: boolean;
        };
        numericFilter?: {
          operation: string;
          value: {
            int64Value?: string;
            doubleValue?: number;
          };
        };
        inListFilter?: {
          values: string[];
          caseSensitive?: boolean;
        };
        betweenFilter?: {
          fromValue: { int64Value?: string; doubleValue?: number };
          toValue: { int64Value?: string; doubleValue?: number };
        };
        emptyFilter?: Record<string, never>;
      };
      andGroup?: { expressions: FilterExpression[] };
      orGroup?: { expressions: FilterExpression[] };
      notExpression?: FilterExpression;
    }
    
    interface OrderBy {
      dimension?: {
        dimensionName: string;
        orderType?: string;
      };
      metric?: {
        metricName: string;
      };
      desc?: boolean;
    }
    
    interface RunReportParams {
      propertyId: string;
      dateRanges: DateRange[];
      dimensions: string[];
      metrics: string[];
      dimensionFilter?: FilterExpression;
      metricFilter?: FilterExpression;
      orderBys?: OrderBy[];
      limit?: number;
      offset?: number;
      currencyCode?: string;
      returnPropertyQuota?: boolean;
    }
  • MCP tool registration definition for ga4_run_report with name, description including hints for dimensions, metrics, dateRanges, filters, and orderBys, plus the complete inputSchema defining all parameters and their requirements.
          name: "ga4_run_report",
          description: `Runs a Google Analytics Data API report. Returns analytics data based on the specified dimensions, metrics, and date ranges.
    
    ## Hints for arguments
    
    ### Hints for dimensions
    The dimensions list must consist solely of either:
    1. Standard dimensions from https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema#dimensions
    2. Custom dimensions for the property. Use ga4_custom_dimensions_metrics to retrieve custom dimensions.
    
    ### Hints for metrics
    The metrics list must consist solely of either:
    1. Standard metrics from https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema#metrics
    2. Custom metrics for the property. Use ga4_custom_dimensions_metrics to retrieve custom metrics.
    
    ### Hints for dateRanges
    Examples:
    - Single range: [{"startDate": "2025-01-01", "endDate": "2025-01-31"}]
    - Relative: [{"startDate": "30daysAgo", "endDate": "yesterday"}]
    - Multiple: [{"startDate": "2025-01-01", "endDate": "2025-01-31", "name": "Jan"}, {"startDate": "2025-02-01", "endDate": "2025-02-28", "name": "Feb"}]
    
    ### Hints for dimensionFilter
    Example: {"filter": {"fieldName": "eventName", "stringFilter": {"matchType": "BEGINS_WITH", "value": "page"}}}
    
    ### Hints for orderBys
    Example: [{"dimension": {"dimensionName": "eventName"}, "desc": false}] or [{"metric": {"metricName": "eventCount"}, "desc": true}]`,
          inputSchema: {
            type: "object" as const,
            properties: {
              propertyId: {
                type: "string",
                description: "The Google Analytics property ID. Accepted formats: '123456789' or 'properties/123456789'",
              },
              dateRanges: {
                type: "array",
                items: {
                  type: "object",
                  properties: {
                    startDate: { type: "string", description: "Start date (YYYY-MM-DD, yesterday, today, NdaysAgo)" },
                    endDate: { type: "string", description: "End date (YYYY-MM-DD, yesterday, today, NdaysAgo)" },
                    name: { type: "string", description: "Optional name for the date range" },
                  },
                  required: ["startDate", "endDate"],
                },
                description: "List of date ranges for the report",
              },
              dimensions: {
                type: "array",
                items: { type: "string" },
                description: "List of dimension names (e.g., 'eventName', 'country', 'deviceCategory')",
              },
              metrics: {
                type: "array",
                items: { type: "string" },
                description: "List of metric names (e.g., 'activeUsers', 'eventCount', 'sessions')",
              },
              dimensionFilter: {
                type: "object",
                description: "Filter expression for dimensions",
              },
              metricFilter: {
                type: "object",
                description: "Filter expression for metrics",
              },
              orderBys: {
                type: "array",
                items: { type: "object" },
                description: "List of order by specifications",
              },
              limit: {
                type: "number",
                description: "Maximum number of rows to return (default: 10000, max: 250000)",
              },
              offset: {
                type: "number",
                description: "Row offset for pagination (0-indexed)",
              },
              currencyCode: {
                type: "string",
                description: "ISO4217 currency code (e.g., 'USD', 'EUR', 'JPY')",
              },
              returnPropertyQuota: {
                type: "boolean",
                description: "Whether to return property quota information",
              },
            },
            required: ["propertyId", "dateRanges", "dimensions", "metrics"],
          },
        },
  • The routing switch case that maps the ga4_run_report tool name to the runReport handler function, extracting all parameters from the args object and passing them to the handler.
    case "ga4_run_report":
      return await runReport({
        propertyId: args.propertyId as string,
        dateRanges: args.dateRanges as Array<{ startDate: string; endDate: string; name?: string }>,
        dimensions: args.dimensions as string[],
        metrics: args.metrics as string[],
        dimensionFilter: args.dimensionFilter as Record<string, unknown> | undefined,
        metricFilter: args.metricFilter as Record<string, unknown> | undefined,
        orderBys: args.orderBys as Array<Record<string, unknown>> | undefined,
        limit: args.limit as number | undefined,
        offset: args.offset as number | undefined,
        currencyCode: args.currencyCode as string | undefined,
        returnPropertyQuota: args.returnPropertyQuota as boolean | undefined,
      });
  • Helper functions used by the runReport handler: constructPropertyResourceName (formats property ID for GA4 API), createSuccessResponse (formats successful MCP responses), and createErrorResponse (formats error responses).
    export function constructPropertyResourceName(propertyId: string | number): string {
      let propertyNum: number | null = null;
    
      if (typeof propertyId === "number") {
        propertyNum = propertyId;
      } else if (typeof propertyId === "string") {
        const trimmed = propertyId.trim();
        if (/^\d+$/.test(trimmed)) {
          propertyNum = parseInt(trimmed, 10);
        } else if (trimmed.startsWith("properties/")) {
          const numericPart = trimmed.split("/")[1];
          if (numericPart && /^\d+$/.test(numericPart)) {
            propertyNum = parseInt(numericPart, 10);
          }
        }
      }
    
      if (propertyNum === null) {
        throw new Error(
          `Invalid property ID: ${propertyId}. ` +
          "A valid property value is either a number or a string starting " +
          "with 'properties/' and followed by a number."
        );
      }
    
      return `properties/${propertyNum}`;
    }
    
    /**
     * Creates a successful MCP tool response with JSON data.
     */
    export function createSuccessResponse(data: unknown): ToolResponse {
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(data, null, 2),
          },
        ],
      };
    }
    
    /**
     * Creates an error MCP tool response.
     */
    export function createErrorResponse(message: string, error?: unknown): ToolResponse {
      let errorMessage = message;
    
      if (error) {
        if (error instanceof Error) {
          errorMessage += `: ${error.message}`;
        } else if (typeof error === "string") {
          errorMessage += `: ${error}`;
        } else {
          errorMessage += `: ${JSON.stringify(error)}`;
        }
      }
    
      return {
        content: [
          {
            type: "text",
            text: errorMessage,
          },
        ],
        isError: true,
      };
    }
Behavior3/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 states the tool returns analytics data, which implies a read-only operation, but doesn't explicitly confirm it's non-destructive or mention rate limits, authentication needs, or pagination behavior. The hints for arguments add some behavioral context (e.g., format requirements), but key operational details are missing.

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 appropriately sized and front-loaded with the core purpose in the first sentence. The subsequent hints are well-structured into sections for different arguments, making it easy to scan. However, the hints section is lengthy and could be more concise by integrating some examples directly into the schema or reducing redundancy.

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 (11 parameters, nested objects, no output schema) and no annotations, the description is moderately complete. It covers the purpose, usage guidelines, and parameter semantics well, but lacks details on behavioral aspects like error handling, rate limits, or output format. Without an output schema, the description should ideally hint at the return structure, which it doesn't.

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

Parameters4/5

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

The schema description coverage is 100%, so the baseline is 3. The description adds significant value beyond the schema by providing detailed hints for arguments, including valid sources for dimensions and metrics (standard vs. custom), examples for dateRanges, dimensionFilter, and orderBys, and referencing sibling tools for custom data. This compensates for the schema's lack of examples and external references.

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

Purpose5/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: 'Runs a Google Analytics Data API report. Returns analytics data based on the specified dimensions, metrics, and date ranges.' It specifies the exact action (runs a report), resource (Google Analytics Data API), and distinguishes it from siblings like ga4_run_realtime_report by focusing on standard reporting rather than real-time data.

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

Usage Guidelines4/5

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

The description provides clear context for when to use this tool by specifying it's for running reports with dimensions, metrics, and date ranges. It explicitly references ga4_custom_dimensions_metrics for retrieving custom dimensions and metrics, offering an alternative for those specific needs. However, it doesn't explicitly state when not to use it (e.g., vs. ga4_run_realtime_report for real-time data).

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/wonyoungseong/ga4-mcp-server'

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