Skip to main content
Glama
minholi

Google Analytics MCP Server

Google Analytics MCP Server

MCP (Model Context Protocol) server that integrates with the Google Analytics 4 Data API. Lets AI agents (Claude, Cursor, etc.) query traffic, acquisition sources, top pages, audience breakdowns, geographic performance, events, conversions, and realtime data — all through natural language.


Two operating modes

Mode

Who uses it

Auth

Google refresh token

stdio

Just you (Claude Desktop / Cursor local)

None — local subprocess

Single, in .env (generated by scripts/get_refresh_token.py)

HTTP multi-tenant

Team / remote access

Native MCP OAuth 2.1 — each user authenticates with their own Google account

One per user, encrypted on disk

Pick stdio for personal use (simpler). Pick HTTP when multiple users (with different Google accounts) need to share the same server.


Related MCP server: Google Analytics MCP Server

Available tools

Tool

Description

google_analytics_get_overview

Main KPIs (sessions, users, pageviews, bounce, engagement, conversions), optionally compared to the previous period

google_analytics_get_traffic_sources

Acquisition grouped by channel, source/medium or campaign

google_analytics_get_top_pages

Most-visited pages with engagement metrics; optional path-prefix filter

google_analytics_get_geographic_performance

Sessions/conversions by country, region or city, with highlights

google_analytics_get_audience

Audience breakdown by device, browser or operating system

google_analytics_get_events

Top events by count; optional filter to specific event names

google_analytics_get_conversions

Conversion events with revenue, per channel and event

google_analytics_get_realtime

Users active right now, by page, source, country and device

All accept: flexible date ranges (last_7_days, last_14_days, last_30_days, this_month, last_month, custom YYYY-MM-DD), output format (markdown or json), and optional property_id.


Setup A — stdio mode (single-tenant)

1. Prerequisites

  • Python 3.12+

  • A GA4 property you have access to

  • Project in Google Cloud Console with the Google Analytics Data API enabled

2. Installation

git clone https://github.com/minholi/google-analytics-mcp.git
cd google-analytics-mcp
uv sync

3. "Desktop" OAuth Client in GCP

  1. APIs & Services → Credentials → Create Credentials → OAuth client ID

  2. Application type: Desktop app

  3. Save the Client ID and Client secret

4. Refresh token

Interactive wizard:

uv run python scripts/get_refresh_token.py

Paste the Client ID/Client secret when prompted; it opens the browser, you log in, and copy the resulting refresh_token.

5. .env

GOOGLE_ANALYTICS_CLIENT_ID=...apps.googleusercontent.com
GOOGLE_ANALYTICS_CLIENT_SECRET=...
GOOGLE_ANALYTICS_REFRESH_TOKEN=...
GOOGLE_ANALYTICS_PROPERTY_ID=123456789

Find the numeric property ID in Analytics Admin → Property details.

6. Client configuration

Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "google-analytics": {
      "command": "uv",
      "args": ["run", "python", "main.py", "--transport", "stdio"],
      "cwd": "/path/to/google-analytics-mcp"
    }
  }
}

Cursor.cursor/mcp.json:

{
  "mcpServers": {
    "google-analytics": {
      "command": "uv",
      "args": ["run", "python", "main.py", "--transport", "stdio"],
      "cwd": "/path/to/google-analytics-mcp"
    }
  }
}

uv run loads the .env automatically via python-dotenv.


Setup B — HTTP multi-tenant mode

How it works

