Google Tag Manager MCP

Communauté IA et agents

Google Tag Manager MCP server for managing tags, triggers, and variables from Claude, local or remote.

Que pouvez-vous faire avec Google Tag Manager MCP ?

  • action — the operation to perform (e.g. "list", "create", "publish")
  • parent — resource path of the parent (e.g. "accounts/123/containers/456/workspaces/7") — used for create and list
  • path — full resource path of the entity — used for get, update, remove, and other single-resource operations
  • config — dict of fields for create / update body
  • params — dict of extra query parameters
  • confirm — boolean, required True for destructive actions

Installation

Les étapes d'installation sont dans la documentation ci-dessous. Documentation

Documentation

Tirée du README du projet sur GitHub, actualisée chaque semaine. Les liens ouvrent GitHub.

Sommaire
  1. Google Tag Manager MCP
  2. Tools (18 consolidated per-resource tools)
  3. How auth works
  4. Prerequisites
  5. Step 1 — Set up Google Cloud
  6. Step 2 — Install
  7. Step 3 — Mode A: Local STDIO
  8. Step 3 — Mode B: Remote HTTP server (Claude Teams / claude.ai)
  9. Environment Variables
  10. Quota
  11. OAuth endpoint reference (Mode B)
  12. Reference
  13. Tech Stack
  14. About Dhawal Shah
  15. License

Google Tag Manager MCP

A Python Model Context Protocol (MCP) server for the Google Tag Manager API v2. Connect Claude (or any MCP-compatible AI client) directly to your GTM accounts to manage tags, triggers, variables, workspaces, versions, environments, and more — all in natural language.

The server is also an OAuth 2.1 authorization server, so it works as a remote connector anywhere Claude supports custom MCP servers — claude.ai (personal), Claude Desktop, and Claude Teams. For Teams, the org owner adds the URL once and each member authenticates individually on first use.

Tools (18 consolidated per-resource tools)

Each tool takes an action parameter that selects the operation, plus resource addressing (parent / path) and optional config, params, and confirm fields. Destructive actions require confirm=True.

ToolActions
gtm_accountget, list, update
gtm_containercreate, get, list, update, remove + combine, lookup, move_tag_id, snippet
gtm_workspacecreate, get, list, update, remove + create_version, get_status, sync, quick_preview, resolve_conflict
gtm_tagcreate, get, list, update, remove, revert
gtm_triggercreate, get, list, update, remove, revert
gtm_variablecreate, get, list, update, remove, revert
gtm_built_in_variablecreate, list, remove, revert
gtm_foldercreate, get, list, update, remove + revert, entities, move_entities_to_folder
gtm_clientcreate, get, list, update, remove, revert
gtm_zonecreate, get, list, update, remove, revert
gtm_templatecreate, get, list, update, remove + revert, import_from_gallery
gtm_transformationcreate, get, list, update, remove, revert
gtm_gtag_configcreate, get, list, update, remove
gtm_destinationget, list, link
gtm_environmentcreate, get, list, update, remove + reauthorize
gtm_versionget, live, publish, set_latest, undelete, update, remove
gtm_version_headerlist, latest
gtm_user_permissioncreate, get, list, update, remove
Consolidated tool design

Each tool takes a uniform set of parameters:

  • action — the operation to perform (e.g. "list", "create", "publish")
  • parent — resource path of the parent (e.g. "accounts/123/containers/456/workspaces/7") — used for create and list
  • path — full resource path of the entity — used for get, update, remove, and other single-resource operations
  • config — dict of fields for create / update body
  • params — dict of extra query parameters
  • confirm — boolean, required True for destructive actions

Example — list tags in a workspace:

gtm_tag(action="list", parent="accounts/123/containers/456/workspaces/7")

Example — publish a version (destructive, requires confirmation):

gtm_version(action="publish", path="accounts/123/containers/456/versions/9", confirm=True)
Confirmation guardrail

The following actions are considered destructive and require confirm=True. Without it, the tool immediately returns a structured message asking you to re-call with confirm=True:

publish, remove, revert, undelete, combine, move_tag_id, move_entities_to_folder, sync, create_version, resolve_conflict, reauthorize, link

This prevents accidental mutations from ambiguous prompts.


How auth works

There are two modes. Pick one.

Mode A — Local STDIO (one user, no server)

Use this if you only want it on your own machine. setup_local_auth.py runs the Google OAuth flow once and stores your token in ~/.config/gtm-mcp/token.json. Claude Desktop launches server.py as a subprocess. No Firestore, no Cloud Run, no public URL.

Mode B — Remote HTTP server (Claude Teams, claude.ai, multi-user)

