@analitikisler/ai-tag-debugger 0.7.9 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1 (2026-10-15)
4
+
5
+ - A run with no AI provider key set now stops at once with a message naming the keys to set and the `--scripted --no-ai` option, instead of starting the browser and failing at the first step with the SDK's "Could not resolve authentication method".
6
+ - Over MCP, `run_audit` with `scripted: false` and no key returns an error that tells the chat app what to do next: call it again with `scripted: true`, or browse with `start_audit`. It doesn't mention CLI flags.
7
+ - The README points to `ai-tag-debugger --help` for the full list of commands and options.
8
+ - `npm run demo` replays the demo shop with no AI model (`--no-ai`), so it works without an API key.
9
+
10
+ ## 0.8.0 (2026-10-14)
11
+
12
+ - The browser installs itself the first time an audit starts, so connecting the MCP server no longer needs a separate `playwright install` step. Progress goes to stderr.
13
+ - Listed in the official MCP Registry as `io.github.analitikisler/ai-tag-debugger` (`mcpName` in package.json, `server.json`).
14
+ - The README has one-line and one-click setup for Claude Code, Cursor and VS Code.
15
+ - The MCP server reports its real version instead of a fixed 0.6.3.
16
+
3
17
  ## 0.7.9 (2026-10-14)
4
18
 
5
19
  - A planned event is found under the name the site uses: `dl_add_to_cart` (Elevar), `addToCart` and `Add To Cart` all count as `add_to_cart`, and similar variants of the other ecommerce events too. Sites like these no longer get a false "missing add_to_cart". A plan event can list more names in `aliases`.
package/README.md CHANGED
@@ -112,6 +112,8 @@ ai-tag-debugger run --site https://staging.yourshop.com --journey add_to_cart --
112
112
 
113
113
  Or run it without installing: `npx -y @analitikisler/ai-tag-debugger run --site … --journey add_to_cart`.
114
114
 
115
+ Run `ai-tag-debugger --help` to see every command and option, such as `--providers`, `--scripted` and `--no-ai`.
116
+
115
117
  ### Try the demo shop (from source)
116
118
 
117
119
  The repository includes a demo shop with three deliberate tracking bugs:
@@ -303,7 +305,16 @@ Add the server to the app's MCP config. For Claude Desktop that is Settings →
303
305
  }
304
306
  ```
305
307
 
306
- Install the browser once with `npx -y playwright@1.56.1 install chromium`. Gemini CLI uses the same `mcpServers` block in `~/.gemini/settings.json`. Claude Code: `claude mcp add ai-tag-debugger -- npx -y @analitikisler/ai-tag-debugger mcp`. Restart the app, and the ai-tag-debugger tools appear. If the app can't find `npx`, use its full path (`which npx`, for example `/opt/homebrew/bin/npx` on a Mac with Homebrew). Reports are written to `~/ai-tag-debugger-reports`.
308
+ The browser (Chromium) downloads itself the first time you run an audit, about a minute, one time. To get it ahead of time: `npx -y playwright@1.56.1 install chromium`.
309
+
310
+ One-click and one-line setup:
311
+
312
+ - **Claude Code:** `claude mcp add ai-tag-debugger -- npx -y @analitikisler/ai-tag-debugger mcp`
313
+ - **Cursor:** [Add to Cursor](https://cursor.com/en/install-mcp?name=ai-tag-debugger&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBhbmFsaXRpa2lzbGVyL2FpLXRhZy1kZWJ1Z2dlciIsIm1jcCJdfQ==)
314
+ - **VS Code:** [Install in VS Code](https://insiders.vscode.dev/redirect/mcp/install?name=ai-tag-debugger&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40analitikisler%2Fai-tag-debugger%22%2C%22mcp%22%5D%7D), or `code --add-mcp '{"name":"ai-tag-debugger","command":"npx","args":["-y","@analitikisler/ai-tag-debugger","mcp"]}'`
315
+ - **Claude Desktop, Gemini CLI and others:** the JSON block above. Gemini CLI uses the same `mcpServers` block in `~/.gemini/settings.json`.
316
+
317
+ Restart the app, and the ai-tag-debugger tools appear. If the app can't find `npx`, use its full path (`which npx`, for example `/opt/homebrew/bin/npx` on a Mac with Homebrew). Reports are written to `~/ai-tag-debugger-reports`.
307
318
 
308
319
  Running from a clone instead? Use `"command": "node"` and `"args": ["/path/to/ai-tag-debugger/src/cli.js", "mcp"]`.
309
320
 
package/README.tr.md CHANGED
@@ -112,6 +112,8 @@ ai-tag-debugger run --site https://staging.yourshop.com --journey add_to_cart --
112
112
 
113
113
  Ya da kurmadan çalıştırın: `npx -y @analitikisler/ai-tag-debugger run --site … --journey add_to_cart`.
114
114
 
115
+ Tüm komutları ve seçenekleri (`--providers`, `--scripted`, `--no-ai` gibi) görmek için `ai-tag-debugger --help` çalıştırın.
116
+
115
117
  ### Demo mağazayı deneyin (kaynak koddan)
116
118
 
117
119
  Depoda, bilerek bırakılmış üç ölçümleme hatası olan bir demo mağaza var:
@@ -303,7 +305,16 @@ Sunucuyu uygulamanın MCP ayarlarına ekleyin. Claude Desktop'ta bu Settings →
303
305
  }
304
306
  ```
