Google Analytics 4 MCP
A read-only Google Analytics 4 MCP server for discovering accessible GA4 properties and running both reporting and admin APIs.
Discovery: list account summaries, get property details, list data streams.
Reporting: run standard, batch (up to 5), pivot, real-time (last 30 min), and funnel reports.
Admin/config listings: custom dimensions, custom metrics, key events, audiences, Google Ads links, property annotations.
Metadata & validation: fetch the full GA4 dimension/metric catalog and check dimension/metric compatibility before querying.
Audit: run access reports showing who read what over the last 12 months.
Rich query controls: filters (dimension/metric), order_bys, metric aggregations, date ranges, pagination, cohort/comparison specs, and quota/sampling info in responses.
Provides read-only access to Google Analytics 4 properties, including standard, batch, pivot, realtime, and funnel reports, metadata catalog, and admin lists for properties, streams, custom dimensions/metrics, key events, audiences, Google Ads links, annotations, and access reports.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Google Analytics 4 MCP show me sessions and users by country for the last 7 days"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Google Analytics 4 MCP
Read-only MCP server for Google Analytics 4. Seventeen tools cover the read surface of both the Data API and the Admin API: standard, batch, pivot, realtime and funnel reports; the metadata catalog and compatibility check; and admin lists for properties, streams, custom dimensions and metrics, key events, audiences, Google Ads links, annotations and access reports. We run this server in production for every analytics client.
Install
Claude Code:
claude mcp add ga4 -e GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json -- uvx scalably-ga4-mcpCodex:
codex mcp add ga4 --env GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json -- uvx scalably-ga4-mcpClaude Desktop: download ga4-mcp.mcpb from the latest GitHub release and open it.
Related MCP server: google_analytics
Setup
In Google Cloud, create or pick a project and enable the Google Analytics Data API and the Google Analytics Admin API on it (APIs and Services, Library).
Create a service account in that project and download its JSON key.
Add the service account's email as a Viewer on each GA4 property you want to query (Admin, Property access management).
No OAuth consent screen is needed; the server authenticates as the service account with the analytics.readonly scope. One service account can read any property that grants it access, so there is no per-client configuration beyond step 3 above.
Call ga4_list_account_summaries first to discover which property_id values the service account can see, then pass one into the reporting or admin tools.
Tools (17)
Tool | What it does |
| List every GA4 account and its child properties accessible to this service account |
| Full metadata for a single GA4 property |
| List all data streams (web, iOS, Android) on a GA4 property |
| List custom dimensions configured on a GA4 property |
| List custom metrics configured on a GA4 property |
| List key events (formerly conversions) on a GA4 property |
| List audiences defined on a GA4 property |
| List Google Ads links attached to a GA4 property |
| List reporting annotations on a GA4 property |
| Audit log of who read what on a GA4 property, last 12 months |
| Fetch the full GA4 dimension and metric catalog for a property |
| Run a GA4 standard report, the workhorse tool |
| Run up to 5 GA4 reports in a single round trip |
| Run a GA4 pivot report |
| Run a GA4 real-time report covering the last 30 minutes |
| Validate whether a dimension and metric combo can be queried together |
| Run a GA4 funnel report (v1alpha, drop-off analysis) |
Configuration
Variable | Required | Purpose |
| yes | Path to a Google service-account JSON file with the |
| no | INFO (default) or DEBUG |
Filter expression shape
Every tool accepting dimension_filter / metric_filter takes a dict:
{"filter": {"field_name": "country", "string_filter": {"value": "US"}}}Compose with boolean groups:
{"and_group": {"expressions": [
{"filter": {"field_name": "country", "string_filter": {"value": "US"}}},
{"filter": {"field_name": "deviceCategory", "string_filter": {"value": "mobile"}}}
]}}A raw {"field_name": ..., "string_filter": ...} is auto-wrapped into {"filter": {...}}.
Reply shape
Every tool returns JSON with status (succeeded, partial, no_op), operation, summary, target, result, proof, warnings, recovery. Failures surface as a tool error whose text is ga4_request_failed: <message> <hint>. proof.nextOffset on a partial status means: continue from that offset. proof.propertyQuota carries the property's remaining Data API quota so the caller can self-throttle. proof.dataQualityLimited is true when the response was sampled, thresholded, or collapsed high-cardinality rows into (other); treat such a result as directionally useful, not exact.
Limits
250,000 rows per Data API response (the server caps any higher client-supplied limit). Batch reports: up to 5 per call. Funnel reports: up to 10 steps. Realtime reports use a separate, smaller dimension and metric catalog covering only the last 30 minutes; non-realtime data typically lags 24 to 48 hours. Funnel reporting is v1alpha and its shape can change upstream. Every quota bucket (core reports, realtime, funnel) allows roughly 14,000 quota tokens per project per property per hour (a complex query can cost more than one token); 360 properties get a 10x multiplier. Re-check Google's current quota page before relying on an exact number.
Verify
Each release lists the package version, the .mcpb sha256 and the production commit it was derived from in CHANGELOG.md. CI runs the tests and a clean install of the built wheel on every push.
Privacy Policy
This server runs locally, on your machine, under your own credentials. It collects no personal data, contains no telemetry, stores nothing persistently, and talks only to the vendor API it wraps. No third party, including Scalably, receives your data. Contact: hello@scalably.io. Canonical copy: https://scalably.io/connector-privacy.html
License
MIT. Copyright Scalably.
Available Tools
17 toolsga4_batch_run_reportsARead-only
Run up to 5 GA4 reports in a single round-trip.
requests: list of run_report-shaped dicts. Same top-level parameters as ga4_run_report (minus property_id, inferred from the batch). Each entry accepts: dimensions, metrics, date_ranges, dimension_filter, metric_filter, order_bys, metric_aggregations, limit, offset, keep_empty_rows, currency_code.
Useful when the agent needs paired views (e.g. landing pages + referrers for the same window) and wants them atomically + under one quota call.
Inputs: requests (required), property_id (required). Returns: result
ga4_check_compatibilityARead-only
Validate whether a dim/metric combo can be queried together. Cheap pre-flight.
compatibility_filter: 'COMPATIBLE' (default, returns only fields that work with the provided selection) or 'INCOMPATIBLE' (returns only fields that would conflict).
Use before run_report when composing exploratory queries, cheaper than catching a GoogleAdsException after a big failed report.
Inputs: property_id (required), metrics, dimensions, metric_filter, +2 more. Returns: result
ga4_get_metadataARead-only
Fetch the full GA4 dimension + metric catalog for a property.
If property_id is omitted, returns the universal catalog (excludes property-specific custom fields). Pass a property_id to include custom dimensions and metrics like 'customEvent:foo' and 'customUser:signup_plan'.
Response: {"dimensions": [...], "metrics": [...]}. Each entry has api_name, ui_name, description, category, custom_definition.
Call this before run_report when the agent is uncertain about field names: GA4's catalog is large and custom fields require the property to resolve.
Inputs: property_id. Returns: result
ga4_get_property_detailsARead-only
Full metadata for a single GA4 property.
Returns display_name, property_type, parent account, time_zone, currency_code, industry_category, service_level, delete_time, expire_time, account reference, create_time, update_time.
property_id: numeric (e.g. "123456789") or resource (e.g. "properties/123456789").
Inputs: property_id (required). Returns: result
ga4_list_account_summariesARead-only
List every GA4 account and its child properties accessible to this service account.
CALL THIS FIRST to discover which property_id to pass into other tools. The SA only sees accounts/properties where its email has been explicitly added as a user.
Response: {"accounts": [{account, display_name, property_summaries: [...], ...}]}. Each property_summary has property, display_name, property_type, parent.
Inputs: no named parameters. Returns: result
ga4_list_audiencesARead-only
List audiences defined on a GA4 property.
Each audience has name, display_name, description, membership_duration_days, ads_personalization_enabled, event_trigger, exclusion_duration_mode, filter_clauses. Reference audienceId as a dimension in run_report for audience-based breakdowns.
NOTE: Uses Admin v1alpha since audiences are still alpha in April 2026.
Inputs: property_id (required). Returns: result
ga4_list_custom_dimensionsARead-only
List custom dimensions configured on a GA4 property.
Each entry contains the parameter_name (how you query it, use prefix 'customEvent:' or 'customUser:' when passing to get_metadata / run_report), display_name, scope (EVENT|USER|ITEM), and description. Essential grounding before running reports that reference custom fields.
Inputs: property_id (required). Returns: result
ga4_list_custom_metricsARead-only
List custom metrics configured on a GA4 property.
Each entry: parameter_name, display_name, measurement_unit, scope, restricted_metric_type.
Inputs: property_id (required). Returns: result
ga4_list_data_streamsARead-only
List all data streams (web, iOS, Android) on a GA4 property.
Useful for finding the Measurement ID (web), firebase_app_id (mobile), or the hostname of a web stream. Each stream has its creation/update timestamps.
Inputs: property_id (required). Returns: result
ga4_list_google_ads_linksARead-only
List Google Ads links attached to a GA4 property.
Returns customer_id (the Ads account), can_manage_clients, ads_personalization_enabled, and creator_email. Useful to confirm Ads ↔ GA4 cross-reporting availability before querying Ads-related dimensions like sessionGoogleAdsCampaignId.
Inputs: property_id (required). Returns: result
ga4_list_key_eventsARead-only
List key events (formerly conversions) on a GA4 property.
These are the events the client has marked as conversion-worthy. Query
conversion metrics via conversions dimension or direct metric names.
Inputs: property_id (required). Returns: result
ga4_list_property_annotationsARead-only
List reporting annotations on a GA4 property.
Annotations are markers (user-added or Google-system) flagging important events on a timeline: product launches, outages, marketing pushes, so downstream analysis can correlate metric swings with known events.
Each annotation has: name, title, description, annotation_date, annotation_date_range, color, creator_email, system_generated.
NOTE: Uses Admin v1alpha since annotations are still alpha in April 2026.
Inputs: property_id (required). Returns: result
ga4_run_access_reportARead-only
Audit log of who-read-what on a GA4 property (last 12 months).
dimensions (common): userEmail, accessedPropertyName. metrics: accessCount. date_ranges: list of {start_date, end_date} (YYYY-MM-DD or relative). Defaults to last 7d.
Use cases: identify usage of GA4 data by staff, confirm SA activity, compliance audits.
Inputs: property_id (required), limit, metrics, dimensions, +1 more. Returns: result
ga4_run_funnel_reportARead-only
Run a GA4 funnel report. v1alpha, API surface may change.
funnel: { is_open_funnel: bool, steps: [ { name: str, is_directly_followed_by: bool, filter_expression: FunnelFilterExpression, within_duration_from_prior_step: {seconds: int}, }, ... ] } funnel_breakdown: {dimension_name, limit}, optional per-step breakdown. funnel_next_action: {dimension_name, limit}, optional "what came after".
Use for drop-off analysis across a sequence of events. Behind an alpha flag because Google can change the shape, check the Feb 2026 release notes if this fails with a validation error.
Inputs: funnel (required), property_id (required), limit, date_ranges, +4 more. Returns: result
ga4_run_pivot_reportARead-only
Run a GA4 pivot report.
pivots: list of pivot specs, each with:
field_names: list of dimension names to pivot on
limit: max rows per pivot
offset: pagination offset
order_bys: list of OrderBy dicts (same shape as run_report's order_bys)
metric_aggregations: list of TOTAL|MINIMUM|MAXIMUM|COUNT
Use when you want a 2D view: e.g. rows=date, columns=device, values=sessions.
Inputs: property_id (required), pivots, metrics, dimensions, +5 more. Returns: result
ga4_run_realtime_reportARead-only
Run a GA4 real-time report. Covers the last 30 minutes only.
IMPORTANT: realtime uses a SEPARATE, smaller dimension/metric catalog. Do NOT pass 'date', 'totalRevenue', etc: they don't exist in realtime. Common dims: 'minutesAgo', 'country', 'deviceCategory', 'unifiedScreenName', 'eventName'. Common metrics: 'activeUsers', 'screenPageViews', 'eventCount'.
minute_ranges: list of {start_minutes_ago, end_minutes_ago, name?}. Max 2 ranges. Values are 0-29 (0 = now, 29 = 30 min ago).
For historical / batch reports use ga4_run_report instead.
Inputs: property_id (required), limit, metrics, order_bys, +5 more. Returns: result
ga4_run_reportARead-only
Run a GA4 standard report. The workhorse tool.
Args: property_id: numeric or 'properties/NNN'. dimensions: list of dimension api_names (e.g. ["country", "deviceCategory", "date"]). Custom dims require 'customEvent:' or 'customUser:' prefixes. metrics: list of metric api_names (e.g. ["activeUsers", "sessions", "totalRevenue"]). date_ranges: list of {start_date, end_date, name?}. Accepts 'YYYY-MM-DD', 'NdaysAgo', 'today', 'yesterday'. Up to 4 ranges. dimension_filter / metric_filter: filter expression dicts. Shapes: {"filter": {"field_name": "country", "string_filter": {"value": "US"}}} {"and_group": {"expressions": [...]}} {"or_group": {"expressions": [...]}} {"not_expression": {...}} order_bys: list of {metric: {metric_name}, desc} or {dimension: {...}}. metric_aggregations: list of TOTAL|MINIMUM|MAXIMUM|COUNT. limit: max rows (hard cap 250000 per response). offset: pagination offset. keep_empty_rows: include rows where all metrics are zero. currency_code: override property default for revenue metrics. cohort_spec / comparisons: advanced specs (see REST docs).
Gotchas surfaced in response.metadata:
samplingMetadatas: present if query was sampled (> 10M events scanned)
dataLossFromOtherRow: true if high-cardinality dims collapsed into "(other)"
schemaRestrictionResponse: active thresholding rules
subjectToThresholding: true if user-privacy thresholding dropped rows
propertyQuota always included so the agent can self-throttle.
Inputs: property_id (required), limit, offset, metrics, +10 more. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 17 tools
Tools are grouped into clear resource-based listings and distinct report modes (standard, batch, pivot, realtime, funnel, access). The only real overlap is that ga4_get_metadata also surfaces custom fields alongside ga4_list_custom_dimensions/metrics, but the descriptions clarify the catalog-vs-config distinction.
Every tool uses the ga4_ prefix and follows a predictable verb-first pattern: list_* for enumeration, get_* for single-item metadata, run_* for report execution, and check_* for validation. This is internally consistent and makes behavior predictable from the name alone.
17 tools is on the heavy side for an MCP server and approaches the borderline zone, especially with many list_* variants plus multiple report modes. However, each tool does map to a distinct GA4 API operation, so the count is defensible even if slightly large.
The surface covers the full read-only analytics workflow: property discovery, stream/config enumeration, field metadata, compatibility validation, and all major report types including realtime, funnel, batch, and access reporting. There are no glaring dead ends; write/admin mutations are absent but not part of this server's stated purpose.
Maintenance
Related MCP Connectors
Read and edit GA4, Search Console and Google Tag Manager from any MCP client. 29 tools.
Read-only Google Analytics 4 MCP server for AI assistants.
Hosted MCP server for GA4, Google Ads and Search Console. Google OAuth, nothing to install.
Hosted, read-only Google Analytics 4 MCP: reports, period comparisons, realtime, custom fields.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceMCP server that provides tools to interact with Google Analytics APIs, enabling account and property retrieval, report running, and custom dimension/metrics access.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables querying Google Analytics 4 data through MCP tools for running reports, listing properties, getting real-time metrics, and exploring metadata.462 npmMIT
- AlicenseBqualityCmaintenanceMCP server for Google Analytics 4 and Google Tag Manager with read and write access, providing 40 tools to manage GA4 reports, audiences, and GTM tags, triggers, and variables via natural language.40MIT
- AlicenseNot gradedqualityCmaintenanceEnables querying Google Analytics 4 reports, realtime data, funnels, and property configurations from any MCP client using natural language, with a one-command setup requiring only a service account key.20 npmApache 2.0