mcp-scraper 0.3.28 → 0.3.40

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.
Files changed (44) hide show
  1. package/dist/bin/api-server.cjs +2459 -1137
  2. package/dist/bin/api-server.cjs.map +1 -1
  3. package/dist/bin/api-server.js +3 -3
  4. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  5. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  6. package/dist/bin/mcp-scraper-cli.js +1 -1
  7. package/dist/bin/mcp-scraper-install.cjs +1 -1
  8. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  9. package/dist/bin/mcp-scraper-install.js +1 -1
  10. package/dist/bin/mcp-stdio-server.cjs +237 -6
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +2 -2
  13. package/dist/bin/paa-harvest.cjs +109 -5
  14. package/dist/bin/paa-harvest.cjs.map +1 -1
  15. package/dist/bin/paa-harvest.js +2 -2
  16. package/dist/{chunk-TRNSXR46.js → chunk-DVRPXPYH.js} +22 -3
  17. package/dist/chunk-DVRPXPYH.js.map +1 -0
  18. package/dist/{chunk-QSSH4RCX.js → chunk-GMTS35L6.js} +111 -7
  19. package/dist/chunk-GMTS35L6.js.map +1 -0
  20. package/dist/chunk-I5EFBTYE.js +7 -0
  21. package/dist/chunk-I5EFBTYE.js.map +1 -0
  22. package/dist/{chunk-H74L743B.js → chunk-KJWNVQFL.js} +39 -2
  23. package/dist/chunk-KJWNVQFL.js.map +1 -0
  24. package/dist/{chunk-NGD4ITAO.js → chunk-QLQYNE7E.js} +238 -7
  25. package/dist/chunk-QLQYNE7E.js.map +1 -0
  26. package/dist/{db-FP2ABUU4.js → db-LYQENFPW.js} +2 -2
  27. package/dist/index.cjs +109 -5
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +8 -0
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.js +2 -2
  32. package/dist/{server-IDDX7KGT.js → server-HB7WJO3N.js} +1154 -251
  33. package/dist/server-HB7WJO3N.js.map +1 -0
  34. package/dist/{worker-NV4GTIRG.js → worker-TL4J65DE.js} +4 -4
  35. package/package.json +1 -1
  36. package/dist/chunk-7MY2NSQX.js +0 -7
  37. package/dist/chunk-7MY2NSQX.js.map +0 -1
  38. package/dist/chunk-H74L743B.js.map +0 -1
  39. package/dist/chunk-NGD4ITAO.js.map +0 -1
  40. package/dist/chunk-QSSH4RCX.js.map +0 -1
  41. package/dist/chunk-TRNSXR46.js.map +0 -1
  42. package/dist/server-IDDX7KGT.js.map +0 -1
  43. /package/dist/{db-FP2ABUU4.js.map → db-LYQENFPW.js.map} +0 -0
  44. /package/dist/{worker-NV4GTIRG.js.map → worker-TL4J65DE.js.map} +0 -0
@@ -9,7 +9,7 @@ import {
9
9
  } from "../chunk-NNEIXK5L.js";
10
10
  import {
11
11
  PACKAGE_VERSION
12
- } from "../chunk-7MY2NSQX.js";
12
+ } from "../chunk-I5EFBTYE.js";
13
13
  import "../chunk-K443GQY5.js";
14
14
  import "../chunk-BP27CZ5Q.js";
15
15
 
@@ -101,7 +101,7 @@ function renderInstallTerminal(options) {
101
101
  }
102
102
 
103
103
  // src/version.ts
104
- var PACKAGE_VERSION = "0.3.28";
104
+ var PACKAGE_VERSION = "0.3.40";
105
105
 
106
106
  // bin/mcp-scraper-install.ts
