mcp-scraper 0.33.6 → 0.34.0

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 +2 -2
  2. package/dist/bin/api-server.cjs +13558 -12516
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +3 -3
  5. package/dist/bin/mcp-scraper-cli.cjs +13 -2
  6. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  7. package/dist/bin/mcp-scraper-cli.js +14 -4
  8. package/dist/bin/mcp-scraper-cli.js.map +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs +2 -2
  10. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  11. package/dist/bin/mcp-scraper-install.js +2 -2
  12. package/dist/bin/mcp-stdio-server.cjs +117 -7
  13. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  14. package/dist/bin/mcp-stdio-server.js +7 -7
  15. package/dist/bin/paa-harvest.cjs +128 -13
  16. package/dist/bin/paa-harvest.cjs.map +1 -1
  17. package/dist/bin/paa-harvest.js +4 -4
  18. package/dist/{chunk-DUS4ENOY.js → chunk-2EDFOQD7.js} +2 -2
  19. package/dist/{chunk-DUS4ENOY.js.map → chunk-2EDFOQD7.js.map} +1 -1
  20. package/dist/{chunk-C2OD4ELT.js → chunk-3HBPKR5G.js} +2 -2
  21. package/dist/{chunk-A54ELKJK.js → chunk-4KQVIYIH.js} +139 -17
  22. package/dist/chunk-4KQVIYIH.js.map +1 -0
  23. package/dist/{chunk-AIK7WQD5.js → chunk-62DQAWPF.js} +141 -3
  24. package/dist/chunk-62DQAWPF.js.map +1 -0
  25. package/dist/{chunk-V73MPRU6.js → chunk-CB5C3BPB.js} +17 -1
  26. package/dist/chunk-CB5C3BPB.js.map +1 -0
  27. package/dist/{chunk-6ZUK4H66.js → chunk-J32XSMJI.js} +117 -8
  28. package/dist/chunk-J32XSMJI.js.map +1 -0
  29. package/dist/{chunk-LKIP7VDN.js → chunk-JBSGBSGT.js} +46 -3
  30. package/dist/chunk-JBSGBSGT.js.map +1 -0
  31. package/dist/chunk-NFMIHEYE.js +7 -0
  32. package/dist/chunk-NFMIHEYE.js.map +1 -0
  33. package/dist/{chunk-XL6YIWJ3.js → chunk-SUOHUXQS.js} +3 -3
  34. package/dist/{chunk-WJJTGRK4.js → chunk-V36LS5YV.js} +2 -2
  35. package/dist/{chunk-AT2PFWHW.js → chunk-ZID3WQID.js} +2 -2
  36. package/dist/{db-5SZQUI7A.js → db-YAI5AQOI.js} +16 -2
  37. package/dist/{extract-bundle-YNXTFCC3.js → extract-bundle-GPZRLBVY.js} +2 -2
  38. package/dist/index.cjs +128 -13
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +4 -1
  41. package/dist/index.d.ts +4 -1
  42. package/dist/index.js +4 -4
  43. package/dist/{server-SHOYO52X.js → server-LKVPEQFE.js} +1386 -778
  44. package/dist/server-LKVPEQFE.js.map +1 -0
  45. package/dist/{site-extract-repository-ZDTO7ODZ.js → site-extract-repository-JHMVHENZ.js} +4 -4
  46. package/dist/{worker-BJ64GCDE.js → worker-ZZHSYI3Y.js} +7 -7
  47. package/docs/mcp-tool-manifest.generated.json +213 -3
  48. package/package.json +1 -1
  49. package/dist/chunk-6ZUK4H66.js.map +0 -1
  50. package/dist/chunk-A54ELKJK.js.map +0 -1
  51. package/dist/chunk-AIK7WQD5.js.map +0 -1
  52. package/dist/chunk-F65IIUHI.js +0 -7
  53. package/dist/chunk-F65IIUHI.js.map +0 -1
  54. package/dist/chunk-LKIP7VDN.js.map +0 -1
  55. package/dist/chunk-V73MPRU6.js.map +0 -1
  56. package/dist/server-SHOYO52X.js.map +0 -1
  57. /package/dist/{chunk-C2OD4ELT.js.map → chunk-3HBPKR5G.js.map} +0 -0
  58. /package/dist/{chunk-XL6YIWJ3.js.map → chunk-SUOHUXQS.js.map} +0 -0
  59. /package/dist/{chunk-WJJTGRK4.js.map → chunk-V36LS5YV.js.map} +0 -0
  60. /package/dist/{chunk-AT2PFWHW.js.map → chunk-ZID3WQID.js.map} +0 -0
  61. /package/dist/{db-5SZQUI7A.js.map → db-YAI5AQOI.js.map} +0 -0
  62. /package/dist/{extract-bundle-YNXTFCC3.js.map → extract-bundle-GPZRLBVY.js.map} +0 -0
  63. /package/dist/{site-extract-repository-ZDTO7ODZ.js.map → site-extract-repository-JHMVHENZ.js.map} +0 -0
  64. /package/dist/{worker-BJ64GCDE.js.map → worker-ZZHSYI3Y.js.map} +0 -0
