{
  "markdown": "# mcp-gtm-ga4\n\nMCP server for Google Tag Manager and GA4 -- tag management, consent auditing, workspace versioning, and analytics reporting via Claude.\n\n## Features\n\n- **14 tools** spanning GTM workspace management (tags, triggers, variables), consent compliance auditing, workspace preview/versioning, and GA4 reporting\n- **Sandbox safety** -- all write operations verify they target the resolved workspace, refusing to write to non-sandbox workspaces\n- **Multi-client support** -- configure per-client via environment variables\n- **Auto-detects Default Workspace ID** -- no need to manually specify workspace IDs unless using a custom sandbox\n\n## Installation\n\n```bash\nnpm install mcp-gtm-ga4\n```\n\nOr clone and build:\n\n```bash\ngit clone https://github.com/mharnett/mcp-gtm-ga4.git\ncd mcp-gtm-ga4\nnpm install\nnpm run build\n```\n\n## Configuration\n\n**Security:** Never share your `.mcp.json` file or commit it to git -- it may contain API credentials. Add `.mcp.json` to your `.gitignore`.\n\nRuntime configuration is via environment variables.\n\n| Variable | Required | Description |\n|---|---|---|\n| `GOOGLE_APPLICATION_CREDENTIALS` | Yes | Path to a Google credential JSON key file (service account **or** authorized-user — see Authentication below) |\n| `GTM_ACCOUNT_ID` | Yes | GTM account ID |\n| `GTM_CONTAINER_ID` | Yes | GTM container ID |\n| `GA4_PROPERTY_ID` | Yes | GA4 property ID |\n| `GTM_SANDBOX_WORKSPACE_ID` | No | Override workspace ID (auto-detects Default Workspace if omitted) |\n| `MCP_SERVER_NAME` | No | Server name (defaults to package name `mcp-gtm-ga4`) |\n\nSee `config.example.json` for a reference template. The only value read from disk is `oauth.scope` (in an optional `config.json`) — the single source of truth for the OAuth scope the onboarding paths request. If no `config.json` is present, the committed minimum scope is used.\n\n## Authentication\n\nThis MCP supports **two** auth models. Both feed the same `GOOGLE_APPLICATION_CREDENTIALS` runtime path.\n\n### Precedence & mechanism\n\nThere is **no runtime service-account-vs-OAuth toggle**. Both models converge on a\nsingle slot — `GOOGLE_APPLICATION_CREDENTIALS` — which the runtime hands to\n`GoogleAuth({ keyFile })`. Whichever file you point that env var at *is* the\ncredential:\n\n- a **service-account JSON key** (option 1 below), or\n- the **`authorized_user` keyfile** the `auth` subcommand writes (option 2) — a\n  file that plugs into the exact same slot.\n\nSo the only real precedence rule is failure handling: **an explicitly-configured\nkeyfile is used; when `GOOGLE_APPLICATION_CREDENTIALS` is unset the server fails\nloudly at startup** with an onboarding error naming both the service-account path\nand the `auth` OAuth helper. It deliberately does **not** fall back to Google's\nApplication Default Credentials (gcloud user creds / GCE metadata server) — no\nsilent machine-local default, no silent runtime failover.\n\n### 1. Service account (primary, recommended for unattended/server use)\n\nCreate a service account in your GCP project, download its JSON key, grant it the\nGTM container role (on the target GTM container) and GA4 property access, and\npoint `GOOGLE_APPLICATION_CREDENTIALS` at the key file. No OAuth flow, no browser,\nno refresh token. **This is the recommended path for headless/server/unattended\ndeployments.**\n\n### 2. User OAuth (interactive, for users without a service account)\n\nIf you can't use a service account, mint a user credential with your **own** Google OAuth client (a \"Desktop app\" OAuth 2.0 Client ID created in your own GCP project — enable the Tag Manager API and the Google Analytics Admin + Data APIs). Two equivalent onboarding commands, both hardened with PKCE (RFC 7636, S256) and both requesting the scope from `config.json` (`oauth.scope`) so they never drift:\n\n```bash\nexport GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com\nexport GOOGLE_CLIENT_SECRET=your-client-secret\n\n# Option A: write an authorized_user credential file directly\nnode dist/index.js auth --output ./gtm-ga4-credentials.json\n# then set GOOGLE_APPLICATION_CREDENTIALS=./gtm-ga4-credentials.json\n\n# Option B: the standalone helper (prints GOOGLE_REFRESH_TOKEN + a ready-to-save\n# authorized_user JSON you can write to a file for GOOGLE_APPLICATION_CREDENTIALS)\nnode get-refresh-token.cjs\n```\n\nThe refresh token / credential is written by you and read from your environment only. Nothing is shared and no OAuth client keyfile is bundled. **Do not** run the helper with stdout redirected to a shared log — the refresh token is printed to stdout by design.\n\n### OAuth scopes requested\n\nThe onboarding paths request exactly the scopes this MCP's tools use (from `config.example.json` → `oauth.scope`):\n\n| Scope | Needed by |\n|---|---|\n| `tagmanager.edit.containers` | `gtm_create_tag`, `gtm_update_tag`, `gtm_create_variable`, `gtm_update_variable`, `gtm_delete_variable`, tag/trigger/variable reads |\n| `tagmanager.edit.containerversions` | `gtm_create_version` |\n| `tagmanager.publish` | `gtm_create_version`, `gtm_preview` (quick preview) |\n| `analytics.readonly` | `gtm_ga4_run_report`, `gtm_ga4_realtime_report`, `gtm_ga4_list_custom_dimensions` |\n| `analytics.edit` | `gtm_ga4_create_custom_dimension` |\n\n`tagmanager.readonly` is intentionally **not** requested — the edit scopes already grant read access.\n\n## Usage\n\n### Claude Code (.mcp.json)\n\n```json\n{\n  \"mcpServers\": {\n    \"gtm-ga4\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/mcp-gtm-ga4/dist/index.js\"],\n      \"env\": {\n        \"GOOGLE_APPLICATION_CREDENTIALS\": \"/path/to/service-account.json\",\n        \"GTM_ACCOUNT_ID\": \"1234567890\",\n        \"GTM_CONTAINER_ID\": \"9876543\",\n        \"GA4_PROPERTY_ID\": \"331956119\"\n      }\n    }\n  }\n}\n```\n\n### npx\n\n```bash\nGTM_ACCOUNT_ID=1234567890 \\\nGTM_CONTAINER_ID=9876543 \\\nGA4_PROPERTY_ID=331956119 \\\nGOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json \\\nnpx mcp-gtm-ga4\n```\n\n**Claude Desktop:** Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows).\n\n## Safety\n\n### Workspace sandbox protection\n\nAll write operations (`gtm_update_tag`, `gtm_create_tag`, `gtm_preview`, `gtm_create_version`) verify they target the resolved workspace before executing. If a request attempts to write to a different workspace, the server returns a `SafetyError` and blocks the operation.\n\nThis prevents accidental production changes when the MCP server is configured against a sandbox workspace.\n\n## Tools\n\n### GTM Tags\n\n| Tool | Description |\n|---|---|\n| `gtm_list_tags` | List all tags in the workspace with consent status and firing triggers |\n| `gtm_get_tag` | Get full tag configuration by tag ID |\n| `gtm_update_tag` | Update an existing tag (merge patch via JSON) |\n| `gtm_create_tag` | Create a new tag from a JSON definition |\n\n### GTM Structure\n\n| Tool | Description |\n|---|---|\n| `gtm_list_triggers` | List all triggers (ID, name, type) |\n| `gtm_list_variables` | List all variables (ID, name, type) |\n\n### GTM Consent\n\n| Tool | Description |\n|---|---|\n| `gtm_audit_consent` | Audit all tags for consent configuration compliance |\n\n### GTM Workspace\n\n| Tool | Description |\n|---|---|\n| `gtm_preview` | Generate a quick preview of the current workspace |\n| `gtm_create_version` | Create a new container version from the workspace |\n\n### GA4 Reports\n\n| Tool | Description |\n|---|---|\n| `gtm_ga4_run_report` | Run a GA4 report with dimensions, metrics, date range, and filters |\n| `gtm_ga4_realtime_report` | Run a GA4 realtime report (last 30 minutes) |\n\n### GA4 Admin\n\n| Tool | Description |\n|---|---|\n| `gtm_ga4_list_custom_dimensions` | List all custom dimensions for the property |\n| `gtm_ga4_create_custom_dimension` | Create a new custom dimension |\n\n## Architecture\n\n- **GTM API**: `googleapis` (Tag Manager v2)\n- **GA4 Data**: `@google-analytics/data` (BetaAnalyticsDataClient)\n- **GA4 Admin**: `@google-analytics/admin` (AnalyticsAdminServiceClient)\n- **Resilience**: `cockatiel` (retry, circuit breaker, timeout policies)\n- **Logging**: `pino` with `pino-pretty`\n- **Transport**: MCP SDK stdio transport\n\n## License\n\nMIT\n\n## Author\n\nBuilt by Mark Harnett / [drak-marketing](https://github.com/drak-marketing)\n",
  "bytes": 8196,
  "sha": "9437d94d4c7f69ca2a93cfdc516b34662db3edd73b38c255629e800622330662",
  "repo_slug": "mharnett/mcp-gtm-ga4",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_mharnett_gtm_ga4_912a1399/readme"
}