GA4-MCP-Server

buyte-io/GA4-MCP-Server

3.2

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.

Tools
4
Resources
0
Prompts
0

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:

VariableRequiredDescription
GA4_AUTH_MODESet 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_PERMISSIONSJSON object mapping API keys to allowed GA4 properties and optional accounts
GOOGLE_CLIENT_EMAILService account email
GOOGLE_PRIVATE_KEYService account private key (with escaped newlines)
GOOGLE_PROJECT_IDGoogle Cloud project ID
DEFAULT_GA4_PROPERTY_IDFallback GA4 property ID used by metadata endpoint
LOG_LEVELOne of error, warn, info, debug (defaults to info)
RATE_LIMIT_MAXRequests allowed per window (defaults to 100)
RATE_LIMIT_WINDOW_MSRate limit window in ms (defaults to 900000)
PORTExpress 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 .env file (or .env.local) alongside API_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_PERMISSIONS secret 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

  1. Open the Google Analytics UI and choose the desired organisation.
  2. Click Admin (gear icon) in the lower-left corner.
  3. The Account column header displays the numeric account ID beneath the account name. Copy that value and prepend accounts/ when populating API_KEY_PERMISSIONS.
  4. 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 with properties/—both formats are accepted by the server.
  5. 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

  1. Clone and install
    git clone https://github.com/buyte-io/GA4-MCP-Server.git
    cd GA4-MCP-Server
    npm install
    
  2. Configure environment
    cp .env.example .env
    # edit .env with your credentials
    
  3. Run the optional Express server
    npm run dev
    # Express listens on http://localhost:3333 by default (override with PORT)
    
  4. 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:

  1. Go to ChatGPT Settings → Custom Connectors
  2. Add your server URL: https://ga4-mcp.buyte.workers.dev/ (or your domain)
  3. ChatGPT will automatically discover and connect to the MCP endpoint

Claude Web Custom Connectors:

  1. Open Claude and click "Add Server"
  2. Enter your server URL: https://ga4-mcp.buyte.workers.dev/
  3. 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-known suffix is required in the connector settings. The discovery manifest advertises a Streamable HTTP endpoint at https://<your-domain>/mcp, which ChatGPT and Claude use to negotiate MCP sessions. In the default public mode the manifest reports authentication.type: "none", signalling that requests do not require API keys. You can manually check /.well-known/mcp.json to 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 account
  • ga4_listProperties - List properties for a specific GA4 account
  • ga4_getMetadata - Retrieve available metrics and dimensions for a property
  • ga4_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 build and 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.