Sandbox
@sprawz/gtm-mcp-server

MCP server for Google Tag Manager

This server connects MCP clients to the Google Tag Manager API. It exposes tools for accounts, containers, workspaces, tags, triggers, variables, templates, versions, and GTM server-side features, plus prompts and resources for audits and tracking plans.

162 stars31 forksGoUpdated 6d ago
Who it's for

Builders who want their MCP client to inspect and change Google Tag Manager through chat.

What it delivers

You can manage GTM containers, version changes, and publish updates from your agent instead of the GTM UI.

What it does

GTM tool surface

Provides tools for listing, creating, updating, deleting, and inspecting GTM accounts, containers, workspaces, tags, triggers, variables, folders, zones, templates, clients, transformations, and versi

OAuth and service-account auth

Supports browser-based Google OAuth for individual users and service-account mode for self-hosted automation.

Workspace safety checks

Uses fingerprints, merge checks, confirmation flags, and explicit publish steps before live changes go out.

MCP resources and prompts

Exposes `gtm://` resources and prompts like `audit_container`, `generate_tracking_plan`, and `best_practices_review`.

Hosted and self-hosted deployment

Runs as a remote MCP server at `https://mcp.gtmeditor.com` or from source with Go or Docker.

How to get it

  1. 1Create an OAuth 2.0 Web application in Google Cloud and add this authorized redirect URI
    https://your-host.example/oauth/callback
  2. 2The scheme and host must match BASE_URL exactly. For local development, use
    http://localhost:8080/oauth/callback

README

GTM MCP Server

License Go MCP Security Checks GitHub release

GTM MCP Server connects MCP clients to the Google Tag Manager API. It can inspect containers, create and update workspace entities, create versions, and publish a selected version after explicit confirmation.

Use the hosted server at:

https://mcp.gtmeditor.com

The server supports browser-based Google OAuth for individual users and service-account authentication for self-hosted automation.

Project status

ItemCurrent state
Version in server.json1.10.1
TransportMCP Streamable HTTP
Runtime tools64 GTM tools by default; 94 with GTM_TOOL_GROUPS=all, plus 2 utility tools
MCP resources8 resource definitions
MCP prompts6 prompts
Official GTM API coverage101 of 106 methods
Product parity target101 of 106 methods, reached
Hosted endpointhttps://mcp.gtmeditor.com

The agreed API parity scope is complete. The project implements 101 methods from Google's 106-method GTM v2 discovery surface. The five accounts.user_permissions methods are intentionally excluded because granting and revoking GTM access needs a separate privilege-management design.

Tool count and API-method count are different. Some tools provide local guidance, while some helpers cover more than one Google API call.

Connect an MCP client

Add the hosted URL as a remote HTTP MCP server. The client should discover the OAuth metadata, open Google sign-in, and reconnect with the issued bearer token.

Claude Code

claude mcp add --transport http gtm https://mcp.gtmeditor.com

Gemini CLI

gemini mcp add --transport http gtm https://mcp.gtmeditor.com

Gemini CLI can also use this settings.json entry:

{
  "mcpServers": {
    "gtm": {
      "httpUrl": "https://mcp.gtmeditor.com"
    }
  }
}

Cursor

Add the following to .cursor/mcp.json:

{
  "mcpServers": {
    "gtm": {
      "url": "https://mcp.gtmeditor.com"
    }
  }
}

ChatGPT, Codex, and Claude web

Add a custom or remote MCP connection in the client and use https://mcp.gtmeditor.com as the server URL. Product menus change more often than this server, so refer to the client's current MCP connection instructions if the label differs.

After connecting, try:

List my GTM accounts, containers, and workspaces. Do not make changes.

The Google account used during OAuth determines which GTM accounts the server can access.

Typical workflow

  1. Call list_accounts, list_containers, and list_workspaces to discover IDs. Do not guess IDs.
  2. Inspect the workspace with list/get tools or the audit_container prompt.
  3. Create or update tags, triggers, variables, templates, clients, or transformations in the selected workspace.
  4. Call get_workspace_status and resolve conflicts before versioning.
  5. Call create_version to snapshot the workspace.
  6. Inspect the saved version with get_version.
  7. Call publish_version with confirm: true only when the selected version is ready to go live.
  8. Verify the published state with get_live_version.

