Skip to main content
Glama
ga4-mcp

ga4-mcp

by Walma-Labs

ga4-mcp — Google Analytics 4 MCP server

A read-only Model Context Protocol server that gives Claude, Cursor, Claude Code and any other MCP client access to Google Analytics 4: core, realtime, funnel and conversions reports, plus account and property information.

It is a TypeScript port of Google's own analytics-mcp. It has the same nine tools, the same argument names and the same filter hints, so prompts written for one work on the other. What differs:

  • Service-account auth, no gcloud. Point it at a key file, or pass the key inline. Nothing to log in to on the machine that runs the agent.

  • Streamable HTTP as well as stdio. It can run centrally. This is how Walma serves it to whole organisations through its AI gateway.

  • Compact results. Report rows come back as one object per row ({ "country": "Sweden", "sessions": 120 }) instead of parallel value arrays, so the model spends fewer tokens on the same data.

  • Three runtime dependencies. It calls the REST API directly instead of using the gRPC client libraries and Google's agent framework.

Built and used by Walma AI.

Tools

Tool

What it does

get_account_summaries

Accounts and properties the service account can read, with property IDs

get_property_details

Time zone, currency, industry and service level of a property

list_google_ads_links

Google Ads accounts linked to a property

list_property_annotations

Notes left on dates in GA4 (releases, campaigns, outages)

get_custom_dimensions_and_metrics

The property's custom definitions, with the apiName to report on

run_report

Data API report: any dimensions and metrics, filters, ordering, several date ranges, pagination

run_realtime_report

The last ~30 minutes

run_funnel_report

Funnel progression and abandonment, with breakdowns and next actions

run_conversions_report

Ad cost, ROAS and conversions with a chosen attribution model

Everything is read-only (analytics.readonly scope). The server cannot change anything in Google Analytics.

Related MCP server: google_analytics

Setup

1. Create a service account and key

  1. In Google Cloud Console, pick or create a project.

  2. Enable the Google Analytics Data API and the Google Analytics Admin API.

  3. IAM & Admin → Service accounts → Create. No project roles are needed.

  4. Keys → Add key → JSON. Save the file somewhere outside any repository, for example ~/.config/gcloud/ga4-service-account.json.

2. Give the service account access in Google Analytics

In GA4, open Admin → Property access management (or Account access management to cover every property in the account). Add the service account's email address (…@….iam.gserviceaccount.com) with the Viewer role.

The server sees exactly the properties it was invited to.

3. Add it to your MCP client

Claude Code:

claude mcp add ga4 --scope user \
  -e GA4_SERVICE_ACCOUNT_FILE=~/.config/gcloud/ga4-service-account.json \
  -- npx -y github:Walma-Labs/ga4-mcp