The MCP server is also an OAuth 2.1 authorization server. When Claude connects:

  1. Claude discovers our metadata at /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server.
  2. Claude registers itself via Dynamic Client Registration (POST /oauth/register).
  3. Claude redirects the user to /oauth/authorize. We delegate identification to Google OAuth.
  4. After Google login, we issue our own opaque bearer token to Claude — Google credentials never leave the server.
  5. On each /mcp request Claude sends our bearer; we map it server-side to the right user's stored Google credentials and call the GTM APIs.

Prerequisites

  • Python 3.10+
  • A Google Tag Manager account you have access to
  • A Google Cloud project

Step 1 — Set up Google Cloud

1a. Create a project and enable the Tag Manager API
  1. Go to the Google Cloud Console.
  2. Create or select a project.
  3. APIs & Services → Library, enable Tag Manager API.
1b. Create OAuth 2.0 credentials
  1. APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID.
  2. Application type: Web application.
  3. Add Authorized redirect URIs:
    • http://localhost:8080/auth/callback (local dev / setup_local_auth.py)
    • https://YOUR-CLOUD-RUN-URL/auth/callback (remote deployment — add after deploy)
  4. Click Create, then Download JSON → save as client_secret.json in the project root (gitignored). You can also copy the Client ID / Client Secret straight into env vars.
  1. APIs & Services → OAuth consent screen.
  2. Choose Internal for a Google Workspace org (recommended for teams), or External for personal/individual use.
  3. Add scopes:
    • https://www.googleapis.com/auth/tagmanager.readonly
    • https://www.googleapis.com/auth/tagmanager.edit.containers
    • https://www.googleapis.com/auth/tagmanager.delete.containers
    • https://www.googleapis.com/auth/tagmanager.edit.containerversions
    • https://www.googleapis.com/auth/tagmanager.publish
    • https://www.googleapis.com/auth/tagmanager.manage.users
  4. If using External in Testing mode, add each user's email under Test users.
1d. Enable Firestore (Mode B only)

The server stores OAuth bearer tokens and per-user Google credentials in Firestore (collection user_tokens_gtm).

  1. In Cloud Console, Firestore → Create database → Native mode, pick a region.
  2. Grant the Cloud Run service account Cloud Datastore User role under IAM & Admin → IAM.

Step 2 — Install

git clone https://github.com/dhawalshah/gtm-mcp
cd gtm-mcp
pip install -r requirements.txt
cp .env.example .env       # fill in values

Step 3 — Mode A: Local STDIO

python setup_local_auth.py

A browser opens, you sign in with Google, the script writes ~/.config/gtm-mcp/token.json.

Then add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):

{
  "mcpServers": {
    "google-tag-manager": {
      "command": "python",
      "args": ["/absolute/path/to/gtm-mcp/server.py"],
      "env": {
        "OAUTH_CONFIG_PATH": "/absolute/path/to/client_secret.json",
        "MCP_USER_EMAIL": "you@yourcompany.com"
      }
    }
  }
}

Restart Claude Desktop. You're done — skip the rest.


Step 3 — Mode B: Remote HTTP server (Claude Teams / claude.ai)

Deploy to Cloud Run
gcloud run deploy gtm-mcp \
  --source . \
  --region YOUR_REGION \
  --project YOUR_PROJECT_ID \
  --platform managed \
  --port 8080 \
  --allow-unauthenticated \
  --set-env-vars "GCP_PROJECT_ID=your-project-id,BASE_URL=https://YOUR-SERVICE-URL.run.app,OAUTHLIB_RELAX_TOKEN_SCOPE=1,ALLOWED_DOMAINS=yourcompany.com" \
  --set-secrets "OAUTH_CONFIG_PATH=gtm-mcp-client-secret:latest"

Required: OAUTHLIB_RELAX_TOKEN_SCOPE=1 — Google's OAuth response often returns a superset of the requested scopes; without this env var, google-auth-oauthlib raises "Scope has changed" in /auth/callback and the entire auth flow fails.

Recommended: load client_secret.json from Secret Manager rather than baking it into the image or passing the raw secret as a plain env var. Store the file as a secret, then mount it at runtime via --set-secrets.

After it's up, go back to APIs & Services → Credentials → your OAuth client and add the live callback URL:

https://YOUR-SERVICE-URL.run.app/auth/callback
Connect from Claude

Claude Teams (org owner adds it once for everyone):

  • Settings → Connectors → Add custom connector
  • URL: https://YOUR-SERVICE-URL.run.app/mcp
  • Each member clicks Connect, signs in with Google, done.

claude.ai personal:

  • Settings → Connectors → Add custom connector
  • URL: https://YOUR-SERVICE-URL.run.app/mcp