Example requests:

  • "Audit this workspace for duplicate tags and unused triggers."
  • "Create a GA4 purchase event tag, but do not publish it."
  • "Show the difference between the latest saved version and the live version."
  • "Generate a Markdown tracking plan from this workspace."
  • "Import the iubenda template from the Community Template Gallery."

Tools

All GTM tools use the authenticated Google identity from the current MCP request. Inputs and outputs are structured JSON.

Utility

ToolPurpose
pingTest MCP connectivity
auth_statusCheck whether the current request is authenticated

Accounts and containers

ToolPurpose
list_accountsList accessible GTM accounts
update_accountRename an account
list_containersList containers and their public IDs and settings
lookup_containerFind a container by destination ID or GTM public tag ID
get_container_snippetGet a web install snippet or server-container configuration
create_containerCreate a web, app, AMP, or server container
update_containerRename a container while preserving its other settings
delete_containerPermanently delete a container; requires confirm: true
combine_containersMerge a source container into a target; requires confirm: true
move_tag_idMove a tag ID into a new container; requires confirmation and terms acceptance

Workspaces

ToolPurpose
list_workspacesList workspaces in a container
create_workspaceCreate a workspace
get_workspaceGet workspace metadata and its current fingerprint
update_workspaceUpdate selected workspace fields
delete_workspaceDelete a workspace; requires confirm: true
quick_preview_workspaceCompile an ephemeral preview without saving or publishing
get_workspace_statusShow pending changes and merge conflicts
bulk_update_workspaceApply multiple entity changes; requires confirm: true
resolve_workspace_conflictReplace a conflict with a resolved entity; requires confirm: true
sync_workspaceSynchronize with the latest container version; requires confirm: true

Bulk update and conflict resolution accept raw GTM Entity JSON so every entity type supported by the official API remains available.

Tags

ToolPurpose
list_tagsList workspace tags
get_tagGet complete tag details
create_tagCreate a tag
update_tagUpdate a tag with fingerprint-based concurrency control
delete_tagDelete a tag; requires confirm: true

Triggers

ToolPurpose
list_triggersList workspace triggers
get_triggerGet complete trigger details
create_triggerCreate a trigger
update_triggerUpdate a trigger with fingerprint-based concurrency control
delete_triggerDelete a trigger; requires confirm: true

For update_trigger, omit filterJson, customEventFilterJson, autoEventFilterJson, or parameterJson to preserve the current field. Pass the JSON string "[]" to clear a field, or a non-empty JSON array to replace it. For click, link-click, and form-submission triggers, use filterJson because GTM drops autoEventFilter for those trigger types.

Variables

ToolPurpose
list_variablesList workspace variables
get_variableGet complete variable details
create_variableCreate a variable
update_variableUpdate a variable with fingerprint-based concurrency control
delete_variableDelete a variable; requires confirm: true

Folders and built-in variables

ToolPurpose
list_foldersList workspace folders
get_folderGet complete folder metadata
create_folderCreate a folder
update_folderUpdate a folder with fingerprint protection
delete_folderDelete a folder; requires confirm: true
get_folder_entitiesList tags, triggers, and variables assigned to a folder
move_entities_to_folderMove tags, triggers, and variables; requires confirm: true
revert_folderDiscard workspace folder changes; requires confirm: true
list_built_in_variablesList enabled built-in variables
enable_built_in_variablesEnable built-in variable types
disable_built_in_variablesDisable built-in variable types; requires confirm: true

Use revert_workspace_entity to discard changes to a built-in variable.

Zones

ToolPurpose
list_zonesList all zones across every result page
get_zoneGet boundary, child-container, and type-restriction configuration
create_zoneCreate a workspace zone
update_zoneUpdate selected fields with fingerprint concurrency control
delete_zoneDelete a zone; requires confirm: true

Use revert_workspace_entity to discard changes to a zone.

Environments

The environments tool group is optional. Enable it with GTM_TOOL_GROUPS=all or add environments to an explicit group list.

ToolPurpose
list_environmentsList all container environments across every result page
get_environmentGet environment configuration and authorization metadata
create_environmentCreate a user environment
update_environmentUpdate selected fields with fingerprint concurrency control
reauthorize_environmentRotate the authorization code; requires confirm: true
delete_environmentDelete a user environment; requires confirm: true

