TL;DR

I know you want to try it out:

Connect the MCP to your LLM, login, and you're good to go.


The Problem with GTM at Scale

Google Tag Manager is essential for modern analytics implementations. It is also painfully tedious at scale.

Consider setting up GA4 ecommerce tracking from scratch. You need:

Each element requires multiple clicks, careful configuration, and precise parameter mapping. Miss a checkbox, misspell a dataLayer key, or forget to enable a trigger—and your purchase events silently fail. You only discover the problem when stakeholders ask why conversion data disappeared.

For agencies managing dozens of client containers, this becomes a significant operational burden. Want to rename all tags to follow a new naming convention? Manual clicks or write a GTM API script. Need to audit a container for orphaned triggers? Click through every single one.

What if you could describe what you need in plain English and have it implemented correctly?

"Create a GA4 event tag for form submissions. It should fire when the contact_form_submit event is pushed to the dataLayer. Include the form_name and form_location as event parameters."

This is now possible.


What is the Model Context Protocol (MCP)?

The Model Context Protocol is an open standard developed by Anthropic that enables AI assistants to interact with external systems. When Claude searches the web, reads files, or executes code, it uses MCP tools under the hood.

MCP defines three core primitives:

Tools: Functions the AI can call with structured inputs and outputs. A tool like create_tag accepts parameters (name, type, trigger IDs) and returns results (created tag ID, success status).

Resources: URI-based data access. Instead of calling a function, the AI reads data from a URI like gtm://accounts/123/containers/456/tags.

Prompts: Workflow templates that guide the AI through multi-step processes. An "audit_container" prompt structures how the AI analyzes and reports on container health.

The key architectural insight: instead of building AI features into every product, MCP creates a standardized bridge between AI assistants and existing APIs. Claude does not need native GTM integration. It needs an MCP server that translates natural language into GTM API calls and returns structured results.

User: "Create a scroll depth trigger at 25%, 50%, 75%, 100%"
       ↓
Claude → MCP Protocol → GTM MCP Server → GTM API
       ↓
GTM MCP Server → MCP Protocol → Claude
       ↓
Claude: "Created trigger 'Scroll Depth - All Thresholds' (ID: 42)"

This separation means the GTM integration works with any MCP-compatible client—Claude, ChatGPT, or custom applications built with the MCP SDK.


GTM MCP Server

I built gtm-mcp-server to connect AI assistants to the Google Tag Manager API. It is hosted at mcp.gtmeditor.com and available for anyone to use. The project is open source under the BSD-3-Clause license.

Complete Tool Reference

The server exposes 37 tools covering the full GTM workflow:

Read Operations

Tool Description
list_accounts List all GTM accounts accessible to authenticated user
list_containers List containers within an account
list_workspaces List workspaces within a container
list_tags List all tags in a workspace
list_triggers List all triggers in a workspace
get_trigger Get detailed trigger configuration by ID
list_variables List all variables in a workspace
get_variable Get detailed variable configuration by ID
list_folders List folders (for organization)
list_templates List custom templates in workspace
list_versions List container versions with change counts
get_tag Get detailed tag configuration by ID
get_template Get template code and parameters
get_folder_entities Get tags, triggers, variables in a folder
get_workspace_status Check pending changes and merge conflicts

Write Operations

Tool Description
create_tag Create new tag (GA4, HTML, image pixel, etc.)
create_trigger Create trigger (pageview, custom event, click, etc.)
create_variable Create variable (Data Layer, Cookie, JavaScript, etc.)
create_template Create custom tag template
create_workspace Create new workspace for changes
create_container Create new container in account
update_tag Modify existing tag configuration
update_trigger Modify trigger conditions
update_variable Modify existing variable configuration
update_template Update template code
delete_tag Remove tag (requires confirmation)
delete_trigger Remove trigger (requires confirmation)
delete_variable Remove variable (requires confirmation)
delete_template Remove template (requires confirmation)
delete_container Remove container (requires confirmation)

Publishing & Versioning

Tool Description
create_version Snapshot workspace changes into a version
publish_version Push version live (requires confirmation)