Claude Desktop, Cursor and others (mcpServers in the client's config):

{
  "mcpServers": {
    "ga4": {
      "command": "npx",
      "args": ["-y", "github:Walma-Labs/ga4-mcp"],
      "env": { "GA4_SERVICE_ACCOUNT_FILE": "/absolute/path/to/ga4-service-account.json" }
    }
  }
}

Environment variables

Variable

Meaning

GA4_SERVICE_ACCOUNT_FILE

Path to the service-account JSON key (~ is expanded)

GA4_SERVICE_ACCOUNT_JSON

The key as inline JSON. Wins over the file when both are set

PORT

Port for the HTTP entrypoint (default 8080)

Running over HTTP

npm install && npm run build
GA4_SERVICE_ACCOUNT_FILE=… npm run start:http

This starts a stateless streamable-HTTP endpoint on POST /, plus GET /healthz. It has no authentication of its own. Put it behind something that authenticates callers. In Walma's deployment, the gateway relay does that.

Using it as a library

import { createGa4Server, createGa4Client } from "@walma-labs/ga4-mcp";

const client = createGa4Client();          // or createGa4Client(myRequestFn)
const server = createGa4Server(client);    // an McpServer; connect any transport

createGa4Client takes an optional request function, so you can supply your own authorisation or a test double.

Development

npm install
npm test        # unit + MCP-wire tests against a mocked client, no network
npm run build

License

Apache-2.0. Tool names, argument contracts and hint text derive from Google's analytics-mcp; see NOTICE.

Available Tools

9 tools
get_account_summariesA

List the Google Analytics accounts and properties the service account can read, with property IDs.

Inputs: no named parameters.

get_custom_dimensions_and_metricsA

Return the property's custom dimensions and metrics, with the apiName to use in reports.

Inputs: property_id (required).

get_property_detailsB

Return details about a property: time zone, currency, industry, service level, create time.

Inputs: property_id (required).

list_property_annotationsA

List a property's annotations: notes left in GA4 on specific dates or periods, typically for releases, campaign launches or changes, and traffic jumps or drops with an external cause.

Inputs: property_id (required).

run_conversions_reportA

Runs a Google Analytics Data API conversions report.

USE THIS TOOL INSTEAD OF run_report WHEN:

  • You need to report on conversions, ad performance, return on ad spend (ROAS) or attribution.

  • You need conversion metrics such as advertiserAdCost, returnOnAdSpendByInteractionDate or allConversionsByConversionDate.

  • You need a specific attribution model (DATA_DRIVEN or LAST_CLICK).

  • The question is explicitly about conversions, ad clicks, ad costs, or campaigns related to conversions.

See https://developers.google.com/analytics/devguides/reporting/data/v1/conversions-api-basics.

dimensions

Only these standard dimensions: campaignName, continent, country, defaultChannelGroup, deviceCategory, medium, platform, primaryChannelGroup, source, sourceMedium, sourcePlatform, subcontinent.

metrics

Only these standard metrics: advertiserAdClicks, advertiserAdCost, advertiserAdCostPerAllConversionsByConversionDate, advertiserAdCostPerAllConversionsByInteractionDate, advertiserAdCostPerClick, advertiserAdImpressions, allConversionsByConversionDate, allConversionsByInteractionDate, returnOnAdSpendByConversionDate, returnOnAdSpendByInteractionDate, totalRevenueByConversionDate, totalRevenueByInteractionDate.

conversion_spec

Required. Pass an empty conversion_actions list for all conversion events: {"conversion_actions": ["conversionActions/12345"], "attribution_model": "DATA_DRIVEN"} (or [] / "LAST_CLICK")

date_ranges

Example date_ranges arguments:

  1. A single date range: [ {"start_date":"2025-01-01","end_date":"2025-01-31","name":"Jan2025"} ]

  2. A relative date range using 'yesterday' and 'today': [ {"start_date":"yesterday","end_date":"today","name":"YesterdayAndToday"} ]

  3. A relative date range using 'NdaysAgo': [ {"start_date":"30daysAgo","end_date":"yesterday","name":"Previous30Days"} ]

  4. Multiple date ranges (adds a 'dateRange' column to every row): [ {"start_date":"2025-01-01","end_date":"2025-01-31","name":"Jan2025"}, {"start_date":"2025-02-01","end_date":"2025-02-28","name":"Feb2025"} ]

dimension_filter

Example dimension_filter arguments:

  1. A simple filter: {"filter":{"field_name":"eventName","string_filter":{"match_type":"BEGINS_WITH","value":"add"}}}

  2. A NOT filter: {"not_expression":{"filter":{"field_name":"eventName","string_filter":{"match_type":"BEGINS_WITH","value":"add"}}}}

  3. An empty value filter: {"filter":{"field_name":"source","empty_filter":{}}}

  4. An AND group filter: {"and_group":{"expressions":[{"filter":{"field_name":"sourceMedium","string_filter":{"match_type":"EXACT","value":"google / cpc"}}},{"filter":{"field_name":"eventName","in_list_filter":{"case_sensitive":true,"values":["first_visit","purchase","add_to_cart"]}}}]}}

  5. An OR group filter: {"or_group":{"expressions":[{"filter":{"field_name":"sourceMedium","string_filter":{"match_type":"EXACT","value":"google / cpc"}}},{"filter":{"field_name":"eventName","in_list_filter":{"case_sensitive":true,"values":["first_visit","purchase","add_to_cart"]}}}]}}

Notes: The API applies dimension_filter and metric_filter independently, so some combinations are impossible in one request, e.g. ((eventName = "page_view" AND eventCount > 100) OR (eventName = "join_group" AND eventCount < 50)). For conditions shaped ((D1 AND M1) OR (D2 AND M2)) either: a) run one report filtering on the supported subset (eventName in ["page_view","join_group"]) with eventCount included, and apply the metric conditions to the rows yourself; or b) run one report per (dimension condition, metric condition) pair. Prefer (a) — fewer reports — unless it would use excessive quota (https://developers.google.com/analytics/blog/2023/data-api-quota-management).

metric_filter

Example metric_filter arguments:

  1. A simple filter: {"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}}

  2. A NOT filter: {"not_expression":{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}}}

  3. An empty value filter: {"filter":{"field_name":"purchaseRevenue","empty_filter":{}}}

  4. An AND group filter: {"and_group":{"expressions":[{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}},{"filter":{"field_name":"purchaseRevenue","between_filter":{"from_value":{"double_value":10},"to_value":{"double_value":25}}}}]}}

  5. An OR group filter: {"or_group":{"expressions":[{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}},{"filter":{"field_name":"purchaseRevenue","between_filter":{"from_value":{"double_value":10},"to_value":{"double_value":25}}}}]}}

