google-tag-manager-mcp
A stdio MCP server for Google Tag Manager with full API coverage. 🚀🚀🚀 Empower your AI agent with the whole workflow: audit, edit, version, publish.
✨ Highlights
- Full API coverage, small tool surface. Six verb tools and 23 dedicated ones reach all 106 API methods, and a test holds them to it.
- Built for context windows. Lists render as Markdown tables, with
jsonandjson_fullone argument away. - Safe writes. Merge patches carrying the entity's fingerprint, and
confirm=trueon all 11 destructive tools. - Quota aware. Calls are serialised and paced under the API's 25 per 100 seconds, and errors say what to fix.
- Runs as you. Your own OAuth client through Application Default Credentials.
🧰 Tools
| Tier | Tools |
|---|---|
| Read (10) | gtm_list, gtm_get, gtm_lookup_container, gtm_get_container_snippet, gtm_get_latest_version_header, gtm_get_live_version, gtm_get_workspace_status, gtm_quick_preview_workspace, gtm_list_folder_entities, gtm_describe_schema |
| Write (9) | gtm_create, gtm_update, gtm_sync_workspace, gtm_resolve_workspace_conflict, gtm_enable_built_in_variables, gtm_disable_built_in_variables, gtm_move_entities_to_folder, gtm_import_template_from_gallery, gtm_undelete_version |
Destructive (11), confirm=true required |
gtm_delete, gtm_revert, gtm_revert_built_in_variable, gtm_publish_version, gtm_set_latest_version, gtm_create_version, gtm_bulk_update_workspace, gtm_combine_containers, gtm_move_tag_id, gtm_link_destination, gtm_reauthorize_environment |
🔑 Setup
You need a Google Cloud project and the gcloud CLI.
-
Enable the API on the project that will carry the quota:
gcloud services enable tagmanager.googleapis.com --project=YOUR_PROJECT
-
Create a Desktop app OAuth client in that project (Manage OAuth Clients) and download its JSON. Publish the consent screen to production, otherwise refresh tokens expire after seven days; an app for your own use needs no verification.
-
Log in. Drop any scope you do not want. The matching tools will fail with a clear message instead of silently doing less:
gcloud auth application-default login \ --client-id-file=YOUR_DESKTOP_CLIENT.json \ --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,\ https://www.googleapis.com/auth/tagmanager.manage.accounts,\ https://www.googleapis.com/auth/cloud-platform
Scope Unlocks tagmanager.readonlyreading accounts, containers, workspaces, versions and the entities in them tagmanager.edit.containersworkspace edits, containers, environments, gtm_set_latest_versiontagmanager.delete.containersdeleting workspaces and containers tagmanager.edit.containerversionsgtm_create_version,gtm_quick_preview_workspace, version update / delete / undeletetagmanager.publishgtm_publish_version,gtm_reauthorize_environmenttagmanager.manage.usersuser permissions tagmanager.manage.accountsupdating an account cloud-platformnothing in GTM; needed when you point the quota at another project Already using Google's analytics-mcp? Reuse its OAuth client and add the
tagmanagerscopes to theanalytics.readonlyscope in one login. -
Check that it works (expects a JSON list of accounts):
curl -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ https://tagmanager.googleapis.com/tagmanager/v2/accounts
A
SERVICE_DISABLEDerror names the project the quota goes to; either enable the API there or pointGOOGLE_CLOUD_QUOTA_PROJECTat a project that has it.
🔌 Connect an MCP client
Install it:
uv tool install google-tag-manager-mcp
pipx install google-tag-manager-mcp does the same. Either one puts a
google-tag-manager-mcp executable on your PATH.
Which project the API calls count against, first match wins:
GOOGLE_CLOUD_QUOTA_PROJECT, as set in the client configs below.quota_project_idin your ADC file, written bygcloud auth application-default set-quota-project. Any laterapplication-default loginrewrites that file and drops it.- Neither: the project that owns the OAuth client you logged in with.
Setting it in the client config pins it whatever gcloud does elsewhere.
Claude
Claude Code:
claude mcp add --scope user google-tag-manager-mcp \
-e GOOGLE_CLOUD_QUOTA_PROJECT=YOUR_PROJECT \
-- google-tag-manager-mcp
Claude Desktop reads a config file instead. Open Settings > Developer > Edit
Config and add the absolute path printed by which google-tag-manager-mcp,
since the app does not inherit your shell PATH:
{
"mcpServers": {
"google-tag-manager-mcp": {
"command": "/absolute/path/to/google-tag-manager-mcp",
"env": { "GOOGLE_CLOUD_QUOTA_PROJECT": "YOUR_PROJECT" }
}
}
}
ChatGPT
The ChatGPT desktop app, Codex CLI and the Codex IDE extension share one config file, so a single command covers all three:
codex mcp add google-tag-manager-mcp \
--env GOOGLE_CLOUD_QUOTA_PROJECT=YOUR_PROJECT \
-- google-tag-manager-mcp
The entry lands in ~/.codex/config.toml, or in .codex/config.toml to scope it
to one project; codex mcp list shows what is configured.
The desktop app can do the same without the CLI:
- Open Settings > MCP servers > Add server.
- Choose STDIO and give it the command
google-tag-manager-mcp. - Save, then select Restart.
ChatGPT on the web takes remote servers only, so it cannot reach this one.
⚙️ Configuration
| Variable | Default | Effect |
|---|---|---|
GOOGLE_APPLICATION_CREDENTIALS |
unset | Standard ADC variable: an authorized_user or service_account JSON file, used before the gcloud ADC file |
GOOGLE_CLOUD_QUOTA_PROJECT |
unset | Standard variable: the project the calls count against, overriding the ADC file |
GTM_MCP_REQUESTS_PER_WINDOW |
20 | Requests allowed per 100 s sliding window (the API allows 25 per project) |
GTM_MCP_READ_ONLY |
0 | 1 registers the read tools only |
GTM_MCP_ENABLE_RAW_API |
0 | 1 registers gtm_call_api, an escape hatch that calls any API method by id |
GTM_MCP_LOG_LEVEL |
INFO | Log level; logs go to stderr |
Service accounts work too: add the account's email as a user in GTM and point
GOOGLE_APPLICATION_CREDENTIALS at its key. Version history then shows the
service account, not you.
🛡️ Safety model
- Editing and going live are separate:
gtm_create,gtm_update,gtm_deleteandgtm_reverttouch the workspace draft; onlygtm_publish_versionchanges the live site. Rollback means publishing an older version. gtm_updatereads the entity, applies a shallow merge patch (null deletes a key, lists are replaced whole) and writes back with the fingerprint, so concurrent edits fail instead of being clobbered.- Destructive tools require
confirm=true; the model is told to ask you first. - Rate limits are retried; 5xx responses are retried for reads only, because the API has no idempotency key and a retried write could duplicate.
- stdout carries the protocol, logging goes to stderr, and nothing is written anywhere on your machine.
Development
uv sync # dependencies into .venv
uv run pytest # offline tests (an HTTP-layer fake under the real discovery client)
uv run ruff check && uv run ruff format --check && uv run mypy
uv run pre-commit run --files $(git ls-files)
uv run python scripts/check_discovery_drift.py # live vs bundled discovery document
The real-API smoke test needs a throwaway container and the delete.containers
scope for its cleanup:
GTM_MCP_E2E_CONTAINER=accounts/ACCOUNT/containers/CONTAINER \
GTM_MCP_E2E_PUBLISH=1 uv run pytest -m e2e tests/e2e
License
MIT, see LICENSE.
Release files for google-tag-manager-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| google_tag_manager_mcp-0.1.0.tar.gz | 32.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| google_tag_manager_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 80.0 kB
Release files / google_tag_manager_mcp-0.1.0.tar.gz
| Download URL | google_tag_manager_mcp-0.1.0.tar.gz |
|---|---|
| Size | 32.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ebe1bdfc9c1fad69650edb9ec0ae83bed43cbe08b87efad981903fae16d126e1
|
|
BLAKE2b-256 checksum How to use checksums |
052072c3b7f8d1ca440661d74b604a0c21c8eeed74ec44afb5363cca150f25dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency logRelease files / google_tag_manager_mcp-0.1.0-py3-none-any.whl
| Download URL | google_tag_manager_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 47.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
446d4fed6db418995f01024da81ffca68a6c69e62faeb9bdedaa14761b4ae825
|
|
BLAKE2b-256 checksum How to use checksums |
e43e1eb0bbf1a7a5565eca72ff93088b4a5d63a2291e4a03119173126613d533
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency log