Web analytics
Tandem Analytics is privacy-friendly, first-party web analytics (powered by Umami). A server-side request baseline is always on for every service; these tools add per-visitor / per-session / per-event data and let you query it — all over MCP.
Enable it
{ "name": "provision_analytics", "arguments": { "serviceId": "…" } }
provision_analytics creates a first-party analytics website, injects two runtime env vars (TANDEM_ANALYTICS_WEBSITE_ID, TANDEM_ANALYTICS_SCRIPT_URL), and returns the exact <script> tag to add to the app’s HTML <head>. It is idempotent — re-enabling repairs the env vars and returns the existing site.
Add the returned tag to your pages (or fetch it again later with get_analytics_install_snippet). Because the script is served first-party, it survives most ad-blockers. list_analytics shows a project’s analytics sites.
External sites (hosted anywhere)
Analytics does not require Tandem to host the site. provision_external_analytics creates a standalone site in a project, keyed by the site’s domain:
{ "name": "provision_external_analytics", "arguments": { "projectId": "…", "domain": "docs.example.com" } }
The returned <script> tag is the whole integration — paste it into the external site’s <head>; there is no service and no env vars. Query it with the read tools below via siteId (from list_analytics).
The domain is the site’s identity, not an ingest filter. The tag records traffic from every host it is embedded on — www.example.com and the apex need no second site. Segment by hostname with the host filter/breakdown, or restrict collection client-side with the data-domains attribute (below).
Moving an app off Tandem? convert_analytics_to_external rebinds a service’s site as external with the same website id and script host — the already-installed tag keeps working and the full history is preserved. delete_service does this automatically by default (analytics: "keep"); pass analytics: "delete" to tear the data down instead.
First-party serving on your own domain
On an external site, the assigned analytics-*.launchtandem.com script host is third-party from the visitor’s browser, so strict ad-blockers may drop it. Upgrade to first-party serving:
{ "name": "set_analytics_custom_host", "arguments": { "siteId": "…" } }
Defaults to analytics.<the site's domain> (pass host to override). If the domain’s DNS zone is hosted on Tandem (same org), the record publishes automatically; otherwise the response returns the exact A/CNAME record to add at your DNS provider. Then:
{ "name": "verify_analytics_custom_host", "arguments": { "siteId": "…" } }
Verification also happens automatically — a background sweep rechecks pending hosts every minute and activates them as soon as DNS resolves, so calling verify is just the instant-feedback path. On success the route publishes, the TLS certificate issues automatically, and the install snippet switches to the custom host — update the tag on your pages to complete the upgrade. The assigned host keeps serving forever, so tags installed with either host continue to work; remove_analytics_custom_host reverses the upgrade the same way. Propagation before verify passes: minutes for external DNS, up to ~3h for zones hosted here (secondary-nameserver lag).
Tag attributes & custom events
The tag is the Umami tracker; the attributes agents typically need:
data-domains="example.com,www.example.com"— only collect on these hostnames (recommended, keeps localhost/dev traffic out).data-auto-track="false"— disable automatic pageview/SPA-route tracking; callumami.track()yourself. By default the tracker auto-tracks history/pushState navigation, so SPAs work with no extra wiring.- Custom events: add
data-umami-event="signup"(plus optionaldata-umami-event-*properties) to any element, or callumami.track('signup', { plan: 'pro' })from JS. Events appear inget_analytics_breakdownwithtype: "event"and in theeventfilter.
Query the data
All query tools address a site by serviceId (service-bound) or siteId (any site, including external) and take a window — either range (24h/7d/30d/90d, default 7d) or explicit startTime+endTime (ISO 8601) — plus optional filters (referrer, country, device, url, …). Analytics must be enabled first.
| Tool | Answers | Notable params |
|---|---|---|
get_analytics_summary |
Headline totals — pageviews, visitors, visits, bounces (+ bounceRate, avgVisitSeconds), each vs the previous equal window. | range / startTime + endTime, filters |
get_analytics_breakdown |
“Where is traffic from / what’s popular” — top values for one axis. | type (referrer / country / region / city / url / device / browser / os / event), limit |
get_analytics_timeseries |
Trends over time — pageviews + sessions bucketed by unit. |
unit (hour/day/month), timezone |
get_analytics_realtime |
Visitors active right now (~last 5 min). | — |
get_analytics_markers |
“What changed after this deploy?” — timeline of deploys + domain/DNS changes from the audit log. | range (works with or without tags enabled) |
Pair get_analytics_timeseries with get_analytics_markers to correlate traffic shifts with deploys.
{ "name": "get_analytics_breakdown",
"arguments": { "serviceId": "…", "type": "referrer", "range": "30d", "limit": 10 } }
Disable it
{ "name": "disable_analytics", "arguments": { "serviceId": "…" } }
Removes the injected analytics env vars and deletes the analytics website and its data. Works with siteId too (external sites). The always-on server-side baseline is unaffected. To keep the data when a service goes away, use convert_analytics_to_external instead.