@theethosteam/gtm-mcp
v1.1.0
Published
Google Tag Manager (API v2) as an MCP server + CLI — manage tags, triggers, variables, versions and publishing for any container.
Downloads
169
Maintainers
Readme
@theethosteam/gtm-mcp
Programmatic control of Google Tag Manager (API v2) for Claude and humans.
One shared TypeScript core (core/), exposed two ways:
- MCP server — native tools for Claude (
gtm_*) - CLI —
gtm <command>(run anywhere via a shell)
Works against any GTM account the authenticated user can access; defaults to
container GTM-WHRH7CHV (override with --container / GTM_CONTAINER_PUBLIC_ID).
Install on any project (global MCP)
Publish once, then register at user scope so every project gets the tools:
# 1) publish (from this directory). Public — the code has no secrets;
# all auth is runtime env vars. (Private needs a paid npm org.)
npm login
npm publish # publishConfig.access is "public"
# 2) register globally for the Claude Code CLI/desktop (one time):
claude mcp add gtm --scope user \
-e GTM_OAUTH_CLIENT_ID=<client-id> \
-e GTM_OAUTH_CLIENT_SECRET=<client-secret> \
-e GTM_OAUTH_REFRESH_TOKEN=<refresh-token> \
-- npx -y @theethosteam/gtm-mcpThe theethosteam npm scope must be an org/user you can publish to (create the
free org at npmjs.com, or change the name to your own scope). On Claude Code
web, add the same server + env to each environment's config instead of user
scope. The CLI is also exposed: npx -y -p @theethosteam/gtm-mcp gtm <command>.
1. Auth — OAuth (recommended)
Google currently has a bug that blocks adding service accounts as GA4 / Tag Manager users via the UI ("This email doesn't match a Google Account"). Until that's fixed, use OAuth, which acts as a real Google user (e.g. the GTM account owner) — so nothing needs to be added inside GTM at all.
- Enable the Tag Manager API: https://console.cloud.google.com/apis/library/tagmanager.googleapis.com
- Configure the OAuth consent screen (APIs & Services → OAuth consent screen): User type External, fill app name + your email, add yourself as a Test user. (No verification needed while in "Testing".)
- Create an OAuth client (APIs & Services → Credentials → Create credentials → OAuth client ID), application type Desktop app. Copy the client ID and client secret.
- Put them in
tools/gtm/.env(copy from.env.example) or export them:export GTM_OAUTH_CLIENT_ID=... export GTM_OAUTH_CLIENT_SECRET=... - Log in to mint a refresh token:
pnpm gtm auth:url # prints a consent URL — open it, approve # the browser redirects to a "localhost" page that fails to load; # copy the `code=...` value out of the URL bar, then: pnpm gtm auth:exchange <code> # saves GTM_OAUTH_REFRESH_TOKEN to .env - Verify:
pnpm gtm whoami
For permanent / MCP use, store GTM_OAUTH_CLIENT_ID,
GTM_OAUTH_CLIENT_SECRET, and GTM_OAUTH_REFRESH_TOKEN as environment secrets.
Fallback — service account
If the service-account UI block is lifted, set GTM_SA_KEY (full JSON) or
GTM_SA_KEY_FILE (path) instead, and add the SA email as a GTM user with
Publish. The tool prefers OAuth when both are present.
3. CLI usage
# discovery
pnpm gtm accounts:list
pnpm gtm containers:list
pnpm gtm container:show # resolves the default container
# inventory (defaults to the Default Workspace)
pnpm gtm tags:list
pnpm gtm triggers:list
pnpm gtm variables:list
pnpm gtm builtins:list
pnpm gtm workspace:status # pending changes
# create things (recipes)
pnpm gtm recipe:ga4-config --measurement-id G-XXXXXXX
pnpm gtm recipe:meta-pixel --pixel-id 1234567890
pnpm gtm recipe:custom-event-trigger --name "Booking Click" --event-name booking_click
pnpm gtm recipe:ga4-event --name "GA4 - Booking" --event-name booking --trigger 12345
# raw create from a JSON object/file/stdin
pnpm gtm tag:create ./mytag.json
echo '{"name":"X","type":"html","parameter":[...]}' | pnpm gtm tag:create -
# versioning + publish
pnpm gtm version:live
pnpm gtm publish --name "v2" --notes "Add GA4" # version + publish in one step
# escape hatch: any API method by dotted path
pnpm gtm raw accounts.list
pnpm gtm raw accounts.containers.workspaces.folders.list '{"parent":"accounts/123/containers/456/workspaces/7"}'Global flags (any command): --container GTM-XXXX, --account <id>,
--container-id <id>, --workspace <nameOrId>.
4. MCP usage (Claude)
.mcp.json at the repo root registers a gtm stdio server. Claude exposes the
core as gtm_* tools (e.g. gtm_list_tags, gtm_recipe_ga4_config,
gtm_publish, gtm_raw). The server reads the same GTM_SA_KEY /
GTM_SA_KEY_FILE env vars. After adding the key to the environment, reload the
session so the MCP server picks it up.
Notes
- Workspaces & versions: edits land in a workspace; nothing is live until
you create a version and publish it (
pnpm gtm publish). - Scopes requested: read, edit containers + versions, delete, publish, manage accounts + users (full capability).
- The
rawcommand/tool reaches every endpoint (folders, zones, clients, templates, environments, user permissions, gtag config, destinations) even without a dedicated wrapper.