Template Gallery

Tool Description
import_gallery_template Import from Community Template Gallery
get_tag_templates Get example parameter structures for common tags
get_trigger_templates Get example structures for trigger types

Practical Examples

Basic operations:

You: "List all my GTM containers"
AI: [Calls list_accounts, then list_containers]
    "Found 3 containers:
    - GTM-ABC123: Production Website (web)
    - GTM-DEF456: Mobile App (iOS)
    - GTM-GHI789: Server-Side (server)"

Creating tracking:

You: "Create a GA4 event tag for newsletter signups that fires
      when newsletter_signup is pushed to the dataLayer"

AI: [Calls create_trigger for customEvent type]
    [Calls create_tag with GA4 event configuration]
    "Created:
    - Trigger: 'CE - newsletter_signup' (ID: 15)
    - Tag: 'GA4 Event - Newsletter Signup' (ID: 42)
    The tag will fire when dataLayer.push({event: 'newsletter_signup'}) is called."

Container audit:

You: "Audit this container for issues"

AI: [Calls list_tags, list_triggers, list_variables]
    [Analyzes relationships and configurations]
    "Container Audit Results:

    ⚠️ Issues Found:
    - 3 triggers not used by any tag (orphaned)
    - 2 tags share identical configurations (potential duplicates)
    - 5 tags use hardcoded Measurement ID instead of variable

    ✅ Healthy:
    - Naming conventions are consistent
    - No deprecated tag types
    - All variables are referenced"

Bulk operations:

You: "Add 'PROD - ' prefix to all GA4 event tags"

AI: [Calls list_tags to find GA4 event tags]
    [Calls update_tag for each matching tag]
    "Updated 8 tags:
    - 'GA4 Event - Purchase' → 'PROD - GA4 Event - Purchase'
    - 'GA4 Event - Add to Cart' → 'PROD - GA4 Event - Add to Cart'
    ..."

Getting Started

Setup takes less than two minutes. Choose your preferred AI assistant:

Claude.ai (Web/Desktop)

  1. Open Claude and go to SettingsConnectors
  2. Click Add Custom Connector
  3. Enter the URL: https://mcp.gtmeditor.com
  4. Click Add
  5. When prompted, sign in with your Google account and authorize GTM access

Claude Code (CLI)

For developers using Claude Code in the terminal:

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

The CLI will open a browser window for Google OAuth. After authorization, the connector is ready.

ChatGPT

  1. Go to OpenAI Apps Platform
  2. Add an MCP integration
  3. Enter the URL: https://mcp.gtmeditor.com
  4. Complete Google OAuth authorization

Verify Connection

After setup, test the connection:

You: "List my GTM accounts"

If you see your accounts and containers, the connection is working. If you see an authorization error, re-add the connector and ensure you authorize the correct Google account that has GTM access.

Required Permissions

The OAuth flow requests these scopes:

You can revoke access anytime from Google Account Permissions.


Use Cases

1. Complete GA4 Ecommerce Implementation

Building GA4 ecommerce from scratch is the canonical use case. A single conversation replaces an hour of manual configuration.

The prompt:

Set up complete GA4 ecommerce tracking for my online store.

Measurement ID: G-XXXXXXXXXX
Events needed: view_item, add_to_cart, remove_from_cart, begin_checkout, purchase

My dataLayer follows the GA4 recommended schema with items array, value, and currency.

What the AI creates:

  1. GA4 Configuration Tag with Measurement ID
  2. 5 Event Tags — one for each ecommerce action
  3. 5 Custom Event Triggers — matching dataLayer event names
  4. Data Layer Variables:
    • dlv - ecommerce.items (items array)
    • dlv - ecommerce.value (transaction value)
    • dlv - ecommerce.currency (currency code)
    • dlv - ecommerce.transaction_id (order ID for purchase)
  5. Correct parameter mapping between variables and GA4 event parameters

The AI follows GA4's expected parameter names (items, value, currency, transaction_id) and links everything correctly.

2. Container Audits

Auditing an inherited or legacy container reveals hidden problems.

The prompt:

Audit this container for issues. Check for:
- Orphaned triggers and variables
- Duplicate tags
- Naming inconsistencies
- Security concerns
- Missing consent mode integration

Example audit output:

## Container Audit: GTM-ABC123

### Critical Issues

- **Hardcoded Measurement IDs**: 7 tags contain hardcoded GA4/UA IDs instead of using variables
- **Missing consent checks**: 12 tags fire without consent mode conditions

### Warnings

- **Orphaned triggers**: 4 triggers are not used by any tag
  - "Click - Old CTA Button" (ID: 23)
  - "Timer - 30 seconds" (ID: 31)
  - ...
- **Duplicate configurations**: 2 tag pairs have identical settings
  - "GA4 - Page View" and "GA4 Page View Copy"

### Recommendations

1. Create a {{GA4 Measurement ID}} constant variable
2. Update all GA4 tags to reference the variable
3. Add consent mode trigger conditions to marketing tags
4. Delete or archive orphaned triggers

3. Bulk Operations

Managing containers at scale becomes conversational:

Standardize naming:

Rename all triggers to follow this convention:
- Custom Event triggers: "CE - {event_name}"
- Click triggers: "Click - {description}"
- Pageview triggers: "PV - {description}"

Migrate from hardcoded values:

Find all tags with hardcoded GA4 Measurement IDs.
Create a constant variable called "GA4 - Measurement ID" with value G-XXXXXXXXXX.
Update all tags to use this variable instead of the hardcoded value.

Clean up paused tags:

List all tags that have been paused.
Show me which ones haven't been modified in the last 6 months.

4. Template Gallery Integration

Import community templates without leaving the conversation:

Import the Cookiebot consent template from the Community Gallery.
Then create a trigger that fires when consent is granted for analytics cookies.

The AI:

  1. Calls import_gallery_template with owner "Cookiebot" and repository name
  2. Returns the template type string for creating tags
  3. Creates triggers using the template's consent signals

Available gallery templates include: Cookiebot, OneTrust, Usercentrics, Facebook Pixel, TikTok Pixel, and hundreds more.

5. Documentation Generation

Before client handoff or compliance review:

Generate a tracking plan document for this container.
Include all custom events, their triggers, required dataLayer parameters,
and which tags they fire.

Output format:

# Tracking Plan: example.com

## Events

### purchase

- **Trigger**: CE - purchase
- **Tags**: GA4 Event - Purchase, Meta CAPI - Purchase
- **Required dataLayer**:
  - `event`: "purchase"
  - `ecommerce.transaction_id`: string
  - `ecommerce.value`: number
  - `ecommerce.currency`: string (ISO 4217)
  - `ecommerce.items`: array of item objects

### add_to_cart

- **Trigger**: CE - add_to_cart
- **Tags**: GA4 Event - Add to Cart
- **Required dataLayer**:
  ...

6. Cross-Container Consistency

For agencies managing multiple client containers:

I'm setting up a new client container (GTM-XYZ789).
Copy the standard event tracking structure from GTM-ABC123:
- All GA4 event tags
- Their matching triggers
- Required variables

Update the Measurement ID to G-NEWCLIENT.

The AI reads the source container, recreates the structure in the destination, and updates client-specific values.


How It Works

The server is written in Go and acts as a protocol bridge between MCP clients and the GTM API v2.

┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐
│  Claude/ChatGPT │────▶│  GTM MCP Server  │────▶│   GTM API   │
│   (MCP Client)  │◀────│  (MCP Server)    │◀────│   (Google)  │
└─────────────────┘     └──────────────────┘     └─────────────┘
      JSON-RPC             HTTP/REST              REST API
      over HTTP            with OAuth             with OAuth

Authentication Flow

The server implements OAuth 2.1 with PKCE (Proof Key for Code Exchange), following modern security standards:

  1. Discovery: Client fetches /.well-known/oauth-authorization-server to discover endpoints
  2. Authorization: User is redirected to Google's consent screen
  3. Token exchange: Server exchanges authorization code for access token
  4. Session binding: Token is bound to the MCP session (not persisted)
