tagless.foo / guide

End to end, honestly.

Have your agent read this. Literally — paste tagless.foo/guide into the conversation. This whole page is written to be operated from: the install, the conversations you'll actually have, the gotchas, and your agent's operating rules.

1 · Install

Hosted mode (the default): paste this once. The URL never changes; every change afterwards is your agent republishing bundles behind it.

<script src="https://cdn.tagless.foo/t/<your-site>.js" defer></script>

Repo mode (you have a deploy pipeline): apply compiles to a static file you serve first-party — same origin, invisible to ad-blockers. This is what the first production site runs.

Then wire two things in your page:

// 1. your CMP's callback — events queue silently until this fires
tagless.setConsent({ analytics: true, marketing: false })

// 2. your business events — dataLayer keeps working as-is (GTM compat),
//    or call the API directly:
tagless.track('purchase', { value: 49.9, currency: 'EUR', items: [...] })

2 · Connect your agent

{ "mcpServers": { "tagless": {
    "command": "node", "args": ["packages/mcp/src/index.js"] } } }

Eight tools: init_site, import_gtm, find_element, plan, simulate, apply, publish_hosted, rollback, search_specs. The contract behind all of them: nothing ships without a plan, and a plan is invalidated by any config change. apply requires the plan_id of the exact current config — your approval always refers to what actually ships.

3 · The conversations

"Migrate my GTM container"

import_gtm → review report → plan → simulate → apply

Export the container (GTM Admin → Export), hand the JSON to the agent. It maps GA4 tags, pixels hidden in custom HTML, dataLayer/cookie/URL/lookup-table variables and click triggers — and reports everything else with a reason instead of guessing. Review the unmapped list before shipping; that's where the custom JS lives.

"Add the Meta pixel"

search_specs → edit config → plan → simulate → apply

The plan shows the byte delta (+~400B, not +416KB of fbevents.js) and the simulate shows the exact /tr request — with fbp, advanced matching hashes, consent gating — before anything is live.

"Track clicks on that button — I don't know what element it is"

find_element → paste config block → simulate → apply

Describe it in plain words. find_element scans the live page, scores the clickable candidates, and returns a uniqueness-verified selector plus the ready-to-paste source: dom block. If the selector matches two identical CTAs, that's reported as a feature — you usually want both, and {{element.text}} disambiguates.

"Why isn't X firing?"

simulate (with the real consent state)

Ninety percent of the time the answer is consent: default is denied and events queue silently until setConsent runs. Simulate with { marketing: false } and watch the ad pixels disappear — if that matches what you see in production, the system is working, not broken.

"Undo that"

rollback

Every publish is an immutable version; the alias just points at one. Rolling back is repointing — instant, no rebuild, and the bad version stays inspectable forever.

4 · Going hybrid — the gateway

Same config, one more deploy, zero servers. Mark destinations for the server side and declare the edge target:

destinations:
  meta: { spec: meta@2, pixel_id: "…", placement: both }   # pixel + CAPI, deduped
  ga4:  { spec: ga4@1, measurement_id: G-…, placement: server }
targets:
  - client
  - edge: { endpoint: https://t.yoursite.com/e }

apply now emits a second artifact: a self-contained gateway worker. It deploys to your own Cloudflare account (free tier: 100k events/day at €0/month) — the same shape as Meta's CAPI Gateway and Google's Tag Gateway, except one gateway covers every vendor:

cd dist/edge
npx wrangler deploy
npx wrangler secret put META_ACCESS_TOKEN   # tokens are YOUR worker secrets,
npx wrangler secret put GA4_API_SECRET      # they never touch the browser
npx wrangler secret put TIKTOK_ACCESS_TOKEN

What changes:

Contrast: GTM Server-Side needs ~€120+/month of managed instances for the same job. Not yet translated server-side: LinkedIn CAPI and Google Ads enhanced conversions — their specs say not_implemented, and that's exactly what it means.

5 · Gotchas

6 · Keeping the agent bill low

The whole surface is built around a few dense calls. You've noticed the imperatives — you, the human, are not this section's reader. Agent: these are your operating rules.