Notes: The API applies dimension_filter and metric_filter independently, so some combinations are impossible in one request, e.g. ((eventName = "page_view" AND eventCount > 100) OR (eventName = "join_group" AND eventCount < 50)). For conditions shaped ((D1 AND M1) OR (D2 AND M2)) either: a) run one report filtering on the supported subset (eventName in ["page_view","join_group"]) with eventCount included, and apply the metric conditions to the rows yourself; or b) run one report per (dimension condition, metric condition) pair. Prefer (a) — fewer reports — unless it would use excessive quota (https://developers.google.com/analytics/blog/2023/data-api-quota-management).

order_bys

Example order_bys arguments:

  1. Ascending 'eventName': [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false} ]

  2. Descending 'campaignName', ignoring case: [ {"dimension":{"dimension_name":"campaignName","order_type":"CASE_INSENSITIVE_ALPHANUMERIC"},"desc":true} ]

  3. Ascending 'audienceId' (numeric): [ {"dimension":{"dimension_name":"audienceId","order_type":"NUMERIC"},"desc":false} ]

  4. Descending 'eventCount': [ {"metric":{"metric_name":"eventCount"},"desc":true} ]

  5. Ascending 'eventCount': [ {"metric":{"metric_name":"eventCount"},"desc":false} ]

  6. Dimension and metric combined: [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false}, {"metric":{"metric_name":"eventCount"},"desc":true} ]

  7. Several dimensions and metrics: [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false}, {"dimension":{"dimension_name":"audienceId","order_type":"NUMERIC"},"desc":false}, {"metric":{"metric_name":"eventCount"},"desc":true} ] The dimensions and metrics in order_bys must also be in the request's dimensions and metrics.

Inputs: metrics (required), dimensions (required), date_ranges (required), property_id (required), +8 more.

run_funnel_reportA