107
107
  var noColor = process.argv.includes("--no-color") || process.env.NO_COLOR !== void 0 || process.env.FORCE_COLOR === "0" || !process.stdout.isTTY;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/install-terminal.ts","../../src/version.ts","../../bin/mcp-scraper-install.ts"],"sourcesContent":["export interface InstallTerminalOptions {\n color?: boolean\n version: string\n apiKeyConfigured?: boolean\n}\n\ntype Tone = 'cyan' | 'lime' | 'amber' | 'red' | 'muted' | 'bold'\n\nconst CODES: Record<Tone | 'reset', string> = {\n reset: '\\x1b[0m',\n cyan: '\\x1b[36m',\n lime: '\\x1b[32m',\n amber: '\\x1b[33m',\n red: '\\x1b[31m',\n muted: '\\x1b[90m',\n bold: '\\x1b[1m',\n}\n\nfunction colorize(value: string, tone: Tone, enabled: boolean): string {\n if (!enabled) return value\n return `${CODES[tone]}${value}${CODES.reset}`\n}\n\nfunction toolRow(label: string, tools: string[], enabled: boolean): string {\n const padded = label.padEnd(9, ' ')\n return ` ${colorize(padded, 'muted', enabled)} ${tools.join(colorize(' . ', 'muted', enabled))}`\n}\n\nexport function renderInstallTerminal(options: InstallTerminalOptions): string {\n const color = options.color ?? true\n const apiKeyValue = options.apiKeyConfigured ? '$MCP_SCRAPER_API_KEY' : 'sk_live_your_key'\n const ascii = String.raw`\n __ __ ____ ____ \n| \\/ |/ ___| _ \\\n| |\\/| | | | |_) |\n| | | | |___| __/\n|_| |_|\\____|_|\n\n ____ ____ ____ _ ____ _____ ____\n/ ___| / ___| _ \\ / \\ | _ \\| ____| _ \\\n\\___ \\| | | |_) | / _ \\ | |_) | _| | |_) |\n ___) | |___| _ < / ___ \\| __/| |___| _ <\n|____/ \\____|_| \\_\\/_/ \\_\\_| |_____|_| \\_\\\n`\n\n const claudeCommand = [\n `MCP_SCRAPER_API_KEY=${apiKeyValue} npx -y -p mcp-scraper@latest \\\\`,\n ' mcp-scraper-cli agent install claude --apply',\n ].join('\\n')\n\n const codexConfig = [\n '[mcp_servers.mcp-scraper]',\n 'command = \"npx\"',\n 'args = [\"-y\", \"-p\", \"mcp-scraper@latest\", \"mcp-scraper\"]',\n `env = { MCP_SCRAPER_API_KEY = \"${apiKeyValue}\" }`,\n ].join('\\n')\n\n return [\n colorize(`mcp-scraper v${options.version}`, 'bold', color),\n colorize('> mcp-scraper-install', 'muted', color),\n colorize(ascii, 'amber', color),\n `${colorize('MCP Scraper Agent', 'cyan', color)} . v${options.version} . mcpscraper.dev`,\n '1/1 install surfaces ready',\n colorize('Newest: saved browser profiles that hold MANY site logins. Share the watch_url, user signs in, then verify with browser_profile_list. Browser sessions are direct/no-proxy by default.', 'lime', color),\n '',\n `${colorize('Tools', 'cyan', color)} ${colorize('(48 MCP tools)', 'muted', color)}`,\n toolRow('search', ['harvest_paa', 'search_serp', 'maps_search', 'maps_place_intel'], color),\n toolRow('extract', ['extract_url', 'map_site_urls', 'extract_site', 'audit_site', 'directory_workflow'], color),\n toolRow('build', ['rank_tracker_workflow', 'cron plan', 'database prompt'], color),\n toolRow('media', ['youtube_harvest', 'youtube_transcribe', 'facebook_ad_search', 'facebook_page_intel', 'facebook_ad_transcribe', 'facebook_video_transcribe', 'instagram_profile_content', 'instagram_media_download', 'reddit_thread'], color),\n toolRow('browser', ['browser_open', 'browser_profile_connect', 'browser_profile_list', 'browser_close', 'browser_screenshot', 'browser_read', 'browser_locate', 'browser_replay_mark', 'browser_replay_annotate'], color),\n toolRow('account', ['credits_info', 'reports', 'MCP resources'], color),\n `${colorize('Workflows', 'cyan', color)} ${colorize('(MCP + CLI + API)', 'muted', color)}`,\n toolRow('route', ['workflow_list', 'workflow_suggest', 'workflow_run', 'workflow_step', 'workflow_status', 'workflow_artifact_read'], color),\n toolRow('seo', ['directory', 'agent-packet', 'competitive audit', 'map/serp comparison', 'PAA/AIO briefs', 'scheduled runs'], color),\n '',\n colorize('Usage tips:', 'amber', color),\n 'Run mcp-scraper-install for this visible card. Run mcp-scraper-cli for setup utilities and subcommands.',\n 'Run mcp-scraper in a human terminal to print this card; MCP clients get the same command as a silent stdio server.',\n 'Explicit card command: npx -y -p mcp-scraper@latest mcp-scraper-install',\n 'Customer auth setup: run browser_profile_connect, send the watch_url, let the user sign in, then call browser_profile_list until AUTHENTICATED.',\n 'Stack logins / reconnect: run browser_profile_connect again with the same profile name and another domain to add accounts or refresh a login.',\n 'Start with workflow_suggest for broad jobs like market analysis, ICP research, CRO audits, brand briefs, content gaps, and AI visibility.',\n 'For MCP clients, use mcp-scraper so one install can mix SERP, Maps, browser, reports, and saved MCP resources.',\n 'If you hit the concurrency limit, add an extra slot for $5/month with mcp-scraper-cli billing concurrency checkout.',\n '',\n `${colorize('Ready.', 'lime', color)} Install the combined MCP server with one command:`,\n '',\n colorize('Setup doctor', 'amber', color),\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli doctor',\n '',\n colorize('Hosted profile setup', 'amber', color),\n 'In your MCP client, call browser_profile_connect with email=\"seo@example.com\" and domain=\"chatgpt.com\".',\n 'Give the returned watch_url to the user. After they sign in, call browser_profile_list, then browser_open with the returned profile. Add more logins by calling browser_profile_connect again with the same profile and a new domain.',\n '',\n colorize('Claude Code one-command setup', 'amber', color),\n claudeCommand,\n 'Then fully exit Claude Code and open a new Claude terminal. Check with: claude mcp list',\n '',\n colorize('Codex config', 'amber', color),\n codexConfig,\n '',\n colorize('Claude Desktop Extension', 'amber', color),\n 'Download: https://mcpscraper.dev/downloads/mcp-scraper.mcpb',\n '',\n colorize('Safety note:', 'muted', color),\n 'mcp-scraper prints this card only when stdin/stdout are an interactive TTY. In MCP clients it writes only JSON-RPC to stdout.',\n 'Use --stdio or MCP_SCRAPER_FORCE_STDIO=1 to force server mode from a terminal.',\n '',\n ].join('\\n')\n}\n","export const PACKAGE_VERSION = '0.3.28'\n","#!/usr/bin/env node\nimport { renderInstallTerminal } from '../src/install-terminal.js'\nimport { PACKAGE_VERSION } from '../src/version.js'\n\nconst noColor =\n process.argv.includes('--no-color') ||\n process.env.NO_COLOR !== undefined ||\n process.env.FORCE_COLOR === '0' ||\n !process.stdout.isTTY\n\nconst help = process.argv.includes('--help') || process.argv.includes('-h')\nif (help) {\n process.stdout.write([\n 'Usage: mcp-scraper-install [--no-color]',\n '',\n 'Prints the branded MCP Scraper terminal install card and copyable install commands.',\n 'mcp-scraper prints the same card in a human terminal and runs as the MCP stdio server in clients.',\n '',\n ].join('\\n'))\n process.exit(0)\n}\n\nprocess.stdout.write(renderInstallTerminal({\n version: PACKAGE_VERSION,\n color: !noColor,\n apiKeyConfigured: Boolean(process.env.MCP_SCRAPER_API_KEY?.trim()),\n}))\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/install-terminal.ts","../../src/version.ts","../../bin/mcp-scraper-install.ts"],"sourcesContent":["export interface InstallTerminalOptions {\n color?: boolean\n version: string\n apiKeyConfigured?: boolean\n}\n\ntype Tone = 'cyan' | 'lime' | 'amber' | 'red' | 'muted' | 'bold'\n\nconst CODES: Record<Tone | 'reset', string> = {\n reset: '\\x1b[0m',\n cyan: '\\x1b[36m',\n lime: '\\x1b[32m',\n amber: '\\x1b[33m',\n red: '\\x1b[31m',\n muted: '\\x1b[90m',\n bold: '\\x1b[1m',\n}\n\nfunction colorize(value: string, tone: Tone, enabled: boolean): string {\n if (!enabled) return value\n return `${CODES[tone]}${value}${CODES.reset}`\n}\n\nfunction toolRow(label: string, tools: string[], enabled: boolean): string {\n const padded = label.padEnd(9, ' ')\n return ` ${colorize(padded, 'muted', enabled)} ${tools.join(colorize(' . ', 'muted', enabled))}`\n}\n\nexport function renderInstallTerminal(options: InstallTerminalOptions): string {\n const color = options.color ?? true\n const apiKeyValue = options.apiKeyConfigured ? '$MCP_SCRAPER_API_KEY' : 'sk_live_your_key'\n const ascii = String.raw`\n __ __ ____ ____ \n| \\/ |/ ___| _ \\\n| |\\/| | | | |_) |\n| | | | |___| __/\n|_| |_|\\____|_|\n\n ____ ____ ____ _ ____ _____ ____\n/ ___| / ___| _ \\ / \\ | _ \\| ____| _ \\\n\\___ \\| | | |_) | / _ \\ | |_) | _| | |_) |\n ___) | |___| _ < / ___ \\| __/| |___| _ <\n|____/ \\____|_| \\_\\/_/ \\_\\_| |_____|_| \\_\\\n`\n\n const claudeCommand = [\n `MCP_SCRAPER_API_KEY=${apiKeyValue} npx -y -p mcp-scraper@latest \\\\`,\n ' mcp-scraper-cli agent install claude --apply',\n ].join('\\n')\n\n const codexConfig = [\n '[mcp_servers.mcp-scraper]',\n 'command = \"npx\"',\n 'args = [\"-y\", \"-p\", \"mcp-scraper@latest\", \"mcp-scraper\"]',\n `env = { MCP_SCRAPER_API_KEY = \"${apiKeyValue}\" }`,\n ].join('\\n')\n\n return [\n colorize(`mcp-scraper v${options.version}`, 'bold', color),\n colorize('> mcp-scraper-install', 'muted', color),\n colorize(ascii, 'amber', color),\n `${colorize('MCP Scraper Agent', 'cyan', color)} . v${options.version} . mcpscraper.dev`,\n '1/1 install surfaces ready',\n colorize('Newest: saved browser profiles that hold MANY site logins. Share the watch_url, user signs in, then verify with browser_profile_list. Browser sessions are direct/no-proxy by default.', 'lime', color),\n '',\n `${colorize('Tools', 'cyan', color)} ${colorize('(48 MCP tools)', 'muted', color)}`,\n toolRow('search', ['harvest_paa', 'search_serp', 'maps_search', 'maps_place_intel'], color),\n toolRow('extract', ['extract_url', 'map_site_urls', 'extract_site', 'audit_site', 'directory_workflow'], color),\n toolRow('build', ['rank_tracker_workflow', 'cron plan', 'database prompt'], color),\n toolRow('media', ['youtube_harvest', 'youtube_transcribe', 'facebook_ad_search', 'facebook_page_intel', 'facebook_ad_transcribe', 'facebook_video_transcribe', 'instagram_profile_content', 'instagram_media_download', 'reddit_thread'], color),\n toolRow('browser', ['browser_open', 'browser_profile_connect', 'browser_profile_list', 'browser_close', 'browser_screenshot', 'browser_read', 'browser_locate', 'browser_replay_mark', 'browser_replay_annotate'], color),\n toolRow('account', ['credits_info', 'reports', 'MCP resources'], color),\n `${colorize('Workflows', 'cyan', color)} ${colorize('(MCP + CLI + API)', 'muted', color)}`,\n toolRow('route', ['workflow_list', 'workflow_suggest', 'workflow_run', 'workflow_step', 'workflow_status', 'workflow_artifact_read'], color),\n toolRow('seo', ['directory', 'agent-packet', 'competitive audit', 'map/serp comparison', 'PAA/AIO briefs', 'scheduled runs'], color),\n '',\n colorize('Usage tips:', 'amber', color),\n 'Run mcp-scraper-install for this visible card. Run mcp-scraper-cli for setup utilities and subcommands.',\n 'Run mcp-scraper in a human terminal to print this card; MCP clients get the same command as a silent stdio server.',\n 'Explicit card command: npx -y -p mcp-scraper@latest mcp-scraper-install',\n 'Customer auth setup: run browser_profile_connect, send the watch_url, let the user sign in, then call browser_profile_list until AUTHENTICATED.',\n 'Stack logins / reconnect: run browser_profile_connect again with the same profile name and another domain to add accounts or refresh a login.',\n 'Start with workflow_suggest for broad jobs like market analysis, ICP research, CRO audits, brand briefs, content gaps, and AI visibility.',\n 'For MCP clients, use mcp-scraper so one install can mix SERP, Maps, browser, reports, and saved MCP resources.',\n 'If you hit the concurrency limit, add an extra slot for $5/month with mcp-scraper-cli billing concurrency checkout.',\n '',\n `${colorize('Ready.', 'lime', color)} Install the combined MCP server with one command:`,\n '',\n colorize('Setup doctor', 'amber', color),\n 'npx -y -p mcp-scraper@latest mcp-scraper-cli doctor',\n '',\n colorize('Hosted profile setup', 'amber', color),\n 'In your MCP client, call browser_profile_connect with email=\"seo@example.com\" and domain=\"chatgpt.com\".',\n 'Give the returned watch_url to the user. After they sign in, call browser_profile_list, then browser_open with the returned profile. Add more logins by calling browser_profile_connect again with the same profile and a new domain.',\n '',\n colorize('Claude Code one-command setup', 'amber', color),\n claudeCommand,\n 'Then fully exit Claude Code and open a new Claude terminal. Check with: claude mcp list',\n '',\n colorize('Codex config', 'amber', color),\n codexConfig,\n '',\n colorize('Claude Desktop Extension', 'amber', color),\n 'Download: https://mcpscraper.dev/downloads/mcp-scraper.mcpb',\n '',\n colorize('Safety note:', 'muted', color),\n 'mcp-scraper prints this card only when stdin/stdout are an interactive TTY. In MCP clients it writes only JSON-RPC to stdout.',\n 'Use --stdio or MCP_SCRAPER_FORCE_STDIO=1 to force server mode from a terminal.',\n '',\n ].join('\\n')\n}\n","export const PACKAGE_VERSION = '0.3.40'\n","#!/usr/bin/env node\nimport { renderInstallTerminal } from '../src/install-terminal.js'\nimport { PACKAGE_VERSION } from '../src/version.js'\n\nconst noColor =\n process.argv.includes('--no-color') ||\n process.env.NO_COLOR !== undefined ||\n process.env.FORCE_COLOR === '0' ||\n !process.stdout.isTTY\n\nconst help = process.argv.includes('--help') || process.argv.includes('-h')\nif (help) {\n process.stdout.write([\n 'Usage: mcp-scraper-install [--no-color]',\n '',\n 'Prints the branded MCP Scraper terminal install card and copyable install commands.',\n 'mcp-scraper prints the same card in a human terminal and runs as the MCP stdio server in clients.',\n '',\n ].join('\\n'))\n process.exit(0)\n}\n\nprocess.stdout.write(renderInstallTerminal({\n version: PACKAGE_VERSION,\n color: !noColor,\n apiKeyConfigured: Boolean(process.env.MCP_SCRAPER_API_KEY?.trim()),\n}))\n"],"mappings":"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","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-T3VZD2I4.js";
5
5
  import {
6
6
  PACKAGE_VERSION
7
- } from "../chunk-7MY2NSQX.js";
7
+ } from "../chunk-I5EFBTYE.js";
8
8
 
