buyte-io/GA4-MCP-Server
If you are the rightful owner of GA4-MCP-Server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
A production-ready Model Context Protocol (MCP) server that provides a secure REST API for Google Analytics 4 (GA4) data, optimized for edge deployment with Cloudflare Workers support.
GA4 MCP Server
A production-ready Model Context Protocol (MCP) server that exposes a secure REST API for Google Analytics 4 (GA4) data. The only GA4 MCP server with ready-to-deploy Cloudflare Workers support – optimized for edge deployment with an optional Express adapter for local debugging and self-hosted use.
🚀 Features
- ⚡ One-click Cloudflare Workers deployment – The only GA4 MCP server with production-ready Cloudflare Workers support out of the box. Deploy to the edge in minutes.
- Worker-first architecture – Cloudflare Workers host the production runtime while Express remains available for local use.
- Shared GA4 controller – Both runtimes delegate to the same GA4 controller to avoid divergence between environments.
- 🤖 Full MCP compatibility – Tested and working with ChatGPT custom connectors and Claude web custom connectors.
- Public access with optional hardening – Requests are open by default, while API key authentication, rate limiting, and strict request validation remain available when you need additional controls.
- Worker-native rate limiting – Cloudflare Workers enforce per-client quotas and emit standard
X-RateLimit-*headers. - Caching built in – Accounts, properties, and metadata responses are cached to stay within GA4 quota limits.
- MCP tools – Ships with MCP definitions so AI agents can call GA4 endpoints safely.
- Type-safe codebase – TypeScript with strict mode, comprehensive unit tests, and integration coverage for the worker path.
🧱 Architecture Overview
src/
├── api/ # Runtime-agnostic HTTP handlers
├── config/ # Environment and configuration helpers
├── core/ # Errors, middleware, and shared utilities
├── features/
│ └── ga4/ # GA4 domain logic, controller, and services
├── mcp/ # MCP server implementation
├── server.ts # Express bootstrap (optional)
└── worker.ts # Cloudflare Worker entry point
The GA4 controller (src/features/ga4/http/controller.ts) accepts an injected analytics service. Cloudflare requests install the
worker-safe service (WorkerAnalyticsService), while the Express server wires the Node client implementation for local usage.
This keeps validations, error handling, and response shapes identical across runtimes.
📦 Prerequisites
- Node.js 18 or newer
- npm 9 or newer
- A Google Cloud project with the GA4 Data & Admin APIs enabled
- A Google service account with at least Viewer access to the desired GA4 properties
- Cloudflare account with Wrangler CLI for worker deployment
⚙️ Environment Variables
Create a .env (or configure Worker secrets) with the following variables:
| Variable | Required | Description |
|---|---|---|
GA4_AUTH_MODE | ➖ | Set to api-key to require x-api-key headers or public (default) to expose all service-account data |
API_KEYS | ⚠️ | Comma-separated list of API keys accepted by the server (required when GA4_AUTH_MODE=api-key) |
API_KEY_PERMISSIONS | ➖ | JSON object mapping API keys to allowed GA4 properties and optional accounts |
GOOGLE_CLIENT_EMAIL | ✅ | Service account email |
GOOGLE_PRIVATE_KEY | ✅ | Service account private key (with escaped newlines) |
GOOGLE_PROJECT_ID | ✅ | Google Cloud project ID |
DEFAULT_GA4_PROPERTY_ID | ➖ | Fallback GA4 property ID used by metadata endpoint |
LOG_LEVEL | ➖ | One of error, warn, info, debug (defaults to info) |
RATE_LIMIT_MAX | ➖ | Requests allowed per window (defaults to 100) |
RATE_LIMIT_WINDOW_MS | ➖ | Rate limit window in ms (defaults to 900000) |
PORT | ➖ | Express port when running locally (defaults to 3333) |
When API_KEY_PERMISSIONS is set, each entry must declare at least one GA4 property identifier that the API key can access. Example:
{
"team-api-key": {
"properties": ["properties/1234", "5678"],
"accounts": ["accounts/4321"]
}
}
If the mapping is omitted, all configured API keys inherit the full access granted to the shared Google service account. In
public mode the server skips API key enforcement entirely and every caller can view the GA4 resources available to the shared
service account—use this mode only for trusted or isolated deployments.
Where to configure API key permissions
- Local development: store the JSON mapping in your
.envfile (or.env.local) alongsideAPI_KEYS. Keep the file out of version control and rotate keys regularly. - Production: prefer your platform's secret manager (Cloudflare Secrets, AWS Parameter Store, Vault, etc.). Upload the raw JSON string as the
API_KEY_PERMISSIONSsecret so rotations do not require rebuilding the worker. - Change management: edit the mapping whenever a teammate gains or loses access to specific GA4 properties. Because the JSON is evaluated on startup, redeploy after updating the secret to ensure the latest scope is active.
Each API key entry may list GA4 accounts in addition to properties. Account filters limit /accounts responses, while property filters apply to property listings and report executions. Supplying only account IDs implicitly allows every property that belongs to the permitted accounts.
Locating GA4 account and property IDs
- Open the Google Analytics UI and choose the desired organisation.
- Click Admin (gear icon) in the lower-left corner.
- The Account column header displays the numeric account ID beneath the account name. Copy that value and prepend
accounts/when populatingAPI_KEY_PERMISSIONS. - Select the target Property in the middle column. The property ID appears directly under the property name and in the URL (
p<PROPERTY_ID>). Copy the numeric value and either use it as-is (123456789) or prefix it withproperties/—both formats are accepted by the server. - Repeat for every property and account you want to authorise. Record the identifiers in the JSON mapping before redeploying.
Tip: Maintain an internal spreadsheet or access management tool that mirrors the JSON allowlists so auditing which team member can reach each GA4 asset remains simple.
🏁 Quick Start
- Clone and install
git clone https://github.com/buyte-io/GA4-MCP-Server.git cd GA4-MCP-Server npm install - Configure environment
cp .env.example .env # edit .env with your credentials - Run the optional Express server
npm run dev # Express listens on http://localhost:3333 by default (override with PORT) - Exercise the API
curl http://localhost:3333/health # Add -H "x-api-key: <your-key>" only when GA4_AUTH_MODE=api-key
For a full setup (including Cloudflare secrets) see .
🌐 API Overview
When GA4_AUTH_MODE=api-key, GA4 endpoints require an x-api-key header. In the default public mode, requests omit the
header and operate with the full scope of the configured service account.
# List GA4 accounts
curl http://localhost:3333/api/ga4/accounts
# Supply -H "x-api-key: <your-key>" when GA4_AUTH_MODE=api-key
# List properties for an account
curl http://localhost:3333/api/ga4/accounts/<accountId>/properties
# Supply -H "x-api-key: <your-key>" when GA4_AUTH_MODE=api-key
# Fetch metadata (uses DEFAULT_GA4_PROPERTY_ID when propertyId is omitted)
curl "http://localhost:3333/api/ga4/metadata?propertyId=<propertyId>"
# Supply -H "x-api-key: <your-key>" when GA4_AUTH_MODE=api-key
# Run a GA4 report
curl \
-H 'content-type: application/json' \
-d '{
"propertyId": "properties/123456789",
"metrics": ["sessions", "activeUsers"],
"dimensions": ["date"],
"dateRanges": [{ "startDate": "7daysAgo", "endDate": "today" }]
}' \
http://localhost:3333/api/ga4/report
# Supply -H "x-api-key: <your-key>" when GA4_AUTH_MODE=api-key
See for complete request and response schemas.
Rate Limiting
The Cloudflare Worker runtime enforces rate limits for every authenticated request except /health. Defaults allow 100
requests per 15 minutes per client IP. Worker responses include:
X-RateLimit-Limit– Maximum requests permitted in the current window.X-RateLimit-Remaining– Requests left before the limit is hit.X-RateLimit-Reset– UNIX timestamp when the window resets.
Set RATE_LIMIT_MAX or RATE_LIMIT_WINDOW_MS to override the defaults. Exceeding the limit returns 429 Too Many Requests
with a Retry-After hint in seconds. The optional Express adapter skips rate limiting so local debugging stays
frictionless—place it behind your own gateway if you need quotas in self-hosted environments.
🤖 MCP Integration
The repository includes a fully wired Model Context Protocol implementation so AI agents can call GA4 safely. This is the only GA4 MCP server with proven ChatGPT and Claude web connector support.
For CLI-based agents (stdio):
npm run mcp
The command loads the same GA4 credentials used by the HTTP adapters and registers tools for listing accounts/properties,
fetching metadata, and running reports. Configure the ga4-mcp-server transport inside your MCP-compatible agent and ensure
Google credentials (and API_KEYS when GA4_AUTH_MODE=api-key) are present in the environment before launching the agent.
For ChatGPT and Claude (Streamable HTTP):
ChatGPT Custom Connectors:
- Go to ChatGPT Settings → Custom Connectors
- Add your server URL:
https://ga4-mcp.buyte.workers.dev/(or your domain) - ChatGPT will automatically discover and connect to the MCP endpoint
Claude Web Custom Connectors:
- Open Claude and click "Add Server"
- Enter your server URL:
https://ga4-mcp.buyte.workers.dev/ - Claude will discover available tools automatically
Connection details: Share the clean base URL of your deployment (for example,
https://<your-domain>/) when configuring connectors. Both platforms automatically follow the MCP discovery flow – no.well-knownsuffix is required in the connector settings. The discovery manifest advertises a Streamable HTTP endpoint athttps://<your-domain>/mcp, which ChatGPT and Claude use to negotiate MCP sessions. In the defaultpublicmode the manifest reportsauthentication.type: "none", signalling that requests do not require API keys. You can manually check/.well-known/mcp.jsonto debug the manifest.
Available MCP Tools
The server exposes four GA4 operations as MCP tools:
ga4_listAccounts- List all GA4 accounts accessible to the service accountga4_listProperties- List properties for a specific GA4 accountga4_getMetadata- Retrieve available metrics and dimensions for a propertyga4_runReport- Execute GA4 reports with custom metrics, dimensions, and date ranges
🧪 Testing & Quality
npm run type-check # strict TypeScript checks
npm test # Jest unit and integration suite
Worker-focused tests exercise src/api/handler.ts directly so production behaviour is covered. Express-only smoke tests remain
lightweight and reuse the same controller.
☁️ Deployment
- Cloudflare Workers (recommended): follow to build and deploy with Wrangler.
- Self-hosted: run
npm run buildand start the generated Express server with your preferred process manager.
📚 Additional Documentation
🤝 Contributing
Contributions are welcome! Please review the development guide and open a pull request with passing tests.
📄 License
Distributed under the MIT License. See for details.