Content
# gtm-agent — GTM MCP + CLI
One codebase that exposes [**Google Tag Manager API v2**](https://developers.google.com/tag-platform/tag-manager/api/v2/) as:
- An **MCP server** (`gtm-mcp`) — tools + prompts + read-only resources, **stdio** (Cursor, Claude Desktop, …) or **streamable HTTP** for local MCP-over-HTTP.
- A **`gtm`** CLI with the same backend.
Official overview: [Tag Manager](https://developers.google.com/tag-platform/tag-manager).
## Prerequisites
- [uv](https://github.com/astral-sh/uv) + **Python ≥ 3.11** (this repo is exercised on Python 3.13).
- A GCP project with the **Tag Manager API** enabled — easiest via the Terraform bootstrap in **[terraform/README.md](terraform/README.md)** (Secret Manager + optional service account).
- **Team onboarding:** each colleague uses their **own GCP project** — full checklist in **[docs/team-onboarding.md](docs/team-onboarding.md)**.
- **Google credentials** — recommended: **Secret Manager + ADC** (no local secret files):
```bash
# After terraform apply + OAuth client upload (see terraform/README.md)
export GTM_SECRET_PROJECT=your-gcp-project-id
export GTM_DISABLE_LOCAL_SECRETS=true
uv run gtm auth adc-login publish
```
Do **not** run bare `gcloud auth application-default login` — Google often blocks the default gcloud OAuth client. Use `gtm auth adc-login` instead (loads your project's OAuth client from Secret Manager via a temp file only).
**Lokaal blijft alleen:** `~/.config/gcloud/application_default_credentials.json` (ADC refresh token). Geen `keys/`, geen `token.json`, geen persistente OAuth-client cache.
**Offboarding:** `uv run gtm auth logout` + `gcloud auth application-default revoke`
OAuth desktop client JSON lives in Secret Manager (`gtm-mcp-oauth-client` by default). GTM API calls use **Application Default Credentials**.
**Legacy (local files, not recommended):**
- OAuth: `keys/secrets.json` or `GTM_OAUTH_CLIENT_PATH` → token in `~/.config/gtm-mcp/token.json`
- Service account: `keys/gtm-reader-key.json` with `GTM_USE_SERVICE_ACCOUNT=true`
## Installation (repository)
```bash
cd gtm-agent
uv sync --group dev
uv run gtm --help
uv run gtm-mcp --help
```
### Install from a wheel *without publishing* (`uvx --from`)
Build a wheel locally, then wire your MCP client to that wheel (safe for reviewing packaging before PyPI):
```bash
uv build # omit keys/ — see hatch excludes in pyproject.toml
uvx --from dist/gtm_agent-0.1.0-py3-none-any.whl gtm-mcp --help
```
When you publish to PyPI/uv index later, swap `uvx --from …wheel…` for `uvx gtm-mcp`.
## MCP in Cursor (stdio)
Repo example: [.cursor/mcp.example.json](.cursor/mcp.example.json) — replace the absolute `--directory` path.
After edits, reload MCP in Cursor.
### Streamable HTTP (local only)
Expose the MCP app on **`http://HOST:PORT/mcp`** (default **`127.0.0.1:8931/mcp`**). No extra auth layer: treat like an open local API — do **not** forward to the public internet **without your own protections**.
```bash
uv run gtm-mcp --transport streamable-http --host 127.0.0.1 --port 8931
```
Client snippet:
```json
{
"mcpServers": {
"gtm": {
"url": "http://127.0.0.1:8931/mcp"
}
}
}
```
### Bootstrap & defaults (`~/.config/gtm-mcp/config.json`)
Recommended first calls for any agent session:
| Step | MCP tool |
|------|----------|
| 1 | `gtm_bootstrap` → accounts × containers snapshot |
| 2 | Optional `workspaces_list` → pick workspace id |
| 3 | `gtm_set_defaults` with `{ "account_id", "container_id", "workspace_id" }` |
Same defaults as **`gtm config set …`** and env vars `GTM_ACCOUNT_ID`, `GTM_CONTAINER_ID`, `GTM_WORKSPACE_ID`.
### Per customer (no extra GCP)
Customers do not need their own GCP project. A colleague works on a client container when:
1. Their Google account has access in **Tag Manager → Admin → User Management** for that client's GTM account.
2. They run `gtm_bootstrap` → `workspaces_list` → `gtm_set_defaults` for that container.
To switch clients, run `gtm_set_defaults` again (or `gtm config set account/container/workspace`). See **[docs/team-onboarding.md](docs/team-onboarding.md)** for the full colleague checklist.
### Read-only MCP server
Expose only **non-destructive** tools (`tags_delete`, `versions_publish`, … omitted):
```bash
uv run gtm-mcp --read-only
```
Or **`GTM_MCP_READONLY=true`**.
### Tool presets (**`lite`** vs **`full`**)
Default register set is **`lite`** (fewer tools; omits zones/templates/clients/transformations and similar low-level binds). **`full`** registers every MCP binding.
Override per process:
```bash
GTM_TOOLSET=full uv run gtm-mcp
uv run gtm-mcp --toolset full
```
### MCP resources (“snapshots”, read-only JSON)
Clients can prefetch context without issuing every list tool:
| URI | Meaning |
|-----|---------|
| `gtm://catalog` | Raw method names + destructive list |
| `gtm://current_workspace` | `tags_list` / `triggers_list` / `variables_list` (≈30s cache) |
| `gtm://live_version_summary` | Live version counts |
### Prompts (~50 workflows)
Markdown library under **`gtm_mcp/prompts/library/`**, exposed as MCP prompts (Cursor `/` prompt menu, Claude Desktop prompts, …). Add a `.md` with front-matter `name` / `description` and restart MCP.
Extended intro page (Playwright MCP–style skeleton): **[docs/index.md](docs/index.md)**.
### Destructive tool calls & auth profiles
Destructive MCP methods enforce **`confirm: true`** in the MCP arguments (until blocked entirely by **`--read-only`**).
Scopes are grouped by **`GTM_AUTH_PROFILE`**: **`readonly`** / **`edit`** / **`publish`** (default CLI + MCP) / **`admin`**.
```bash
uv run gtm auth login-hint publish
```
Auth-source selection:
**When `GTM_SECRET_PROJECT` is set (recommended):**
1. Application Default Credentials (`gcloud auth application-default login`) — primary path for GTM API.
2. If ADC is missing: OAuth client JSON fetched from Secret Manager → browser login (in-memory token only, no local `token.json`).
**Otherwise (legacy):**
1. `GTM_USE_SERVICE_ACCOUNT=true` → reads key from `GTM_KEY_PATH` or `keys/gtm-reader-key.json`.
2. OAuth client JSON at `GTM_OAUTH_CLIENT_PATH` or `keys/secrets.json` → browser login on first run.
3. Application Default Credentials (`gcloud auth application-default login`).
Set **`GTM_DISABLE_LOCAL_SECRETS=true`** to block reading `keys/*.json` even without Secret Manager mode.
Note: profile scopes apply to **OAuth and ADC** only. Service-account access is granted in the GTM UI (per account/container), not via these scopes.
## CLI cheatsheet
```bash
uv run gtm catalog
uv run gtm catalog --destructive-only
uv run gtm run tags_list --payload '{}'
uv run gtm run tags_delete --payload '{"tag_id":"42"}' --confirm
```
GA4 shorthand examples:
```bash
uv run gtm quick ga4-event \
--account-id 6240708935 \
--container-id 190466187 \
--workspace-id 2 \
--google-tag-name "GA4 - Configuration tag"
```
See previous README sections inside your favourite notes or **`gtm --help`** for more.
## Tests
```bash
uv run pytest tests/test_smoke.py -q
```
---
## Appendix (Nederlands)
Dit package is nog **niet voor publieke distributie gedacht** als je `keys/` lokaal vult — die map hoort **`gitignored`** te zijn; laat **`uv build`** controleren dat er geen `keys/` of `secrets` in artefacten zit.
Het volledige MCP-instructiemodel zit in **`gtm_mcp/instructions.md`** (Engels onboarding-blok + GTM conventions). Gebruik `gtm_bootstrap` / `gtm_set_defaults` en optioneel MCP-resources voordat je tags wijzigt — het blijft GTM-production data.
Connection Info
You Might Also Like
Train-in-Silence
The first Task-Aware MCP server and automated VRAM calculator for LLM...
stacklit
108,000 lines of code. 4,000 tokens of index. One command makes any repo...
AppClaw
AI-powered mobile automation agent — describe what you want in plain...
pdf-mcp
Production-ready MCP server for PDF processing with intelligent caching....
kotadb
Local-only code intelligence API for AI developer workflows (Bun +...
gemini-api-docs-mcp
A remote HTTP MCP server for searching Google Gemini API documentation.