Runs a Google Analytics Data API funnel report (https://developers.google.com/analytics/devguides/reporting/data/v1/funnels).

Returns funnel_table (progression through the steps) and funnel_visualization, both flattened to one object per row.

funnel_steps

Each step is {name, filter_expression} or the shorthand {name, event}. Example funnel_steps configurations:

  1. Shorthand event step: {"name":"Session start","event":"session_start"}

  2. Several events with OR: {"name":"First open/visit","filter_expression":{"or_group":{"expressions":[{"funnel_event_filter":{"event_name":"first_open"}},{"funnel_event_filter":{"event_name":"first_visit"}}]}}}

  3. Field filter for organic traffic: {"name":"Organic visitors","filter_expression":{"funnel_field_filter":{"field_name":"firstUserMedium","string_filter":{"match_type":"CONTAINS","case_sensitive":false,"value":"organic"}}}}

  4. Event with a parameter filter (value > 50): {"name":"Add to cart (value > 50)","filter_expression":{"funnel_event_filter":{"event_name":"add_to_cart","funnel_parameter_filter_expression":{"funnel_parameter_filter":{"event_parameter_name":"value","numeric_filter":{"operation":"GREATER_THAN","value":{"double_value":50}}}}}}}

  5. AND condition (page view on a path): {"name":"Home page view","filter_expression":{"and_group":{"expressions":[{"funnel_event_filter":{"event_name":"page_view"}},{"funnel_field_filter":{"field_name":"pageLocation","string_filter":{"match_type":"CONTAINS","value":"/"}}}]}}}

A typical e-commerce funnel: [ {"name":"First open/visit","filter_expression":{"or_group":{"expressions":[{"funnel_event_filter":{"event_name":"first_open"}},{"funnel_event_filter":{"event_name":"first_visit"}}]}}}, {"name":"Organic visitors","filter_expression":{"funnel_field_filter":{"field_name":"firstUserMedium","string_filter":{"match_type":"CONTAINS","case_sensitive":false,"value":"organic"}}}}, {"name":"Session start","filter_expression":{"funnel_event_filter":{"event_name":"session_start"}}}, {"name":"Screen/Page view","filter_expression":{"or_group":{"expressions":[{"funnel_event_filter":{"event_name":"screen_view"}},{"funnel_event_filter":{"event_name":"page_view"}}]}}}, {"name":"Purchase","filter_expression":{"or_group":{"expressions":[{"funnel_event_filter":{"event_name":"purchase"}},{"funnel_event_filter":{"event_name":"in_app_purchase"}}]}}} ]

funnel_breakdown

Segment the funnel by a dimension: {"breakdown_dimension": "deviceCategory"}. Common: deviceCategory, country, operatingSystem, browser.

funnel_next_action

What users do after completing or dropping off: {"next_action_dimension": "eventName", "limit": 5}. Common: eventName, pagePath.

segments

Data API Segment objects (https://developers.google.com/analytics/devguides/reporting/data/v1/funnels#segments).

date_ranges

Example date_ranges arguments:

  1. A single date range: [ {"start_date":"2025-01-01","end_date":"2025-01-31","name":"Jan2025"} ]

  2. A relative date range using 'yesterday' and 'today': [ {"start_date":"yesterday","end_date":"today","name":"YesterdayAndToday"} ]

  3. A relative date range using 'NdaysAgo': [ {"start_date":"30daysAgo","end_date":"yesterday","name":"Previous30Days"} ]

  4. Multiple date ranges (adds a 'dateRange' column to every row): [ {"start_date":"2025-01-01","end_date":"2025-01-31","name":"Jan2025"}, {"start_date":"2025-02-01","end_date":"2025-02-28","name":"Feb2025"} ]

Inputs: property_id (required), funnel_steps (required), segments, date_ranges, +3 more.

run_realtime_reportA

Runs a Google Analytics Data API realtime report covering roughly the last 30 minutes (https://developers.google.com/analytics/devguides/reporting/data/v1/realtime-basics).

dimensions

Realtime dimensions (https://developers.google.com/analytics/devguides/reporting/data/v1/realtime-api-schema#dimensions), or user-scoped custom dimensions: from get_custom_dimensions_and_metrics, those whose apiName begins with "customUser:".

metrics

Realtime metrics only (https://developers.google.com/analytics/devguides/reporting/data/v1/realtime-api-schema#metrics). Realtime reports can't use custom metrics.

dimension_filter

Example dimension_filter arguments:

  1. A simple filter: {"filter":{"field_name":"eventName","string_filter":{"match_type":"BEGINS_WITH","value":"add"}}}

  2. A NOT filter: {"not_expression":{"filter":{"field_name":"eventName","string_filter":{"match_type":"BEGINS_WITH","value":"add"}}}}

  3. An empty value filter: {"filter":{"field_name":"source","empty_filter":{}}}

  4. An AND group filter: {"and_group":{"expressions":[{"filter":{"field_name":"sourceMedium","string_filter":{"match_type":"EXACT","value":"google / cpc"}}},{"filter":{"field_name":"eventName","in_list_filter":{"case_sensitive":true,"values":["first_visit","purchase","add_to_cart"]}}}]}}

  5. An OR group filter: {"or_group":{"expressions":[{"filter":{"field_name":"sourceMedium","string_filter":{"match_type":"EXACT","value":"google / cpc"}}},{"filter":{"field_name":"eventName","in_list_filter":{"case_sensitive":true,"values":["first_visit","purchase","add_to_cart"]}}}]}}

Notes: The API applies dimension_filter and metric_filter independently, so some combinations are impossible in one request, e.g. ((eventName = "page_view" AND eventCount > 100) OR (eventName = "join_group" AND eventCount < 50)). For conditions shaped ((D1 AND M1) OR (D2 AND M2)) either: a) run one report filtering on the supported subset (eventName in ["page_view","join_group"]) with eventCount included, and apply the metric conditions to the rows yourself; or b) run one report per (dimension condition, metric condition) pair. Prefer (a) — fewer reports — unless it would use excessive quota (https://developers.google.com/analytics/blog/2023/data-api-quota-management).

metric_filter

Example metric_filter arguments:

  1. A simple filter: {"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}}

  2. A NOT filter: {"not_expression":{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}}}

  3. An empty value filter: {"filter":{"field_name":"purchaseRevenue","empty_filter":{}}}

  4. An AND group filter: {"and_group":{"expressions":[{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}},{"filter":{"field_name":"purchaseRevenue","between_filter":{"from_value":{"double_value":10},"to_value":{"double_value":25}}}}]}}

  5. An OR group filter: {"or_group":{"expressions":[{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}},{"filter":{"field_name":"purchaseRevenue","between_filter":{"from_value":{"double_value":10},"to_value":{"double_value":25}}}}]}}

Notes: The API applies dimension_filter and metric_filter independently, so some combinations are impossible in one request, e.g. ((eventName = "page_view" AND eventCount > 100) OR (eventName = "join_group" AND eventCount < 50)). For conditions shaped ((D1 AND M1) OR (D2 AND M2)) either: a) run one report filtering on the supported subset (eventName in ["page_view","join_group"]) with eventCount included, and apply the metric conditions to the rows yourself; or b) run one report per (dimension condition, metric condition) pair. Prefer (a) — fewer reports — unless it would use excessive quota (https://developers.google.com/analytics/blog/2023/data-api-quota-management).

order_bys

Example order_bys arguments:

  1. Ascending 'eventName': [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false} ]

  2. Descending 'campaignName', ignoring case: [ {"dimension":{"dimension_name":"campaignName","order_type":"CASE_INSENSITIVE_ALPHANUMERIC"},"desc":true} ]

  3. Ascending 'audienceId' (numeric): [ {"dimension":{"dimension_name":"audienceId","order_type":"NUMERIC"},"desc":false} ]

  4. Descending 'eventCount': [ {"metric":{"metric_name":"eventCount"},"desc":true} ]

  5. Ascending 'eventCount': [ {"metric":{"metric_name":"eventCount"},"desc":false} ]

  6. Dimension and metric combined: [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false}, {"metric":{"metric_name":"eventCount"},"desc":true} ]

  7. Several dimensions and metrics: [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false}, {"dimension":{"dimension_name":"audienceId","order_type":"NUMERIC"},"desc":false}, {"metric":{"metric_name":"eventCount"},"desc":true} ] The dimensions and metrics in order_bys must also be in the request's dimensions and metrics.

