GTM MCP Server
by laiskickow
README.md
# GTM MCP Server
A local MCP server that lets any MCP-compatible AI assistant — Claude Code, GitHub Copilot, Cursor, Windsurf, or VS Code — read and manage your Google Tag Manager containers, tags, triggers, variables, and more through the GTM API v2.
Everything runs on your machine. No data leaves your laptop except direct Google API calls authenticated with your own credentials.
---
## Quick start
### Prerequisites
- **Node.js 18+** — check with `node -v`
- **npm** — comes with Node
- **A Google Cloud project** with the Tag Manager API enabled (see Step 1 below)
### Step 1 — Create Google Cloud OAuth credentials
You only need to do this once. Everyone on the team can use the same GCP project, or create their own.
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Create a new project (or pick an existing one)
3. **Enable the Tag Manager API:**
- Go to **APIs & Services > Library**
- Search for **"Tag Manager API"**
- Click **Enable**
4. **Create OAuth credentials:**
- Go to **APIs & Services > Credentials**
- Click **+ Create Credentials > OAuth client ID**
- If prompted, configure the OAuth consent screen first:
- User type: **Internal** (if using a Google Workspace org) or **External**
- Fill in the required fields (app name, support email)
- Add scopes: search for "Tag Manager" and add all Tag Manager scopes
- Add yourself as a test user (if External)
- Back in Credentials, create the OAuth client:
- Application type: **Desktop app**
- Name: anything (e.g. "GTM MCP Server")
- Copy the **Client ID** and **Client Secret** — you'll need them next
### Step 2 — Clone and install
```bash
git clone https://github.com/laiskickow/gtm-mcp-server.git
cd gtm-mcp-server
npm install
npm run build
```
### Step 3 — Get your refresh token
The included helper script opens a browser auth flow and gives you a refresh token.
```bash
GTM_CLIENT_ID="your-client-id" GTM_CLIENT_SECRET="your-client-secret" npm run get-token
```
This will:
1. Print a URL — open it in your browser
2. Sign in with your Google account and approve access
3. Print your **refresh token** in the terminal — copy it
### Step 4 — Create your `.env` file
```bash
cp .env.example .env
```
Edit `.env` and paste your values:
```
GTM_CLIENT_ID=your-client-id.apps.googleusercontent.com
GTM_CLIENT_SECRET=GOCSPX-your-secret
GTM_REFRESH_TOKEN=1//your-refresh-token
```
> **The `.env` file is gitignored** — your credentials stay local and are never committed.
### Step 5 — Connect to your AI tool
Pick the tool you use. In every example below, replace `/full/path/to` with the actual path where you cloned the repo (e.g. `/Users/yourname`).
#### Claude Code
Add to your **project** `.mcp.json` (in your working directory) or **user-level** `~/.claude/.mcp.json`:
```json
{
"mcpServers": {
"gtm": {
"command": "node",
"args": ["/full/path/to/gtm-mcp-server/dist/index.js"],
"env": {
"GTM_CLIENT_ID": "your-client-id",
"GTM_CLIENT_SECRET": "your-client-secret",
"GTM_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}
```
Or use the `start.sh` wrapper which reads from `.env` automatically:
```json
{
"mcpServers": {
"gtm": {
"command": "/full/path/to/gtm-mcp-server/start.sh"
}
}
}
```
#### GitHub Copilot (VS Code)
Add to your **workspace** `.vscode/mcp.json` or **user** settings (`Cmd+Shift+P` > "Preferences: Open User Settings (JSON)"):
```json
{
"mcp": {
"servers": {
"gtm": {
"type": "stdio",
"command": "node",
"args": ["/full/path/to/gtm-mcp-server/dist/index.js"],
"env": {
"GTM_CLIENT_ID": "your-client-id",
"GTM_CLIENT_SECRET": "your-client-secret",
"GTM_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}
}
```
> Enable MCP in Copilot: go to **Settings > GitHub Copilot > MCP** and tick **"Enable MCP"** if it isn't already on.
#### Cursor
Add to **Settings > MCP Servers > Add Server**, or edit `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"gtm": {
"command": "node",
"args": ["/full/path/to/gtm-mcp-server/dist/index.js"],
"env": {
"GTM_CLIENT_ID": "your-client-id",
"GTM_CLIENT_SECRET": "your-client-secret",
"GTM_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}
```
#### Windsurf
Add to `~/.codeium/windsurf/mcp_config.json`:
```json
{
"mcpServers": {
"gtm": {
"command": "node",
"args": ["/full/path/to/gtm-mcp-server/dist/index.js"],
"env": {
"GTM_CLIENT_ID": "your-client-id",
"GTM_CLIENT_SECRET": "your-client-secret",
"GTM_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}
```
### Step 6 — Verify it works
Restart your AI tool (in Claude Code run `/mcp` to refresh servers). Then ask:
> "List my GTM accounts"
You should see your GTM accounts listed. Done!
---
## Alternative: Service Account auth
If you prefer a service account (e.g. for a shared container):
1. In Google Cloud Console, go to **IAM & Admin > Service Accounts**
2. Create a service account, download the JSON key
3. Grant the service account access in GTM (Admin > User Management)
4. Set in `.env`:
```
GTM_SERVICE_ACCOUNT_KEY=/path/to/service-account-key.json
```
---
## Available tools
Once connected, your AI assistant has access to all GTM API v2 resources:
| Resource | Actions |
|----------|---------|
| Accounts | list, get, update |
| Containers | list, get, create, update, delete, get snippet |
| Workspaces | list, get, create, update, delete, sync, quick preview, create version, get status |
| Tags | list, get, create, update, delete, revert |
| Triggers | list, get, create, update, delete, revert |
| Variables | list, get, create, update, delete, revert |
| Built-in Variables | list, create, delete, revert |
| Folders | list, get, create, update, delete, move entities |
| Environments | list, get, create, update, delete |
| Versions | get, publish, list headers, set latest, delete, undelete |
| Templates | list, get, create, update, delete, revert |
| User Permissions | list, get, create, update, delete |
| Zones | list, get, create, update, delete, revert |
| Clients (server-side) | list, get, create, update, delete, revert |
| Destinations | list, get, link, unlink |
| Transformations | list, get, create, update, delete, revert |
| Gtag Configs | list, get, create, update, delete |
Plus convention and preset tools:
- `gtm_naming_guide` — show the naming standard
- `gtm_validate_name` — check a tag name
- `gtm_build_tag_name` / `gtm_build_trigger_name` / `gtm_build_variable_name` — generate compliant names
- `gtm_list_presets` / `gtm_preview_preset` / `gtm_deploy_preset` — reusable tracking bundles
---
## Tag naming convention
All tags, triggers, and variables follow a shared naming pattern:
**Tags:** `[app]_[category]_[action]_[detail]`
- e.g. `mcui_data_create_rule-group`, `golf_action_click_kill-switch`
**Triggers:** `trigger_[app]_[event]_[detail?]`
- e.g. `trigger_golf_click_odds-cell`
**Variables:** `[scope]_[type]_[name]`
- e.g. `user_dl_role`, `app_const_version`
Registered apps: `golf`, `mcui`, `bopui`, `ivory`, `shared`. Add yours in `src/conventions.ts`.
---
## Tracking presets
Reusable bundles you can deploy to any workspace. Preview before deploying with `gtm_preview_preset`.
| Preset | Creates |
|--------|---------|
| `app-bootstrap` | DataLayer init, page view trigger, core variables |
| `error-tracking` | Error listener, error event trigger, error variables |
| `user-action-tracking` | User action trigger, category/name/target variables |
| `api-performance` | API call trigger, endpoint/duration/status variables |
| `crud-operations` | Data operation trigger, entity type/count variables |
| `workflow-tracking` | Workflow step trigger, step name/index/status variables |
---
## Troubleshooting
| Problem | Fix |
|---------|-----|
| "No valid authentication configuration found" | Check your `.env` has all three OAuth values (or the service account path) |
| "invalid_grant" or "Token has been revoked" | Re-run `npm run get-token` to get a fresh refresh token |
| Server not showing up | Check the path in your MCP config is **absolute** and points to `dist/index.js` |
| "Cannot find module" errors | Run `npm run build` — the `dist/` folder is not committed |
| Google consent screen says "unverified" | Normal for internal/test apps — click "Advanced" > "Go to (app name)" |
| Copilot doesn't show MCP tools | Enable MCP in VS Code: Settings > GitHub Copilot > MCP > tick "Enable MCP" |
| Cursor/Windsurf not connecting | Restart the editor after saving the MCP config file |
---
## Project structure
```
gtm-mcp-server/
├── src/
│ ├── index.ts # MCP server entry point
│ ├── auth.ts # Google auth (OAuth2 / service account)
│ ├── gtm-client.ts # GTM API client wrapper
│ ├── conventions.ts # Naming conventions + validation
│ └── tools/ # One file per GTM resource
├── get-token.ts # OAuth refresh token helper
├── start.sh # Wrapper that loads .env
├── .env.example # Template for credentials
├── package.json
└── tsconfig.json
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues