mcp-scraper 0.3.24 → 0.3.25
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/dist/bin/api-server.cjs +8140 -5941
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/mcp-scraper-cli.cjs +320 -58
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +4 -3
- package/dist/bin/mcp-scraper-cli.js.map +1 -1
- package/dist/bin/mcp-scraper-install.cjs +3 -3
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.cjs +121 -24
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +5 -5
- package/dist/bin/paa-harvest.cjs +284 -10
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +5 -3
- package/dist/bin/paa-harvest.js.map +1 -1
- package/dist/{chunk-ZAP7MYVO.js → chunk-3PRO376E.js} +2 -3
- package/dist/chunk-3PRO376E.js.map +1 -0
- package/dist/{chunk-LFATOGDF.js → chunk-BP27CZ5Q.js} +4 -3
- package/dist/chunk-BP27CZ5Q.js.map +1 -0
- package/dist/{chunk-DE7WP4UU.js → chunk-DOBQN3EY.js} +3 -3
- package/dist/chunk-DOBQN3EY.js.map +1 -0
- package/dist/{chunk-GZ4W72WF.js → chunk-FE2WC4JR.js} +119 -23
- package/dist/chunk-FE2WC4JR.js.map +1 -0
- package/dist/{chunk-IGOEAKC7.js → chunk-G7PQ64LM.js} +274 -9
- package/dist/chunk-G7PQ64LM.js.map +1 -0
- package/dist/chunk-K443GQY5.js +24 -0
- package/dist/chunk-K443GQY5.js.map +1 -0
- package/dist/{chunk-H2R232HK.js → chunk-NNEIXK5L.js} +307 -62
- package/dist/chunk-NNEIXK5L.js.map +1 -0
- package/dist/{chunk-3KYRG7O7.js → chunk-TRNSXR46.js} +185 -2
- package/dist/chunk-TRNSXR46.js.map +1 -0
- package/dist/chunk-WPHAMNT7.js +7 -0
- package/dist/chunk-WPHAMNT7.js.map +1 -0
- package/dist/{chunk-JTWQGPKF.js → chunk-WYCER2HW.js} +33 -10
- package/dist/chunk-WYCER2HW.js.map +1 -0
- package/dist/{db-EG5ETPTY.js → db-FP2ABUU4.js} +32 -2
- package/dist/index.cjs +284 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/{server-2SRARAO5.js → server-RRCKVBHG.js} +1563 -265
- package/dist/server-RRCKVBHG.js.map +1 -0
- package/dist/{worker-UM5UM6O5.js → worker-UC6D2756.js} +7 -6
- package/dist/{worker-UM5UM6O5.js.map → worker-UC6D2756.js.map} +1 -1
- package/docs/kernel-proxy-future-enhancements.md +80 -0
- package/docs/spec-browser-routing.md +108 -0
- package/docs/spec-mcp-tools.md +146 -0
- package/docs/specs/audit-visual-demo.md +309 -0
- package/package.json +5 -2
- package/dist/chunk-3KYRG7O7.js.map +0 -1
- package/dist/chunk-DE7WP4UU.js.map +0 -1
- package/dist/chunk-GZ4W72WF.js.map +0 -1
- package/dist/chunk-H2R232HK.js.map +0 -1
- package/dist/chunk-H7R4PPMG.js +0 -7
- package/dist/chunk-H7R4PPMG.js.map +0 -1
- package/dist/chunk-IGOEAKC7.js.map +0 -1
- package/dist/chunk-JTWQGPKF.js.map +0 -1
- package/dist/chunk-LFATOGDF.js.map +0 -1
- package/dist/chunk-ZAP7MYVO.js.map +0 -1
- package/dist/server-2SRARAO5.js.map +0 -1
- /package/dist/{db-EG5ETPTY.js.map → db-FP2ABUU4.js.map} +0 -0
package/dist/bin/paa-harvest.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
harvest
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-G7PQ64LM.js";
|
|
5
5
|
import {
|
|
6
6
|
browserServiceApiKey
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-3PRO376E.js";
|
|
8
|
+
import "../chunk-K443GQY5.js";
|
|
9
|
+
import "../chunk-BP27CZ5Q.js";
|
|
10
|
+
import "../chunk-TRNSXR46.js";
|
|
9
11
|
|
|
10
12
|
// src/cli.ts
|
|
11
13
|
import { Command } from "commander";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli.ts","../../bin/paa-harvest.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { browserServiceApiKey } from './lib/browser-service-env.js'\nimport { harvest } from './harvest.js'\n\nconst program = new Command()\n\nprogram\n .name('paa-harvest')\n .description('Recursively extract Google People Also Ask questions')\n .requiredOption('-q, --query <query>', 'Seed query')\n .option('-l, --location <location>', 'Location name (e.g. \"austin\" or \"Austin,Texas,United States\")')\n .option('--gl <gl>', 'Google country code', 'us')\n .option('--hl <hl>', 'Google language code', 'en')\n .option('-d, --depth <depth>', 'BFS depth (1-30)', '3')\n .option('-m, --max-questions <n>', 'Max questions to harvest', '100')\n .option('-o, --output <dir>', 'Output directory', './paa-output')\n .option('-f, --format <format>', 'Output format: json, csv, or both', 'both')\n .option('--headless', 'Run browser in headless mode', false)\n .option('--profile <dir>', 'Persistent browser profile directory')\n .option('--proxy <url>', 'Proxy server URL')\n .option('--kernel-api-key <key>', 'Browser service API key (or set BROWSER_SERVICE_API_KEY env var)')\n .action(async (opts) => {\n try {\n const result = await harvest({\n query: opts.query,\n location: opts.location,\n gl: opts.gl,\n hl: opts.hl,\n depth: parseInt(opts.depth, 10),\n maxQuestions: parseInt(opts.maxQuestions, 10),\n outputDir: opts.output,\n format: opts.format,\n headless: opts.headless,\n profileDir: opts.profile,\n proxy: opts.proxy,\n kernelApiKey: opts.kernelApiKey ?? browserServiceApiKey(),\n })\n console.log(JSON.stringify({ totalQuestions: result.totalQuestions, outputDir: result.stats.seed }))\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n })\n\nexport async function runCli(): Promise<void> {\n await program.parseAsync()\n}\n","#!/usr/bin/env node\nimport { runCli } from '../src/cli.js'\nrunCli()\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/cli.ts","../../bin/paa-harvest.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { browserServiceApiKey } from './lib/browser-service-env.js'\nimport { harvest } from './harvest.js'\n\nconst program = new Command()\n\nprogram\n .name('paa-harvest')\n .description('Recursively extract Google People Also Ask questions')\n .requiredOption('-q, --query <query>', 'Seed query')\n .option('-l, --location <location>', 'Location name (e.g. \"austin\" or \"Austin,Texas,United States\")')\n .option('--gl <gl>', 'Google country code', 'us')\n .option('--hl <hl>', 'Google language code', 'en')\n .option('-d, --depth <depth>', 'BFS depth (1-30)', '3')\n .option('-m, --max-questions <n>', 'Max questions to harvest', '100')\n .option('-o, --output <dir>', 'Output directory', './paa-output')\n .option('-f, --format <format>', 'Output format: json, csv, or both', 'both')\n .option('--headless', 'Run browser in headless mode', false)\n .option('--profile <dir>', 'Persistent browser profile directory')\n .option('--proxy <url>', 'Proxy server URL')\n .option('--kernel-api-key <key>', 'Browser service API key (or set BROWSER_SERVICE_API_KEY env var)')\n .action(async (opts) => {\n try {\n const result = await harvest({\n query: opts.query,\n location: opts.location,\n gl: opts.gl,\n hl: opts.hl,\n depth: parseInt(opts.depth, 10),\n maxQuestions: parseInt(opts.maxQuestions, 10),\n outputDir: opts.output,\n format: opts.format,\n headless: opts.headless,\n profileDir: opts.profile,\n proxy: opts.proxy,\n kernelApiKey: opts.kernelApiKey ?? browserServiceApiKey(),\n })\n console.log(JSON.stringify({ totalQuestions: result.totalQuestions, outputDir: result.stats.seed }))\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n })\n\nexport async function runCli(): Promise<void> {\n await program.parseAsync()\n}\n","#!/usr/bin/env node\nimport { runCli } from '../src/cli.js'\nrunCli()\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,eAAe;AAIxB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,aAAa,EAClB,YAAY,sDAAsD,EAClE,eAAe,uBAAuB,YAAY,EAClD,OAAO,6BAA6B,+DAA+D,EACnG,OAAO,aAAa,uBAAuB,IAAI,EAC/C,OAAO,aAAa,wBAAwB,IAAI,EAChD,OAAO,uBAAuB,oBAAoB,GAAG,EACrD,OAAO,2BAA2B,4BAA4B,KAAK,EACnE,OAAO,sBAAsB,oBAAoB,cAAc,EAC/D,OAAO,yBAAyB,qCAAqC,MAAM,EAC3E,OAAO,cAAc,gCAAgC,KAAK,EAC1D,OAAO,mBAAmB,sCAAsC,EAChE,OAAO,iBAAiB,kBAAkB,EAC1C,OAAO,0BAA0B,kEAAkE,EACnG,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ;AAAA,MAC3B,OAAO,KAAK;AAAA,MACZ,UAAU,KAAK;AAAA,MACf,IAAI,KAAK;AAAA,MACT,IAAI,KAAK;AAAA,MACT,OAAO,SAAS,KAAK,OAAO,EAAE;AAAA,MAC9B,cAAc,SAAS,KAAK,cAAc,EAAE;AAAA,MAC5C,WAAW,KAAK;AAAA,MAChB,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,OAAO,KAAK;AAAA,MACZ,cAAc,KAAK,gBAAgB,qBAAqB;AAAA,IAC1D,CAAC;AACD,YAAQ,IAAI,KAAK,UAAU,EAAE,gBAAgB,OAAO,gBAAgB,WAAW,OAAO,MAAM,KAAK,CAAC,CAAC;AAAA,EACrG,SAAS,KAAK;AACZ,YAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,eAAsB,SAAwB;AAC5C,QAAM,QAAQ,WAAW;AAC3B;;;AC5CA,OAAO;","names":[]}
|
|
@@ -4,8 +4,7 @@ function browserServiceApiKey() {
|
|
|
4
4
|
return value || void 0;
|
|
5
5
|
}
|
|
6
6
|
function browserServiceProxyId() {
|
|
7
|
-
|
|
8
|
-
return value || void 0;
|
|
7
|
+
return void 0;
|
|
9
8
|
}
|
|
10
9
|
function browserServiceProfileName() {
|
|
11
10
|
const value = (process.env.BROWSER_AGENT_PROFILE_NAME ?? process.env.BROWSER_SERVICE_PROFILE_NAME ?? process.env.KERNEL_BROWSER_PROFILE_NAME ?? process.env.KERNEL_PROFILE_NAME)?.trim();
|
|
@@ -65,4 +64,4 @@ export {
|
|
|
65
64
|
RequestAbortedError,
|
|
66
65
|
LocationMismatchError
|
|
67
66
|
};
|
|
68
|
-
//# sourceMappingURL=chunk-
|
|
67
|
+
//# sourceMappingURL=chunk-3PRO376E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/browser-service-env.ts","../src/errors.ts"],"sourcesContent":["export function browserServiceApiKey(): string | undefined {\n const value = (process.env.BROWSER_SERVICE_API_KEY ?? process.env.KERNEL_API_KEY)?.trim()\n return value || undefined\n}\n\nexport function browserServiceProxyId(): string | undefined {\n return undefined\n}\n\nexport function browserServiceProfileName(): string | undefined {\n const value = (\n process.env.BROWSER_AGENT_PROFILE_NAME ??\n process.env.BROWSER_SERVICE_PROFILE_NAME ??\n process.env.KERNEL_BROWSER_PROFILE_NAME ??\n process.env.KERNEL_PROFILE_NAME\n )?.trim()\n return value || undefined\n}\n\nexport function browserServiceProfileSaveChanges(): boolean | undefined {\n const value = (\n process.env.BROWSER_AGENT_PROFILE_SAVE_CHANGES ??\n process.env.BROWSER_SERVICE_PROFILE_SAVE_CHANGES ??\n process.env.KERNEL_BROWSER_PROFILE_SAVE_CHANGES ??\n process.env.KERNEL_PROFILE_SAVE_CHANGES\n )?.trim().toLowerCase()\n if (!value) return undefined\n if (['1', 'true', 'yes', 'on'].includes(value)) return true\n if (['0', 'false', 'no', 'off'].includes(value)) return false\n return undefined\n}\n","export const RECAPTCHA_INSTRUCTIONS = 'Google returned a CAPTCHA. Run with --headless=false to re-warm the browser profile, then retry.'\n\nexport function sanitizeVendorName(message: string): string {\n return message\n .replace(/kernel\\.sh\\s+sessions?/gi, 'sessions')\n .replace(/kernel\\.sh\\s+session/gi, 'this session')\n .replace(/kernel\\.sh/gi, 'the service')\n .replace(/kernel\\s+sessions?/gi, 'sessions')\n .replace(/kernel\\s+session/gi, 'this session')\n .replace(/\\bkernel\\b/gi, 'the service')\n .replace(/ +/g, ' ')\n .trim()\n}\n\nexport class CaptchaError extends Error {\n readonly name = 'CaptchaError'\n constructor(public readonly instructions: string) {\n super(`CAPTCHA detected. ${instructions}`)\n }\n}\n\nexport class ExtractionError extends Error {\n readonly name = 'ExtractionError'\n constructor(message: string, public readonly cause?: unknown) {\n super(message)\n }\n}\n\nexport class RequestAbortedError extends Error {\n readonly name = 'RequestAbortedError'\n constructor(message = 'Request aborted before harvest completed') {\n super(message)\n }\n}\n\nexport class LocationMismatchError extends Error {\n readonly name = 'LocationMismatchError'\n constructor(message = 'Google returned results for a different location than requested') {\n super(message)\n }\n}\n"],"mappings":";AAAO,SAAS,uBAA2C;AACzD,QAAM,SAAS,QAAQ,IAAI,2BAA2B,QAAQ,IAAI,iBAAiB,KAAK;AACxF,SAAO,SAAS;AAClB;AAEO,SAAS,wBAA4C;AAC1D,SAAO;AACT;AAEO,SAAS,4BAAgD;AAC9D,QAAM,SACJ,QAAQ,IAAI,8BACZ,QAAQ,IAAI,gCACZ,QAAQ,IAAI,+BACZ,QAAQ,IAAI,sBACX,KAAK;AACR,SAAO,SAAS;AAClB;AAEO,SAAS,mCAAwD;AACtE,QAAM,SACJ,QAAQ,IAAI,sCACZ,QAAQ,IAAI,wCACZ,QAAQ,IAAI,uCACZ,QAAQ,IAAI,8BACX,KAAK,EAAE,YAAY;AACtB,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,CAAC,KAAK,QAAQ,OAAO,IAAI,EAAE,SAAS,KAAK,EAAG,QAAO;AACvD,MAAI,CAAC,KAAK,SAAS,MAAM,KAAK,EAAE,SAAS,KAAK,EAAG,QAAO;AACxD,SAAO;AACT;;;AC9BO,IAAM,yBAAyB;AAE/B,SAAS,mBAAmB,SAAyB;AAC1D,SAAO,QACJ,QAAQ,4BAA4B,UAAU,EAC9C,QAAQ,0BAA0B,cAAc,EAChD,QAAQ,gBAAgB,aAAa,EACrC,QAAQ,wBAAwB,UAAU,EAC1C,QAAQ,sBAAsB,cAAc,EAC5C,QAAQ,gBAAgB,aAAa,EACrC,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACV;AAEO,IAAM,eAAN,cAA2B,MAAM;AAAA,EAEtC,YAA4B,cAAsB;AAChD,UAAM,qBAAqB,YAAY,EAAE;AADf;AAAA,EAE5B;AAAA,EAF4B;AAAA,EADnB,OAAO;AAIlB;AAEO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAEzC,YAAY,SAAiC,OAAiB;AAC5D,UAAM,OAAO;AAD8B;AAAA,EAE7C;AAAA,EAF6C;AAAA,EADpC,OAAO;AAIlB;AAEO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EACpC,OAAO;AAAA,EAChB,YAAY,UAAU,4CAA4C;AAChE,UAAM,OAAO;AAAA,EACf;AACF;AAEO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EACtC,OAAO;AAAA,EAChB,YAAY,UAAU,mEAAmE;AACvF,UAAM,OAAO;AAAA,EACf;AACF;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/schemas.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
var DEFAULT_PROXY_MODE = "
|
|
3
|
+
var DEFAULT_PROXY_MODE = "none";
|
|
4
4
|
var DEFAULT_MAPS_PROXY_MODE = "location";
|
|
5
5
|
var HarvestOptionsSchema = z.object({
|
|
6
6
|
query: z.string().min(1),
|
|
@@ -22,7 +22,8 @@ var HarvestOptionsSchema = z.object({
|
|
|
22
22
|
outputDir: z.string().default("./paa-output"),
|
|
23
23
|
format: z.enum(["json", "csv", "both"]).default("both"),
|
|
24
24
|
serpOnly: z.boolean().default(false),
|
|
25
|
-
pages: z.number().int().min(1).max(2).default(1)
|
|
25
|
+
pages: z.number().int().min(1).max(2).default(1),
|
|
26
|
+
softDeadlineMs: z.number().optional()
|
|
26
27
|
});
|
|
27
28
|
var MapsPlaceOptionsSchema = z.object({
|
|
28
29
|
businessName: z.string().min(1),
|
|
@@ -108,4 +109,4 @@ export {
|
|
|
108
109
|
RawMapsReviewStatsSchema,
|
|
109
110
|
RawMapsAboutAttributeSchema
|
|
110
111
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
112
|
+
//# sourceMappingURL=chunk-BP27CZ5Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schemas.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const DEFAULT_PROXY_MODE = 'none' as const\nexport const DEFAULT_MAPS_PROXY_MODE = 'location' as const\n\nexport const HarvestOptionsSchema = z.object({\n query: z.string().min(1),\n location: z.string().optional(),\n gl: z.string().length(2).default('us'),\n hl: z.string().length(2).default('en'),\n device: z.enum(['desktop', 'mobile']).default('desktop'),\n proxyMode: z.enum(['location', 'configured', 'none']).default(DEFAULT_PROXY_MODE),\n proxyZip: z.string().regex(/^\\d{5}$/).optional(),\n debug: z.boolean().default(false),\n depth: z.number().int().min(1).max(30).default(3),\n maxQuestions: z.number().int().min(1).max(1000).default(100),\n headless: z.boolean().default(false),\n profileDir: z.string().optional(),\n proxy: z.string().url().optional(),\n kernelApiKey: z.string().optional(),\n kernelProxyId: z.string().optional(),\n kernelProxyResolution: z.unknown().optional(),\n outputDir: z.string().default('./paa-output'),\n format: z.enum(['json', 'csv', 'both']).default('both'),\n serpOnly: z.boolean().default(false),\n pages: z.number().int().min(1).max(2).default(1),\n softDeadlineMs: z.number().optional(),\n})\n\nexport const MapsPlaceOptionsSchema = z.object({\n businessName: z.string().min(1),\n location: z.string().min(1),\n gl: z.string().length(2).default('us'),\n hl: z.string().length(2).default('en'),\n includeReviews: z.boolean().default(false),\n maxReviews: z.number().int().min(1).max(500).default(50),\n kernelApiKey: z.string().optional(),\n kernelProxyId: z.string().optional(),\n headless: z.boolean().default(true),\n})\n\nexport const MapsSearchOptionsSchema = z.object({\n query: z.string().min(1),\n location: z.string().optional(),\n gl: z.string().length(2).default('us'),\n hl: z.string().length(2).default('en'),\n maxResults: z.number().int().min(1).max(50).default(10),\n proxyMode: z.enum(['location', 'configured', 'none']).default(DEFAULT_MAPS_PROXY_MODE),\n proxyZip: z.string().regex(/^\\d{5}$/).optional(),\n debug: z.boolean().default(false),\n kernelApiKey: z.string().optional(),\n kernelProxyId: z.string().optional(),\n kernelProxyResolution: z.unknown().optional(),\n headless: z.boolean().default(true),\n})\n\nexport const RawPAAItemSchema = z.object({\n question: z.string().min(1),\n answer: z.string().optional(),\n sourceTitle: z.string().optional(),\n sourceSite: z.string().optional(),\n sourceCite: z.string().optional(),\n})\n\nexport const RawMapsOverviewSchema = z.object({\n name: z.string().nullable(),\n rating: z.string().nullable(),\n reviewCount: z.string().nullable(),\n category: z.string().nullable(),\n address: z.string().nullable(),\n hoursSummary: z.string().nullable(),\n phone: z.string().nullable(),\n phoneDisplay: z.string().nullable(),\n website: z.string().nullable(),\n plusCode: z.string().nullable(),\n bookingUrl: z.string().nullable(),\n})\n\nexport const RawMapsHoursRowSchema = z.object({\n day: z.string(),\n hours: z.string(),\n})\n\nexport const RawMapsReviewStatsSchema = z.object({\n reviewHistogram: z.array(z.object({\n stars: z.number(),\n count: z.string(),\n })),\n reviewTopics: z.array(z.object({\n label: z.string(),\n count: z.string(),\n })),\n})\n\nexport const RawMapsReviewCardSchema = z.object({\n reviewId: z.string(),\n author: z.string().nullable(),\n stars: z.string().nullable(),\n date: z.string().nullable(),\n text: z.string().nullable(),\n ownerResponse: z.string().nullable(),\n})\n\nexport const RawMapsAboutAttributeSchema = z.object({\n section: z.string(),\n attribute: z.string(),\n})\n"],"mappings":"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","names":[]}
|
|
@@ -50,11 +50,11 @@ function renderInstallTerminal(options) {
|
|
|
50
50
|
"1/1 install surfaces ready",
|
|
51
51
|
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),
|
|
52
52
|
"",
|
|
53
|
-
`${colorize("Tools", "cyan", color)} ${colorize("(
|
|
53
|
+
`${colorize("Tools", "cyan", color)} ${colorize("(46 MCP tools)", "muted", color)}`,
|
|
54
54
|
toolRow("search", ["harvest_paa", "search_serp", "maps_search", "maps_place_intel"], color),
|
|
55
55
|
toolRow("extract", ["extract_url", "map_site_urls", "extract_site", "audit_site", "directory_workflow"], color),
|
|
56
56
|
toolRow("build", ["rank_tracker_workflow", "cron plan", "database prompt"], color),
|
|
57
|
-
toolRow("media", ["youtube_harvest", "youtube_transcribe", "facebook_ad_search", "facebook_page_intel", "facebook_ad_transcribe", "facebook_video_transcribe", "instagram_profile_content", "instagram_media_download"], color),
|
|
57
|
+
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),
|
|
58
58
|
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),
|
|
59
59
|
toolRow("account", ["credits_info", "reports", "MCP resources"], color),
|
|
60
60
|
`${colorize("Workflows", "cyan", color)} ${colorize("(MCP + CLI + API)", "muted", color)}`,
|
|
@@ -100,4 +100,4 @@ function renderInstallTerminal(options) {
|
|
|
100
100
|
export {
|
|
101
101
|
renderInstallTerminal
|
|
102
102
|
};
|
|
103
|
-
//# sourceMappingURL=chunk-
|
|
103
|
+
//# sourceMappingURL=chunk-DOBQN3EY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/install-terminal.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('(46 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"],"mappings":"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","names":[]}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PACKAGE_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WPHAMNT7.js";
|
|
4
4
|
import {
|
|
5
5
|
browserServiceProfileName,
|
|
6
6
|
browserServiceProfileSaveChanges,
|
|
7
7
|
sanitizeVendorName
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3PRO376E.js";
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_MAPS_PROXY_MODE,
|
|
11
11
|
DEFAULT_PROXY_MODE
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-BP27CZ5Q.js";
|
|
13
13
|
|
|
14
14
|
// src/harvest-timeout.ts
|
|
15
15
|
var VERCEL_FUNCTION_MAX_MS = 3e5;
|
|
@@ -17,10 +17,10 @@ var CLIENT_OVER_SERVER_MARGIN_MS = 15e3;
|
|
|
17
17
|
function harvestTimeoutBudget(maxQuestions, serpOnly = false) {
|
|
18
18
|
const requested = Number.isFinite(maxQuestions) && maxQuestions > 0 ? Math.trunc(maxQuestions) : 30;
|
|
19
19
|
let serverMs;
|
|
20
|
-
if (serpOnly
|
|
21
|
-
else if (requested <=
|
|
22
|
-
else if (requested <=
|
|
23
|
-
else serverMs =
|
|
20
|
+
if (serpOnly) serverMs = 11e4;
|
|
21
|
+
else if (requested <= 50) serverMs = 17e4;
|
|
22
|
+
else if (requested <= 100) serverMs = 23e4;
|
|
23
|
+
else serverMs = 285e3;
|
|
24
24
|
const clientMs = Math.min(serverMs + CLIENT_OVER_SERVER_MARGIN_MS, VERCEL_FUNCTION_MAX_MS - 5e3);
|
|
25
25
|
return { serverMs, clientMs };
|
|
26
26
|
}
|
|
@@ -144,6 +144,17 @@ var WORKFLOW_RECIPES = [
|
|
|
144
144
|
produces: ["city summary", "competitor CSV", "review insight CSV", "market difficulty/opportunity notes", "HTML report"],
|
|
145
145
|
runHint: "Use workflow_run with workflowId local-competitive-audit for state-wide market batches, or map-comparison for one city/location comparison."
|
|
146
146
|
},
|
|
147
|
+
{
|
|
148
|
+
id: "lead_generation",
|
|
149
|
+
title: "Lead Generation",
|
|
150
|
+
description: "Build an outreach-ready local lead list for a niche in one market: Google Maps businesses with confirmed review counts and booking URLs, enriched with email and social links scraped from each business website (proxy/browser-backed so blocked sites still resolve).",
|
|
151
|
+
primaryWorkflowId: "get-leads",
|
|
152
|
+
recommendedTools: ["workflow_run", "maps_search", "maps_place_intel", "extract_url"],
|
|
153
|
+
requiredInputs: ["query", "location"],
|
|
154
|
+
optionalInputs: ["maxResults", "enrichWebsites", "hydrateReviewCounts", "concurrency", "proxyMode"],
|
|
155
|
+
produces: ["leads CSV (name, phone, website, email, socials, review count, booking URL)", "evidence JSON", "HTML report", "outreach task list"],
|
|
156
|
+
runHint: 'Use workflow_run with workflowId get-leads. Put the niche in query (e.g. "roofers") and the market in location (e.g. "Houston, TX"); do not combine them.'
|
|
157
|
+
},
|
|
147
158
|
{
|
|
148
159
|
id: "icp_research",
|
|
149
160
|
title: "ICP Research",
|
|
@@ -1092,6 +1103,16 @@ ${bodyMd.slice(0, 3e3)}${bodyMd.length > 3e3 ? `
|
|
|
1092
1103
|
const bodySectionFull = bodyMd ? `
|
|
1093
1104
|
## Page Content
|
|
1094
1105
|
${bodyMd}` : "";
|
|
1106
|
+
const mem = d.memory ?? null;
|
|
1107
|
+
const memSection = mem?.deposited ? `
|
|
1108
|
+
## \u2705 Saved to memory
|
|
1109
|
+
Full content stored in vault \`${mem.vault ?? "\u2014"}\` as note \`${mem.noteId ?? "\u2014"}\`${mem.chunks !== void 0 ? ` (${mem.chunks} chunks indexed)` : ""}. Recall it later with memory search \u2014 no need to re-scrape.` : mem?.fileUrl ? `
|
|
1110
|
+
## \u{1F4C4} Saved as a temporary file
|
|
1111
|
+
Memory vault was unavailable${mem.error ? ` (${mem.error})` : ""}, so the full content was saved to a download link instead:
|
|
1112
|
+
- **Link:** ${mem.fileUrl}
|
|
1113
|
+
- **Expires:** ${mem.fileExpiresAt ?? "in 24 hours"} (auto-deleted)` : mem ? `
|
|
1114
|
+
## \u26A0\uFE0F Memory deposit skipped
|
|
1115
|
+
${mem.error ?? "unknown error"} \u2014 the page content is still in the truncated preview above.` : "";
|
|
1095
1116
|
const screenshotSection = screenshotMeta ? `
|
|
1096
1117
|
## Screenshot
|
|
1097
1118
|
- **File:** ${screenshotPath ?? "(returned inline only \u2014 disk write unavailable in this environment)"}
|
|
@@ -1121,10 +1142,10 @@ ${bodyMd}` : "";
|
|
|
1121
1142
|
- ${schemaCount} JSON-LD schema block(s) detected`;
|
|
1122
1143
|
const full = `# URL Extract: ${url}
|
|
1123
1144
|
**${title}**
|
|
1124
|
-
${headingSection}${kpoSection}${brandingSection}${bodySection}${screenshotSection}${mediaSection}${tips}`;
|
|
1145
|
+
${headingSection}${kpoSection}${brandingSection}${bodySection}${memSection}${screenshotSection}${mediaSection}${tips}`;
|
|
1125
1146
|
const diskReport = `# URL Extract: ${url}
|
|
1126
1147
|
**${title}**
|
|
1127
|
-
${headingSection}${kpoSection}${brandingSection}${bodySectionFull}${screenshotSection}${mediaSection}${tips}`;
|
|
1148
|
+
${headingSection}${kpoSection}${brandingSection}${bodySectionFull}${memSection}${screenshotSection}${mediaSection}${tips}`;
|
|
1128
1149
|
const textResult = oneBlock(full, diskReport);
|
|
1129
1150
|
const structuredContent = {
|
|
1130
1151
|
url,
|
|
@@ -1137,7 +1158,8 @@ ${headingSection}${kpoSection}${brandingSection}${bodySectionFull}${screenshotSe
|
|
|
1137
1158
|
missingSchemaFields: kpo?.missingFields ?? [],
|
|
1138
1159
|
screenshotSaved: screenshotPath ?? null,
|
|
1139
1160
|
branding: branding ?? null,
|
|
1140
|
-
mediaAssets: media?.assets ?? null
|
|
1161
|
+
mediaAssets: media?.assets ?? null,
|
|
1162
|
+
memory: mem ?? void 0
|
|
1141
1163
|
};
|
|
1142
1164
|
if (screenshotMeta?.base64) {
|
|
1143
1165
|
return {
|
|
@@ -1523,6 +1545,38 @@ ${adBlocks}`,
|
|
|
1523
1545
|
}
|
|
1524
1546
|
};
|
|
1525
1547
|
}
|
|
1548
|
+
function formatRedditThread(raw, input) {
|
|
1549
|
+
const parsed = parseData(raw);
|
|
1550
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
1551
|
+
const d = parsed.data;
|
|
1552
|
+
const comments = d.comments ?? [];
|
|
1553
|
+
const commentMd = comments.map((cm) => {
|
|
1554
|
+
const indent = " ".repeat(Math.min(Number(cm.depth ?? 0), 6));
|
|
1555
|
+
return `${indent}- **u/${cm.author || "[unknown]"}** (${cm.score || "\u2014"}): ${(cm.body || "").replace(/\s+/g, " ").trim()}`;
|
|
1556
|
+
}).join("\n");
|
|
1557
|
+
const full = [
|
|
1558
|
+
`# ${d.title || "Reddit thread"}`,
|
|
1559
|
+
`**u/${d.author || "[unknown]"}** \xB7 ${d.score || "\u2014"} \xB7 ${comments.length} comments captured`,
|
|
1560
|
+
d.postBody ? `
|
|
1561
|
+
${d.postBody}` : "",
|
|
1562
|
+
`
|
|
1563
|
+
## Comments
|
|
1564
|
+
${commentMd || "_No comments captured._"}`
|
|
1565
|
+
].join("\n");
|
|
1566
|
+
return {
|
|
1567
|
+
...oneBlock(full),
|
|
1568
|
+
structuredContent: {
|
|
1569
|
+
sourceUrl: d.sourceUrl ?? input.url ?? null,
|
|
1570
|
+
oldRedditUrl: d.oldRedditUrl ?? null,
|
|
1571
|
+
title: d.title ?? null,
|
|
1572
|
+
author: d.author ?? null,
|
|
1573
|
+
score: d.score ?? null,
|
|
1574
|
+
postBody: d.postBody ?? null,
|
|
1575
|
+
numComments: comments.length,
|
|
1576
|
+
comments: comments.map((cm) => ({ author: cm.author ?? null, score: cm.score ?? null, depth: Number(cm.depth ?? 0), body: cm.body ?? "" }))
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1526
1580
|
function formatFacebookAdSearch(raw, input) {
|
|
1527
1581
|
const parsed = parseData(raw);
|
|
1528
1582
|
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
@@ -2551,7 +2605,7 @@ var HarvestPaaInputSchema = {
|
|
|
2551
2605
|
gl: z.string().length(2).default("us").describe("Google country code inferred from location or user language. Examples: United States us, United Kingdom gb, Japan jp, Canada ca, Australia au."),
|
|
2552
2606
|
hl: z.string().default("en").describe("Google interface/content language inferred from the user request. Use en unless the user asks for another language or locale."),
|
|
2553
2607
|
device: z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use desktop by default; use mobile only when the user asks for mobile rankings."),
|
|
2554
|
-
proxyMode: z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe(
|
|
2608
|
+
proxyMode: z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe('Leave unset \u2014 routing is automatic (clean egress by default; residential only when a location is given, with fallback). Set "location" only to force local-pack targeting.'),
|
|
2555
2609
|
proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use only with proxyMode location when the user gives a specific ZIP or city-center targeting needs to be forced."),
|
|
2556
2610
|
debug: z.boolean().default(false).describe("Include sanitized browser/session/location diagnostics in the response. Use true when debugging localization, CAPTCHA, or proxy behavior.")
|
|
2557
2611
|
};
|
|
@@ -2562,7 +2616,9 @@ var ExtractUrlInputSchema = {
|
|
|
2562
2616
|
extractBranding: z.boolean().default(false).describe("Extract brand colors, fonts, logo, and favicon using a rendered browser session. Returns colorScheme (light/dark), colors (primary/accent/background/text/heading as hex), fonts (heading/body family names), and assets (logo URL, favicon URL). Use when the user asks about brand colors, site theme, or brand assets."),
|
|
2563
2617
|
downloadMedia: z.boolean().default(false).describe("Extract and download all page media (images, video, audio) to ~/Downloads/mcp-scraper/media/. Ad networks, tracking pixels, and noise URLs are filtered automatically. Use when the user asks to download or harvest assets from a page."),
|
|
2564
2618
|
mediaTypes: z.array(z.enum(["image", "video", "audio"])).default(["image", "video", "audio"]).describe("Which media types to download. Default all three."),
|
|
2565
|
-
allowLocal: z.boolean().default(false).describe("Allow localhost and private-network URLs. For local development only.")
|
|
2619
|
+
allowLocal: z.boolean().default(false).describe("Allow localhost and private-network URLs. For local development only."),
|
|
2620
|
+
depositToVault: z.boolean().default(false).describe("Also save the full page content into the user's MCP Memory Library vault, server-side. The complete body is stored and embedded for semantic recall WITHOUT passing through this conversation \u2014 the tool result only confirms the saved note. Use when the user asks to save/remember/store a page, or to build a knowledge base from scrapes."),
|
|
2621
|
+
vaultName: z.string().trim().min(1).max(120).optional().describe("Optional vault to deposit into. Defaults to the user's personal vault.")
|
|
2566
2622
|
};
|
|
2567
2623
|
var MapSiteUrlsInputSchema = {
|
|
2568
2624
|
url: z.string().url().describe("Public website URL or domain to crawl for internal URLs. Use before extract_site when the user asks to audit/map/crawl a site."),
|
|
@@ -2603,6 +2659,10 @@ var FacebookAdSearchInputSchema = {
|
|
|
2603
2659
|
country: z.string().length(2).default("US").describe("Two-letter Ad Library country code. Default US. Examples: US, CA, GB, AU."),
|
|
2604
2660
|
maxResults: z.number().int().min(1).max(20).default(10).describe("Maximum advertisers to return. Default 10, maximum 20. Prefer tighter search terms over maxing this out.")
|
|
2605
2661
|
};
|
|
2662
|
+
var RedditThreadInputSchema = {
|
|
2663
|
+
url: z.string().min(1).describe("A reddit.com thread/post URL (www, old, or new Reddit, or a redd.it link). The service fetches it via old.reddit through a residential proxy and returns the post plus its comment tree."),
|
|
2664
|
+
maxComments: z.number().int().min(1).max(2e3).optional().describe("Optional cap on comments returned. Omit to return all captured comments.")
|
|
2665
|
+
};
|
|
2606
2666
|
var FacebookAdTranscribeInputSchema = {
|
|
2607
2667
|
videoUrl: z.string().url().describe("Direct Facebook CDN video URL from a facebook_page_intel ad result. Do not pass a public Facebook reel/post/share URL here; use facebook_video_transcribe for organic Facebook URLs.")
|
|
2608
2668
|
};
|
|
@@ -2869,7 +2929,17 @@ var ExtractUrlOutputSchema = {
|
|
|
2869
2929
|
entityTypes: z.array(z.string()),
|
|
2870
2930
|
napScore: z.number().nullable(),
|
|
2871
2931
|
missingSchemaFields: z.array(z.string()),
|
|
2872
|
-
screenshotSaved: NullableString
|
|
2932
|
+
screenshotSaved: NullableString,
|
|
2933
|
+
memory: z.object({
|
|
2934
|
+
deposited: z.boolean(),
|
|
2935
|
+
vault: z.string().optional(),
|
|
2936
|
+
noteId: z.string().optional(),
|
|
2937
|
+
path: z.string().optional(),
|
|
2938
|
+
chunks: z.number().int().optional(),
|
|
2939
|
+
fileUrl: z.string().optional(),
|
|
2940
|
+
fileExpiresAt: z.string().optional(),
|
|
2941
|
+
error: z.string().optional()
|
|
2942
|
+
}).optional()
|
|
2873
2943
|
};
|
|
2874
2944
|
var ExtractSiteOutputSchema = {
|
|
2875
2945
|
url: z.string(),
|
|
@@ -3001,6 +3071,21 @@ var FacebookAdSearchOutputSchema = {
|
|
|
3001
3071
|
sampleLibraryId: NullableString
|
|
3002
3072
|
}))
|
|
3003
3073
|
};
|
|
3074
|
+
var RedditThreadOutputSchema = {
|
|
3075
|
+
sourceUrl: NullableString,
|
|
3076
|
+
oldRedditUrl: NullableString,
|
|
3077
|
+
title: NullableString,
|
|
3078
|
+
author: NullableString,
|
|
3079
|
+
score: NullableString,
|
|
3080
|
+
postBody: NullableString,
|
|
3081
|
+
numComments: z.number().int().min(0),
|
|
3082
|
+
comments: z.array(z.object({
|
|
3083
|
+
author: NullableString,
|
|
3084
|
+
score: NullableString,
|
|
3085
|
+
depth: z.number().int().min(0),
|
|
3086
|
+
body: z.string()
|
|
3087
|
+
}))
|
|
3088
|
+
};
|
|
3004
3089
|
var FacebookPageIntelOutputSchema = {
|
|
3005
3090
|
advertiserName: NullableString,
|
|
3006
3091
|
totalAds: z.number().int().min(0),
|
|
@@ -3194,6 +3279,7 @@ var CreditsInfoInputSchema = {
|
|
|
3194
3279
|
};
|
|
3195
3280
|
var WorkflowIdSchema = z.enum([
|
|
3196
3281
|
"directory",
|
|
3282
|
+
"get-leads",
|
|
3197
3283
|
"agent-packet",
|
|
3198
3284
|
"local-competitive-audit",
|
|
3199
3285
|
"map-comparison",
|
|
@@ -3215,8 +3301,8 @@ var WorkflowSuggestInputSchema = {
|
|
|
3215
3301
|
maxSuggestions: z.number().int().min(1).max(8).default(3).describe("Number of matching workflow recipes to return.")
|
|
3216
3302
|
};
|
|
3217
3303
|
var WorkflowRunInputSchema = {
|
|
3218
|
-
workflowId: WorkflowIdSchema.describe("Workflow to run: directory, agent-packet, local-competitive-audit, map-comparison, serp-comparison, paa-expansion-brief, ai-overview-language. Use only these values; call workflow_list or workflow_suggest first when unsure."),
|
|
3219
|
-
input: z.record(z.unknown()).default({}).describe("Workflow-specific input object. Examples: agent-packet uses {keyword, domain?, location?, maxQuestions?}; local-competitive-audit uses {query, state, minPopulation?, maxCities?, maxResultsPerCity?, hydrateTop?, maxReviews?}; serp-comparison uses {keyword, domain?, url?, location?, extractTop?}."),
|
|
3304
|
+
workflowId: WorkflowIdSchema.describe("Workflow to run: directory, get-leads, agent-packet, local-competitive-audit, map-comparison, serp-comparison, paa-expansion-brief, ai-overview-language. Use only these values; call workflow_list or workflow_suggest first when unsure."),
|
|
3305
|
+
input: z.record(z.unknown()).default({}).describe("Workflow-specific input object. Examples: get-leads uses {query, location, maxResults?, enrichWebsites?, hydrateReviewCounts?}; agent-packet uses {keyword, domain?, location?, maxQuestions?}; local-competitive-audit uses {query, state, minPopulation?, maxCities?, maxResultsPerCity?, hydrateTop?, maxReviews?}; serp-comparison uses {keyword, domain?, url?, location?, extractTop?}."),
|
|
3220
3306
|
webhookUrl: z.string().url().optional().describe("Optional HTTPS webhook to receive the completed hosted workflow run event.")
|
|
3221
3307
|
};
|
|
3222
3308
|
var WorkflowStepInputSchema = {
|
|
@@ -3292,7 +3378,7 @@ var SearchSerpInputSchema = {
|
|
|
3292
3378
|
gl: z.string().length(2).default("us").describe("Google country code inferred from location or user language."),
|
|
3293
3379
|
hl: z.string().default("en").describe("Google interface/content language inferred from user request."),
|
|
3294
3380
|
device: z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use desktop by default; use mobile only when the user asks for mobile rankings."),
|
|
3295
|
-
proxyMode: z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe(
|
|
3381
|
+
proxyMode: z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe('Leave unset \u2014 routing is automatic. Set "location" only to force local-pack targeting.'),
|
|
3296
3382
|
proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use only with proxyMode location when the user gives a specific ZIP or city-center targeting needs to be forced."),
|
|
3297
3383
|
debug: z.boolean().default(false).describe("Include sanitized browser/session/location diagnostics in the response. Use true when debugging localization, CAPTCHA, or proxy behavior."),
|
|
3298
3384
|
pages: z.number().int().min(1).max(2).default(1).describe("Number of result pages to fetch (1\u20132)")
|
|
@@ -3303,7 +3389,7 @@ var CaptureSerpSnapshotInputSchema = {
|
|
|
3303
3389
|
gl: z.string().length(2).default("us").describe("Google country code inferred from the requested market, e.g. us, gb, ca, au."),
|
|
3304
3390
|
hl: z.string().default("en").describe("Google interface/content language inferred from the user request."),
|
|
3305
3391
|
device: z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use mobile only when the user asks for mobile rankings or mobile SERP evidence."),
|
|
3306
|
-
proxyMode: z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe(
|
|
3392
|
+
proxyMode: z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe('Leave unset \u2014 routing is automatic. Set "location" only to force city/ZIP targeting.'),
|
|
3307
3393
|
proxyZip: z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use only with proxyMode location when a precise city-center or ZIP proxy is needed."),
|
|
3308
3394
|
pages: z.number().int().min(1).max(2).default(1).describe("Number of Google result pages to capture. Use 1 normally and 2 only when the user needs deeper ranking evidence."),
|
|
3309
3395
|
debug: z.boolean().default(false).describe("Include sanitized browser, proxy, and location diagnostics. Use true when debugging localization, CAPTCHA, proxy selection, or capture reliability."),
|
|
@@ -3715,21 +3801,21 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
3715
3801
|
if (savesReports) registerSavedReportResources(server);
|
|
3716
3802
|
server.registerTool("harvest_paa", {
|
|
3717
3803
|
title: "Google PAA + SERP Harvest",
|
|
3718
|
-
description: withReportNote('Best default tool for Google search research. Extracts People Also Ask questions plus answers/source URLs, organic SERP, local pack when present, entity IDs (CID/GCID/KG MID), and AI Overview. Infer the user language: split topic from location (e.g. "best hvac company in Denver CO" => query "best hvac company", location "Denver, CO", gl "us", hl "en").
|
|
3804
|
+
description: withReportNote('Best default tool for Google search research. Extracts People Also Ask questions plus answers/source URLs, organic SERP, local pack when present, entity IDs (CID/GCID/KG MID), and AI Overview. Infer the user language: split topic from location (e.g. "best hvac company in Denver CO" => query "best hvac company", location "Denver, CO", gl "us", hl "en"). Routing is automatic \u2014 leave proxyMode unset. If a deep harvest exceeds the time budget it returns the questions gathered so far (partial), billed per returned question. Use maxQuestions 30 normally, 100-200 for "full", "deep", "all", or comprehensive research. Deep harvests above 100 questions can run for several minutes with no interim progress \u2014 warn the user before starting one and keep maxQuestions at or below 100 unless they explicitly want a deep harvest. Credits are charged by extracted question; unused request hold is refunded.'),
|
|
3719
3805
|
inputSchema: HarvestPaaInputSchema,
|
|
3720
3806
|
outputSchema: HarvestPaaOutputSchema,
|
|
3721
3807
|
annotations: liveWebToolAnnotations("Google PAA + SERP Harvest")
|
|
3722
3808
|
}, async (input) => formatHarvestPaa(await executor.harvestPaa(input), input));
|
|
3723
3809
|
server.registerTool("search_serp", {
|
|
3724
3810
|
title: "Google SERP Lookup",
|
|
3725
|
-
description: withReportNote("Fast Google SERP lookup without PAA expansion. Use when the user asks for rankings, organic results, local pack, quick SERP, or positions. Split topic from location and infer gl/hl from the user request.
|
|
3811
|
+
description: withReportNote("Fast Google SERP lookup without PAA expansion. Use when the user asks for rankings, organic results, local pack, quick SERP, or positions. Split topic from location and infer gl/hl from the user request. Routing is automatic \u2014 leave proxyMode unset; set location only to force local-pack targeting."),
|
|
3726
3812
|
inputSchema: SearchSerpInputSchema,
|
|
3727
3813
|
outputSchema: SearchSerpOutputSchema,
|
|
3728
3814
|
annotations: liveWebToolAnnotations("Google SERP Lookup")
|
|
3729
3815
|
}, async (input) => formatSearchSerp(await executor.searchSerp(input), input));
|
|
3730
3816
|
server.registerTool("extract_url", {
|
|
3731
3817
|
title: "Single URL Extract",
|
|
3732
|
-
description: withReportNote("Extract structured data from one public URL when the user provides one page, asks to inspect/scrape a page, or needs page content, schema, headings, metadata, screenshots, branding, or media assets. Returns structured page fields plus artifact handles for saved reports/screenshots/media when requested. Use map_site_urls before extracting a site inventory; use extract_site for multi-page crawling."),
|
|
3818
|
+
description: withReportNote("Extract structured data from one public URL when the user provides one page, asks to inspect/scrape a page, or needs page content, schema, headings, metadata, screenshots, branding, or media assets. Returns structured page fields plus artifact handles for saved reports/screenshots/media when requested. Set depositToVault:true to also save the full page into the user's MCP Memory vault server-side (the full body is NOT returned to chat). Use map_site_urls before extracting a site inventory; use extract_site for multi-page crawling."),
|
|
3733
3819
|
inputSchema: ExtractUrlInputSchema,
|
|
3734
3820
|
outputSchema: ExtractUrlOutputSchema,
|
|
3735
3821
|
annotations: liveWebToolAnnotations("Single URL Extract")
|
|
@@ -3783,6 +3869,13 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
3783
3869
|
outputSchema: FacebookAdSearchOutputSchema,
|
|
3784
3870
|
annotations: liveWebToolAnnotations("Facebook Ad Library Search")
|
|
3785
3871
|
}, async (input) => formatFacebookAdSearch(await executor.facebookAdSearch(input), input));
|
|
3872
|
+
server.registerTool("reddit_thread", {
|
|
3873
|
+
title: "Reddit Thread + Comments",
|
|
3874
|
+
description: withReportNote("Capture a Reddit post and its comment tree from any reddit.com thread URL. Use this when the user wants Reddit comments, opinions, discussion, or audience voice from a specific thread (find threads first with search_serp, which surfaces reddit.com/comments links). Returns the post title, author, score, body, and a list of comments with author, score, nesting depth, and text. Handles Reddit's bot protection automatically (fetches via old.reddit through a residential proxy with retries); pass maxComments to cap the list."),
|
|
3875
|
+
inputSchema: RedditThreadInputSchema,
|
|
3876
|
+
outputSchema: RedditThreadOutputSchema,
|
|
3877
|
+
annotations: liveWebToolAnnotations("Reddit Thread + Comments")
|
|
3878
|
+
}, async (input) => formatRedditThread(await executor.redditThread(input), input));
|
|
3786
3879
|
server.registerTool("facebook_ad_transcribe", {
|
|
3787
3880
|
title: "Facebook Ad Transcription",
|
|
3788
3881
|
description: "Transcribe audio from a Facebook ad video CDN URL. Use this when facebook_page_intel returned a direct videoUrl and the user asks what the ad says, what claims it makes, or wants ad-message extraction. Returns full transcript, timestamped chunks, word count, and resolvedInputs. Use only with the direct videoUrl value from facebook_page_intel results; do not pass public Facebook post/reel/share URLs. Use facebook_video_transcribe for organic Facebook URLs, and facebook_page_intel first when you only have a brand/page/ad library handle.",
|
|
@@ -3820,14 +3913,14 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
3820
3913
|
}, async (input) => formatMapsPlaceIntel(await executor.mapsPlaceIntel(input), input));
|
|
3821
3914
|
server.registerTool("maps_search", {
|
|
3822
3915
|
title: "Google Maps Business Search",
|
|
3823
|
-
description: withReportNote('Search Google Maps for multiple businesses/profiles by category, niche, keyword, or local market. Use this when the user asks for several Google Business Profiles, GMBs, GBPs, leads, prospects, competitors, or "more than the 3-pack."
|
|
3916
|
+
description: withReportNote('Search Google Maps for multiple businesses/profiles by category, niche, keyword, or local market. Use this when the user asks for several Google Business Profiles, GMBs, GBPs, leads, prospects, competitors, or "more than the 3-pack." Routing is automatic and location-aware \u2014 leave proxyMode unset; pass proxyZip only to force a specific ZIP. Returns up to 50 candidates with names, place URLs, CIDs when available, ratings, review counts, profile metadata, and sanitized attempt telemetry. Default maxResults is 10; maximum is 50. Use maps_place_intel afterward only when a selected business needs full details and reviews.'),
|
|
3824
3917
|
inputSchema: MapsSearchInputSchema,
|
|
3825
3918
|
outputSchema: MapsSearchOutputSchema,
|
|
3826
3919
|
annotations: liveWebToolAnnotations("Google Maps Business Search")
|
|
3827
3920
|
}, async (input) => formatMapsSearch(await executor.mapsSearch(input), input));
|
|
3828
3921
|
server.registerTool("directory_workflow", {
|
|
3829
3922
|
title: "Directory Workflow: Markets + Maps",
|
|
3830
|
-
description: withReportNote('Build directory/prospecting datasets by selecting US city markets from the free Census Population Estimates city/place dataset, optionally joining configured US ZIPS/Lead Magician ZIP groups, then running Google Maps business searches for each city in parallel. Use this when the user wants "all cities over 100k population in a state", "build a directory CSV", "find markets then get Maps data", or similar location-database + Maps workflows. Set minPopulation, state, query, maxResultsPerCity, and concurrency.
|
|
3923
|
+
description: withReportNote('Build directory/prospecting datasets by selecting US city markets from the free Census Population Estimates city/place dataset, optionally joining configured US ZIPS/Lead Magician ZIP groups, then running Google Maps business searches for each city in parallel. Use this when the user wants "all cities over 100k population in a state", "build a directory CSV", "find markets then get Maps data", or similar location-database + Maps workflows. Set minPopulation, state, query, maxResultsPerCity, and concurrency. Routing is automatic per city. Saved CSV rows include source_location, result_position, business_name, review_stars, review_count, category, address, phone, hours_status, website_url, directions_url, place_url, cid, cid_decimal, city population, and ZIP groups. Structured city results include sanitized attempt telemetry. Use maps_place_intel only when a selected profile needs deeper review topics, profile review count confirmation, or review cards. For local Lead Magician ZIP enrichment, set MCP_SCRAPER_USZIPS_CSV_PATH on the API server or pass usZipsCsvPath only in local/test mode.'),
|
|
3831
3924
|
inputSchema: DirectoryWorkflowInputSchema,
|
|
3832
3925
|
outputSchema: DirectoryWorkflowOutputSchema,
|
|
3833
3926
|
annotations: liveWebToolAnnotations("Directory Workflow: Markets + Maps")
|
|
@@ -4100,6 +4193,9 @@ var HttpMcpToolExecutor = class {
|
|
|
4100
4193
|
facebookAdSearch(input) {
|
|
4101
4194
|
return this.call("/facebook/search", input);
|
|
4102
4195
|
}
|
|
4196
|
+
redditThread(input) {
|
|
4197
|
+
return this.call("/reddit/thread", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
4198
|
+
}
|
|
4103
4199
|
facebookAdTranscribe(input) {
|
|
4104
4200
|
return this.call("/facebook/transcribe", input);
|
|
4105
4201
|
}
|
|
@@ -4315,4 +4411,4 @@ export {
|
|
|
4315
4411
|
HttpMcpToolExecutor,
|
|
4316
4412
|
exportFanout
|
|
4317
4413
|
};
|
|
4318
|
-
//# sourceMappingURL=chunk-
|
|
4414
|
+
//# sourceMappingURL=chunk-FE2WC4JR.js.map
|