MCP Client                    GTM MCP Server                    Google
    │                              │                               │
    │─── GET /.well-known ────────▶│                               │
    │◀── Auth endpoints ───────────│                               │
    │                              │                               │
    │─── Authorization request ───▶│                               │
    │◀── Redirect to Google ───────│                               │
    │                              │                               │
    │─────────────────────────────────── Google OAuth consent ────▶│
    │◀──────────────────────────────── Authorization code ─────────│
    │                              │                               │
    │─── Token exchange ──────────▶│                               │
    │                              │─── Exchange code ────────────▶│
    │                              │◀── Access token ──────────────│
    │◀── MCP session ready ────────│                               │

Security properties:

Safety Features

GTM changes can break tracking catastrophically if done incorrectly. The server implements multiple safeguards:

Workspace isolation: All changes occur within a GTM workspace, never directly on the live container. The published version remains untouched until you explicitly publish. This mirrors GTM's native workflow.

Confirmation gates: Destructive operations require explicit confirmation:

// This will fail:
{ "tool": "delete_tag", "tagId": "123" }

// This is required:
{ "tool": "delete_tag", "tagId": "123", "confirm": true }

The AI must explicitly pass confirm: true to delete resources or publish versions. This prevents accidental deletions or pushes.

Version control: Publishing creates a version snapshot before going live:

Workspace changes → create_version → publish_version → Live
                         │
                         └── Rollback point preserved

If something breaks, you can roll back to the previous version in GTM.

Fingerprint handling: GTM uses optimistic concurrency control. Each resource has a fingerprint that changes on update. The server automatically fetches current fingerprints before updates, preventing "someone else modified this" errors when multiple people edit the same container.

Read-before-write: For updates, the server reads the current state, merges changes, and writes back—preserving fields you didn't modify.


Technical Architecture

The server is designed for reliability, security, and easy deployment.

Technology Stack

Component Technology Purpose
Language Go 1.25 Performance, single binary deployment
MCP SDK github.com/modelcontextprotocol/go-sdk MCP protocol implementation
GTM API google.golang.org/api/tagmanager/v2 Official Google API client
OAuth OAuth 2.1 with PKCE Modern authentication flow
Standards RFC 8414, RFC 7591, RFC 9728 Authorization server metadata, dynamic registration
Deployment Docker + Caddy Containerization with automatic TLS

Project Structure

gtm-mcp-server/
├── main.go                    # HTTP server, MCP session handling
├── auth/
│   ├── google.go              # Google OAuth provider configuration
│   ├── handlers.go            # /authorize, /callback, /token endpoints
│   ├── middleware.go          # Bearer token validation, session extraction
│   └── pkce.go                # PKCE challenge/verifier handling
├── gtm/
│   ├── client.go              # GTM API client wrapper with retry logic
│   ├── tools.go               # MCP tool registration and routing
│   ├── read.go                # List/Get operations
│   ├── mutations.go           # Create/Update/Delete operations
│   ├── templates.go           # Parameter examples for LLM context
│   └── prompts.go             # Audit, tracking plan, GA4 setup workflows
├── mcp/
│   ├── server.go              # MCP server configuration
│   ├── resources.go           # URI-based resource handlers
│   └── session.go             # Session state management
├── config/
│   └── config.go              # Environment variable parsing
├── Dockerfile                 # Multi-stage build
├── docker-compose.yml         # Local development setup
└── Caddyfile                  # Reverse proxy with automatic TLS

MCP Resources

Beyond tools, the server exposes MCP resources for URI-based data access. Resources are useful when the AI needs to read data without performing an action:

gtm://accounts
gtm://accounts/{accountId}/containers
gtm://accounts/{accountId}/containers/{containerId}/workspaces
gtm://accounts/{accountId}/containers/{containerId}/workspaces/{workspaceId}/tags
gtm://accounts/{accountId}/containers/{containerId}/workspaces/{workspaceId}/triggers
gtm://accounts/{accountId}/containers/{containerId}/workspaces/{workspaceId}/variables

The AI can read these URIs to understand the current state before making changes.

MCP Prompts

