Google Tag Manager CF Worker (gtm-mcp)
ADR-060 — Google Tag Manager CF Worker (gtm-mcp)
Status: Superseded by ADR-061 Date: 2026-05-19 Author: Mishaal Murawala Supersedes: none Superseded by: ADR-061 (Pipedream Connect as sanctioned gap-fill OAuth vendor — 2026-05-19, same day)
NOTE: This ADR was superseded the same day it was written. The underlying need (GTM access for client audits) is unchanged, but the implementation pivoted from building a dedicated CF Worker to using Pipedream Connect as a sanctioned gap-fill OAuth vendor. Pipedream’s $99/mo Connect tier covers GTM plus 2,700+ other apps via Streamable HTTP MCP, eliminating per-toolkit Worker builds. See ADR-061 for the canonical decision. INVARIANTS-UNCHANGED — this ADR does not modify any of the 15 V5 gateway invariants. It adds a new sibling CF Worker; the V5 gateway is untouched and remains frozen per ADR-057.
Context
Ascend needs Google Tag Manager (GTM) access to audit client containers — verify pixels fire, inspect tags/triggers/variables, make edits, version workspaces, and publish updates. Both Mishaal and autonomous agents must be able to call GTM across all client tenants.
Composio’s catalog does not include googletagmanager (verified 2026-05-19: GET backend.composio.dev/api/v3/tools?search=tagmanager returns empty across multiple keyword variants). This is the “missing toolkit” case in ADR-058 Path 2.
Per ADR-058 + docs/composio/missing-toolkit-runbook.md, the only three resolution paths for missing toolkits are:
- Path A — PENDING-NATIVE: wait for Composio to ship native support. Acceptable for low-criticality providers with a ≤14-day tolerance.
- Path B — SESSION-ONLY SDK:
composio.experimental.Toolkit(). Explicitly NOT for production — not callable from n8n, other agents, or future sessions. - Path C — Dedicated CF Worker: thin CF Worker implementing the provider’s API surface as a Streamable HTTP MCP server. The only path that gives persistent, cross-session, production-grade access.
GTM is critical-path for the client audit use case (multiple clients, autonomous agents, production reliability required). Paths A and B are insufficient.
Decision
Build gtm-mcp — a dedicated Cloudflare Worker exposing Google Tag Manager v2 as a Streamable HTTP MCP server. Owned by this repo (mishaal-cloud/ascend-gtm-ops), deployed via the standard wrangler deploy pipeline.
Worker responsibilities
- OAuth 2.1 inbound — same auth contract as the V5 gateway: bearer token in
Authorizationheader, hashed lookup in KV (tenant_auth:{sha256}), per-tenant isolation. - Google OAuth outbound — Worker holds its own GCP OAuth client (separate from the existing
googlesuperclient to keep the GTM consent screen distinct and the sensitive-scope verification cycle independent). Refresh tokens stored in KV attokens:{tenant}:gtm:{account_id}(this Worker’s own namespace — not the V5 gateway’s KV). - MCP surface — Streamable HTTP MCP per spec
2025-11-25. Tools followgtm_*naming. - GTM v2 coverage — accounts, containers, workspaces, tags, triggers, variables, versions, publish. Full audit + edit + publish surface.
- Multi-tenant routing —
user_id={slug}selects the righttokens:{slug}:gtm:{account_id}entry. Same pattern as Composio.
Capability set (initial)
gtm_list_accounts·gtm_list_containers·gtm_list_workspacesgtm_list_tags·gtm_get_tag·gtm_create_tag·gtm_update_tag·gtm_delete_taggtm_list_triggers·gtm_get_trigger·gtm_create_trigger·gtm_update_trigger·gtm_delete_triggergtm_list_variables·gtm_get_variable·gtm_create_variable·gtm_update_variable·gtm_delete_variablegtm_create_version·gtm_publish_version·gtm_get_container_status
Each tool registers as a capability bucket in docs/tools/composio-googletagmanager/capability_registry.yaml (post-deploy) so discover_apis({query: "gtm tag"}) finds them via Vectorize.
OAuth scopes
https://www.googleapis.com/auth/tagmanager.readonlyhttps://www.googleapis.com/auth/tagmanager.edit.containershttps://www.googleapis.com/auth/tagmanager.edit.containerversionshttps://www.googleapis.com/auth/tagmanager.publish
tagmanager.edit.* + publish are sensitive scopes. Google may require app verification before non-internal users can connect — acceptable for Ascend internal use immediately; client-facing onboarding may need the verification cycle.
Constraints inherited from V5 invariants
- KV-only hot path (no D1 reads in request path)
- Fail-fast (no retries; callers retry)
- 30s
AbortControlleron every outboundfetch - Every admin endpoint gated by Cloudflare Access
- Secrets via
wrangler secret put; client_id/client_secret mirrored in Infisical atcomposio/oauth-apps/googletagmanager(the registry path stays consistent even though this isn’t going through Composio)
Explicitly rejected alternatives
| Alternative | Rejection reason |
|---|---|
| Path A — PENDING-NATIVE only | Client audit use case is live; cannot wait. Acceptable as a parallel hedge (file GH issue) but not as the sole path. |
| Path B — SESSION-ONLY SDK | Not callable from n8n, autonomous agents, or future sessions. Runbook explicitly flags as non-production. |
| Adding GTM to V5 gateway | V5 is frozen (ADR-057). No new tools. |
| Composio Custom Auth Config for new toolkit | Composio’s “Custom Auth Config” is BYO OAuth for EXISTING toolkits (ADR-058 Path 1). It does not register new toolkits. Verified against current Composio v3 docs 2026-05-19. |
V5 call_api shim with api_config:googletagmanager | Would still require V5 to hold the OAuth refresh token, violating invariant #5 (V5 KV holds only AWS + Anthropic post-cutover). |
Implementation plan
See docs/plans/gtm-cf-worker.md. Three windows:
- W1 — Scaffold + OAuth round-trip: new Worker, GCP OAuth client, Composio-style
tokens:{tenant}:gtm:{account_id}KV, read-only smoke test against Ascend’s own GTM. - W2 — Full tool surface + capability_registry.yaml: all 21 tools, MCP server registration in 5 IDE configs, ADR-042 capability index entries.
- W3 — Multi-tenant onboarding + docs: per-client OAuth link flow,
docs/composio/manual-googletagmanager.md(discovery recipe), updatetoolkit-registry.mdrow from PROPOSED → CF-WORKER (live).
Consequences
- A new production Worker = new ADR, new deploy pipeline, ongoing maintenance, 5 IDE MCP config updates.
- Establishes a template for any future “missing toolkit” Path C build (e.g., the QBO Reports API case in LEDGER tech-debt).
- GTM tokens live in this Worker’s KV, not the V5 gateway’s. The two Workers do not share state — each owns its own tenant_auth and tokens namespaces.
- Adds
gtm-mcpto the standing weekly ops surface: KV backup, secret rotation tracking, CF Access policy review. - Filing a Composio GitHub feature request in parallel (Path A as a hedge) — if Composio ships native GTM support during W1/W2/W3, we can pause the Worker build and migrate.