Skip to content

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

  1. OAuth 2.1 inbound — same auth contract as the V5 gateway: bearer token in Authorization header, hashed lookup in KV (tenant_auth:{sha256}), per-tenant isolation.
  2. Google OAuth outbound — Worker holds its own GCP OAuth client (separate from the existing googlesuper client to keep the GTM consent screen distinct and the sensitive-scope verification cycle independent). Refresh tokens stored in KV at tokens:{tenant}:gtm:{account_id} (this Worker’s own namespace — not the V5 gateway’s KV).
  3. MCP surface — Streamable HTTP MCP per spec 2025-11-25. Tools follow gtm_* naming.
  4. GTM v2 coverage — accounts, containers, workspaces, tags, triggers, variables, versions, publish. Full audit + edit + publish surface.
  5. Multi-tenant routinguser_id={slug} selects the right tokens:{slug}:gtm:{account_id} entry. Same pattern as Composio.

Capability set (initial)

  • gtm_list_accounts · gtm_list_containers · gtm_list_workspaces
  • gtm_list_tags · gtm_get_tag · gtm_create_tag · gtm_update_tag · gtm_delete_tag
  • gtm_list_triggers · gtm_get_trigger · gtm_create_trigger · gtm_update_trigger · gtm_delete_trigger
  • gtm_list_variables · gtm_get_variable · gtm_create_variable · gtm_update_variable · gtm_delete_variable
  • gtm_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.readonly
  • https://www.googleapis.com/auth/tagmanager.edit.containers
  • https://www.googleapis.com/auth/tagmanager.edit.containerversions
  • https://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 AbortController on every outbound fetch
  • Every admin endpoint gated by Cloudflare Access
  • Secrets via wrangler secret put; client_id/client_secret mirrored in Infisical at composio/oauth-apps/googletagmanager (the registry path stays consistent even though this isn’t going through Composio)

Explicitly rejected alternatives

AlternativeRejection reason
Path A — PENDING-NATIVE onlyClient 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 SDKNot callable from n8n, autonomous agents, or future sessions. Runbook explicitly flags as non-production.
Adding GTM to V5 gatewayV5 is frozen (ADR-057). No new tools.
Composio Custom Auth Config for new toolkitComposio’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:googletagmanagerWould 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:

  1. 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.
  2. W2 — Full tool surface + capability_registry.yaml: all 21 tools, MCP server registration in 5 IDE configs, ADR-042 capability index entries.
  3. W3 — Multi-tenant onboarding + docs: per-client OAuth link flow, docs/composio/manual-googletagmanager.md (discovery recipe), update toolkit-registry.md row 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-mcp to 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.