9
9
  // bin/mcp-scraper-install.ts
10
10
  var noColor = process.argv.includes("--no-color") || process.env.NO_COLOR !== void 0 || process.env.FORCE_COLOR === "0" || !process.stdout.isTTY;
@@ -270,6 +270,15 @@ var HttpMcpToolExecutor = class {
270
270
  facebookVideoTranscribe(input) {
271
271
  return this.call("/facebook/video-transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
272
272
  }
273
+ googleAdsSearch(input) {
274
+ return this.call("/google-ads/search", input, this.httpTimeoutOverrideMs ?? 24e4);
275
+ }
276
+ googleAdsPageIntel(input) {
277
+ return this.call("/google-ads/page-intel", input, this.httpTimeoutOverrideMs ?? 3e5);
278
+ }
279
+ googleAdsTranscribe(input) {
280
+ return this.call("/google-ads/transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
281
+ }
273
282
  instagramProfileContent(input) {
274
283
  return this.call("/instagram/profile-content", withDefaultHostedProfile(input), this.httpTimeoutOverrideMs ?? 24e4);
275
284
  }
@@ -459,7 +468,7 @@ render();
459
468
  }
460
469
 
461
470
  // src/version.ts
462
- var PACKAGE_VERSION = "0.3.28";
471
+ var PACKAGE_VERSION = "0.3.40";
463
472
 
464
473
  // src/mcp/browser-agent-tool-schemas.ts
465
474
  var import_zod = require("zod");
@@ -2700,7 +2709,9 @@ ${paaRows}` : "## People Also Ask\n*Google did not return a People Also Ask bloc
2700
2709
  ${serpRows}` : "";
2701
2710
  const aiSection = aiOvw?.detected && aiOvw.text ? `
2702
2711
  ## AI Overview
2703
- > ${truncate(aiOvw.text, 600)}` : "";
2712
+ > ${truncate(aiOvw.text, 600)}` + (aiOvw.shareUrl ? `
2713
+
2714
+ **Shareable link:** ${aiOvw.shareUrl}` : "") : "";
2704
2715
  const statsLine = durationMs ? `
2705
2716
  ## Stats
2706
2717
  - Status: ${diagnostics?.completionStatus ?? (flat.length ? "paa_found" : "no_paa")} \xB7 Questions: ${flat.length} \xB7 Duration: ${(durationMs / 1e3).toFixed(1)}s` : "";
@@ -2733,7 +2744,7 @@ ${paaTable}${serpTable}${entityIdsSection(entityIds)}${aiSection}${statsLine}${d
2733
2744
  domain: String(r.domain ?? ""),
2734
2745
  snippet: r.snippet ?? null
2735
2746
  })),
2736
- aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null } : null,
2747
+ aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null, shareUrl: aiOvw.shareUrl ?? null } : null,
2737
2748
  entityIds: entityIds ? { entities: entityIds.entities ?? [], kgIds: entityIds.kgIds ?? [], cids: entityIds.cids ?? [], gcids: entityIds.gcids ?? [] } : null,
2738
2749
  durationMs: durationMs ?? null
2739
2750
  }
@@ -2765,7 +2776,9 @@ ${serpRows}` : "## Organic Results\n*None found*";
2765
2776
  ${localRows}` : "";
2766
2777
  const aiSection = aiOvw?.detected && aiOvw.text ? `
2767
2778
  ## AI Overview
2768
- > ${truncate(aiOvw.text, 600)}` : "";
2779
+ > ${truncate(aiOvw.text, 600)}` + (aiOvw.shareUrl ? `
2780
+
2781
+ **Shareable link:** ${aiOvw.shareUrl}` : "") : "";
2769
2782
  const tips = `
2770
2783
  ---
2771
2784
  \u{1F4A1} **Tips**
@@ -2794,7 +2807,7 @@ ${serpTable}${localSection}${entityIdsSection(entityIds)}${aiSection}${debugSect
2794
2807
  reviewCount: b.reviewCount ?? null,
2795
2808
  websiteUrl: b.websiteUrl ?? null
2796
2809
  })),
2797
- aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null } : null,
2810
+ aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null, shareUrl: aiOvw.shareUrl ?? null } : null,
2798
2811
  entityIds: entityIds ? { entities: entityIds.entities ?? [], kgIds: entityIds.kgIds ?? [], cids: entityIds.cids ?? [], gcids: entityIds.gcids ?? [] } : null
2799
2812
  }
2800
2813
  };
@@ -3352,6 +3365,136 @@ ${rows}`,
3352
3365
  }
3353
3366
  };
