ga4-audit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ga4-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 Analytics 4 property — configuration and data quality. GA4 collects data the moment the tag fires, but most properties quietly leak trust: unmarked key events, PII in parameters, no Ads link, (not set) traffic. This skill finds those before they corrupt a quarter of reporting.
Requires: Cogny Agent subscription ($9/mo) — Sign up
| Job | Primary | Also answered by (enhanced when connected) |
|---|---|---|
| Property config, key events, dimensions, Ads link | GA4 | — (only GA4 has a config API) |
| Fix a missing / noisy key event (asks first, if exposed) | GA4 tool_mark_key_event, tool_unmark_key_event | GA4 UI (Admin → Events → Mark as key event) when the tools aren't exposed |
| Parameters the tags send | GTM tool_list_tags | — (ask the user for the tracking plan) |
| Consent / ad-blocker gap | GA4 users | Plausible aggregate, Fathom aggregate, Simple Analytics stats |
| Purchase capture rate | GA4 purchase count | Shopify tool_list_orders, WooCommerce get_sales_report |
Cookieless tools (Plausible, Fathom, Simple) and the store count what GA4 misses, so they turn "data quality" from guesswork into a measured gap.
If no mcp__cogny__* tools exist (no list_integrations, no cogny_status), print this and stop:
GA4 Audit needs your marketing data through Cogny's MCP gateway.
1. Sign up at https://cogny.com and connect Google Analytics 4 (Google Tag Manager optional).
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: GA4's configuration is only readable through its API.If Cogny is connected but mcp__cogny__mcp__Google_Analytics_4__tool_get_property isn't, say "connect Google Analytics 4 in Cogny" and stop.
Health check: call list_integrations, then tool_list_account_summaries as the cheap read (and GTM tool_list_accounts if GTM is connected). 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 use the step's fallback for this run and mark it ⚠ "needs reconnect".
/ga4-audit — full property audit /ga4-audit config — property + stream + link settings only /ga4-audit events — events, key events, custom dimensions only /ga4-audit data-quality — (not set), PII, anomaly checks only
tool_list_account_summaries
tool_get_property (property_id)Record: time zone, currency, industry category, and property type.
Data retention: not verifiable via API — ask the user. tool_get_property has no retention field. Ask them to check Admin → Data collection and modification → Data retention, and flag it if it's at the 2-month default (it should be 14 months so year-over-year exploration works). Mark it "unverified" in the report until they confirm.
tool_list_data_streams (property_id)Flag:
response has no enhanced-measurement data). Infer what you can from the event list in step 3 (scroll, file_download, view_search_results, video_start, form_start present or absent), and label it as inferred.
tool_list_conversion_events (property_id)
tool_run_report (property_id,
date_ranges: [{"startDate": "30daysAgo", "endDate": "yesterday"}],
dimensions: ["eventName"], metrics: ["eventCount", "keyEvents"])The report shows which events actually fire and which key events recorded anything. Step 2's enhanced-measurement inference reads from it.
Flag:
page_view, scroll) — inflates theconversion count and misleads everyone
generate_lead, purchase,sign_up)
Fix (write, asks first, if available): tool_mark_key_event (property_id, event_name, counting_method: ONCE_PER_EVENT or ONCE_PER_SESSION) for a real goal that isn't marked, tool_unmark_key_event (property_id, event_name) for a noisy one. Takes effect for new data only. Unmarking also removes it from any Ads import; say so before asking. These tools exist in the proxy but aren't exposed on every workspace. If they don't load, don't refresh in a loop: tell the user to mark/unmark the event in the GA4 UI (Admin → Events → toggle "Mark as key event") and give them the exact event name, case-sensitive.
tool_list_custom_dimensions (property_id)
tool_list_custom_metrics (property_id)
tool_get_metadata (property_id)tool_get_metadata returns ~121k characters (every standard field). Don't read it whole: when the result is saved to a file, filter it with jq, e.g. jq -r '.dimensions[] | select(.apiName | startswith("customEvent:") or startswith("customUser:")) | .apiName'.
Flag:
(the data is invisible in reports). With GTM connected, tool_list_tags on the live workspace and collect the event parameter names the GA4 event tags send; any parameter with no matching customEvent:<name> dimension is unregistered. Without GTM, ask the user for the tracking plan and say the check is partial
(other) rowstool_run_report (property_id, last 30 days,
dimensions: sessionDefaultChannelGroup, sessionSource, sessionMedium,
metrics: sessions, keyEvents, totalUsers)Use keyEvents, not the deprecated conversions metric. If run_report times out with order_bys, retry once without them and sort the rows yourself.
Flag:
Unassigned or (not set) share — broken UTMs or tagging gaps(point the user to /utm-builder)
Direct share above ~40% — usually lost referrer data or missing tagsaccounts.google.com, checkout.stripe.com, paypal.com,OAuth providers such as login.microsoftonline.com, github.com/login, appleid.apple.com) — they steal credit for the conversion from the real source; add them to the unwanted-referral list
Call list_integrations once. For each healthy source, compare the same 30 days:
| Source | Call | Compare with GA4 | Flag when |
|---|---|---|---|
| Plausible | aggregate (site_id, metrics: [visitors, visits], date_range: 30d) | totalUsers, sessions | GA4 is >30% lower: consent banner or blockers dropping data |
| Fathom Analytics | aggregate (entity: pageview, entity_id: <site id>, aggregates: visits,uniques, date_from, date_to) | same | same |
| Simple Analytics | stats (hostname, start, end) | same | same |
| Shopify | tool_list_orders (query_filter: "created_at:>=YYYY-MM-DD") | purchase event count | GA4 purchases <85% of orders, or above 100% (duplicate firing) |
| WooCommerce | get_sales_report (date_min, date_max) | purchase count, totalRevenue | same |
Report each source's number next to GA4's, named. Don't expect a match: cookieless tools count visitors differently. A gap you can measure goes in the Data quality score; deep reconciliation belongs in /conversion-debug.
Scan event and parameter names from get_metadata for anything that could carry personal data — email, phone, user_email, full names, raw addresses in parameters or in the page path / query string.
Then query the URLs themselves; names alone miss PII in query strings. This found real email addresses and magic-link tokens in a live run:
tool_run_report (property_id, last 90 days,
dimensions: ["pageLocation"], metrics: ["screenPageViews"], limit: 50,
dimension_filter: {"orGroup": {"expressions": [
{"filter": {"fieldName": "pageLocation", "stringFilter": {"matchType": "CONTAINS", "value": "email="}}},
{"filter": {"fieldName": "pageLocation", "stringFilter": {"matchType": "CONTAINS", "value": "%40"}}},
{"filter": {"fieldName": "pageLocation", "stringFilter": {"matchType": "CONTAINS", "value": "@"}}},
{"filter": {"fieldName": "pageLocation", "stringFilter": {"matchType": "CONTAINS", "value": "token"}}}]}})Any row is a finding. Redact the values when you quote them (show the path and parameter name, never the address or token).
🔴 PII in GA4 violates Google's policy and risks the property being deleted. Flag it as critical and name the exact parameter.
tool_list_google_ads_links (property_id)Flag:
means no link on this property; confirm with the user if they import via another property)
(the API exposes neither link). Ask them to check Admin → Product links. Recommend BigQuery export for any property that wants raw-data analysis (/ga4-bigquery-schema covers the schema). Mark both "unverified" in the report until confirmed
GA4 Audit — [Property Name]
GA4 Health Score: X/100
Score breakdown:
Configuration ...... X/20 (streams, retention, time zone)
Key events ......... X/25 (the reporting + bidding anchor)
Custom dimensions .. X/15
Data quality ....... X/25 ((not set), Direct, self-referrals)
Compliance ......... X/15 (PII, internal traffic)
🔴 Critical — PII collected, no key events, retention at 2 months
🟡 Important — unregistered dimensions, high (not set), no Ads link
🟢 Optimization — BigQuery export, enhanced measurement tuning
Top 3 Actions:
1. [Highest impact]
2. ...
3. ...{
"title": "Email address captured in GA4 'page_location' — policy violation",
"body": "The checkout-success URL includes ?email=… and GA4 stores it in page_location and page_path. Google policy prohibits PII in Analytics and can delete the property. Strip the parameter at the tag, or redact it before the event fires.",
"action_type": "data_quality",
"expected_outcome": "PII removed from collection; property compliant",
"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: tracking_configuration, conversion_setup, data_quality, custom_dimension, integration, compliance.
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.
(not set) %, dimension counts. Settings the API can't read(retention, enhanced measurement, BigQuery and Search Console links) are "unverified", never guessed.
tool_mark_key_event /tool_unmark_key_event, only when the workspace exposes them, and only after the user says yes to the exact event. Otherwise point to the GA4 UI.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.