Inputs: metrics (required), dimensions (required), property_id (required), limit, +5 more.

run_reportA

Runs a Google Analytics Data API report (sessions, users, page views, events, revenue, and so on).

Rows come back flattened: one object per row keyed by dimension and metric name, metrics as numbers.

dimensions

Standard dimensions (https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema#dimensions) available to every property, or the property's custom dimensions from get_custom_dimensions_and_metrics.

metrics

Standard metrics (https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema#metrics) available to every property, or the property's custom metrics from get_custom_dimensions_and_metrics.

date_ranges

Example date_ranges arguments:

  1. A single date range: [ {"start_date":"2025-01-01","end_date":"2025-01-31","name":"Jan2025"} ]

  2. A relative date range using 'yesterday' and 'today': [ {"start_date":"yesterday","end_date":"today","name":"YesterdayAndToday"} ]

  3. A relative date range using 'NdaysAgo': [ {"start_date":"30daysAgo","end_date":"yesterday","name":"Previous30Days"} ]

  4. Multiple date ranges (adds a 'dateRange' column to every row): [ {"start_date":"2025-01-01","end_date":"2025-01-31","name":"Jan2025"}, {"start_date":"2025-02-01","end_date":"2025-02-28","name":"Feb2025"} ]

dimension_filter

Example dimension_filter arguments:

  1. A simple filter: {"filter":{"field_name":"eventName","string_filter":{"match_type":"BEGINS_WITH","value":"add"}}}

  2. A NOT filter: {"not_expression":{"filter":{"field_name":"eventName","string_filter":{"match_type":"BEGINS_WITH","value":"add"}}}}

  3. An empty value filter: {"filter":{"field_name":"source","empty_filter":{}}}

  4. An AND group filter: {"and_group":{"expressions":[{"filter":{"field_name":"sourceMedium","string_filter":{"match_type":"EXACT","value":"google / cpc"}}},{"filter":{"field_name":"eventName","in_list_filter":{"case_sensitive":true,"values":["first_visit","purchase","add_to_cart"]}}}]}}

  5. An OR group filter: {"or_group":{"expressions":[{"filter":{"field_name":"sourceMedium","string_filter":{"match_type":"EXACT","value":"google / cpc"}}},{"filter":{"field_name":"eventName","in_list_filter":{"case_sensitive":true,"values":["first_visit","purchase","add_to_cart"]}}}]}}

Notes: The API applies dimension_filter and metric_filter independently, so some combinations are impossible in one request, e.g. ((eventName = "page_view" AND eventCount > 100) OR (eventName = "join_group" AND eventCount < 50)). For conditions shaped ((D1 AND M1) OR (D2 AND M2)) either: a) run one report filtering on the supported subset (eventName in ["page_view","join_group"]) with eventCount included, and apply the metric conditions to the rows yourself; or b) run one report per (dimension condition, metric condition) pair. Prefer (a) — fewer reports — unless it would use excessive quota (https://developers.google.com/analytics/blog/2023/data-api-quota-management).

metric_filter

Example metric_filter arguments:

  1. A simple filter: {"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}}

  2. A NOT filter: {"not_expression":{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}}}

  3. An empty value filter: {"filter":{"field_name":"purchaseRevenue","empty_filter":{}}}

  4. An AND group filter: {"and_group":{"expressions":[{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}},{"filter":{"field_name":"purchaseRevenue","between_filter":{"from_value":{"double_value":10},"to_value":{"double_value":25}}}}]}}

  5. An OR group filter: {"or_group":{"expressions":[{"filter":{"field_name":"eventCount","numeric_filter":{"operation":"GREATER_THAN","value":{"int64_value":"10"}}}},{"filter":{"field_name":"purchaseRevenue","between_filter":{"from_value":{"double_value":10},"to_value":{"double_value":25}}}}]}}