Claude Desktop ──(MCP OAuth: tokens issued by us)──▶ our MCP (AS+RS)
                                                          │
                                                          └──(Google OAuth: user's refresh_token)──▶ GA4 Data API

There are two chained OAuth flows: the server is simultaneously an Authorization Server (issues its own JWTs to Claude Desktop) and a Google OAuth Client (holds the encrypted refresh_token for the user's Google account). Desktop never sees the Google refresh_token.

UX in Claude Desktop:

  1. User adds the MCP URL under Settings → Connectors (once).

  2. Clicks "Connect" → browser opens → Google login → analytics.readonly consent → returns connected.

  3. From then on, all calls are authenticated. Refresh is silent.

1. Prerequisites

  • Public domain with TLS (e.g., mcp.your-domain.com)

  • Caddy (or Nginx) running on the host — will terminate TLS and reverse-proxy to 127.0.0.1:8000

  • Docker + Docker Compose

2. "Web application" OAuth Client in GCP

⚠️ Separate client from the Desktop one used in Setup A.

  1. Enable the Google Analytics Data API if you haven't already.

  2. Configure the OAuth consent screen:

    • User type: External

    • Add the scope https://www.googleapis.com/auth/analytics.readonly

    • Under "Test users" add the emails that will test (or click Publish).

  3. Create Credentials → OAuth client ID:

    • Application type: Web application

    • Authorized redirect URIs: https://mcp.your-domain.com/auth/callback (must match the MCP_PUBLIC_URL you configure exactly)

  4. Save the Client ID and Client secret.

3. (Optional) Generate a JWT signing key

# Only if you want to pin the key (e.g., multiple replicas).
# Without this, GoogleProvider derives the key from the client_secret.
uv run python -c "import secrets; print(secrets.token_urlsafe(48))"

4. .env

# Web OAuth Client (step 2)
GOOGLE_OAUTH_WEB_CLIENT_ID=123456789-abc.apps.googleusercontent.com
GOOGLE_OAUTH_WEB_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxx

# Public URL — no trailing slash, with https
MCP_PUBLIC_URL=https://mcp.your-domain.com

# Optional: pinned key for signing MCP JWTs (step 3)
# OAUTH_JWT_SIGNING_KEY=Z9K1u7c-paste-the-output-of-secrets-token-urlsafe-48

# Server
MCP_TRANSPORT=http
MCP_HOST=0.0.0.0
MCP_PORT=8000

Setup A variables (GOOGLE_ANALYTICS_CLIENT_ID, GOOGLE_ANALYTICS_CLIENT_SECRET, GOOGLE_ANALYTICS_REFRESH_TOKEN) are not used in HTTP mode — you can remove or comment them out. GOOGLE_ANALYTICS_PROPERTY_ID is still handy as a default that individual users can override per tool call.

5. Host Caddy

mcp.your-domain.com {
    reverse_proxy 127.0.0.1:8000
}

6. Start

mkdir -p data
docker compose up -d --build
docker compose logs -f google-analytics-mcp

7. Verification

curl https://mcp.your-domain.com/.well-known/oauth-authorization-server

Should return JSON with issuer, authorization_endpoint, etc.

8. Connect from Claude Desktop

Settings → Connectors → Add custom connector → paste https://mcp.your-domain.com/mcp → click Connect → Google flow opens → log in → property authorized.

Done. Other users can do the same on their machines.


Environment variables

Common (both modes)

Variable

Description

GOOGLE_ANALYTICS_PROPERTY_ID

Default numeric GA4 property ID (optional; can be passed per call)

MCP_TRANSPORT

stdio (default) or http

MCP_PORT

HTTP port (default 8000)

MCP_HOST

HTTP host (default 0.0.0.0)

stdio mode

Variable

Required

Description

GOOGLE_ANALYTICS_CLIENT_ID

Desktop OAuth Client ID

GOOGLE_ANALYTICS_CLIENT_SECRET

Desktop OAuth Client Secret

GOOGLE_ANALYTICS_REFRESH_TOKEN

Refresh token generated by scripts/get_refresh_token.py

HTTP multi-tenant mode

Variable

Required

Description

MCP_PUBLIC_URL

Public URL, no trailing /. Must match the redirect URI in GCP

GOOGLE_OAUTH_WEB_CLIENT_ID

Web OAuth Client ID

GOOGLE_OAUTH_WEB_CLIENT_SECRET

Web OAuth Client Secret

OAUTH_JWT_SIGNING_KEY

Optional. If absent, GoogleProvider derives the key from the client_secret. Set only for multiple replicas


Architecture

google-analytics-mcp/
├── main.py                       # Entry point — stdio or HTTP (mcp.http_app)
├── src/
│   ├── server.py                 # 8 MCP tools (fastmcp) + conditional GoogleProvider
│   ├── client.py                 # GA4 Data API v1beta client (httpx + REST)
│   ├── auth.py                   # GoogleAnalyticsAuth — env vars (stdio) or injected access_token (HTTP)
│   └── formatters.py             # Markdown / JSON
├── scripts/
│   └── get_refresh_token.py      # OAuth wizard (stdio mode)
├── Dockerfile
├── docker-compose.yml            # Exposes 127.0.0.1:8000 (host Caddy handles TLS)
├── pyproject.toml
└── .env.example

Data flow (HTTP mode): tool call → JWT verified by GoogleProvider (token-swap JTI → decrypted upstream Google access_token) → get_access_token().token → GoogleAnalyticsAuth.for_access_token → GA4 Data API REST v1beta → formatter. The Google refresh_token stays encrypted in GoogleProvider's internal key-value store; refresh is transparent when the access_token expires.


Usage examples (natural language)

"Show the overview for the last 7 days, comparing with the previous week"
"Which channels are driving the most sessions in the last 30 days?"
"Which pages have the highest bounce rate this month?"
"Where are my users coming from geographically?"
"How is the audience split between mobile and desktop?"
"Which events are firing most in the last 14 days?"
"How many conversions did I get last month, and where did the revenue come from?"
"How many users are on the site right now?"

See USAGE_GUIDE.md for scenario-driven playbooks and a full tool reference.


Operations

Logs

docker compose logs -f google-analytics-mcp

Backup

  • GoogleProvider persists DCR clients and encrypted Google refresh_tokens in ./data/ (mounted from the host). Back this directory up if user reconnection would be disruptive.

  • OAUTH_JWT_SIGNING_KEY — if you change it (or if you're using the default derivation and change GOOGLE_OAUTH_WEB_CLIENT_SECRET), all users must reconnect.

Rebuild after code changes

docker compose up -d --build

Troubleshooting

Symptom

Likely cause

Fix

redirect_uri_mismatch in Google consent

URI in GCP ≠ ${MCP_PUBLIC_URL}/auth/callback

Check character by character (https, no trailing slash in MCP_PUBLIC_URL)

"Google did not return a refresh_token" in the callback

User previously authorized the app (Google only returns refresh_token on the first consent)

Ask them to revoke at myaccount.google.com/permissions and reconnect

401 invalid_token on /mcp

MCP JWT expired or OAUTH_JWT_SIGNING_KEY changed

Desktop refreshes on its own; if it persists, reconnect from Connectors

Tool returns PERMISSION_DENIED from the GA4 API

User doesn't have access to the requested property, or the property_id is wrong

Confirm the account has at least Viewer access in GA4

Docker healthcheck failing

Missing MCP_PUBLIC_URL or some OAUTH_* in .env

docker compose logs google-analytics-mcp shows which variable is missing

OPENID_DISCOVERY_FAILED in Claude Desktop

Broken DNS/TLS or Caddy not routing

curl -v https://mcp.../.well-known/oauth-authorization-server


Security

  • Credentials never in code — always in .env (already in .gitignore).

  • Host Caddy — terminates TLS. The container only listens on 127.0.0.1:8000, not reachable directly from the internet.

  • Rate limiting — configure on the host Caddy, not in the app.

  • Container runs as a non-root user.

  • Google refresh_tokens encrypted at rest by GoogleProvider's internal key-value store (persisted in ./data/).

  • Short-lived MCP JWTs with transparent upstream Google refresh and MCP refresh token rotation (OAuth 2.1) — all managed by GoogleProvider.

  • Treat OAUTH_JWT_SIGNING_KEY (if set) and GOOGLE_OAUTH_WEB_CLIENT_SECRET as critical secrets (vault/secret manager in production).


License

MIT — see LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Connects Google Analytics 4 data to Claude, Cursor and other MCP clients, enabling natural language queries of website traffic, user behavior, and analytics data with access to 200+ GA4 dimensions and metrics.
    10
    1,020 PyPI
    241
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to interact with Google Analytics 4 data, providing tools for historical reporting, real-time activity monitoring, and property management. It supports secure service account authentication to access metrics like traffic summaries, user acquisition, and custom dimensions.
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables querying Google Analytics 4 data through natural language, including running reports, comparing periods, and exploring realtime metrics across multi-tenant properties with OAuth-based authentication.
    -