3354
3367
  }
3368
+ function formatGoogleAdsSearch(raw, input) {
3369
+ const parsed = parseData(raw);
3370
+ if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
3371
+ const d = parsed.data;
3372
+ const advertisers = d.advertisers ?? [];
3373
+ const rows = advertisers.map(
3374
+ (a, i) => `| ${i + 1} | ${cell(a.name)} | ${cell(a.domain)} | ${a.approxAdCount ?? "\u2014"} | \`${a.advertiserId ?? "\u2014"}\` |`
3375
+ ).join("\n");
3376
+ const full = [
3377
+ `# Google Ads Transparency Search: "${input.query}"`,
3378
+ `**${advertisers.length} advertisers found**${d.region ? ` \xB7 ${d.region}` : ""}`,
3379
+ `
3380
+ ## Advertisers
3381
+ | # | Name | Domain | ~Ads | Advertiser ID |
3382
+ |---|------|--------|------|---------------|
3383
+ ${rows}`,
3384
+ `
3385
+ ---
3386
+ \u{1F4A1} **Tips**
3387
+ - Pull an advertiser's ads: use \`google_ads_page_intel\` with \`advertiserId\`
3388
+ - Or pass a \`domain\` directly to \`google_ads_page_intel\``
3389
+ ].join("\n");
3390
+ return {
3391
+ ...oneBlock(full),
3392
+ structuredContent: {
3393
+ query: input.query,
3394
+ region: d.region ?? "US",
3395
+ advertiserCount: advertisers.length,
3396
+ advertisers: advertisers.map((a) => ({
3397
+ advertiserId: a.advertiserId ?? null,
3398
+ name: a.name ?? null,
3399
+ domain: a.domain ?? null,
3400
+ approxAdCount: typeof a.approxAdCount === "number" ? a.approxAdCount : null,
3401
+ detailUrl: a.detailUrl ?? null
3402
+ }))
3403
+ }
3404
+ };
3405
+ }
3406
+ function formatGoogleAdsPageIntel(raw, input) {
3407
+ const parsed = parseData(raw);
3408
+ if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
3409
+ const d = parsed.data;
3410
+ const ads = d.ads ?? [];
3411
+ const rows = ads.map((a, i) => {
3412
+ const media = a.youtubeVideoId ? `yt:${a.youtubeVideoId}` : a.videoUrl ? "video" : a.imageUrls.length ? `${a.imageUrls.length} img` : "\u2014";
3413
+ return `| ${i + 1} | ${cell(a.format)} | ${media} | \`${a.creativeId ?? "\u2014"}\` |`;
3414
+ }).join("\n");
3415
+ const label = d.advertiserName ?? input.advertiserId ?? input.domain ?? "";
3416
+ const full = [
3417
+ `# Google Ads Transparency: ${label}`,
3418
+ `**${ads.length} creatives** \xB7 ${d.videoCount ?? 0} video \xB7 ${d.imageCount ?? 0} image \xB7 ${d.textCount ?? 0} text${d.region ? ` \xB7 ${d.region}` : ""}`,
3419
+ `
3420
+ ## Creatives
3421
+ | # | Format | Media | Creative ID |
3422
+ |---|--------|-------|-------------|
3423
+ ${rows}`,
3424
+ `
3425
+ ---
3426
+ \u{1F4A1} **Tips**
3427
+ - Transcribe a video ad: pass its \`videoUrl\` to \`google_ads_transcribe\`, or its \`youtubeVideoId\` to \`youtube_transcribe\`
3428
+ - Full ad detail is at each creative's \`detailUrl\``
3429
+ ].join("\n");
3430
+ return {
3431
+ ...oneBlock(full),
3432
+ structuredContent: {
3433
+ advertiserId: d.advertiserId ?? null,
3434
+ advertiserName: d.advertiserName ?? null,
3435
+ domain: d.domain ?? null,
3436
+ region: d.region ?? "US",
3437
+ totalCreatives: typeof d.totalCreatives === "number" ? d.totalCreatives : ads.length,
3438
+ videoCount: d.videoCount ?? 0,
3439
+ imageCount: d.imageCount ?? 0,
3440
+ textCount: d.textCount ?? 0,
3441
+ ads: ads.map((a) => ({
3442
+ creativeId: a.creativeId ?? null,
3443
+ advertiserId: a.advertiserId ?? null,
3444
+ format: a.format ?? null,
3445
+ lastShown: a.lastShown ?? null,
3446
+ detailUrl: a.detailUrl ?? null,
3447
+ landingDomain: a.landingDomain ?? null,
3448
+ imageUrls: Array.isArray(a.imageUrls) ? a.imageUrls : [],
3449
+ youtubeVideoId: a.youtubeVideoId ?? null,
3450
+ videoUrl: a.videoUrl ?? null,
3451
+ variations: typeof a.variations === "number" ? a.variations : null
3452
+ }))
3453
+ }
3454
+ };
3455
+ }
3456
+ function formatGoogleAdsTranscribe(raw, input) {
3457
+ const parsed = parseData(raw);
3458
+ if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
3459
+ const d = parsed.data;
3460
+ const text = d.text ?? "";
3461
+ const chunks = d.chunks ?? [];
3462
+ const durSec = d.durationMs ? (d.durationMs / 1e3).toFixed(0) : "\u2014";
3463
+ const words = wordCount(text);
3464
+ const chunkRows = chunks.slice(0, 50).map((c) => {
3465
+ const sec = Number.isFinite(c.timestamp[0]) ? Math.floor(c.timestamp[0]) : 0;
3466
+ const mm = String(Math.floor(sec / 60)).padStart(2, "0");
3467
+ const ss = String(sec % 60).padStart(2, "0");
3468
+ return `| ${mm}:${ss} | ${cell(truncate(c.text, 120))} |`;
3469
+ }).join("\n");
3470
+ const full = [
3471
+ `# Google Ad Transcript`,
3472
+ `**Duration:** ${durSec}s \xB7 **${words} words**`,
3473
+ `
3474
+ ## Full Transcript
3475
+ ${text}`,
3476
+ chunks.length ? `
3477
+ ## Timestamped Chunks
3478
+ | Time | Text |
3479
+ |------|------|
3480
+ ${chunkRows}` : "",
3481
+ `
3482
+ ---
3483
+ \u{1F4A1} Get more ads from this advertiser: use \`google_ads_page_intel\`.`
3484
+ ].filter(Boolean).join("\n");
3485
+ return {
3486
+ ...oneBlock(full),
3487
+ structuredContent: {
3488
+ videoUrl: input.videoUrl,
3489
+ wordCount: words,
3490
+ chunkCount: chunks.length,
3491
+ durationMs: typeof d.durationMs === "number" ? d.durationMs : null,
3492
+ transcriptText: text,
3493
+ chunks: structuredTranscriptChunks(chunks),
3494
+ resolvedInputs: { videoUrl: input.videoUrl }
3495
+ }
3496
+ };
3497
+ }
3355
3498
  function normalizeMapsAttempts(value) {
3356
3499
  const attempts = Array.isArray(value) ? value : [];
3357
3500
  return attempts.map((attempt, index) => ({
@@ -4370,6 +4513,13 @@ seam is noted so you can chain them.
4370
4513
  - Transcribe an ad video -> **facebook_ad_transcribe** (takes a videoUrl from \`facebook_page_intel\`).
4371
4514
  - Transcribe an organic reel/video/post -> **facebook_video_transcribe** (takes the Facebook url directly).
4372
4515
 
4516
+ ## Google Ads (Ads Transparency Center)
4517
+ - Find advertisers by domain or brand -> **google_ads_search** (returns \`advertisers[]\` with advertiserId).
4518
+ - One advertiser's ads -> **google_ads_page_intel** (takes advertiserId or domain; returns \`ads[]\` with
4519
+ format, imageUrls, youtubeVideoId, and videoUrl).
4520
+ - Transcribe a video ad -> **google_ads_transcribe** (takes a googlevideo \`videoUrl\` from
4521
+ \`google_ads_page_intel\`), or **youtube_transcribe** for a returned \`youtubeVideoId\`.
4522
+
4373
4523
  ## Instagram
4374
4524
  - Profile inventory -> **instagram_profile_content** (returns post/reel/tv urls).
4375
4525
  - One post or reel -> **instagram_media_download** (takes a url from \`instagram_profile_content\`, or a
@@ -4579,6 +4729,20 @@ var FacebookVideoTranscribeInputSchema = {
4579
4729
  url: import_zod3.z.string().url().describe("Organic Facebook reel, video, watch, post, or share URL from facebook.com, m.facebook.com, or fb.watch. The tool renders the page, extracts the best matching public Facebook CDN MP4 URL, then transcribes it. Use this when the user pastes a normal Facebook video page URL and asks for the transcript or downloadable MP4."),
4580
4730
  quality: import_zod3.z.enum(["best", "hd", "sd"]).default("best").describe("Preferred progressive MP4 quality. Use best by default; hd prefers the highest HD progressive URL; sd forces the SD URL.")
4581
4731
  };
4732
+ var GoogleAdsSearchInputSchema = {
4733
+ query: import_zod3.z.string().min(1).describe("A domain (e.g. getviktor.com) or advertiser/brand name to look up in the Google Ads Transparency Center. Domains resolve to the advertisers running ads that point to that site."),
4734
+ region: import_zod3.z.string().length(2).default("US").describe("Two-letter region code for where the ads are shown. Default US. Examples: US, CA, GB, AU."),
4735
+ maxResults: import_zod3.z.number().int().min(1).max(20).default(10).describe("Maximum advertisers to return. Default 10, maximum 20.")
4736
+ };
4737
+ var GoogleAdsPageIntelInputSchema = {
4738
+ advertiserId: import_zod3.z.string().optional().describe("Google Ads Transparency advertiser ID (starts with AR...). Use one returned by google_ads_search; do not construct one yourself."),
4739
+ domain: import_zod3.z.string().optional().describe("A domain (e.g. getviktor.com) whose primary advertiser to inspect when advertiserId is unknown. One of advertiserId or domain is required."),
4740
+ region: import_zod3.z.string().length(2).default("US").describe("Two-letter region code for where the ads are shown. Default US."),
4741
+ maxAds: import_zod3.z.number().int().min(1).max(200).default(50).describe("Maximum creatives to inspect and hydrate. Default 50, maximum 200. Prefer 25-50 for focused scans.")
4742
+ };
4743
+ var GoogleAdsTranscribeInputSchema = {
4744
+ videoUrl: import_zod3.z.string().url().describe("Direct googlevideo.com playback URL from a google_ads_page_intel video ad result (the videoUrl field). For YouTube-hosted ads use youtube_transcribe with the returned youtubeVideoId instead.")
4745
+ };
4582
4746
  var InstagramProfileContentInputSchema = {
4583
4747
  handle: import_zod3.z.string().min(1).optional().describe("Instagram handle, with or without @. Provide handle or url."),
4584
4748
  url: import_zod3.z.string().url().optional().describe("Instagram profile URL, e.g. https://www.instagram.com/nasaartemis/. Provide handle or url."),
@@ -4789,7 +4953,8 @@ var OrganicResultOutput = import_zod3.z.object({
4789
4953
  });
4790
4954
  var AiOverviewOutput = import_zod3.z.object({
4791
4955
  detected: import_zod3.z.boolean(),
4792
- text: NullableString2
4956
+ text: NullableString2,
4957
+ shareUrl: NullableString2.optional()
4793
4958
  }).nullable();
4794
4959
  var EntityIdsOutput = import_zod3.z.object({
4795
4960
  entities: import_zod3.z.array(import_zod3.z.object({
@@ -5023,6 +5188,40 @@ var FacebookPageIntelOutputSchema = {
5023
5188
  variations: import_zod3.z.number().int().nullable()
5024
5189
  }))
5025
5190
  };
5191
+ var GoogleAdsSearchOutputSchema = {
5192
+ query: import_zod3.z.string(),
5193
+ region: import_zod3.z.string(),
5194
+ advertiserCount: import_zod3.z.number().int().min(0),
5195
+ advertisers: import_zod3.z.array(import_zod3.z.object({
5196
+ advertiserId: NullableString2,
5197
+ name: NullableString2,
5198
+ domain: NullableString2,
5199
+ approxAdCount: import_zod3.z.number().int().nullable(),
5200
+ detailUrl: NullableString2
5201
+ }))
5202
+ };
5203
+ var GoogleAdsPageIntelOutputSchema = {
5204
+ advertiserId: NullableString2,
5205
+ advertiserName: NullableString2,
5206
+ domain: NullableString2,
5207
+ region: import_zod3.z.string(),
5208
+ totalCreatives: import_zod3.z.number().int().min(0),
5209
+ videoCount: import_zod3.z.number().int().min(0),
5210
+ imageCount: import_zod3.z.number().int().min(0),
5211
+ textCount: import_zod3.z.number().int().min(0),
5212
+ ads: import_zod3.z.array(import_zod3.z.object({
5213
+ creativeId: NullableString2,
5214
+ advertiserId: NullableString2,
5215
+ format: NullableString2,
5216
+ lastShown: NullableString2,
5217
+ detailUrl: NullableString2,
5218
+ landingDomain: NullableString2,
5219
+ imageUrls: import_zod3.z.array(import_zod3.z.string()),
5220
+ youtubeVideoId: NullableString2,
5221
+ videoUrl: NullableString2,
5222
+ variations: import_zod3.z.number().int().nullable()
5223
+ }))
5224
+ };
5026
5225
  var FacebookVideoTranscribeOutputSchema = {
5027
5226
  sourceUrl: import_zod3.z.string().url(),
5028
5227
  pageUrl: import_zod3.z.string().url(),
@@ -5165,6 +5364,17 @@ var FacebookAdTranscribeOutputSchema = {
5165
5364
  videoUrl: import_zod3.z.string().url()
5166
5365
  })
5167
5366
  };
5367
+ var GoogleAdsTranscribeOutputSchema = {
5368
+ videoUrl: import_zod3.z.string().url(),
5369
+ wordCount: import_zod3.z.number().int().min(0),
5370
+ chunkCount: import_zod3.z.number().int().min(0),
5371
+ durationMs: import_zod3.z.number().nullable(),
5372
+ transcriptText: import_zod3.z.string(),
5373
+ chunks: import_zod3.z.array(TranscriptChunkOutput),
5374
+ resolvedInputs: import_zod3.z.object({
5375
+ videoUrl: import_zod3.z.string().url()
5376
+ })
5377
+ };
5168
5378
  var CaptureSerpSnapshotOutputSchema = {
5169
5379
  schemaVersion: import_zod3.z.literal("serp-intelligence.capture.v1"),
5170
5380
  status: import_zod3.z.string(),
@@ -5809,6 +6019,27 @@ function registerPaaExtractorMcpTools(server2, executor, options = {}) {
5809
6019
  outputSchema: FacebookAdTranscribeOutputSchema,
5810
6020
  annotations: liveWebToolAnnotations("Facebook Ad Transcription")
5811
6021
  }, async (input) => formatFacebookAdTranscribe(await executor.facebookAdTranscribe(input), input));
6022
+ server2.registerTool("google_ads_search", {
6023
+ title: "Google Ads Transparency Search",
6024
+ description: withReportNote("Search the Google Ads Transparency Center to find advertisers running Google ads (Search, Display, YouTube) by domain or brand/advertiser name. Pass a domain like getviktor.com to see every advertiser account pointing ads at that site, or an advertiser name. Returns advertisers with their advertiser ID and an approximate ad count. Use this to discover an advertiserId, then pass it (or the domain) to google_ads_page_intel for the actual ad creatives."),
6025
+ inputSchema: GoogleAdsSearchInputSchema,
6026
+ outputSchema: GoogleAdsSearchOutputSchema,
6027
+ annotations: liveWebToolAnnotations("Google Ads Transparency Search")
6028
+ }, async (input) => formatGoogleAdsSearch(await executor.googleAdsSearch(input), input));
6029
+ server2.registerTool("google_ads_page_intel", {
6030
+ title: "Google Ads Advertiser Intel",
6031
+ description: withReportNote("Harvest an advertiser's ads from the Google Ads Transparency Center when the user wants a competitor's Google/YouTube ad creatives, copy angles, image URLs, or video ads. Accepts an advertiserId (from google_ads_search) or a domain directly. Returns each creative's format (text/image/video), image URLs, landing info, a detail URL, and \u2014 for video ads \u2014 the YouTube video ID and/or a direct video URL ready for transcription. Use google_ads_search first when you only have a brand name. Transcribe video ads with google_ads_transcribe (video URL) or youtube_transcribe (YouTube ID)."),
6032
+ inputSchema: GoogleAdsPageIntelInputSchema,
6033
+ outputSchema: GoogleAdsPageIntelOutputSchema,
6034
+ annotations: liveWebToolAnnotations("Google Ads Advertiser Intel")
6035
+ }, async (input) => formatGoogleAdsPageIntel(await executor.googleAdsPageIntel(input), input));
6036
+ server2.registerTool("google_ads_transcribe", {
6037
+ title: "Google Ad Video Transcription",
6038
+ description: "Transcribe audio from a Google video ad. Use this when google_ads_page_intel returned a direct videoUrl (a googlevideo.com playback URL) for a video creative and the user asks what the ad says or wants its claims/messaging. Returns full transcript, timestamped chunks, and word count. For YouTube-hosted ads, pass the returned youtubeVideoId to youtube_transcribe instead; run google_ads_page_intel first when you only have an advertiser/domain.",
6039
+ inputSchema: GoogleAdsTranscribeInputSchema,
6040
+ outputSchema: GoogleAdsTranscribeOutputSchema,
6041
+ annotations: liveWebToolAnnotations("Google Ad Video Transcription")
6042
+ }, async (input) => formatGoogleAdsTranscribe(await executor.googleAdsTranscribe(input), input));
5812
6043
  server2.registerTool("facebook_video_transcribe", {
5813
6044
  title: "Facebook Organic Video Transcription",
5814
6045
  description: withReportNote("Transcribe audio from an organic Facebook reel, video, watch, post, or share URL, including fb.watch links. Use this when the user pastes a normal Facebook video page URL and wants the transcript or downloadable MP4. Renders the Facebook page in a browser, selects the best matching public Facebook CDN MP4 URL from page state, then returns sourceUrl, resolved pageUrl, videoId, ownerName, selectedQuality, bitrate, videoDurationSec, extracted MP4 URL, full transcript, and timestamped chunks."),