If you have a GA4 property and want Cursor, Claude Code, or Codex to query it directly, connect the official Google Analytics MCP server (analytics-mcp). Access is read-only through the analytics.readonly scope, so the agent can run reports and list properties but never touch your configuration or your data. The setup is the same pipx run analytics-mcp process everywhere; only the client config file changes between Cursor, Claude Code, and Codex.
I run this server across all three clients on the buildwithmatija repo, and Google's own README only documents the Gemini and Claude Code paths. This guide adds the Cursor and Codex configs I use day to day, plus a setup script that patches all three at once if you're working in this repo.
List GA accounts and properties your credentials can access
get_property_details
Return metadata for a property (timezone, currency, etc.)
list_google_ads_links
List Google Ads account links for a property
run_report
Run standard GA4 reports (dimensions, metrics, date ranges)
run_funnel_report
Run funnel reports
get_custom_dimensions_and_metrics
List custom dimensions and metrics on a property
run_realtime_report
Realtime activity, typically the last 30 minutes (GA360 properties can report up to 60 minutes)
Two things you need before you start
Keep these separate in your head, because mixing them up is where most setups stall:
A Google Cloud project with the Admin API and Data API enabled.
GA4 Viewer access for whichever identity you authenticate with. Enabling the APIs in GCP grants nothing in GA4 on its own. You still have to add that identity inside GA4's own access management screen.
You'll also need Python 3.10+, pipx to run analytics-mcp in an isolated environment, and one auth path: a service account JSON for headless setups, or OAuth through Application Default Credentials for local dev. The Google Cloud SDK is optional and only needed for the OAuth path.
This step only touches GCP. GA4 access still happens in Step 5.
Step 2: Set up credentials
Pick one of these two paths. Don't run both.
Option A: Service account JSON (headless setups)
This is the fastest path for a server, but it produces a long-lived key file. Store it outside version control, restrict its permissions, and rotate it the moment you suspect it's exposed. Google's own README favors service account impersonation over a downloaded key for production use, and that's worth following once you move past local testing.
In GCP, go to IAM & Admin → Service Accounts → Create service account (for example, analytics-mcp-reader).
Under Keys → Add key → JSON, download the file somewhere gitignored, like keys/gc-service-key.json.
In GA4 → Admin → Property access management, add the service account email (…@….iam.gserviceaccount.com) as Viewer.
Point GOOGLE_APPLICATION_CREDENTIALS at that file:
gcloud prints the path to the credentials it saved:
text
Credentials saved to file: [/home/you/.config/gcloud/application_default_credentials.json]
That printed ADC path is what goes into GOOGLE_APPLICATION_CREDENTIALS for every client config. The OAuth client JSON from step 1 only exists to run the login command itself; once you've logged in, it has no further role in any config file. Your Google account also needs Viewer access on the GA4 property, granted inside GA4 directly.
Step 3: Smoke-test the server
bash
# File: terminal
pipx run analytics-mcp
You should see:
text
Starting MCP Stdio Server: Google Analytics MCP Server
The process sits there waiting on stdio, which is normal for an MCP server. Ctrl+C to exit.
Three environment variables are required for every client:
If pipx isn't on your PATH, use the full path, something like /home/you/.local/bin/pipx.
Step 4: Wire it into each client
Every client uses the same command (pipx), the same args (["run", "analytics-mcp"]), and the same three env vars. Only the file format changes. Restart the client after editing its config.
Cursor
Edit .cursor/mcp.json at your project root, or the global MCP settings inside Cursor:
If you already have other servers configured, merge this entry into the existing mcpServers object rather than replacing the file.
Restart Cursor, then try:
text
What can the analytics-mcp server do?
List my Google Analytics properties
Claude Code
Add the same shape to .mcp.json at the repo root, or register it through the CLI:
bash
# File: terminal
claude mcp add analytics-mcp \
--scope user \
-e "GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/credentials.json" \
-e "GOOGLE_PROJECT_ID=YOUR_GCP_PROJECT_ID" \
-e "GOOGLE_CLOUD_PROJECT=YOUR_GCP_PROJECT_ID" \
-- pipx run analytics-mcp
Start a new session and run the same verification prompts as above.
Codex
Edit ~/.codex/config.toml for a user-wide setup, or .codex/config.toml inside the project. Codex uses a [mcp_servers.<name>] table, per the Codex MCP configuration docs:
In service account mode, it writes the key path straight into GOOGLE_APPLICATION_CREDENTIALS. In OAuth mode, it uses the client JSON only to run gcloud auth application-default login, then writes the resulting ADC path into the configs. The script prints the API enablement links and the service account email to add in GA4 when relevant. Restart Cursor, Claude Code, and Codex once it finishes. This script is specific to this repo; on any other machine, follow Steps 1 through 4 directly.
Step 5: Confirm GA4 access for real
This is the step people skip, and it's the most common reason the server connects but returns nothing. Enabling the GCP APIs never grants GA4 access by itself. Whatever identity you configured in Step 2 needs Viewer access inside GA4's own access management screen.
Auth path
Who needs Viewer on the GA4 property
OAuth / ADC
Your Google account, the one used during gcloud auth application-default login
Service account JSON
The service account email (…@….iam.gserviceaccount.com)
Add it under GA4 → Admin → Property access management → +, with role Viewer. Without this, get_account_summaries can return empty results or permission errors even when every GCP API shows as enabled.
Verifying the setup
Check
Expected result
pipx run analytics-mcp
"Starting MCP Stdio Server…"
MCP tools visible in the client
get_account_summaries, run_report, and the rest
"List my Google Analytics properties"
Your property name and ID come back
run_report on a date range
Rows with actual metrics
The first prompt worth running confirms access before you ask anything analytical:
text
Use analytics-mcp to list the GA4 properties I can access. Then tell me which property ID I should use for my site.
A few more to try once that works:
text
What can the analytics-mcp server do?
List my Google Analytics properties
What are the top 20 events in the last 90 days?
How many page views did /tools/cms-picker get in the last 30 days?
Troubleshooting
Symptom
Likely cause
Fix
MCP tools not listed in Cursor
Server didn't reload after a config change
Restart Cursor and validate the JSON in .cursor/mcp.json
get_account_summaries returns empty
The configured identity lacks GA4 Viewer
Add Viewer access for your Google account (OAuth) or the service account email
403 / permission denied
APIs disabled, or the wrong GCP project ID
Enable the Admin and Data APIs, and check GOOGLE_PROJECT_ID
pipx: command not found
pipx isn't on PATH
Use the full path, e.g. /home/you/.local/bin/pipx
Auth works in the terminal but not in the IDE
The MCP process started before the config update
Fully restart the client and open a new session
Confusion right after OAuth login
The OAuth client JSON got used as the credentials path
Use the ADC path gcloud printed, not the OAuth client JSON
Precondition check failed in logs
Google auth boundary noise
Usually harmless if reports still return data
Report fails: "11 metrics"
The Data API caps requests at 10 metrics
Split the request into two run_report calls
Funnel fails on pagePath
The Funnel API doesn't support pagePath in steps
Use pageLocation with a CONTAINS filter
Security notes
Treat the credentials here the same way you'd treat any production secret. Never commit service account keys, OAuth client JSON, or ADC files to version control. Grant Viewer only on the GA4 property, never Editor or Administrator. Use a dedicated service account per environment rather than reusing one across projects. For production or CI, lean toward service account impersonation or workload identity instead of a long-lived downloaded key whenever your setup allows it.
What this setup doesn't cover
This server reads GA4 data that already exists; it doesn't create it. It won't install GA4 tracking on your site, so you still need gtag, GTM, or an equivalent. It won't create custom events or mark key events inside GA4. Every tool is read-only, so nothing here writes back to your property, and none of it replaces the GA4 UI for complex explorations or exports. For funnel tracking, lead attribution, or conversion analysis, instrument those events in your application first. The MCP server can only read what GA4 has already collected.
FAQ
Does this MCP server work the same way across Cursor, Claude Code, and Codex?
Yes. The underlying analytics-mcp process, the command, and the three environment variables are identical. Only the config file format changes: JSON for Cursor and Claude Code, TOML for Codex.
Can this server modify my GA4 property or data?
No. Access is scoped to analytics.readonly, so every tool listed here reads data without the ability to change configuration, events, or settings.
Should I use a service account or OAuth?
Service account JSON is the simpler choice for headless or server environments, since there's no browser login step. OAuth through Application Default Credentials fits local development better, especially if you're already authenticated with gcloud for other work.
Why does get_account_summaries return nothing even though I enabled the APIs?
GCP API enablement and GA4 property access are managed separately. Add the relevant identity, your Google account or your service account email, as Viewer inside GA4's own access management screen.
Why did a report fail with a metrics limit error?
The GA4 Data API allows a maximum of 10 metrics per run_report call. If you need more, split the request into two separate calls and combine the results yourself.
Wrapping up
Once the server is wired into your client of choice, GA4 questions stop requiring a trip to the Analytics UI. You can ask for top events, page view counts on a specific path, or which properties a given credential can see, all from inside Cursor, Claude Code, or Codex. The setup is identical everywhere except for the config file format, and the one step that trips people up is GA4 Viewer access, which lives outside GCP entirely.
Let me know in the comments if you have questions, and subscribe for more practical development guides.