Notes: The API applies dimension_filter and metric_filter independently, so some combinations are impossible in one request, e.g. ((eventName = "page_view" AND eventCount > 100) OR (eventName = "join_group" AND eventCount < 50)). For conditions shaped ((D1 AND M1) OR (D2 AND M2)) either: a) run one report filtering on the supported subset (eventName in ["page_view","join_group"]) with eventCount included, and apply the metric conditions to the rows yourself; or b) run one report per (dimension condition, metric condition) pair. Prefer (a) — fewer reports — unless it would use excessive quota (https://developers.google.com/analytics/blog/2023/data-api-quota-management).

order_bys

Example order_bys arguments:

  1. Ascending 'eventName': [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false} ]

  2. Descending 'campaignName', ignoring case: [ {"dimension":{"dimension_name":"campaignName","order_type":"CASE_INSENSITIVE_ALPHANUMERIC"},"desc":true} ]

  3. Ascending 'audienceId' (numeric): [ {"dimension":{"dimension_name":"audienceId","order_type":"NUMERIC"},"desc":false} ]

  4. Descending 'eventCount': [ {"metric":{"metric_name":"eventCount"},"desc":true} ]

  5. Ascending 'eventCount': [ {"metric":{"metric_name":"eventCount"},"desc":false} ]

  6. Dimension and metric combined: [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false}, {"metric":{"metric_name":"eventCount"},"desc":true} ]

  7. Several dimensions and metrics: [ {"dimension":{"dimension_name":"eventName","order_type":"ALPHANUMERIC"},"desc":false}, {"dimension":{"dimension_name":"audienceId","order_type":"NUMERIC"},"desc":false}, {"metric":{"metric_name":"eventCount"},"desc":true} ] The dimensions and metrics in order_bys must also be in the request's dimensions and metrics.