@@ -7,19 +7,19 @@ import {
7
7
  registerMemoryMcpTools,
8
8
  registerPaaExtractorMcpTools,
9
9
  registerSerpIntelligenceCaptureTools
10
- } from "../chunk-6ZUK4H66.js";
10
+ } from "../chunk-J32XSMJI.js";
11
11
  import "../chunk-R7EETU7Z.js";
12
- import "../chunk-WJJTGRK4.js";
12
+ import "../chunk-V36LS5YV.js";
13
13
  import {
14
14
  renderInstallTerminal
15
- } from "../chunk-DUS4ENOY.js";
16
- import "../chunk-V73MPRU6.js";
15
+ } from "../chunk-2EDFOQD7.js";
16
+ import "../chunk-CB5C3BPB.js";
17
17
  import {
18
18
  PACKAGE_VERSION
19
- } from "../chunk-F65IIUHI.js";
20
- import "../chunk-LKIP7VDN.js";
19
+ } from "../chunk-NFMIHEYE.js";
20
+ import "../chunk-JBSGBSGT.js";
21
21
  import "../chunk-M2S27J6Z.js";
22
- import "../chunk-AIK7WQD5.js";
22
+ import "../chunk-62DQAWPF.js";
23
23
 
24
24
  // bin/mcp-stdio-server.ts
25
25
  import { readFileSync } from "fs";
@@ -63,8 +63,23 @@ var HarvestOptionsSchema = import_zod.z.object({
63
63
  format: import_zod.z.enum(["json", "csv", "both"]).default("both"),
64
64
  serpOnly: import_zod.z.boolean().default(false),
65
65
  pages: import_zod.z.number().int().min(1).max(2).default(1),
66
+ recency: import_zod.z.enum(["day", "week", "month", "year"]).optional(),
66
67
  softDeadlineMs: import_zod.z.number().optional()
67
68
  });