Claude Desktop with a remote server:

{
  "mcpServers": {
    "google-tag-manager": {
      "url": "https://YOUR-SERVICE-URL.run.app/mcp"
    }
  }
}

Environment Variables

VariableRequiredDescription
BASE_URLMode BPublic URL of this service. Used for OAuth metadata and as the canonical resource URI tokens are bound to.
GCP_PROJECT_IDMode BGCP project hosting Firestore.
OAUTH_CONFIG_PATHBoth†Path to client_secret.json downloaded from Google Cloud Console.
GOOGLE_CLIENT_IDMode B†Google OAuth client ID (alternative to OAUTH_CONFIG_PATH).
GOOGLE_CLIENT_SECRETMode B†Google OAuth client secret (alternative to OAUTH_CONFIG_PATH).
OAUTHLIB_RELAX_TOKEN_SCOPEMode BSet to 1. Required — Google returns a superset scope and without this the OAuth callback fails.
MCP_USER_EMAILMode AYour email — set in Claude Desktop config so the server finds your stored token.
ALLOWED_DOMAINSNoComma-separated email domain allowlist (e.g. acme.com,beta.com). Empty = no restriction.
PORTNoHTTP port (default 8080).
LOG_LEVELNoPython log level (default INFO).

† Set either OAUTH_CONFIG_PATH or GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET.


Quota

The GTM API v2 has a low default quota: approximately 0.25 QPS, 15 requests/minute, and 10,000 requests/day per GCP project. Unlike Google Ads or Analytics, the quota does not auto-upgrade with billing.

If you hit quota errors, raise limits via Cloud Console → APIs & Services → Tag Manager API → Quotas & System Limits.

The client has built-in rate throttling and exponential backoff to stay within default limits during normal use.


OAuth endpoint reference (Mode B)

EndpointSpecPurpose
GET /.well-known/oauth-protected-resourceRFC 9728Advertises the canonical resource URI and authorization server.
GET /.well-known/oauth-authorization-serverRFC 8414Authorization server metadata.
POST /oauth/registerRFC 7591Dynamic Client Registration.
GET /oauth/authorizeOAuth 2.1Starts the auth code flow with PKCE; redirects to Google.
GET /auth/callback—Google redirects here; we mint our authorization code and bounce back to the MCP client.
POST /oauth/tokenOAuth 2.1Authorization code + refresh token grants.

A GET /mcp without a valid bearer returns 401 with a WWW-Authenticate: Bearer resource_metadata="…" header pointing at the protected-resource metadata document, which is how a standards-compliant MCP client discovers the rest.


Reference

The reference/ folder holds the upstream stape-io/gtm-mcp TypeScript server, cloned as an API-shape reference during development. It is listed in .gitignore and excluded from Docker/Cloud Run builds — it is not part of the deployable app.


Tech Stack

  • FastMCP — MCP server framework
  • FastAPI + uvicorn — HTTP wrapper
  • Google Auth / google-api-python-client — Google OAuth and API access
  • Firestore — Per-user token storage and OAuth-server state (Mode B)
  • Google Cloud Run — Serverless hosting

About Dhawal Shah

I run a 40-plus person digital marketing agency out of Singapore, and I build the automation my own teams use. This server is one of those tools rather than a weekend project: it runs against live Tag Manager accounts every week, which is why the read-only surface is wide and the write surface is deliberately narrow.

Fourteen years building companies across Asia behind it. 5,000+ campaigns, 400+ brands, 30+ startups advised, and 300+ training sessions for teams including Sony, Toyota, DHL and Interpol. I am also an Accredited Director with the Singapore Institute of Directors, which in practice means I get asked what breaks, who is accountable and what it costs before anyone asks what it can do.

I write up the routines and agents I actually run at dhawalshah.net.

Worth reading alongside this repo: Claude Code for Marketing: Every Channel from One Terminal.


License

MIT

Questions fréquentes

Qu'est-ce que Google Tag Manager MCP ?

Google Tag Manager MCP server for managing tags, triggers, and variables from Claude, local or remote.

Comment installer Google Tag Manager MCP ?

Suivez les étapes d'installation de sa documentation, affichée sur cette page et dans son dépôt.

Google Tag Manager MCP est-il open source ?

Oui. Son code est sur GitHub sous licence MIT.

Qui maintient Google Tag Manager MCP ?

Il est publié par dhawalshah. AI.tn le référence sans le maintenir ni le recommander.

Issu des listes communautaires awesome-mcp-servers et du dépôt GitHub du projet. Être listé n'est pas une recommandation : vérifiez le code et les permissions d'un serveur avant de lui donner accès à vos données.