Inputs: metrics (required), dimensions (required), date_ranges (required), property_id (required), +7 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 9 tools

    Disambiguation4/5

    Most tools are clearly distinct, but run_report vs run_conversions_report overlap in scope and the description has to explicitly say 'USE THIS TOOL INSTEAD OF run_report WHEN...' to disambiguate. run_realtime_report, run_funnel_report, and the get_/list_ discovery tools are cleanly separated, so only one boundary is fuzzy and it is well-documented.

    Naming Consistency4/5

    All tools follow a predictable verb_noun pattern (get_*, list_*, run_*), which is readable and consistent. The only weakness is that the generic run_report lacks a qualifier like run_standard_report, making it slightly ambiguous against run_realtime_report/run_funnel_report/run_conversions_report.

    Tool Count5/5

    Nine tools is well-scoped for a GA4 reporting server: five read/discovery tools plus four report runners covering the main Data API report shapes. No tool feels redundant or padded.

    Completeness4/5

    The surface covers discovery (accounts, property, ads links, annotations, custom dims/metrics) and all major report types (standard, realtime, funnel, conversions), so core analytical workflows are fully reachable. Minor gaps remain: no pivot or batch-run report equivalents and no annotation write operations.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Enables querying Google Analytics 4 properties using natural language through MCP clients. Supports customizable reports with any dimensions and metrics, listing properties, and real-time data.
      4
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables querying Google Analytics 4 data through MCP tools for running reports, listing properties, getting real-time metrics, and exploring metadata.
      462 npm
      MIT
    • A
      license
      B
      quality
      B
      maintenance
      Enables read-only Google Analytics 4 reporting through MCP, including daily summaries, top pages, acquisition, events, realtime activity, metadata, and custom reports with CSV/JSON output.
      8
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Enables read-only access to Google Analytics 4 data through seventeen tools covering reports, realtime data, funnels, pivots, metadata, and admin lists. It runs locally with service account credentials and lets users query properties via standard MCP clients.
      17
      23 PyPI
      MIT