mcp-scraper 0.2.7 → 0.2.9

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 (64) hide show
  1. package/README.md +44 -5
  2. package/dist/bin/api-server.cjs +2417 -648
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +3 -3
  5. package/dist/bin/browser-agent-stdio-server.cjs +1 -1
  6. package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
  7. package/dist/bin/browser-agent-stdio-server.js +2 -2
  8. package/dist/bin/mcp-scraper-cli.cjs +1276 -0
  9. package/dist/bin/mcp-scraper-cli.cjs.map +1 -0
  10. package/dist/bin/mcp-scraper-cli.d.cts +1 -0
  11. package/dist/bin/mcp-scraper-cli.d.ts +1 -0
  12. package/dist/bin/mcp-scraper-cli.js +476 -0
  13. package/dist/bin/mcp-scraper-cli.js.map +1 -0
  14. package/dist/bin/mcp-scraper-combined-stdio-server.cjs +69 -14
  15. package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
  16. package/dist/bin/mcp-scraper-combined-stdio-server.js +3 -3
  17. package/dist/bin/mcp-scraper-install.cjs +5 -1
  18. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  19. package/dist/bin/mcp-scraper-install.js +5 -1
  20. package/dist/bin/mcp-scraper-install.js.map +1 -1
  21. package/dist/bin/mcp-stdio-server.cjs +69 -14
  22. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  23. package/dist/bin/mcp-stdio-server.js +2 -2
  24. package/dist/bin/paa-harvest.cjs +42 -6
  25. package/dist/bin/paa-harvest.cjs.map +1 -1
  26. package/dist/bin/paa-harvest.js +1 -1
  27. package/dist/{chunk-3TF6UT2P.js → chunk-5E6KJC26.js} +2 -2
  28. package/dist/{chunk-D4CJBZBY.js → chunk-7GCCOT3M.js} +372 -1
  29. package/dist/chunk-7GCCOT3M.js.map +1 -0
  30. package/dist/chunk-BJDCPCIE.js +7 -0
  31. package/dist/chunk-BJDCPCIE.js.map +1 -0
  32. package/dist/{chunk-MY3S7EX7.js → chunk-CQTAKXBN.js} +43 -7
  33. package/dist/chunk-CQTAKXBN.js.map +1 -0
  34. package/dist/chunk-L6IS63WS.js +869 -0
  35. package/dist/chunk-L6IS63WS.js.map +1 -0
  36. package/dist/{chunk-IQOCZGJJ.js → chunk-ROS67BNV.js} +5 -5
  37. package/dist/{chunk-7SI6XIR3.js → chunk-XAY5U67D.js} +70 -15
  38. package/dist/chunk-XAY5U67D.js.map +1 -0
  39. package/dist/{db-YWCNHBLH.js → db-BVHYI57K.js} +38 -2
  40. package/dist/index.cjs +42 -6
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.js +1 -1
  43. package/dist/{server-RIKRBDOI.js → server-BKJ7LULQ.js} +818 -394
  44. package/dist/server-BKJ7LULQ.js.map +1 -0
  45. package/dist/{worker-NAKGTIF5.js → worker-TDJQ6TH3.js} +8 -8
  46. package/docs/mcp-tool-quality-spec.md +3 -1
  47. package/docs/specs/agent-ready-seo-packet-spec.md +237 -0
  48. package/docs/specs/cli-agent-wiring-spec.md +203 -0
  49. package/docs/specs/deferred-work-spec.md +12 -0
  50. package/docs/specs/local-competitive-audit-spec.md +312 -0
  51. package/docs/specs/scheduled-workflows-api-spec.md +304 -0
  52. package/docs/specs/seo-cli-growth-roadmap-spec.md +179 -0
  53. package/docs/specs/seo-workflow-runner-and-reports-spec.md +241 -0
  54. package/package.json +3 -2
  55. package/dist/chunk-7SI6XIR3.js.map +0 -1
  56. package/dist/chunk-D4CJBZBY.js.map +0 -1
  57. package/dist/chunk-IZE5UW7Y.js +0 -7
  58. package/dist/chunk-IZE5UW7Y.js.map +0 -1
  59. package/dist/chunk-MY3S7EX7.js.map +0 -1
  60. package/dist/server-RIKRBDOI.js.map +0 -1
  61. /package/dist/{chunk-3TF6UT2P.js.map → chunk-5E6KJC26.js.map} +0 -0
  62. /package/dist/{chunk-IQOCZGJJ.js.map → chunk-ROS67BNV.js.map} +0 -0
  63. /package/dist/{db-YWCNHBLH.js.map → db-BVHYI57K.js.map} +0 -0
  64. /package/dist/{worker-NAKGTIF5.js.map → worker-TDJQ6TH3.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -272,6 +272,8 @@ var MOBILE_USER_AGENT = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X)
272
272
  var DEFAULT_KERNEL_BROWSER_TIMEOUT_SECONDS = 180;
273
273
  var KERNEL_BROWSER_CLOSE_TIMEOUT_MS = 3e3;