Prompts are workflow templates that guide the AI through complex multi-step processes:

audit_container: Comprehensive container analysis

Analyze the container at:
- Account: {accountId}
- Container: {containerId}
- Workspace: {workspaceId}

Check for:
1. Orphaned triggers (not used by any tag)
2. Orphaned variables (not referenced)
3. Duplicate tag configurations
4. Naming convention violations
5. Hardcoded values that should be variables
6. Security concerns (inline scripts, external domains)
7. Missing consent mode integration

Output a structured report with severity levels.

generate_tracking_plan: Documentation generation

Generate a tracking plan document for workspace {workspaceId}.

Include:
- All custom events with their triggers
- Required dataLayer parameters for each event
- Which tags fire for each event
- Variable definitions and their sources

Output as markdown suitable for developer handoff.

suggest_ga4_setup: Implementation recommendations

Review the current workspace and suggest GA4 implementation improvements:
- Missing recommended events for this site type
- Parameter mapping issues
- Enhanced measurement opportunities
- Debug mode configuration

Error Handling

The server implements structured error responses:

{
  "error": {
    "code": "GTM_API_ERROR",
    "message": "Tag with ID 123 not found in workspace",
    "details": {
      "accountId": "12345",
      "containerId": "67890",
      "workspaceId": "1",
      "tagId": "123"
    }
  }
}

Common error codes:

Code Meaning
AUTH_REQUIRED Session expired, re-authorization needed
PERMISSION_DENIED User lacks GTM permissions for this resource
NOT_FOUND Resource doesn't exist
CONFIRMATION_REQUIRED Destructive operation needs confirm: true
CONFLICT Fingerprint mismatch (resource was modified)
RATE_LIMITED GTM API quota exceeded

Self-Hosting

The server is fully open source. Self-hosting gives you:

Prerequisites

Step 1: Google Cloud Setup

  1. Go to Google Cloud Console

  2. Create a new project (or use existing)

  3. Enable the Tag Manager API:

    • Navigate to APIs & Services → Library
    • Search "Tag Manager API"
    • Click Enable
  4. Create OAuth credentials:

    • Go to APIs & Services → Credentials
    • Click "Create Credentials" → "OAuth client ID"
    • Application type: Web application
    • Name: "GTM MCP Server" (or your preference)
  5. Configure redirect URIs (add all that apply):

    # For Claude.ai
    https://claude.ai/api/mcp/auth_callback
    
    # For local development
    http://localhost:8080/oauth/callback
    
    # For your production domain
    https://your-domain.com/oauth/callback
    
  6. Copy the Client ID and Client Secret

Step 2: Local Development

# Clone the repository
git clone https://github.com/sprawz/gtm-mcp-server.git
cd gtm-mcp-server

# Generate a JWT secret
JWT_SECRET=$(openssl rand -base64 32)

# Create environment file
cat > .env << EOF
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
JWT_SECRET=${JWT_SECRET}
BASE_URL=http://localhost:8080
PORT=8080
EOF

# Start with Docker Compose
docker compose up -d

# Verify it's running
curl http://localhost:8080/.well-known/oauth-authorization-server

Add to Claude Code:

claude mcp add -t http gtm-local http://localhost:8080

Step 3: Production Deployment

For production, you need HTTPS. The repository includes a Caddy configuration for automatic TLS certificate management.

docker-compose.prod.yml:

version: '3.8'
services:
  gtm-mcp:
    build: .
    environment:
      - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
      - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
      - JWT_SECRET=${JWT_SECRET}
      - BASE_URL=https://gtm.yourdomain.com
    restart: unless-stopped
    networks:
      - caddy

  caddy:
    image: caddy:latest
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - caddy

networks:
  caddy:

volumes:
  caddy_data:
  caddy_config:

Caddyfile:

gtm.yourdomain.com {
    reverse_proxy gtm-mcp:8080
}

Deploy:

# Set environment variables
export GOOGLE_CLIENT_ID="your-client-id"
export GOOGLE_CLIENT_SECRET="your-client-secret"
export JWT_SECRET=$(openssl rand -base64 32)

