Skip to main content
Glama
ukituki

analytics-selfhosted-mcp

by ukituki

analytics-selfhosted-mcp

Unofficial / community project. This is not an official Google product.
Official Google Analytics MCP (local stdio only):
googleanalytics/google-analytics-mcp (PyPI: analytics-mcp).

Self-host the same GA Admin/Data tools over Streamable HTTP on Modal, so clients that only accept a public URL (Cursor remote MCP, agents, etc.) can call Google Analytics with an API key — plus token-efficient report compaction that upstream does not ship.

Official Google MCP

This project

Status

Official

Unofficial wrapper / host

Transport

stdio

Streamable HTTP (https://…/mcp)

Runs on

Your laptop

Modal (or any host you deploy)

Auth to MCP

Process trust

Shared API key

Extra

Compact reports + paged rows

When to use official: one machine, Cursor/Claude Desktop via command + stdio.
When to use this: you need a URL-addressable Analytics MCP.

ClickUp App Center auth works but tools do not load yet (client never calls tools/list). Details: docs/CLICKUP_ISSUE.md.


Quick path (what “done” looks like)

Step

Done when…

0. Prerequisites

uv, modal, GCP APIs enabled

1. Env file

.env exists, keys set, not in git

2. Google credentials

SA (or ADC) can list GA accounts

3. Modal secret

Secret analytics-mcp-creds created

4. Deploy

/healthz returns JSON status: ok

5. MCP smoke

tools/list returns 15 tool names

6. GA smoke

get_account_summaries returns accounts/properties

7. Cursor

Chat can call pingpong

Do not skip the Done when checks — each later step depends on the previous one.


Related MCP server: analytics-mcp

Use cases and suggested queries

Ask these in Cursor (or any MCP client) after step 7. Complexity increases downward.

Level 1 — Connectivity & inventory

Use case

Suggested prompt

Prove MCP works

“Call ping on the Analytics MCP.”

List what I can access

“Use get_account_summaries and list my GA accounts and property IDs.”

Inspect one property

“Call get_property_details for property PROPERTY_ID and summarize timezone, currency, and industry.”

Pass: you get real account/property names (not 401 / empty / credential errors).

Level 2 — Simple reporting

Use case

Suggested prompt

Last 7 days traffic

“For property PROPERTY_ID, run a report for the last 7 days with dimension date and metrics sessions, totalUsers. Prefer run_report_compact.”

Top channels

“Same property, last 28 days: dimension sessionDefaultChannelGroup, metrics sessions and conversions. Summarize the top 5.”

Realtime

“Call run_realtime_report for PROPERTY_ID with metric activeUsers and tell me what’s live now.”

Pass: summary includes a report_id (compact) or clear row values; numbers look plausible vs GA UI.

Level 3 — Metadata & quality

Use case

Suggested prompt

Custom definitions

“Use get_custom_dimensions_and_metrics on PROPERTY_ID and list custom metrics I might use in reports.”

Annotations

“Call list_property_annotations for PROPERTY_ID for the last 90 days and relate them to traffic dips.”

Ads links

“List Google Ads links for PROPERTY_ID with list_google_ads_links.”

Pass: tool returns structured data (or a clear empty list if none configured).

Level 4 — Funnels, conversions, large data

Use case

Suggested prompt

Conversions

“Use run_conversions_report for PROPERTY_ID last 30 days and rank conversion events.”

Funnel

“Build a run_funnel_report for signup: page //pricing/signup (adjust paths to my site).”

Large report without blowing context

“Run a high-cardinality report with run_report_compact, then page with get_report_rows (offset 0, limit 50), then discard_report.”

Pass: compact path returns small summary + report_id; paging returns slices; discard succeeds.

Level 5 — Analysis workflows

Use case

Suggested prompt

Week-over-week

“Compare this week vs last week sessions and users for PROPERTY_ID; call out biggest day-over-day changes.”

Landing page triage

“Top landing pages by sessions last 14 days; flag pages with high sessions and low engagement (use engagement metrics available on the property).”

Incident narrative

“Combine annotations + daily sessions for the last 60 days into a short incident timeline.”

Pass: answers cite tool results (property id, dates, metrics), not invented UI screenshots.


Step-by-step setup

Step 0 — Prerequisites

Install:

# Python 3.12+ recommended
curl -LsSf https://astral.sh/uv/install.sh | sh
uv tool install modal
modal setup   # browser login

In Google Cloud (same project you will put in .env):

  1. Enable Google Analytics Admin API

  2. Enable Google Analytics Data API

Done when:

uv --version          # prints a version
modal --version       # prints a version
modal profile current # shows your logged-in profile / workspace

If it fails: re-run modal setup; confirm the GCP project has billing/API enablement as required by Google.


Step 1 — Clone and create .env

git clone https://github.com/YOUR_GITHUB_USER/analytics-selfhosted-mcp.git
cd analytics-selfhosted-mcp
cp .env.example .env

Edit .env:

GOOGLE_PROJECT_ID=your-gcp-project-id
MCP_API_KEY=$(openssl rand -hex 24)
ANALYTICS_MCP_API_KEY=$MCP_API_KEY   # must match MCP_API_KEY

Done when:

set -a && source .env && set +a
test -n "$GOOGLE_PROJECT_ID" && test -n "$MCP_API_KEY" && test "$MCP_API_KEY" = "$ANALYTICS_MCP_API_KEY" && echo "env ok"
git check-ignore -v .env   # must show .gitignore rule

If it fails: keys empty or mismatched — fix .env. If .env is not ignored, stop and fix .gitignore before any commit.


  1. GCP → IAM → Service Accounts → create SA in GOOGLE_PROJECT_ID

  2. Create a JSON key → save as e.g. ~/secrets/ga-mcp-sa.json (outside this repo)

  3. GA Admin → Account/Property access → add SA email as Viewer on every property you need

Done when (local check):

export GOOGLE_APPLICATION_CREDENTIALS=~/secrets/ga-mcp-sa.json
# Optional: quick Admin API sanity via gcloud / any GA client you prefer.
# On Modal, success is verified in Step 6 (get_account_summaries).
ls -la "$GOOGLE_APPLICATION_CREDENTIALS"   # file exists, contains "client_email"

If it fails later with empty accounts / 403: SA missing Viewer on the GA property, or wrong JSON in the Modal secret.

Temporary alternative: user ADC (gcloud auth application-default login with Analytics readonly scopes). Prefer SA for cloud.


Step 3 — Modal secret

Secret name must be exactly analytics-mcp-creds:

set -a && source .env && set +a

modal secret create analytics-mcp-creds \
  MCP_API_KEY="$MCP_API_KEY" \
  GOOGLE_PROJECT_ID="$GOOGLE_PROJECT_ID" \
  GOOGLE_APPLICATION_CREDENTIALS_JSON="$(cat ~/secrets/ga-mcp-sa.json)" \
  --force

Done when:

modal secret list | grep analytics-mcp-creds

If it fails: path to JSON wrong; or JSON not pasted as file contents (must start with {, not a filesystem path string).


Step 4 — Deploy

modal deploy modal_ga_mcp.py

Note the printed web URL:

https://YOUR_WORKSPACE--analytics-mcp-ga-web.modal.run

(The Modal app id remains analytics-mcp-ga so existing deployments keep a stable hostname.)

Done when:

curl -sS "https://YOUR_WORKSPACE--analytics-mcp-ga-web.modal.run/healthz"
# Expect: {"status":"ok","app":"analytics-mcp-ga"}

If it fails: check Modal dashboard logs for import/boot errors; confirm secret name spelling.

Optional: set in .env:

MODAL_MCP_URL=https://YOUR_WORKSPACE--analytics-mcp-ga-web.modal.run/mcp

Step 5 — Verify MCP protocol (auth + tools)

set -a && source .env && set +a
URL="${MODAL_MCP_URL:-https://YOUR_WORKSPACE--analytics-mcp-ga-web.modal.run/mcp}"
KEY="$MCP_API_KEY"

curl -sS -D /tmp/mcp.hdr -o /tmp/mcp.init -X POST "$URL" \
  -H "Authorization: $KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"setup","version":"0"}}}'

# Expect HTTP 200 and a mcp-session-id header
grep -i mcp-session-id /tmp/mcp.hdr
cat /tmp/mcp.init

SID=$(awk 'BEGIN{IGNORECASE=1} /^mcp-session-id:/{print $2}' /tmp/mcp.hdr | tr -d '\r')

curl -sS -X POST "$URL" \
  -H "Authorization: $KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "mcp-session-id: $SID" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
  | tee /tmp/mcp.tools

Done when: /tmp/mcp.tools lists 15 tools, including:

  • Upstream (9): get_account_summaries, list_google_ads_links, get_property_details, list_property_annotations, get_custom_dimensions_and_metrics, run_report, run_realtime_report, run_funnel_report, run_conversions_report

  • Host (6): ping, run_report_compact, get_report_rows, get_report_summary, get_report_full, discard_report

If it fails:

Symptom

Likely cause

Fix

HTTP 401

Wrong key

Match .env ↔ Modal secret; recreate secret with --force

HTTP 307 loop

Old client hitting wrong slash handling

Use /mcp (no slash) or /mcp/ (both should work on current deploy)

HTTP 406

Accept header

Send Accept: application/json on POST

Empty / error body on initialize

App crash on boot

Modal logs for the web function

Fewer than 15 tools

Stale deploy

Re-run modal deploy modal_ga_mcp.py


Step 6 — Verify Google Analytics access

set -a && source .env && set +a
export MODAL_MCP_URL="${MODAL_MCP_URL:-https://YOUR_WORKSPACE--analytics-mcp-ga-web.modal.run/mcp}"

modal run modal_ga_mcp.py::test_account_summaries
# Then pick a property id from the output:
modal run modal_ga_mcp.py::test_run_report --property-id=YOUR_GA4_PROPERTY_ID
modal run modal_ga_mcp.py::test_run_report_compact --property-id=YOUR_GA4_PROPERTY_ID

Done when: account summaries show your properties; report/compact tests print row data / a report_id.

If it fails:

Symptom

Likely cause

Fix

Empty [] accounts

SA not granted on GA

Add Viewer on account/property

403 / permission errors

Wrong project or APIs off

Enable Admin + Data APIs; fix GOOGLE_PROJECT_ID

Credential JSON errors

Bad secret payload

Secret value must be raw JSON object


Step 7 — Connect Cursor

cp .cursor/mcp.json.example .cursor/mcp.json
# Edit url → your Modal /mcp URL
{
  "mcpServers": {
    "analytics-selfhosted-mcp": {
      "url": "https://YOUR_WORKSPACE--analytics-mcp-ga-web.modal.run/mcp",
      "headers": {
        "x-api-key": "${env:ANALYTICS_MCP_API_KEY}"
      }
    }
  }
}

Cursor does not load project .env for remote MCP headers. Export before launching Cursor:

set -a && source .env && set +a
export ANALYTICS_MCP_API_KEY
# launch Cursor from this shell, or put ANALYTICS_MCP_API_KEY in your shell profile / direnv

Reload MCP servers in Cursor.

Done when: MCP shows tools; in chat: “Call ping” → pong.

If it fails:

Symptom

Fix

Unauthorized / tools missing

Env var not visible to Cursor — launch from a shell where echo $ANALYTICS_MCP_API_KEY is non-empty

Wrong URL

Must end with /mcp; copy from Modal deploy output

Stale config

Restart Cursor or reload MCP after editing mcp.json

Some hosts prefer Authorization instead of x-api-key (raw key or Bearer …). This server accepts both.


How data moves (read once)

Client  --tools/call-->  Modal MCP  --GA API-->  Google
                ^                         |
                +---- tool result JSON ---+

Tool results still enter the model context. Large run_report payloads can blow the context window.

Prefer for big reports:

  1. run_report_compact → summary + report_id (full payload stored server-side)

  2. get_report_rows → page rows

  3. discard_report when finished

  4. get_report_full(..., confirm=true) only if you intentionally want everything in-context


Troubleshooting (cheat sheet)

Problem

Check

401 on /mcp

Key mismatch .env vs Modal secret

Healthz OK, MCP fails

Auth headers; path /mcp

Tools list OK, GA empty

SA Viewer on property; APIs enabled

Cursor can’t auth

ANALYTICS_MCP_API_KEY not in Cursor’s environment

ClickUp “no tools”

Known client gap — docs/CLICKUP_ISSUE.md

High Modal bill

min_containers=1 keeps a warm replica; change only if you accept cold starts

Still stuck: Modal function logs for analytics-mcp-ga / web, plus the curl transcript from Step 5.


Security

Never commit: .env, ADC/SA JSON, client_secret_*.json, .cursor/mcp.json.

Checklist: docs/PUBLISHING.md.

openssl rand -hex 24   # rotate MCP_API_KEY → update .env + Modal secret + Cursor

Repo layout

Path

Role

modal_ga_mcp.py

Production MCP (parity + compaction)

report_compact.py

Summary / paging helpers

modal_mcp_ping.py

Optional ping-only deploy

.env.example

Env template

.cursor/mcp.json.example

Cursor template

docs/CLICKUP_ISSUE.md

ClickUp tools/list issue

docs/PUBLISHING.md

Public-repo security checklist

Optional ping-only: modal deploy modal_mcp_ping.py (same analytics-mcp-creds secret).


License

MIT. GA tool callables come from Google’s analytics-mcp; this repo hosts them remotely and adds compaction. Not affiliated with Google.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    B
    maintenance
    MCP server for querying Google Analytics accounts, properties, reports, and realtime data using the Data API and Admin API.
    Apache 2.0
  • A
    license
    -
    quality
    F
    maintenance
    MCP 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
  • F
    license
    -
    quality
    C
    maintenance
    Production-ready MCP server integrating Google Search Console, GA4, and PageSpeed Insights for SEO and analytics intelligence, enabling natural-language queries to Google analytics data.
  • F
    license
    -
    quality
    D
    maintenance
    MCP server providing access to Google Analytics Data API, enabling LLMs to retrieve reports, real-time data, and metadata from GA4 properties.

View all related MCP servers

Related MCP Connectors

  • Hosted MCP server for Mini Accountant: invoices, expenses, customers, analytics, tax estimates.

  • Hosted MCP server for LLM cost estimation, model comparison, and budget-aware routing.

  • Streamable HTTP MCP server for Google Calendar and Sheets with OAuth login.

View all MCP Connectors

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/ukituki/analytics-mcp'

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