GA4 Manager
A Go CLI and MCP server that configures GA4 and watches Search Console, from a YAML file or from your assistant
Case study
## The problem
Every new project meant repeating the same ritual in the Google Analytics 4 interface: creating conversions, dimensions and metrics, then setting retention and enhanced measurement. GA4 cannot export a property's configuration, so there was no way to copy it to the next one.
A second problem showed up later: the SEO of my own sites. Search Console has the data, but it does not tell you what to look at first, and it does not automate without a pile of loose scripts.
## Key decisions
Configuration lives in a YAML file
A GA4 property is described in a file that is versioned next to the code, and one command applies it. The --dry-run flag, which previews the changes before anything is touched, was already in the first stable release (1.0.0, 22 November 2025).
Admin API v1alpha, knowingly
Custom dimensions, custom metrics and channel groups only exist in v1alpha. Google can change it without a deprecation window, so I wrote it down as an architecture decision, with the duty to watch its release notes.
Least privilege, split by operation
Writes (setup, cleanup, link) ask for the Analytics edit scope; reports and validation are read-only. A read-only account cannot run anything destructive by mistake.
A tool registry for the MCP server
It started as one if/else branch per tool. As the list grew, each branch repeated validation and error handling, so I replaced it with a registry: every tool is one entry with its schema and its execution, and they all go through the same middleware.
Deterministic diagnostics with local state
The Search Console diagnostics are API queries, not a model giving opinions: I decide what to change. To compare each run with the previous one they keep a JSON file next to the config, schema-versioned and out of git. I rejected the home directory because it breaks the project-with-its-own-config model.
## What went wrong
The MCP swallowed the results (2.3.0, 5 June 2026)
The server layer treated any non-zero exit code as a failure. But the diagnostic commands exit with 2 when they find something, which is exactly what they are for. The assistant received only the error and none of the findings. Exit code 2 now counts as success.
A report Google no longer produces (2.4.0, 15 June 2026)
The tool flagged mobile usability as a failure. Google retired that report in December 2023 and the field came back empty, which read as a fail. Since 2.4.0 it only trusts a real verdict and shows anything else as "n/a".
An empty list that was not an SEO problem
The sitemap listing returned an empty list for a sitemap that had been submitted. Before resubmitting I checked it with a different call and there it was: the bug was in my tool.
A timeout at 40 URLs (3.4.0, October 2026)
The MCP call hit its 30-second limit inspecting 40 URLs and I had to run the CLI directly. The fix was to inspect five at a time and let the MCP wait up to five minutes.
My own documents disagreed
The README, the MCP server's README and my blog gave different counts of available tools. The ADR already said the count should be derived from the registry, and I did not do it. That is why there is no count here.
## What I would do differently
- I would generate the tool count and the README table from the registry instead of typing them.
- I would treat the CLI's exit codes as part of the contract with the MCP from day one, not as a detail you find out when it breaks.
- I would test the MCP with a real URL list from a mid-sized site before releasing it: the timeout only showed up at 40 URLs.
- I would start with the Search Console diagnostics and leave the GA4 configuration for later: it is what changed how I work the most.
## Figures, only measured and dated
- 54 s
- to inspect 38 URLs with v3.4.0, against about four and a half minutes before the fix · post updated 6 October 2026
- 6,012 impressions, 20 clicks
- on my blog over 28 days (0.33% CTR, average position 7.6), measured with the tool itself. These are my site's numbers, not GA4 Manager usage · 6 September to 3 October 2026
I have no adoption figures (installs, stars, downloads) and I do not make them up.
Stack and links
- Go
- TypeScript
- MCP
- YAML
- GA4 Admin API
- Search Console API
- MIT licence
GA4 Manager on the blog
- I Automated GA4 with YAML. Now It Flags What to Check in SEO
A CLI + MCP server that configures GA4 and Search Console from YAML, and flags what to check in your SEO by impact. Deterministic: you make the calls.