Skip to main content
Glama
yeswanthreddyk

google-analytics-mcp

README.md
# Google Analytics (GA4) MCP Server

An MCP server that connects an MCP client (Claude Desktop, etc.) to **Google
Analytics 4**. You log into your Google account once; after that every tool
reports on the GA4 properties that login can access.

Built with the official `google-analytics-data` and `google-analytics-admin`
Python clients and `FastMCP`. Reporting, intelligence, charts, admin reads, and
**write/management** tools (creating/updating key events, custom dimensions/metrics,
and data streams).

> Simpler than the Google Ads server: GA4 needs **no developer token** — just an
> OAuth client and your login.

---

## Tools (45)

**Reporting**

| Tool | Purpose |
|------|---------|
| `ga4_run_report` | Core report: metrics, dimensions, date range, **filters**, **export to CSV/XLSX** |
| `ga4_compare_periods` | Compare a current vs previous period with % change |
| `ga4_run_pivot_report` | Cross-tabulate dimensions (rows × columns) |
| `ga4_run_cohort_report` | Retention by acquisition cohort (daily/weekly/monthly) |
| `ga4_run_funnel_report` | Step-by-step funnel completion / drop-off |
| `ga4_run_realtime_report` | Realtime activity (last ~30 min) |
| `ga4_check_compatibility` | Which metrics + dimensions can be queried together |
| `ga4_get_metadata` | Discover valid metric & dimension names |
| `ga4_batch_run_reports` | Run up to 5 reports in one batched call |
| `ga4_property_overview` | One-call snapshot: totals, top channels & pages, devices, new vs returning |

**Preset shortcuts** (zero-config reports)

| Tool | Purpose |
|------|---------|
| `ga4_top_pages` | Top pages by views |
| `ga4_traffic_by_channel` | Sessions & users by default channel group |
| `ga4_conversions_summary` | Conversions by event name |
| `ga4_trend` | A single metric over time (daily/weekly/monthly) |

**Accounts & configuration (admin reads)**

| Tool | Purpose |
|------|---------|
| `ga4_list_account_summaries` | Accounts + their properties (find property IDs) |
| `ga4_list_properties` | Properties under an account |
| `ga4_list_data_streams` | Data streams (web measurement ID, app streams) |
| `ga4_list_custom_dimensions` | Custom dimensions on a property |
| `ga4_list_custom_metrics` | Custom metrics on a property |
| `ga4_list_key_events` | Key events (conversions) |
| `ga4_list_audiences` | Audiences |
| `ga4_list_google_ads_links` | Linked Google Ads accounts |
| `ga4_get_data_retention_settings` | Event-data retention settings |
| `ga4_search_change_history` | Who changed what, and when |
| `ga4_list_channel_groups` | Channel groups and their grouping rules |

**Audience export** (pull the actual users in an audience — async create → query)

| Tool | Purpose |
|------|---------|
| `ga4_create_audience_export` | Start an export for an audience (waits for it to finish) |
| `ga4_list_audience_exports` | List exports and their state (CREATING/ACTIVE) |
| `ga4_query_audience_export` | Read the exported user rows |

**Intelligence**

| Tool | Purpose |
|------|---------|
| `ga4_what_changed` | Top gainers/losers driving a metric's change between two periods |
| `ga4_detect_anomalies` | Flag days a metric deviated from its baseline (z-score) |
| `ga4_forecast_metric` | Project a metric forward with a linear trend |

**Charts** — `ga4_chart_report`: render a bar/line PNG of a metric by a dimension (saved to `exports/`).

**More admin reads**

| Tool | Purpose |
|------|---------|
| `ga4_list_access_bindings` | Who has access (and roles) to an account/property |
| `ga4_list_bigquery_links` | BigQuery export links |
| `ga4_list_firebase_links` | Firebase project links |
| `ga4_list_measurement_protocol_secrets` | MP secrets for a data stream |
| `ga4_get_property_details` | Full property settings (industry, tz, currency, tier) |
| `ga4_get_enhanced_measurement_settings` | Enhanced-measurement toggles for a stream |

**Write / management** ⚠️ these modify your GA4 configuration

| Tool | Purpose |
|------|---------|
| `ga4_create_key_event` / `ga4_update_key_event` | Create / update a key event (conversion) |
| `ga4_create_custom_dimension` / `ga4_update_custom_dimension` | Create / update a custom dimension |
| `ga4_create_custom_metric` / `ga4_update_custom_metric` | Create / update a custom metric |
| `ga4_create_data_stream` | Create a web data stream (returns its measurement ID) |

### Filtering (in `ga4_run_report`)
Pass `dimension_filters` / `metric_filters` — a list of conditions. Set
`filter_logic` to `AND` (default) or `OR`; set `negate: true` on a condition for NOT.
Each condition has `field`, `operator`, and `value` (or `values` for `IN_LIST`,
or `value` + `value_to` for `BETWEEN`):
- String operators: `EXACT`, `CONTAINS`, `BEGINS_WITH`, `ENDS_WITH`, `REGEXP`, `IN_LIST`
- Numeric operators (metrics): `EQUAL`, `LESS_THAN`, `LESS_EQUAL`, `GREATER_THAN`, `GREATER_EQUAL`, `BETWEEN`

Example: country is India **and** sessions > 100 → dimension filter `country EXACT India`
plus metric filter `sessions GREATER_THAN 100`.

