gtm-audit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited gtm-audit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A one-time audit of a Google Tag Manager container. GTM rarely fails loudly — it fails as a duplicate GA4 tag double-counting conversions, a tag with no trigger that never fires, or analytics tags firing before consent. This skill reads the container and finds those.
Requires: Cogny Agent subscription ($9/mo) — Sign up
| Job | Primary | Enhanced when connected |
|---|---|---|
| Container inventory, triggers, variables | GTM | — (GTM is the only tag manager in Cogny) |
| Consent / privacy scan | GTM tool_audit_container_privacy | — |
| Are the Measurement IDs real? | GTM tag config | GA4 tool_list_data_streams |
| Are pixel / conversion IDs current? | GTM tag config | Google Ads, Meta, TikTok, LinkedIn, Reddit, Snapchat, X (Step 6b) |
| Do event names match key events / Ads imports? | GTM tag config | GA4 tool_list_conversion_events, Google Ads conversion_action (Step 6c) |
| Double sending via a CDP | — | Segment list_destinations |
If no mcp__cogny__* tools exist (no list_integrations, no cogny_status), print this and stop:
GTM Audit needs your marketing data through Cogny's MCP gateway.
1. Sign up at https://cogny.com and connect Google Tag Manager (GA4 and your ad platforms
optional, to verify IDs and event names).
2. Add Cogny to .mcp.json in your project:
{"mcpServers": {"cogny": {"type": "http", "url": "https://app.cogny.com/mcp"}}}
3. Restart Claude Code; it opens a browser for the OAuth login on first use.
Nothing to run without Cogny: the container is only readable through the GTM API.If Cogny is connected but mcp__cogny__mcp__Google_Tag_Manager__tool_list_containers isn't, say "connect Google Tag Manager in Cogny" and stop.
Health check: call list_integrations, then tool_list_accounts as the cheap read (and one cheap read per platform used in Step 6b, e.g. tool_list_ad_accounts). health_status misses revoked tokens, so a 401, Forbidden or scope error is 🔴 "reconnect <integration>"; don't retry it. If a tool named here is missing, call refresh_mcp_discovery once; new tools only load after the client reconnects, so mark that check ⚠ "needs reconnect" for this run.
/gtm-audit — full container audit /gtm-audit tags — tag health only /gtm-audit triggers — trigger coverage only /gtm-audit consent — Consent Mode wiring only
tool_list_accounts
tool_list_containers (account_id)If multiple containers exist, ask which to audit. Note the container type (web vs server) — this audit targets web containers. Then tool_list_workspaces (account_id, container_id) for the workspace_id every later call needs (use the Default Workspace unless the user names another).
tool_list_tags (account_id, container_id, workspace_id)For tags that need detail, call tool_get_tag. Bucket every tag by type (GA4 config, GA4 event, Google Ads conversion, Google Ads remarketing, Floodlight, custom HTML, third-party).
Flag:
double-counting bug
tool_list_triggers (account_id, container_id, workspace_id)Cross-reference every tag's firingTriggerId against the trigger list.
Flag:
tool_list_variables (account_id, container_id, workspace_id)Flag:
tool_audit_container_privacy (account_id, container_id, workspace_id)Start from its list of tags without consent triggers, third-party trackers and cookie variables, then inspect tags and the container setup for Consent Mode wiring.
The privacy tool only counts GA4 tag types, so it misses pixels pasted into Custom HTML. Also:
type: html; the code is in the html parameter, sotool_get_tag if tool_list_tags didn't return it) for fbq( (Meta), twq( (X), ttq. (TikTok), lintrk( / _linkedin_partner_id (LinkedIn). Count each as a third-party tracker.
consentSettings.consentStatus: needed (with the required consent types)is wired, notNeeded is an explicit opt-out, and notSet / missing means no additional consent check. A marketing pixel with notSet and an All Pages trigger is a 🔴 consent finding.
Flag:
in the EEA and UK
denied state) before tags loadad_storage / analytics_storage / ad_user_data / ad_personalization not allhandled
For each GA4 event tag (tool_get_tag):
Flag:
(cross-reference the /ga4-events conventions)
ecommerce object from the data layervalue, currency, transaction_id)Call list_integrations once. For each healthy platform, read the IDs it actually owns and compare with the IDs hard-coded in GTM tags:
| Platform | Read the IDs | Matches the GTM tag's |
|---|---|---|
| GA4 | tool_list_account_summaries → tool_list_data_streams (property_id) | Measurement ID (G-…) |
| Google Ads | tool_list_accessible_accounts → customer_id (digits only; ask if several) → tool_execute_gaql (customer_id): SELECT conversion_action.name, conversion_action.type, conversion_action.status, conversion_action.tag_snippets FROM conversion_action | Conversion ID + label |
| Meta | tool_list_ad_accounts → tool_get_pixels (ad_account_id) | Pixel ID |
| TikTok | tool_list_advertisers → tool_get_pixels (advertiser_id) | Pixel code |
tool_list_ad_accounts → tool_get_conversions (ad_account_id) | Partner ID / conversion IDs | |
tool_list_ad_accounts → tool_get_conversion_events (ad_account_id) | Pixel ID | |
| Snapchat | tool_list_ad_accounts → tool_list_pixels (ad_account_id) | Pixel ID |
| X | tool_list_ad_accounts → tool_get_web_event_tags (account_id) | Pixel / event tag IDs |
Flag:
client's account): data goes nowhere or somewhere wrong. Exception, GA4: a Measurement ID missing from tool_list_data_streams is 🟡 "not listed or not accessible", not 🔴. The connected Google login may simply lack access to that property; ask the user to confirm it
ENABLED) with no GTM tag behind it, unless it isimported from GA4 or sent server-side. Google Ads actions whose conversion_action.type starts with GOOGLE_ANALYTICS_4_ are GA4 imports: they never have an AW- tag, so don't flag them "no AW- tag" (check them in Step 6c instead)
list_destinations shows a GA4 or ad-platform destination and GTM fires thesame tag: likely double counting. Ask which path is meant to be live.
Platforms that aren't connected, or whose read returned any API error (401, scope, quota, GAQL error): say "IDs not verified" for their tags. An error is never evidence that an ID is wrong.
Collect the event name each GA4 event tag sends (eventName parameter). When eventName is {{Event}} (the built-in variable that forwards the dataLayer event), the tag's own config holds no name: take the names from its firing triggers instead (firingTriggerId → tool_get_trigger, type customEvent, the arg1 value of each condition in customEventFilter, e.g. FirstLogin; with a regex match, list each alternative). Skipping this misses the first_login vs FirstLogin mismatch below. Compare each name, exactly and case-sensitively, with:
tool_list_conversion_events (property_id): every key event's eventNametype GOOGLE_ANALYTICS_4_CUSTOM /GOOGLE_ANALYTICS_4_PURCHASE (the import name ends in the GA4 event name)
Flag:
ignored (e.g. GTM sends first_login, GA4 key event is FirstLogin). GA4 event names are case-sensitive, so the key event never fires and the Ads import stays at zero. This was the root cause of dead key events in a live run.
enhanced-measurement event, or sent from code / server-side; ask)
tool_get_workspace_status (account_id, container_id, workspace_id)Flag:
GA4 - Event - purchase style) — unmaintainable at scaleGTM Audit — [Container Name]
Container Health Score: X/100
Score breakdown:
Tag health ......... X/25 (duplicates, orphans, paused)
Trigger coverage ... X/20
GA4 wiring ......... X/20 (incl. key-event name matches)
Consent Mode ....... X/20
Hygiene ............ X/15 (naming, variables, workspace)
Inventory: X tags · X triggers · X variables · Unpublished changes: [yes/no]
🔴 Critical — duplicate GA4 config, tags firing before consent
🟡 Important — orphaned tags, bad event names, missing parameters
🟢 Optimization — naming, unused variables, custom HTML to native
Top 3 Actions:
1. [Highest impact]
2. ...
3. ...{
"title": "Two GA4 configuration tags for G-XXXX — every page view counted twice",
"body": "'GA4 - Config' and 'GA4 Base (old)' both load Measurement ID G-XXXX on All Pages. GA4 is counting every page_view and session twice, inflating traffic and deflating conversion rate. Pause the older tag and republish.",
"action_type": "tag_fix",
"expected_outcome": "Accurate GA4 traffic and conversion-rate numbers",
"estimated_impact_usd": 0,
"priority": "critical"
}On Cloud, create_ticket takes the same content with the fields mapped per section 2 of the routing guide.
Action types: tag_fix, trigger_fix, consent_configuration, container_hygiene, ga4_wiring.
Use create_ticket when the workspace has it (Cloud) and create_finding otherwise (Solo). The fields differ; see section 2 of ../marketing-engineer/references/mcp-routing.md.
corrupt data and create compliance exposure.
go through /gtm-setup, where every write asks first.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.