305
307
 
306
- Tarayıcıyı bir kez `npx -y playwright@1.56.1 install chromium` ile kurun. Gemini CLI, `~/.gemini/settings.json` içinde aynı `mcpServers` bloğunu kullanır. Claude Code için: `claude mcp add ai-tag-debugger -- npx -y @analitikisler/ai-tag-debugger mcp`. Uygulamayı yeniden başlatın, ai-tag-debugger araçları görünür. Uygulama `npx`'i bulamazsa tam yolunu kullanın (`which npx`; örneğin Homebrew kurulu bir Mac'te `/opt/homebrew/bin/npx`). Raporlar `~/ai-tag-debugger-reports` klasörüne yazılır.
308
+ Tarayıcı (Chromium), ilk denetimi çalıştırdığınızda kendiliğinden iner (bir kez, yaklaşık bir dakika). Önceden kurmak için: `npx -y playwright@1.56.1 install chromium`.
309
+
310
+ Tek tıkla ve tek satırla kurulum:
311
+
312
+ - **Claude Code:** `claude mcp add ai-tag-debugger -- npx -y @analitikisler/ai-tag-debugger mcp`
313
+ - **Cursor:** [Cursor'a ekle](https://cursor.com/en/install-mcp?name=ai-tag-debugger&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBhbmFsaXRpa2lzbGVyL2FpLXRhZy1kZWJ1Z2dlciIsIm1jcCJdfQ==)
314
+ - **VS Code:** [VS Code'a kur](https://insiders.vscode.dev/redirect/mcp/install?name=ai-tag-debugger&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40analitikisler%2Fai-tag-debugger%22%2C%22mcp%22%5D%7D) veya `code --add-mcp '{"name":"ai-tag-debugger","command":"npx","args":["-y","@analitikisler/ai-tag-debugger","mcp"]}'`
315
+ - **Claude Desktop, Gemini CLI ve diğerleri:** yukarıdaki JSON bloğu. Gemini CLI, `~/.gemini/settings.json` içinde aynı `mcpServers` bloğunu kullanır.
316
+
317
+ Uygulamayı yeniden başlatın, ai-tag-debugger araçları görünür. Uygulama `npx`'i bulamazsa tam yolunu kullanın (`which npx`; örneğin Homebrew kurulu bir Mac'te `/opt/homebrew/bin/npx`). Raporlar `~/ai-tag-debugger-reports` klasörüne yazılır.
307
318
 
308
319
  Kaynak koddan mı çalıştırıyorsunuz? `"command": "node"` ve `"args": ["/path/to/ai-tag-debugger/src/cli.js", "mcp"]` kullanın.
309
320
 
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@analitikisler/ai-tag-debugger",
3
- "version": "0.7.9",
3
+ "mcpName": "io.github.analitikisler/ai-tag-debugger",
4
+ "version": "0.8.1",
4
5
  "description": "Open-source AI agent that QA-tests your website's analytics, ad and consent tracking (GA4, GTM, Google Ads, Meta, TikTok). Bring your own model (Claude, OpenAI, Gemini) or use it from your chat app over MCP.",
5
6
  "type": "module",
6
7
  "bin": {
@@ -15,7 +16,7 @@
15
16
  "scripts": {
16
17
  "test": "node --test test/*.test.js",
17
18
  "demo:serve": "node examples/serve.js",
18
- "demo": "node src/cli.js run --config examples/demo.config.json --scripted --out reports/demo"
19
+ "demo": "node src/cli.js run --config examples/demo.config.json --scripted --no-ai --out reports/demo"
19
20
  },
20
21
  "engines": {
21
22
  "node": ">=20"
package/src/browser.js CHANGED
@@ -1,4 +1,7 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createRequire } from "node:module";
1
3
  import { readFile, rm, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
2
5
  import { chromium, devices } from "playwright";
3
6
  import { parseTrackingRequest, isCollectionEndpoint, providerOf, vendorOf, resolveProviders } from "./parsers.js";
4
7
  import { redactBody, redactParams, redactPush, redactUrl } from "./redact.js";
@@ -7,6 +10,19 @@ import { startEgressProxy } from "./egress.js";
7
10
 
8
11
  // Consent banners of the common consent platforms (CMPs), by their container. A cover that matches
9
12
  // one of these always blocks the journey, and a reject click is checked against them afterwards.
13
+ let browserInstall = null;
14
+ /** Downloads Chromium with Playwright's own installer. Output goes to stderr, because stdout carries the MCP protocol. */
15
+ export function installBrowser() {
16
+ browserInstall ??= new Promise((resolve, reject) => {
17
+ const cli = path.join(path.dirname(createRequire(import.meta.url).resolve("playwright/package.json")), "cli.js");
18
+ console.error("ai-tag-debugger: installing the Chromium browser (one time, about a minute)...");
19
+ const child = spawn(process.execPath, [cli, "install", "chromium"], { stdio: ["ignore", 2, 2] });
20
+ child.on("error", reject);
21
+ child.on("exit", (code) => (code === 0 ? resolve() : reject(new Error(`Installing the browser failed (exit code ${code}). Run: npx -y playwright@1.56.1 install chromium`))));
22
+ }).finally(() => { browserInstall = null; });
23
+ return browserInstall;
24
+ }
25
+
10
26
  export const CMP_BANNER_SELECTORS = `#onetrust-banner-sdk, #onetrust-consent-sdk, #CybotCookiebotDialog, #usercentrics-root, #didomi-host, #cookiescript_injected, .cky-consent-container, .cc-window, #cmpbox, .qc-cmp2-container, \
11
27
  #truste-consent-track, #consent_blackbar, .osano-cm-window, #iubenda-cs-banner, .iubenda-cs-container, #cmplz-cookiebanner-container, .cmplz-cookiebanner, #cookiefirst-root, .cookiefirst-root, #axeptio_overlay, \
12
28
  #sp_message_container, [id^="sp_message_container_"], #cookie-law-info-bar, #BorlabsCookieBox, .klaro, #termly-code-snippet-support, #cookie-information-template-wrapper, #coiOverlay, \
@@ -378,7 +394,13 @@ export class AuditSession {
378
394
  // flight once they are removed at the end, go to a proxy that isn't there.
379
395
  launch.proxy = { server: "http://127.0.0.1:9", bypass: "<-loopback>" };
380
396
  }
381
- this.browser = await chromium.launch(launch);
397
+ try {
398
+ this.browser = await chromium.launch(launch);
399
+ } catch (err) {
400
+ if (!/Executable doesn't exist|playwright install/i.test(String(err?.message))) throw err;
401
+ await installBrowser();
402
+ this.browser = await chromium.launch(launch);
403
+ }
382
404
  const record = this.opts.recordHar ? { recordHar: { path: this.opts.recordHar, mode: "minimal" } } : {};
383
405
  this.context = await this.browser.newContext({ ...device, locale: "en-US", ...record });
384
406
  await this.context.exposeBinding("__atdRecord", (_source, entry) => {
package/src/mcp.js CHANGED
@@ -9,6 +9,7 @@ import { createServer } from "node:http";
9
9
  import { readFile } from "node:fs/promises";
10
10
  import { homedir } from "node:os";
11
11
  import path from "node:path";
12
+ import { readFileSync } from "node:fs";
12
13
  import { z } from "zod";
13
14
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
15
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -22,7 +23,7 @@ import { loadConfig, readJson, runAudit, writeReports } from "./audit.js";
22
23
  import { DEFAULT_PROVIDERS, PROVIDERS as TRACKING_PROVIDERS } from "./parsers.js";
23
24
  import { expectEvents, filterFindings, JOURNEY_PRESETS, onlyEvents, presetFor, withPresetEvents } from "./journeys.js";
24
25
 
25
- const VERSION = "0.6.3";
26
+ const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
26
27
  const MAX_OPEN_AUDITS = 4;
27
28
  const IDLE_MS = 15 * 60 * 1000; // an audit untouched this long is closed
28
29
  const MAX_BODY_BYTES = 1024 * 1024;
@@ -292,7 +293,16 @@ export function buildServer(opts) {
292
293
  async ({ config: configFile, scripted, language, branding, providers }) => {
293
294
  const loaded = await loadConfig(configFile);
294
295
  const replay = scripted !== false;
295
- const result = await runAudit({ ...loaded, scripted: replay, ai: !replay, lang: language, branding, providers, outDir: path.join(opts.reportsDir, new Date().toISOString().replace(/[:.]/g, "-")) });
296
+ let result;
297
+ try {
298
+ result = await runAudit({ ...loaded, scripted: replay, ai: !replay, lang: language, branding, providers, outDir: path.join(opts.reportsDir, new Date().toISOString().replace(/[:.]/g, "-")) });
299
+ } catch (err) {
300
+ // The CLI wording (--scripted --no-ai) means nothing to a chat app, so say it in this tool's terms.
301
+ if (err.code !== "NO_API_KEY") throw err;
302
+ return fail(
303
+ "No AI provider key (ANTHROPIC_API_KEY, OPENAI_API_KEY or GEMINI_API_KEY) is set on the computer running this server, so scripted false can't browse with a model. Call run_audit again with scripted true (or leave it out) to replay the journeys' fixed steps, or browse the site yourself with start_audit, browser_action and finish_audit. Neither needs a key.",
304
+ );
305
+ }
296
306
  const m = messages(result.lang);
297
307
  const grouped = groupFindings(result.findings).filter((f) => f.severity !== "ok");
298
308
  return text(
package/src/providers.js CHANGED
@@ -28,6 +28,12 @@ export function resolveProviderName(name, env = process.env) {
28
28
  return "anthropic";
29
29
  }
30
30
 
31
+ const NO_API_KEY = `No AI provider key found. Set one of ANTHROPIC_API_KEY, OPENAI_API_KEY or GEMINI_API_KEY (or OPENAI_COMPATIBLE_BASE_URL for an OpenAI-compatible API), or pick one with --provider. To run without any AI model, replay the fixed steps with --scripted --no-ai.`;
32
+
33
+ function hasApiKey(env) {
34
+ return Boolean(env.OPENAI_COMPATIBLE_BASE_URL || env.ANTHROPIC_API_KEY || env.ANTHROPIC_AUTH_TOKEN || env.OPENAI_API_KEY || env.GEMINI_API_KEY || env.GOOGLE_API_KEY);
35
+ }
36
+
31
37
  /**
32
38
  * @param {{ name?: string, model?: string, baseUrl?: string, client?: any }} opts
33
39
  * baseUrl: for openai-compatible, e.g. https://api.deepseek.com or http://localhost:11434/v1.
@@ -36,6 +42,10 @@ export function resolveProviderName(name, env = process.env) {
36
42
  export async function createProvider(opts = {}, env = process.env) {
37
43
  // A bare client (tests, embedding) is an Anthropic client unless named otherwise.
38
44
  const name = opts.client && !opts.name ? "anthropic" : opts.baseUrl && !opts.name ? "openai-compatible" : resolveProviderName(opts.name, env);
45
+ // With no provider named and no key set, the SDK would only fail at the first step with an
46
+ // unclear auth error, after the browser has started. A named provider skips this check, since
47
+ // its SDK may find credentials elsewhere (an Anthropic profile, Google Cloud credentials).
48
+ if (!opts.client && !opts.name && !opts.baseUrl && !hasApiKey(env)) throw Object.assign(new Error(NO_API_KEY), { code: "NO_API_KEY" });
39
49
  if (name === "openai-compatible") {
40
50
  const baseURL = opts.baseUrl ?? env.OPENAI_COMPATIBLE_BASE_URL;
41
51
  const model = opts.model ?? env.OPENAI_COMPATIBLE_MODEL;