内容
tm-mcp
> **克隆到新机器?** 文件夹名称作为显式的 `git clone` 目标,以便本地路径与 everywhere else 使用的约定相匹配(`~/Developer/CLAUDE.md`,MCP 注册):
>
> ```> git clone git@github.com:K9Cloud/gtm-mcp.git ~/Developer/gtm
> ```
>
> 如果没有后面的 `~/Developer/gtm`,git 默认会创建 `~/Developer/gtm-mcp/`,这与设置的其余部分不符。 <!-- stale-paths:skip -->
用于 **Google Tag Manager** 的本地 Claude Code MCP 服务器 — 列出/检查标签、触发器、变量和工作区;创建/更新/暂停/删除;创建 + 发布容器版本。用于取代历史上一系列使用的临时 Python 脚本 (`cleanup_legacy_awct_gtm.py`、`add_clarity_to_gtm.py`、`fix_clarity_tag_ids.py` 等) 进行 GTM 的临时工作。
> ⚠️ **通过 GTM 部署 Microsoft Clarity 标签?请先阅读 `~/Developer/clarity/CLAUDE.md` Gotchas。** Clarity JWT `sub声明是 API 项目 UID,**不是** `clarity.ms/tag/{id}` 中使用的标签 ID — 使用它会静默破坏HTTP 204,无错误;2026-04-20 破 6 个容器)。通过 `curl -sI https://arity.ms/tag/{id}` 验证任何标签 ID → 200 + 非空主体。
## 工具列表
### 发现
工具 | 用途 |
|------|---------|
| `list_accounts` | 列出对 OAuth 用户可见的所有 GTM 账户 |
| `list_containers` | 列出某个账户下的容器 |
| `find_container` | 解析域名(例如 `phoenixk9trainers.com`)**或** GTM 公共 ID (`GTM-MM8HPL`) → 容器 + 默认工作区路径 |
### 读取
| 工具 | 用途 |
|------|---------|
| `list_tags` | 列出工作区中的标签(可选 `type_filter`,例如 `awct`、`awcc`、`html`) |
| `get_tag` |参数和触发器引标签配置 |
| `list_triggers` | 列出工作区中的触发器 |
| `` | 列出用户(可选 `type_filter`) |
| `list_workspaces` | 列出容器中的工作区(用于检测已提交/只读状态) |
| `list_versions` | 容器的版本历史 |
### 修改(默认为 `dry_run=True`)
| 工具 | 用途 |
|------|---------|
| `pause_tag` | 暂停/标签 |
| `update_tag` | 通用标签 — 重命名,操作 ID,替换 HTML 正文,重新定位触发器 |
| `create_tag` | 创建新标签(调用者提供完整主体字典) |
| `delete_tag` | 删除标签 |
| `create_trigger` | 创建新触发器(调用者提供完整主体字典) |
| `delete_trigger` | 删除触发器 |
| `delete_variable` | 删除用户定义的变量 |
| `create_workspace`默认工作区为已提交/只读状态时创建可区 |
| `delete_workspace` | 清理过时的自动化工作区(拒绝删除默认工作区) |
| `create_version_and_publish` | 从工作区创建容器版本并发布上线 |
## 标准工作流
```
find_container("phoenixk9trainers.com")
→ list=..., type_filter="awct")
→ pause_tag(tag_path=..., dry_run=False)
→ create_version_and_publish(workspace_path=..., name="...", dry_run=False)
```
如果没有发布步骤,工作区编辑对已部署是不可见的。
提交工作区恢复
发布后,源工作区被标记为 `submitted` 并 任何 `update_tag` / `delete_tag` 操作都会返回 `400 "Workspace is already submitted."` 错误。通过创建新的工作区恢复:
```
create_workspace(domain_or_public_id="...", name="my-edit", dry_run=False)
→ 所有后续编辑都使用新的 workspace_path
```
新的从最新的已发布版本同步,因此所有现有的标签器/变量都存在且可编辑。
## OAuth 用户在该容器上缺乏编辑/批准/发布权限时,GTM 返回 `HttpError 404: "Not found or permission denied."` 错误 — 即使对同一路径的 `get_tag` 操作成功。如果读取操作成功但写入操作返回 404 错误,则向用户显示并请求访问;不要浪费重试次数。(在 Josh Wilson Team JW 2026-04-28 上看到。)
## OAuth — token 复用
此 MCP **复用现有的 `hybrid-conversions` GTM OAuth 设置**。无需新的 client_se 文件。
- **Token**: `~/Developer/hybrid-conversions_gtm_token.json`
- **Client secrets**: `~/Developer/hybrid_gtm_client_secrets.json`
- **Google Cloud 项目**: K9 Cloud Hybrid Conv Ads API(与托管 gdrive/gsc/ga4 的 `stalwart-camera-484906-k2` 项目分开)
- **Scopes**: `tagmanager.readonly`, `tagmanager.edit.containers`, `tagmanager.edit.containerversions`, `tagmanager.publish`, `tagmanager.manage.accounts`
与 `~/Developer/hybrid-conversions/` 中的旧 Python 脚本使用相同的 token。token 刷新的。
## 每机```bash
#. 克隆
cd ~/Developer && git clone git@:K9Cloud/gtm.git gtm
# 2. 虚拟环境 +
cd gtm
python3 -m venv .venvvenv/bin/pip install pip
.venv/bin install mcp google-api google-auth-oauthlib-httplib2
3. 确保 GTM OAuth token 在本地存在
ls ~/Developer/hybrid-conversions/htm_token.json
# 如果丢失,通过现有的身份验证助手进行初始化:
~/Developer-conversions/.venv/bin/python3 ~/Developer/hybrid-con_hybrid_gtm.py 4. 使用 Claude(路径使用当前机器)
claude mcp add user gtm \
HOME/Developer/gtm/bin/python3" \
"$HOME/Developer/gtm-server/server.py"
5. 验证
claude mcp get gtm
# 应该打印:状态:连接
```
## ```
gtm/
├── (此文件)
├──ignore
├── mcp-server/
│ └── server.py (MCP 服务器 — 19 个工具)
└── .venv/ (gitignored — 每台机器)
```
此处没有 `auth.py` —位于 `hybrid-conversions/auth_hybrid_gtm.py` 中(此 MCP 和旧脚本共享同一个 token)。
## 备注 **容器解析缓存账户列表 MCP 服务器生命周期内。 Code 以获取新添加的 GTM 账户。
- **所有写入工具默认为 `dry_run。设置 `dry_run 以应用。
- **变更保留在工作区中**调用 `create_version`。
- **`update_tag` 补丁是顶级覆盖,而不是深度合并** — 对于 `parameter` 等字段(),请先通过 `get_tag` 获取并提供完整## 使用说明和注意事项
- **每分钟速率限制(~60 次查询/分钟)** — 遍历所有 63 个账户以查找容器可能会遇到 `429 rateLimitExceeded` 错误。**使用稳定的 1.5 秒间隔调用,而不是指数退避。** 指数退避会在每次失败后增加等待时间,在每 60 秒的窗口重置时会。稳定的 1.5 秒/调用 ≈ 40 次调用/分钟,远低于限制;扫描在 ~96 秒内可靠完成。(-05-04 olk9twinc话中学习为 64 时。)
- **容器缓存文件** — `~/Developer/hybrid-conversions/gtm_container_cache.json` 映射 `{public_id: container_path}`,用于约 170 个容器,在 2026-05-04 的间隔扫描期间填充。在后续容器查找中,使用它跳过账户扫描:`cache = json.load(open(CACHE)); container_path = cache[target_id]`。在创建新容器后,重新运行扫描以刷新。
- **工作区在发布后自动回收** — `find_container` 返回的工作区 ID(`default_workspace_path`)**在每次 `create_version_and_publish` 后更改**(例如,工作区 `6` 在发布后变为 `7`;旧工作区在后续读取时返回 404)。始终在每个变更会话开始时重新获取工作区路径 — 绝不跨会话缓存工作区路径。
- **触发器名称不能包含冒号** (`:`)。API 以: name: The name contains invalid character: ":"` 错误拒绝。使用括号代替 — 例如 `"Click - Phone (tel)"` 而不是 `"Click - tel:"`。
- **工具可能通过 ToolSearch 延迟加载** — 会话中初始的延迟工具列表可能只显示约 13 个工具中的 19 个工具;其余工具(`create_tag`、`create_trigger`、`update_tag`、`create_workspace`、`delete_workspace`、`list_workspaces`)在 ToolSearch 查询后出现。在直接使用 Python 处理缺失的功能之前,请先运行 `ToolSearch select:mcp__gtm__<tool_name>`。
- **GA4 事件标签主体结构** (`gaawe` 类型) — 在通过 `create_tag` 或直接 API 创建标签时很有用:
```python
body = {
"name": "GA4 Event - generate_lead", "type": "gaawe",
"parameter": [
{"type": "template", "key": "measurementIdOverride", "value": "G-XXXXXXXXXX"},
{"type": "template", "key": "eventName", "value":_lead"},
{"type": "boolean", "key": "sendEcommerceData", "value": "false"},
],
"firingTriggerId": ["6"], # 现有触发器 ID
"tagFiringOption": "oncePerEvent",
}
```
Tel-click 触发器:`type=linkClick`,过滤器 `startsWith` 在 `{{Click URL `tel:` 上。需要在工作区中启用内置变量 `clickUrl- **历史动机** — 取代 `cleanup_ct_gtm.py` 等一次性脚本。激励因素是在 2026-06-01 后清理遗留的 `__awct` 表单标签和死亡的 `__awec` 变量 / `wpformsData` 宏,适用于所有容器。