The Live and Latest environments are managed by GTM. Creation and deletion apply to user environments; GTM permits URL and debug updates on other types.

Destinations and Google tag configurations

These tools are in the optional destinations and gtag groups.

ToolPurpose
list_destinationsList Google tag destinations linked to a container
get_destinationGet a destination by its link ID
link_destinationMove a destination to a container; requires confirm: true
list_google_tag_configsList Google tag configurations in a workspace
get_google_tag_configGet a Google tag configuration
create_google_tag_configCreate a Google tag configuration
update_google_tag_configUpdate a configuration with fingerprint protection
delete_google_tag_configDelete a configuration; requires confirm: true

Container combine, tag-ID move, and destination link operations do not copy or enable user permissions. Account permission management remains outside the current parity target.

Custom templates

ToolPurpose
list_templatesList custom templates
get_templateGet template metadata and template code
create_templateCreate a custom template from .tpl code
update_templateUpdate template code
delete_templateDelete an unused template; requires confirm: true
import_gallery_templateImport a Community Template Gallery template
get_tag_templatesReturn compact GA4 and Custom HTML input examples
get_trigger_templatesReturn compact trigger input examples

The full JSON examples live in the gtm://best-practices/tool-input-formats resource so every tool listing does not repeat them.

Server-side containers

ToolPurpose
list_clientsList server-container clients
get_clientGet a client
create_clientCreate a client
update_clientUpdate a client
delete_clientDelete a client; requires confirm: true
list_transformationsList transformations
get_transformationGet a transformation
create_transformationCreate a transformation
update_transformationUpdate a transformation
delete_transformationDelete a transformation; requires confirm: true

Use revert_workspace_entity to discard changes to a client or transformation.

Versions and publication

ToolPurpose
list_versionsList all saved version headers across every result page
get_latest_version_headerGet the latest saved header, which may differ from live
get_versionGet a saved version and its complete entity collections
get_live_versionGet the currently published version and its entities
create_versionCreate a version from a conflict-free workspace
publish_versionPublish a selected version; requires confirm: true
update_versionUpdate a saved version's name or description
delete_versionSoft-delete a version; requires confirm: true
undelete_versionRestore a soft-deleted version; requires confirm: true
set_latest_versionMake a version Latest without publishing; requires confirm: true

Workspace reverts

ToolPurpose
revert_workspace_entityDiscard workspace changes to a built-in variable, client, tag, template, transformation, trigger, variable, or zone; requires confirm: true

The tool fetches the current entity fingerprint before calling the matching official revert method. A successful result can have existsAfterRevert: false when the entity does not exist in the latest container version.

Safety model

  • Delete operations, publication, and disabling built-in variables require confirm: true.
  • Update operations fetch the current resource fingerprint and use Google's optimistic concurrency checks.
  • create_version first checks that the workspace has changes and no merge conflicts.
  • Read tools distinguish latest saved state from published live state.
  • Google API errors are mapped to clearer not-found, permission, conflict, and rate-limit failures. Retryable API failures use bounded backoff.
  • MCP request bodies are limited to 5 MiB. OAuth and dynamic registration endpoints have stricter rate and body limits.

Most entity edits affect a workspace and are not live until a version is published. Account and container operations act directly on those resources. Always review the target IDs and the generated version before publication.

Resources

The server exposes two concrete resources and six URI templates:

URIContent
gtm://accountsAccessible accounts
gtm://accounts/{accountId}/containersContainers
gtm://accounts/{accountId}/containers/{containerId}/workspacesWorkspaces
gtm://accounts/{accountId}/containers/{containerId}/workspaces/{workspaceId}/tagsTags
gtm://accounts/{accountId}/containers/{containerId}/workspaces/{workspaceId}/triggersTriggers
gtm://accounts/{accountId}/containers/{containerId}/workspaces/{workspaceId}/variablesVariables
gtm://best-practicesBest-practice topic index
gtm://best-practices/{topic}One embedded guidance document

Best-practice topics include naming and organization, safe edits, GA4 and consent, server-side containers, and detailed tool input formats.

Prompts

PromptPurpose
audit_containerReview tags, triggers, and variables for quality problems
generate_tracking_planBuild a Markdown tracking plan from a workspace
suggest_ga4_setupRecommend a GA4 structure from stated goals
find_gallery_templateGuide Community Gallery discovery and import
best_practices_reviewScore a workspace against embedded guidance
plan_safe_editProduce a staged edit/version/publish plan

Prompts prepare context and instructions for the model. They do not bypass tool authentication or mutation safeguards.

Authentication

Hosted OAuth

The hosted server implements MCP OAuth discovery and Google authorization. It supports PKCE, Dynamic Client Registration, Client ID Metadata Documents, authorization-server metadata, and protected-resource metadata.

The server never receives a Google password. It receives Google OAuth tokens after consent. A self-hosted operator can keep issued MCP and Google tokens across restarts by setting TOKEN_STORE_PATH; without that setting they remain in memory and disappear when the process stops.

Expired MCP access tokens can be renewed transparently while their Google refresh credentials remain valid. AUTH_AUTO_REFRESH_MAX_AGE limits how long one bearer can be silently extended; its default is seven days.

Service-account mode

Service-account mode gives every holder of one server API key the GTM access granted to the configured Google service account.

  1. Create a Google service account.
  2. Add its email address to the required GTM account with only the permissions it needs.
  3. Set a strong SERVICE_ACCOUNT_API_KEY on this server.
  4. Set GOOGLE_SERVICE_ACCOUNT_KEY_JSON to the key JSON, or use Application Default Credentials on Google Cloud.
  5. Configure the MCP client to send Authorization: Bearer <SERVICE_ACCOUNT_API_KEY>.

OAuth and service-account mode can run together. Requests with the configured API key use the service account; OAuth users keep their own Google identity and GTM permissions.

Self-hosting

Requirements

  • Go 1.26 or Docker
  • A Google Cloud project with the Tag Manager API enabled
  • A Google OAuth web client for user OAuth, or a Google service account for S2S
  • HTTPS and a stable public URL for remote OAuth deployments

Google OAuth setup

Create an OAuth 2.0 Web application in Google Cloud and add this authorized redirect URI:

https://your-host.example/oauth/callback

The scheme and host must match BASE_URL exactly. For local development, use:

http://localhost:8080/oauth/callback

Run from source

git clone https://github.com/sprawz/gtm-mcp-server.git
cd gtm-mcp-server

cat > .env <<'EOF'
BASE_URL=http://localhost:8080
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
TOKEN_STORE_PATH=./data/tokens.json
EOF

go run .

The repository reads .env and then .env.local; .env.local overrides .env. Both are ignored by Git.

Run with Docker

docker build -t gtm-mcp-server .

docker run --rm \
  --name gtm-mcp-server \
  -p 8080:8080 \
  --env-file .env \
  -v gtm-mcp-tokens:/data \
  gtm-mcp-server

When using the named volume, set TOKEN_STORE_PATH=/data/tokens.json in .env.

Configuration

VariableDefaultPurpose
PORT8080HTTP listen port
BASE_URLhttp://localhost:8080Canonical public URL used by OAuth metadata and callbacks
GOOGLE_CLIENT_IDemptyGoogle OAuth web-client ID
GOOGLE_CLIENT_SECRETemptyGoogle OAuth web-client secret
ACCESS_TOKEN_TTL8hLifetime of MCP access tokens
AUTH_AUTO_REFRESH_MAX_AGE168hMaximum silent-renewal chain age
TOKEN_STORE_PATHemptyOptional persisted token-store file
SERVICE_ACCOUNT_API_KEYemptyBearer API key that enables S2S mode
GOOGLE_SERVICE_ACCOUNT_KEY_JSONemptyService-account JSON; omit when ADC is available
ALLOWED_HOSTSemptyAdditional trusted hosts for Docker/internal URL resolution
TRUST_PROXYfalseTrust the rightmost X-Forwarded-For hop for rate limiting
LOG_LEVELinfoSet debug for additional structured logs
GTM_TOOL_GROUPScurrent groupsComma-separated tool families advertised through MCP

If OAuth and service-account credentials are both absent, the server starts in open mode. Use open mode only for isolated local development.

TRUST_PROXY=true is appropriate only when a trusted reverse proxy overwrites or appends X-Forwarded-For. The implementation uses the rightmost value. With multiple proxy hops, configure and test the trust boundary before relying on per-client rate limits.