# Start production stack
docker compose -f docker-compose.prod.yml up -d

Environment Variables Reference

Variable Required Description
GOOGLE_CLIENT_ID Yes OAuth client ID from Google Cloud
GOOGLE_CLIENT_SECRET Yes OAuth client secret
JWT_SECRET Yes Secret for signing session tokens (min 32 chars)
BASE_URL Yes Public URL of the server
PORT No Server port (default: 8080)
LOG_LEVEL No Logging verbosity: debug, info, warn, error

Limitations and Considerations

The AI-powered approach excels at many tasks but has boundaries:

What Works Well

What Requires Care

Custom template development: Creating sandboxed JavaScript templates (.tpl files) requires specific syntax knowledge. The AI can create templates if you provide the code, but generating complex template logic from scratch may require iteration.

Visual debugging: The AI cannot see GTM Preview mode or Tag Assistant. It can create and configure tags, but verifying that they fire correctly requires manual browser testing. The workflow is: AI creates → Human verifies in Preview → AI adjusts if needed.

Consent mode integration: Setting up consent mode correctly involves CMP-specific configurations that vary by provider (Cookiebot, OneTrust, custom). The AI can create the consent-aware triggers, but verifying that consent signals flow correctly requires browser testing with your CMP.

Complex trigger logic: Triggers with multiple conditions, regex patterns, or data layer object matching may need refinement. Start simple and iterate.

API Limits

The GTM API has quotas that affect bulk operations:

Quota Limit Impact
Requests per minute 1,200 Rarely hit in normal usage
Requests per 100 seconds per user 500 May affect large bulk operations
Write operations per container per day 500 Affects very active development

For very large containers (100+ tags), consider batching bulk operations across multiple conversations.

When to Use the GTM UI Instead


Best Practices

Start with Read Operations

Before making changes, always understand the current state:

List all tags in my workspace.
Show me the GA4 Configuration tag details.
What triggers fire the purchase event tag?

This context helps the AI make better decisions and avoids duplicating existing work.

Use Descriptive Names

When creating resources, be explicit about naming:

Create a GA4 event tag named "GA4 Event - Newsletter Signup"
with a trigger named "CE - newsletter_signup"

Consistent naming makes audits easier and helps the AI understand your conventions.

Review Before Publishing

The AI creates changes in a workspace. Before publishing:

  1. Ask for a summary: "What changes have we made in this session?"
  2. Review in GTM Preview mode
  3. Test critical tags manually
  4. Then: "Create a version named 'Added newsletter tracking' and publish it"

Iterate on Complex Configurations

For complex setups, work incrementally:

# Step 1: Create basic structure
Create a scroll depth trigger that fires at 25%, 50%, 75%, 100%.

# Step 2: Verify and adjust
Actually, make it fire only on blog posts (URL contains /blog/).

# Step 3: Connect to tags
Now create a GA4 event tag that fires on this trigger.
Send the scroll percentage as a parameter.

The Broader Pattern

MCP represents a fundamental shift in how we interact with marketing technology. Instead of learning each platform's interface, keyboard shortcuts, and menu hierarchies, we describe outcomes and let AI handle the implementation.

This won't replace analytics professionals. It removes the tedious parts—the clicking, the copy-pasting, the repetitive configuration—so you can focus on strategy, analysis, and the decisions that actually matter.

The GTM MCP server is one implementation of this pattern. The same approach applies to:

The tools we use daily are becoming conversational. The question isn't whether this will happen—it's how quickly analytics teams will adopt it.


Resources


Try It Now

Connect to https://mcp.gtmeditor.com in Claude or ChatGPT.

First test:

List my GTM accounts

Then try something practical:

Create a scroll depth trigger that fires at 25%, 50%, 75%, and 100%.
Name it "Scroll - All Thresholds".

Or audit an existing container:

Audit this container for issues.
Focus on orphaned triggers and duplicate tags.

The future of tag management is conversational. Try it.

#GTM #Google Tag Manager #MCP #Model Context Protocol #AI #Claude #ChatGPT #Automation #Analytics Engineering