analytics-selfhosted-mcp

analytics-selfhosted-mcp

Self-hosted MCP server for Google Analytics over HTTP, providing URL-addressable access with API key auth. It enables clients to run reports, list accounts/properties, and use compact paginated reports for token efficiency.

Category
访问服务器

README

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 ping → pong

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


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.


Step 2 — Google credentials (service account recommended)

  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.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选