ALLOWED_HOSTS is a comma-separated allowlist used when the same server is reached through trusted internal Docker hostnames. Do not add arbitrary public hosts.

GTM_TOOL_GROUPS controls schema size for clients that need only part of the API. Available groups are accounts, workspaces, tags, triggers, variables, folders, builtins, zones, templates, server, guidance, environments, destinations, gtag, container-admin, folder-admin, and workspace-admin, version-admin, and reverts. Leaving it unset preserves the established 64-tool surface. New parity groups are opt-in. Use all to include every current and future parity family, or select a subset:

GTM_TOOL_GROUPS=accounts,workspaces,tags,triggers,variables

The two connection utility tools remain available regardless of this setting.

Releases and deployment

server.json is the source of truth for the runtime version. It is embedded in the Go binary and returned by /health; there is no second version constant in Go code.

To publish a release:

  1. Update server.json.
  2. Commit the release changes.
  3. Push a matching tag such as v1.11.0.

The release workflow rejects a tag that does not match server.json, creates cross-platform archives with GoReleaser, and then deploys the tagged source to the production VPS. The deployment uses the GitHub environment auto-deployment and expects these secrets:

  • SSH_PRIVATE_KEY
  • VPS_KNOWN_HOSTS
  • VPS_HOST
  • VPS_USER

The workflow preserves server-only .env, docker-compose.yml, and token data, keeps a rollback image, rebuilds the service, and waits until /health reports the expected version.

Recent commits on main can be newer than the latest tagged release. Check the release page when you need a reproducible published artifact.

Development

go test ./... -count=1
go vet ./...
staticcheck ./...
go run ./cmd/tool-schema-report
go run ./cmd/tool-schema-report -groups tags,zones

The schema report prints the GTM tool count, serialized tools/list size, token estimate, and largest definitions. The default GTM tool surface serializes to 78,734 bytes for 64 tools. A regression test enforces an 80,000-byte ceiling so new parity work does not silently consume unlimited model context. The all group exposes 94 GTM tools and serializes to 115,748 bytes.

Pull requests run govulncheck, gosec, Gitleaks, Trivy, staticcheck, and CodeQL. Request-level GTM tests use local fake Google endpoints. Mutating live tests must use a disposable container and clean up their entities.

See ARCHITECTURE.md for package boundaries, request flow, authentication internals, token persistence, and security invariants.

For a deep dive into the Google Tag Manager MCP server, read the Deep Wiki.

Current limitations

  • The 101-method GTM v2 API parity target is complete. The five account user-permission methods remain outside the product scope.
  • The server supports Streamable HTTP only. Stdio transport is planned but is not implemented.
  • The optional connection dashboard is under review in PR #107; it is not part of main.
  • The hosted service processes OAuth tokens. Self-host the server when your policy requires control of the runtime and token store.
  • Some GTM resource families depend on container type or account entitlement.

Additional context

License

BSD 3-Clause. See LICENSE.

Maintained by Paolo Bietolini.

Files in the repo

Repository payload23 top-level entries
  • .github
  • auth
  • cmd
  • config
  • examples
  • gtm
  • middleware
  • skills
  • .dockerignore
  • .gitignore
  • .gitleaks.toml
  • .goreleaser.yaml
  • ARCHITECTURE.md
  • Dockerfile
  • go.mod
  • go.sum
  • LICENSE
  • llms.txt
  • main.go
  • README.md
  • server.json
  • version_test.go
  • version.go

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More connectors

Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface

86k

Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code

14k
t8y2/dbxConnectors

20 MB lightweight cross-platform database client for 90+ databases, including MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, SQL Server, and Dameng. Built-in AI, MCP Server, CLI, desktop and Docker. | 轻量级跨平台数据库管理工具,支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、达梦等 90+ 数据库,提供桌面端、Docker、CLI、内置 AI 助手和 MCP Server。

19k

The fastest browser for AI agents to run browser automation, built for sharing your logged-in browser state with your AI agents, like Codex or Claude Code, without disturbing you. Zero cost, zero config.

16k
noskillish/
bankmcp

BankMCP™: your AI can now read your bank. Self-hosted, read-only MCP server for your own bank accounts via open banking (Enable Banking). Standard MCP; tested with Claude and Ollama.

177