### More `ga4_run_report` options
- **Sort by dimension**: `order_by_dimension` (+ `order_desc: false` for ascending) — e.g. chronological by `date`.
- **Totals**: `include_totals: true` appends a totals row (and computes min/max).
- **Metadata**: results note the currency, time zone, and warn if data is **sampled** or **thresholded**.

### Export
Set `export_path` on `ga4_run_report` (e.g. `report.csv` or `report.xlsx`). Bare
filenames are written to an `exports/` folder next to the server. CSV works out of
the box; XLSX needs `openpyxl` (already in requirements.txt).

---

## Setup

### 0. Requirements
- Python 3.9+
- A GA4 property and a Google account with at least **Viewer** access to it.

### 1. Install dependencies
```bash
cd google-analytics-mcp
python -m pip install -r requirements.txt
```

### 2. Create an OAuth client (Desktop app)
In [Google Cloud Console](https://console.cloud.google.com/):
1. Create/select a project.
2. **APIs & Services → Library →** enable both **Google Analytics Data API** and
   **Google Analytics Admin API**.
3. **APIs & Services → Credentials → Create credentials → OAuth client ID**.
4. Application type: **Desktop app**. Copy the **client ID** and **client secret**.
   (If the OAuth consent screen is in "Testing", add your Google account as a
   **Test user**.)

### 3. Log in to get your refresh token
```bash
python get_refresh_token.py
```
A browser opens — choose the Google account with access to your GA4 property and
approve. The script writes the client ID/secret + refresh token into `.env`.

### 4. Fill in `.env`
```bash
cp .env.example .env   # Windows: copy .env.example .env
```
Required:
```
GOOGLE_ANALYTICS_CLIENT_ID=...        # from step 2 (also written by step 3)
GOOGLE_ANALYTICS_CLIENT_SECRET=...    # from step 2 (also written by step 3)
GOOGLE_ANALYTICS_REFRESH_TOKEN=...    # from step 3
# Optional convenience:
GOOGLE_ANALYTICS_DEFAULT_PROPERTY_ID=...   # numeric, e.g. 123456789
```
Find your **Property ID** in GA4: **Admin → Property Settings → Property ID**
(a number like `123456789`).

### 5. Smoke-test
```bash
python -c "import server; print('server imports OK')"
```
Then verify credentials end-to-end (lists your accounts/properties):
```bash
python -c "import asyncio, server; from server import AccountSummariesInput; \
print(asyncio.run(server.ga4_list_account_summaries(AccountSummariesInput())))"
```

### 6. Connect to Claude Desktop
Edit `claude_desktop_config.json` (macOS:
`~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`) — use the
**absolute path** to `server.py`:

```json
{
  "mcpServers": {
    "google-analytics": {
      "command": "python",
      "args": ["/ABSOLUTE/PATH/TO/google-analytics-mcp/server.py"]
    }
  }
}
```
The server reads `.env` from its own folder, so no secrets go in this config.
Restart Claude Desktop.

---

## Example prompts

- "List my GA4 accounts and properties."
- "Run a GA4 report of sessions and active users by country for the last 28 days."
- "Show screenPageViews by pagePath for last 7 days, top 20 by views."
- "What metrics and dimensions are available on property 123456789?"
- "How many active users are on the site right now, by country?"
- "Compare sessions and conversions for the last 28 days vs the prior 28 days."
- "Sessions from India only where sessions > 100, by city." (uses filters)
- "Export last month's traffic by channel to channel.xlsx."
- "Build a funnel: session_start → view_item → add_to_cart → purchase."
- "Weekly retention cohorts for the last 6 weeks."
- "Pivot sessions by country (rows) and device category (columns)."

### Common GA4 names
- **Metrics**: `activeUsers`, `sessions`, `screenPageViews`, `conversions`,
  `totalRevenue`, `engagementRate`, `bounceRate`, `averageSessionDuration`.
- **Dimensions**: `date`, `country`, `city`, `deviceCategory`, `pagePath`,
  `sessionDefaultChannelGroup`, `firstUserSource`, `eventName`.

Use `ga4_get_metadata` to see everything (including custom fields) for a property.

---

## Troubleshooting

| Symptom | Fix |
|---------|-----|
| `Missing Google Analytics credentials: ...` | Fill the named vars in `.env`. |
| `invalid_grant` / RefreshError | Refresh token expired/revoked — re-run `get_refresh_token.py`. |
| `Permission denied` | The login lacks access to that property, or the Data/Admin APIs aren't enabled in your Cloud project. |
| `Not found` | Check the numeric `property_id` (Admin → Property Settings). |
| `Invalid request` | Check metric/dimension names (`ga4_get_metadata`) and date formats (`YYYY-MM-DD` or `NdaysAgo`). |

---

## Security
- `.env` holds secrets and is git-ignored — never commit it.
- Scopes: `analytics.readonly` (reporting + config reads), `analytics.edit`
  (config writes — the create/update tools), and `analytics.manage.users.readonly`
  (reading access bindings). The **edit** scope lets the server change GA4
  configuration, so treat the refresh token accordingly. After changing scopes
  you must re-run `get_refresh_token.py` and re-consent.

## Project layout
```
google-analytics-mcp/
├── server.py             # FastMCP server + 6 tools
├── ga_client.py          # credentials, client factories, error handling
├── get_refresh_token.py  # one-time OAuth login -> refresh token
├── requirements.txt
├── .env.example          # copy to .env and fill in
├── .gitignore
└── README.md
```

Maintenance

ActivityStale
ResponsivenessNo issues