274
274
  var KERNEL_SESSION_DELETE_TIMEOUT_MS = 5e3;
275
+ var DEFAULT_SERP_CHALLENGE_SOLVE_WAIT_MS = 2500;
276
+ var SERP_CHALLENGE_SOLVE_POLL_MS = 500;
275
277
  function positiveIntFromEnv(name, fallback) {
276
278
  const raw = process.env[name];
277
279
  if (!raw) return fallback;
@@ -583,20 +585,20 @@ var BrowserDriver = class {
583
585
  const diag = await this.captureDiagnostics(url);
584
586
  throw new ExtractionError(`page.goto failed: ${err.message} | ${diag}`);
585
587
  }
586
- const captchaCount = await this.page.locator(PAASelectors.captchaMarker).count();
587
- if (captchaCount > 0) {
588
+ if (await this.isSerpChallengeVisible()) {
588
589
  await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: false, captchaDetected: true });
589
- throw new CaptchaError(this.captchaMessage());
590
+ const solved = await this.waitForSerpChallengeToClear(navDebug, url);
591
+ if (!solved) throw new CaptchaError(this.captchaMessage());
590
592
  }
591
593
  const fastFound = await this.page.waitForSelector(PAASelectors.item, { timeout: 4e3 }).catch(() => null);
592
594
  if (fastFound) {
593
595
  await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: true, captchaDetected: false });
594
596
  return { hasPaa: true };
595
597
  }
596
- const captchaAfter = await this.page.locator(PAASelectors.captchaMarker).count();
597
- if (captchaAfter > 0) {
598
+ if (await this.isSerpChallengeVisible()) {
598
599
  await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: false, captchaDetected: true });
599
- throw new CaptchaError(this.captchaMessage());
600
+ const solved = await this.waitForSerpChallengeToClear(navDebug, url);
601
+ if (!solved) throw new CaptchaError(this.captchaMessage());
600
602
  }
601
603
  for (let i = 1; i <= 6; i++) {
602
604
  await this.page.evaluate((f) => {
@@ -612,6 +614,35 @@ var BrowserDriver = class {
612
614
  await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: false, captchaDetected: false });
613
615
  return { hasPaa: false };
614
616
  }
617
+ async isSerpChallengeVisible() {
618
+ if (!this.page) return false;
619
+ const captchaCount = await this.page.locator(PAASelectors.captchaMarker).count().catch(() => 0);
620
+ if (captchaCount > 0) return true;
621
+ const finalUrl = this.page.url();
622
+ if (/google\.[^/]+\/sorry\//i.test(finalUrl)) return true;
623
+ const bodySnippet = await this.page.evaluate(() => {
624
+ const text = (document.body?.innerText ?? "").replace(/\s+/g, " ").trim();
625
+ return text.slice(0, 1e3);
626
+ }).catch(() => "");
627
+ return /recaptcha|unusual traffic|are you a robot|about this page|detected unusual traffic/i.test(bodySnippet);
628
+ }
629
+ async waitForSerpChallengeToClear(navDebug, requestedUrl) {
630
+ if (!this.kernelClient || !this.page) return false;
631
+ const waitMs = positiveIntFromEnv("SERP_CHALLENGE_SOLVE_WAIT_MS", DEFAULT_SERP_CHALLENGE_SOLVE_WAIT_MS);
632
+ const deadline = Date.now() + waitMs;
633
+ while (Date.now() < deadline) {
634
+ await this.page.waitForTimeout(Math.min(SERP_CHALLENGE_SOLVE_POLL_MS, Math.max(250, deadline - Date.now())));
635
+ if (!await this.isSerpChallengeVisible()) {
636
+ await this.page.waitForTimeout(1e3);
637
+ if (!await this.isSerpChallengeVisible()) {
638
+ await this.updateSerpNavigationDebug(navDebug, requestedUrl, { hasPaa: null, captchaDetected: false });
639
+ return true;
640
+ }
641
+ }
642
+ }
643
+ await this.updateSerpNavigationDebug(navDebug, requestedUrl, { hasPaa: false, captchaDetected: true });
644
+ return false;
645
+ }
615
646
  async updateSerpNavigationDebug(navDebug, requestedUrl, state) {
616
647
  if (!navDebug || !this.page) return;
617
648
  try {
@@ -659,7 +690,12 @@ var BrowserDriver = class {
659
690
  async navigateTo(url) {
660
691
  try {
661
692
  await this.page.goto(url, { waitUntil: "domcontentloaded", timeout: 45e3 });
693
+ if (/google\.[^/]+\/search\?/i.test(url) && await this.isSerpChallengeVisible()) {
694
+ const solved = await this.waitForSerpChallengeToClear(null, url);
695
+ if (!solved) throw new CaptchaError(this.captchaMessage());
696
+ }
662
697
  } catch (err) {
698
+ if (err instanceof CaptchaError) throw err;
663
699
  const diag = await this.captureDiagnostics(url);
664
700
  throw new ExtractionError(`page.goto failed: ${err.message} | ${diag}`);
665
701
  }