69
+ function recencyToTbs(recency) {
70
+ switch (recency) {
71
+ case "day":
72
+ return "qdr:d";
73
+ case "week":
74
+ return "qdr:w";
75
+ case "month":
76
+ return "qdr:m";
77
+ case "year":
78
+ return "qdr:y";
79
+ default:
80
+ return void 0;
81
+ }
82
+ }
68
83
  var MapsPlaceOptionsSchema = import_zod.z.object({
69
84
  businessName: import_zod.z.string().min(1),
70
85
  location: import_zod.z.string().min(1),
@@ -836,6 +851,7 @@ var BrowserDriver = class {
836
851
  async navigateToSERP(query, uule, gl, hl, options) {
837
852
  const params = new URLSearchParams({ q: query, gl, hl, pws: "0" });
838
853
  if (options?.num) params.set("num", String(options.num));
854
+ if (options?.tbs) params.set("tbs", options.tbs);
839
855
  if (uule) params.set("uule", uule);
840
856
  const url = "https://www.google.com/search?" + params.toString();
841
857
  const navUrl = serpArrivalUrl(url);
@@ -2402,6 +2418,7 @@ var PAAExtractor = class {
2402
2418
  options.hl,
2403
2419
  {
2404
2420
  ...options.serpOnly ? { num: 100 } : {},
2421
+ ...recencyToTbs(options.recency) ? { tbs: recencyToTbs(options.recency) } : {},
2405
2422
  debug: options.debug
2406
2423
  }
2407
2424
  );
@@ -2421,6 +2438,8 @@ var PAAExtractor = class {
2421
2438
  let allOrganic2 = organicResults2;
2422
2439
  if ((options.pages ?? 1) >= 2) {
2423
2440
  const p2params = new URLSearchParams({ q: options.query, gl: options.gl, hl: options.hl, pws: "0", start: "10" });
2441
+ if (recencyToTbs(options.recency)) p2params.set("tbs", recencyToTbs(options.recency));
2442
+ if (recencyToTbs(options.recency)) p2params.set("tbs", recencyToTbs(options.recency));
2424
2443
  if (uule) p2params.set("uule", uule);
2425
2444
  await this.driver.navigateTo("https://www.google.com/search?" + p2params.toString());
2426
2445
  await this.throwIfCaptcha(page, "Google SERP page 2");
@@ -2480,6 +2499,8 @@ var PAAExtractor = class {
2480
2499
  let locationEvidence2 = initialLocationEvidence;
2481
2500
  if ((options.pages ?? 1) >= 2) {
2482
2501
  const p2params = new URLSearchParams({ q: options.query, gl: options.gl, hl: options.hl, pws: "0", start: "10" });
2502
+ if (recencyToTbs(options.recency)) p2params.set("tbs", recencyToTbs(options.recency));
2503
+ if (recencyToTbs(options.recency)) p2params.set("tbs", recencyToTbs(options.recency));
2483
2504
  if (uule) p2params.set("uule", uule);
2484
2505
  await this.driver.navigateTo("https://www.google.com/search?" + p2params.toString());
2485
2506
  await this.throwIfCaptcha(page, "Google SERP page 2");
@@ -2546,6 +2567,7 @@ var PAAExtractor = class {
2546
2567
  let locationEvidence = initialLocationEvidence;
2547
2568
  if ((options.pages ?? 1) >= 2) {
2548
2569
  const p2params = new URLSearchParams({ q: options.query, gl: options.gl, hl: options.hl, pws: "0", start: "10" });
2570
+ if (recencyToTbs(options.recency)) p2params.set("tbs", recencyToTbs(options.recency));
2549
2571
  if (uule) p2params.set("uule", uule);
2550
2572
  await this.driver.navigateTo("https://www.google.com/search?" + p2params.toString());
2551
2573
  await this.throwIfCaptcha(page, "Google SERP page 2");
@@ -2731,7 +2753,76 @@ var ProgressReporter = class {
2731
2753
  };
2732
2754
 
2733
2755
  // src/kernel-proxy-resolver.ts
2756
+ var import_sdk3 = __toESM(require("@onkernel/sdk"), 1);
2757
+
2758
+ // src/backup-proxy.ts
2759
+ var import_node_crypto3 = require("crypto");
2734
2760
  var import_sdk2 = __toESM(require("@onkernel/sdk"), 1);
2761
+ var BACKUP_PROXY_HOST = "pr.oxylabs.io";
2762
+ var BACKUP_PROXY_PORT = 7777;
2763
+ var BACKUP_STICKY_SESSION_MINUTES = 10;
2764
+ var BACKUP_LAST_RESORT_ATTEMPTS = 2;
2765
+ function backupProxyUsername() {
2766
+ const value = process.env.OXYLABS_USERNAME?.trim();
2767
+ return value || void 0;
2768
+ }
2769
+ function backupProxyPassword() {
2770
+ const value = process.env.OXYLABS_PASSWORD?.trim();
2771
+ return value || void 0;
2772
+ }
2773
+ function backupProxyCountry() {
2774
+ const value = process.env.OXYLABS_COUNTRY?.trim();
2775
+ return (value || "US").toUpperCase();
2776
+ }
2777
+ function backupProxyAvailable() {
2778
+ return Boolean(backupProxyUsername() && backupProxyPassword());
2779
+ }
2780
+ function randomStickySessionId() {
2781
+ let digits = "";
2782
+ for (let i = 0; i < 10; i++) digits += String((0, import_node_crypto3.randomInt)(0, 10));
2783
+ return digits;
2784
+ }
2785
+ function buildBackupUsername(opts = {}) {
2786
+ const user = backupProxyUsername();
2787
+ if (!user) return void 0;
2788
+ const base = user.startsWith("customer-") ? user : `customer-${user}`;
2789
+ const country = (opts.country?.trim() || backupProxyCountry()).toUpperCase();
2790
+ const sticky = opts.sticky ? `-sessid-${randomStickySessionId()}-sesstime-${BACKUP_STICKY_SESSION_MINUTES}` : "";
2791
+ return `${base}-cc-${country}${sticky}`;
2792
+ }
2793
+ function scrubBackupProxyText(message) {
2794
+ return message.replace(/pr\.oxylabs\.io(:\d+)?/gi, "backup proxy").replace(/oxylabs/gi, "backup proxy");
2795
+ }
2796
+ function backupProxyName(attemptIndex) {
2797
+ const stamp = `${Date.now()}-${attemptIndex ?? 0}-${Math.random().toString(36).slice(2, 8)}`;
2798
+ return `mcp-backup-custom-${stamp}`;
2799
+ }
2800
+ async function createBackupProxyId(kernelApiKey, opts = {}) {
2801
+ const username = buildBackupUsername({ sticky: opts.sticky });
2802
+ const password = backupProxyPassword();
2803
+ if (!username || !password) throw new Error("backup proxy pool is not configured");
2804
+ const kernel = new import_sdk2.default({ apiKey: kernelApiKey });
2805
+ try {
2806
+ const created = await kernel.proxies.create({
2807
+ type: "custom",
2808
+ name: backupProxyName(opts.attemptIndex),
2809
+ protocol: "http",
2810
+ config: {
2811
+ host: BACKUP_PROXY_HOST,
2812
+ port: BACKUP_PROXY_PORT,
2813
+ username,
2814
+ password
2815
+ }
2816
+ });
2817
+ if (!created.id) throw new Error("backup proxy creation did not return a proxy id");
2818
+ return created.id;
2819
+ } catch (err) {
2820
+ const message = err instanceof Error ? err.message : String(err);
2821
+ throw new Error(scrubBackupProxyText(message));
2822
+ }
2823
+ }
2824
+
2825
+ // src/kernel-proxy-resolver.ts
2735
2826
  var US_STATE_CODES = {
2736
2827
  alabama: "AL",
2737
2828
  alaska: "AK",
@@ -2829,7 +2920,7 @@ function proxyIdSuffix2(proxyId) {
2829
2920
  return proxyId ? proxyId.slice(-6) : null;
2830
2921
  }
2831
2922
  function resolution(source, proxyMode, proxyId, target, error, disposable = false) {
2832
- const proxyType = source === "disabled" ? "none" : source === "configured_fallback" ? "configured" : source === "isp_created" ? "isp" : source === "location_created" || source === "location_reused" ? "residential" : "unknown";
2923
+ const proxyType = source === "disabled" ? "none" : source === "configured_fallback" ? "configured" : source === "isp_created" ? "isp" : source === "backup_custom" ? "custom" : source === "location_created" || source === "location_reused" ? "residential" : "unknown";
2833
2924
  return {
2834
2925
  kernelProxyId: proxyId,
2835
2926
  ...disposable && proxyId ? { disposableProxyId: proxyId } : {},
@@ -3013,6 +3104,26 @@ function freshTargetCandidates(target, explicitZip, attemptIndex) {
3013
3104
  out.push(withProxyName(fallbackTarget, freshProxyName(fallbackTarget.proxyName, attemptIndex)));
3014
3105
  return out;
3015
3106
  }
3107
+ async function settleCreateExhaustion(options, target, createErrors) {
3108
+ if (options.backupOnExhaustion && createErrors.length > 0 && options.kernelApiKey && backupProxyAvailable()) {
3109
+ try {
3110
+ const backupId = await createBackupProxyId(options.kernelApiKey, { attemptIndex: options.attemptIndex });
3111
+ return resolution("backup_custom", options.proxyMode, backupId, target, null, true);
3112
+ } catch (err) {
3113
+ createErrors.push(`backup: ${scrubBackupProxyText(errorText2(err))}`);
3114
+ }
3115
+ }
3116
+ return resolution("configured_fallback", options.proxyMode, options.configuredKernelProxyId, target, createErrors.join(" | "));
3117
+ }
3118
+ async function resolveBackupKernelProxyId(options) {
3119
+ if (!options.kernelApiKey || !backupProxyAvailable()) return null;
3120
+ try {
3121
+ const backupId = await createBackupProxyId(options.kernelApiKey, { attemptIndex: options.attemptIndex });
3122
+ return resolution("backup_custom", options.proxyMode, backupId, null, null, true);
3123
+ } catch {
3124
+ return null;
3125
+ }
3126
+ }
3016
3127
  async function createFreshLocationProxy(kernel, options, target) {
3017
3128
  const createErrors = [];
3018
3129
  for (const candidate of freshTargetCandidates(target, options.proxyZip, options.attemptIndex)) {
@@ -3046,11 +3157,11 @@ async function createFreshLocationProxy(kernel, options, target) {
3046
3157
  createErrors.push(`isp: ${errorText2(err)}`);
3047
3158
  }
3048
3159
  }
3049
- return resolution("configured_fallback", options.proxyMode, options.configuredKernelProxyId, target, createErrors.join(" | "));
3160
+ return settleCreateExhaustion(options, target, createErrors);
3050
3161
  }
3051
3162
  async function deleteKernelProxyId(kernelApiKey, proxyId) {
3052
3163
  if (!kernelApiKey || !proxyId) return;
3053
- const kernel = new import_sdk2.default({ apiKey: kernelApiKey });
3164
+ const kernel = new import_sdk3.default({ apiKey: kernelApiKey });
3054
3165
  await kernel.proxies.delete(proxyId);
3055
3166
  }
3056
3167
  async function resolveKernelProxyId(options) {
@@ -3064,7 +3175,7 @@ async function resolveKernelProxyId(options) {
3064
3175
  if (!target || !options.kernelApiKey) {
3065
3176
  return resolution("configured_fallback", options.proxyMode, options.configuredKernelProxyId, target, target ? null : "location could not be normalized to a US city/state proxy target");
3066
3177
  }
3067
- const kernel = new import_sdk2.default({ apiKey: options.kernelApiKey });
3178
+ const kernel = new import_sdk3.default({ apiKey: options.kernelApiKey });
3068
3179
  try {
3069
3180
  const attemptIndex = options.attemptIndex ?? 0;
3070
3181
  if (options.fresh) {
@@ -3086,7 +3197,7 @@ async function resolveKernelProxyId(options) {
3086
3197
  } catch (err) {
3087
3198
  createErrors2.push(`${escalatedTarget.state}: ${errorText2(err)}`);
3088
3199
  }
3089
- return resolution("configured_fallback", options.proxyMode, options.configuredKernelProxyId, escalatedTarget, createErrors2.join(" | "));
3200
+ return settleCreateExhaustion(options, escalatedTarget, createErrors2);
3090
3201
  }
3091
3202
  const proxies = await kernel.proxies.list();
3092
3203
  const zip = knownZipFor(target, options.proxyZip);
@@ -3165,7 +3276,7 @@ async function resolveKernelProxyId(options) {
3165
3276
  } catch (err) {
3166
3277
  createErrors.push(`${fallbackTarget.state}: ${errorText2(err)}`);
3167
3278
  }
3168
- return resolution("configured_fallback", options.proxyMode, options.configuredKernelProxyId, target, createErrors.join(" | "));
3279
+ return settleCreateExhaustion(options, target, createErrors);
3169
3280
  } catch (err) {
3170
3281
  return resolution("configured_fallback", options.proxyMode, options.configuredKernelProxyId, target, errorText2(err));
3171
3282
  }
@@ -3330,7 +3441,9 @@ async function harvest(rawOptions) {
3330
3441
  const requestedDebug = typeof raw.debug === "boolean" ? raw.debug : false;
3331
3442
  const needsLocationEvidence = proxyMode === "location" && Boolean(proxyOpts.location);
3332
3443
  const requestedMaxAttempts = raw.maxAttempts;
3333
- const maxAttempts = typeof requestedMaxAttempts === "number" && Number.isInteger(requestedMaxAttempts) && requestedMaxAttempts >= 1 ? requestedMaxAttempts : maxAttemptsForProxyMode(proxyMode) + 1;
3444
+ const baseMaxAttempts = typeof requestedMaxAttempts === "number" && Number.isInteger(requestedMaxAttempts) && requestedMaxAttempts >= 1 ? requestedMaxAttempts : maxAttemptsForProxyMode(proxyMode) + 1;
3445
+ const maxAttempts = baseMaxAttempts + (backupProxyAvailable() ? BACKUP_LAST_RESORT_ATTEMPTS : 0);
3446
+ const softDeadlineMs = typeof raw.softDeadlineMs === "number" ? raw.softDeadlineMs : null;
3334
3447
  const serializer = new OutputSerializer();
3335
3448
  let lastError = null;
3336
3449
  let effectiveProxyMode = proxyMode;
@@ -3340,12 +3453,16 @@ async function harvest(rawOptions) {
3340
3453
  const startedAtMs = Date.now();
3341
3454
  try {
3342
3455
  if (signal?.aborted) throw abortReason(signal);
3343
- const resolution2 = await resolveKernelProxyId({
3456
+ const isBackupAttempt = i >= baseMaxAttempts;
3457
+ if (isBackupAttempt && softDeadlineMs !== null && Date.now() >= softDeadlineMs) break;
3458
+ const resolution2 = isBackupAttempt ? await resolveBackupKernelProxyId({ kernelApiKey, proxyMode: effectiveProxyMode, attemptIndex: i }) : await resolveKernelProxyId({
3344
3459
  ...proxyOpts,
3345
3460
  proxyMode: effectiveProxyMode,
3346
3461
  attemptIndex: i,
3347
- fresh: effectiveProxyMode === "location"
3462
+ fresh: effectiveProxyMode === "location",
3463
+ backupOnExhaustion: true
3348
3464
  });
3465
+ if (!resolution2) break;
3349
3466
  const mergedAttempt = {
3350
3467
  ...raw,
3351
3468
  kernelApiKey,
@@ -3354,7 +3471,7 @@ async function harvest(rawOptions) {
3354
3471
  proxyMode: effectiveProxyMode,
3355
3472
  debug: requestedDebug || needsLocationEvidence
3356
3473
  };
3357
- if (effectiveProxyMode === "none") mergedAttempt.kernelProxyId = void 0;
3474
+ if (effectiveProxyMode === "none" && !isBackupAttempt) mergedAttempt.kernelProxyId = void 0;
3358
3475
  const attemptOptions = HarvestOptionsSchema.parse(mergedAttempt);
3359
3476
  await emitAttemptEvent(onAttemptEvent, {
3360
3477
  type: "started",
@@ -3373,10 +3490,8 @@ async function harvest(rawOptions) {
3373
3490
  location: attemptOptions.location ?? null,
3374
3491
  max_questions: attemptOptions.maxQuestions
3375
3492
  }));
3376
- const isHeadfulEscalation = i === maxAttempts - 1;
3377
- const needHeadful = isHeadfulEscalation || !attemptOptions.serpOnly;
3378
3493
  const baseCtx = currentCostContext();
3379
- const attempt = needHeadful && baseCtx ? await runWithCostContext({ ...baseCtx, forceHeadful: true }, () => extractOnce(attemptOptions, signal)) : await extractOnce(attemptOptions, signal);
3494
+ const attempt = baseCtx ? await runWithCostContext({ ...baseCtx, forceHeadful: true }, () => extractOnce(attemptOptions, signal)) : await extractOnce(attemptOptions, signal);
3380
3495
  if (attempt.error) {
3381
3496
  const err = attempt.error;
3382
3497
  const outcome = classifyAttemptError(err);