mcp-scraper 0.2.7 → 0.2.8
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/README.md +6 -4
- package/dist/bin/api-server.cjs +364 -162
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/browser-agent-stdio-server.cjs +1 -1
- package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
- package/dist/bin/browser-agent-stdio-server.js +2 -2
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +68 -13
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +3 -3
- package/dist/bin/mcp-scraper-install.cjs +2 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +2 -1
- package/dist/bin/mcp-scraper-install.js.map +1 -1
- package/dist/bin/mcp-stdio-server.cjs +68 -13
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/bin/paa-harvest.cjs +42 -6
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/{chunk-3TF6UT2P.js → chunk-5HMOPP76.js} +2 -2
- package/dist/{chunk-7SI6XIR3.js → chunk-6NEXSNSA.js} +69 -14
- package/dist/chunk-6NEXSNSA.js.map +1 -0
- package/dist/{chunk-MY3S7EX7.js → chunk-CQTAKXBN.js} +43 -7
- package/dist/chunk-CQTAKXBN.js.map +1 -0
- package/dist/chunk-I26QN7WQ.js +7 -0
- package/dist/chunk-I26QN7WQ.js.map +1 -0
- package/dist/index.cjs +42 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{server-RIKRBDOI.js → server-BTTDFPSQ.js} +238 -133
- package/dist/server-BTTDFPSQ.js.map +1 -0
- package/dist/{worker-NAKGTIF5.js → worker-5O44YBF4.js} +2 -2
- package/docs/mcp-tool-quality-spec.md +3 -1
- package/package.json +1 -1
- package/dist/chunk-7SI6XIR3.js.map +0 -1
- package/dist/chunk-IZE5UW7Y.js +0 -7
- package/dist/chunk-IZE5UW7Y.js.map +0 -1
- package/dist/chunk-MY3S7EX7.js.map +0 -1
- package/dist/server-RIKRBDOI.js.map +0 -1
- /package/dist/{chunk-3TF6UT2P.js.map → chunk-5HMOPP76.js.map} +0 -0
- /package/dist/{worker-NAKGTIF5.js.map → worker-5O44YBF4.js.map} +0 -0
|
@@ -138,7 +138,7 @@ var import_node_os2 = require("os");
|
|
|
138
138
|
var import_node_path2 = require("path");
|
|
139
139
|
|
|
140
140
|
// src/version.ts
|
|
141
|
-
var PACKAGE_VERSION = "0.2.
|
|
141
|
+
var PACKAGE_VERSION = "0.2.8";
|
|
142
142
|
|
|
143
143
|
// src/mcp/browser-agent-tool-schemas.ts
|
|
144
144
|
var import_zod = require("zod");
|
|
@@ -1056,12 +1056,17 @@ function errorAttemptsSection(body) {
|
|
|
1056
1056
|
const browser = debug.browser ?? {};
|
|
1057
1057
|
const kernel = browser.browserRuntime ?? browser.kernel ?? {};
|
|
1058
1058
|
const proxyResolution = kernel.proxyResolution ?? {};
|
|
1059
|
-
const network = browser.networkLocation ?? {
|
|
1059
|
+
const network = browser.networkLocation ?? {
|
|
1060
|
+
ip: attempt.observedIp ?? attempt.observed_ip,
|
|
1061
|
+
city: attempt.observedCity ?? attempt.observed_city,
|
|
1062
|
+
region: attempt.observedRegion ?? attempt.observed_region
|
|
1063
|
+
};
|
|
1060
1064
|
const nav = browser.serpNavigation ?? {};
|
|
1061
1065
|
const geo = [network.ip, network.city, network.region].filter(Boolean).join(" / ") || "geo unknown";
|
|
1062
|
-
const sessionId = attempt.browser_session_id ?? attempt.kernel_session_id ?? kernel.sessionId ?? "unknown";
|
|
1066
|
+
const sessionId = attempt.browser_session_id ?? attempt.browserSessionIdSuffix ?? attempt.kernel_session_id ?? kernel.sessionId ?? "unknown";
|
|
1063
1067
|
const cleanupSucceeded = attempt.session_cleanup_succeeded ?? attempt.kernel_delete_succeeded;
|
|
1064
|
-
|
|
1068
|
+
const proxySource = proxyResolution.source ?? attempt.proxyResolutionSource;
|
|
1069
|
+
return `- Attempt ${attempt.attempt_number ?? attempt.attemptNumber ?? "?"}: ${attempt.outcome ?? attempt.status ?? "unknown"} \xB7 session ${sessionId} \xB7 proxy ${debug.request?.proxyMode ?? kernel.proxyMode ?? attempt.proxyMode ?? "unknown"}${proxySource ? `/${proxySource}` : ""} \xB7 ${geo} \xB7 CAPTCHA ${nav.captchaDetected === true ? "yes" : nav.captchaDetected === false ? "no" : "unknown"} \xB7 cleanup ${cleanupSucceeded === true ? "yes" : cleanupSucceeded === false ? "no" : "unknown"}`;
|
|
1065
1070
|
});
|
|
1066
1071
|
return `
|
|
1067
1072
|
|
|
@@ -1522,6 +1527,29 @@ ${rows}`,
|
|
|
1522
1527
|
}
|
|
1523
1528
|
};
|
|
1524
1529
|
}
|
|
1530
|
+
function normalizeMapsAttempts(value) {
|
|
1531
|
+
const attempts = Array.isArray(value) ? value : [];
|
|
1532
|
+
return attempts.map((attempt, index) => ({
|
|
1533
|
+
attemptNumber: attempt.attemptNumber ?? attempt.attempt_number ?? index + 1,
|
|
1534
|
+
maxAttempts: attempt.maxAttempts ?? attempt.max_attempts ?? attempts.length,
|
|
1535
|
+
status: attempt.status === "ok" ? "ok" : "failed",
|
|
1536
|
+
outcome: attempt.outcome ?? attempt.status ?? "unknown",
|
|
1537
|
+
willRetry: attempt.willRetry ?? attempt.will_retry ?? false,
|
|
1538
|
+
durationMs: attempt.durationMs ?? attempt.duration_ms ?? 0,
|
|
1539
|
+
resultCount: attempt.resultCount ?? attempt.result_count ?? 0,
|
|
1540
|
+
error: attempt.error ? sanitizeVendorText(attempt.error) : null,
|
|
1541
|
+
proxyMode: attempt.proxyMode ?? attempt.proxy_mode ?? "location",
|
|
1542
|
+
proxyResolutionSource: attempt.proxyResolutionSource ?? attempt.proxy_resolution_source ?? null,
|
|
1543
|
+
proxyIdSuffix: attempt.proxyIdSuffix ?? attempt.proxy_id_suffix ?? null,
|
|
1544
|
+
proxyTargetLevel: attempt.proxyTargetLevel ?? attempt.proxy_target_level ?? null,
|
|
1545
|
+
proxyTargetLocation: attempt.proxyTargetLocation ?? attempt.proxy_target_location ?? null,
|
|
1546
|
+
proxyTargetZip: attempt.proxyTargetZip ?? attempt.proxy_target_zip ?? null,
|
|
1547
|
+
browserSessionIdSuffix: attempt.browserSessionIdSuffix ?? attempt.browser_session_id ?? null,
|
|
1548
|
+
observedIp: attempt.observedIp ?? attempt.observed_ip ?? null,
|
|
1549
|
+
observedCity: attempt.observedCity ?? attempt.observed_city ?? null,
|
|
1550
|
+
observedRegion: attempt.observedRegion ?? attempt.observed_region ?? null
|
|
1551
|
+
}));
|
|
1552
|
+
}
|
|
1525
1553
|
function formatCreditsInfo(raw, input) {
|
|
1526
1554
|
const parsed = parseData(raw);
|
|
1527
1555
|
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
@@ -1594,6 +1622,8 @@ function formatMapsSearch(raw, input) {
|
|
|
1594
1622
|
const searchQuery = d.searchQuery ?? [input.query, input.location].filter(Boolean).join(" ");
|
|
1595
1623
|
const requestedMax = d.requestedMaxResults ?? input.maxResults ?? 10;
|
|
1596
1624
|
const durationMs = d.durationMs;
|
|
1625
|
+
const attempts = normalizeMapsAttempts(d.attempts);
|
|
1626
|
+
const lastAttempt = attempts.at(-1);
|
|
1597
1627
|
const rows = results.map((r) => {
|
|
1598
1628
|
const rating = [r.rating, r.reviewCount ? `(${r.reviewCount})` : null].filter(Boolean).join(" ");
|
|
1599
1629
|
return `| ${r.position} | ${cell(r.name)} | ${cell(r.category)} | ${cell(rating)} | ${cell(r.address)} | ${r.cidDecimal ? `\`${r.cidDecimal}\`` : "\u2014"} | ${r.websiteUrl ? `[site](${r.websiteUrl})` : "\u2014"} | [maps](${r.placeUrl}) |`;
|
|
@@ -1608,6 +1638,7 @@ ${meta}`;
|
|
|
1608
1638
|
const full = [
|
|
1609
1639
|
`# Google Maps Search: "${searchQuery}"`,
|
|
1610
1640
|
`**Returned:** ${results.length} profile candidate${results.length === 1 ? "" : "s"} \xB7 **Requested max:** ${requestedMax} \xB7 **Limit:** 50`,
|
|
1641
|
+
attempts.length ? `**Attempts:** ${attempts.length}/${lastAttempt?.maxAttempts ?? attempts.length} \xB7 **Proxy:** ${lastAttempt?.proxyMode ?? "unknown"}${lastAttempt?.proxyResolutionSource ? `/${lastAttempt.proxyResolutionSource}` : ""} \xB7 **Observed:** ${[lastAttempt?.observedCity, lastAttempt?.observedRegion].filter(Boolean).join(", ") || "unknown"}` : null,
|
|
1611
1642
|
`
|
|
1612
1643
|
## Results
|
|
1613
1644
|
| # | Name | Category | Rating | Address | CID | Website | Maps |
|
|
@@ -1631,6 +1662,7 @@ ${rows}`,
|
|
|
1631
1662
|
requestedMaxResults: requestedMax,
|
|
1632
1663
|
resultCount: results.length,
|
|
1633
1664
|
results: normalizedResults,
|
|
1665
|
+
attempts,
|
|
1634
1666
|
durationMs: durationMs ?? 0
|
|
1635
1667
|
}
|
|
1636
1668
|
};
|
|
@@ -1641,6 +1673,7 @@ function formatDirectoryWorkflow(raw, input) {
|
|
|
1641
1673
|
const d = parsed.data;
|
|
1642
1674
|
const cities = (d.cities ?? []).map((city) => ({
|
|
1643
1675
|
...city,
|
|
1676
|
+
attempts: normalizeMapsAttempts(city.attempts),
|
|
1644
1677
|
results: city.results.map((result) => ({
|
|
1645
1678
|
...result,
|
|
1646
1679
|
phone: result.phone ?? null,
|
|
@@ -1868,7 +1901,7 @@ var HarvestPaaInputSchema = {
|
|
|
1868
1901
|
gl: import_zod2.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."),
|
|
1869
1902
|
hl: import_zod2.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."),
|
|
1870
1903
|
device: import_zod2.z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use desktop by default; use mobile only when the user asks for mobile rankings."),
|
|
1871
|
-
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state SERPs; it creates a fresh residential proxy ID per attempt
|
|
1904
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state SERPs; it creates a fresh residential proxy ID per attempt. If Google shows a CAPTCHA/challenge, browser-service sessions briefly wait for the automatic solver first, then retry with a fresh proxy/session if the challenge does not clear. Also retries proxy tunnel failure and wrong-location evidence before returning. Use configured only for the static configured proxy. Use none only for direct-network debugging."),
|
|
1872
1905
|
proxyZip: import_zod2.z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use when the user gives a specific ZIP or when city-center targeting needs to be forced. With proxyMode location this ZIP is used for each fresh proxy attempt."),
|
|
1873
1906
|
debug: import_zod2.z.boolean().default(false).describe("Include sanitized browser/session/location diagnostics in the response. Use true when debugging localization, CAPTCHA, or proxy behavior.")
|
|
1874
1907
|
};
|
|
@@ -1927,7 +1960,7 @@ var MapsSearchInputSchema = {
|
|
|
1927
1960
|
gl: import_zod2.z.string().length(2).default("us").describe("Google country code inferred from location."),
|
|
1928
1961
|
hl: import_zod2.z.string().length(2).default("en").describe("Language inferred from user request."),
|
|
1929
1962
|
maxResults: import_zod2.z.number().int().min(1).max(50).default(10).describe("Number of Google Maps business/profile candidates to return. Default 10. Maximum 50. Use 10 unless the user asks for more."),
|
|
1930
|
-
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state Maps searches;
|
|
1963
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state Maps searches; retryable failures create a new residential proxy ID and new browser session for up to 5 attempts. Use configured for the server proxy ID, and none only for local direct-network debugging."),
|
|
1931
1964
|
proxyZip: import_zod2.z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use when the user gives a specific ZIP or city-center ZIP."),
|
|
1932
1965
|
debug: import_zod2.z.boolean().default(false).describe("Include sanitized browser/proxy diagnostics when debugging Maps localization, CAPTCHA, or proxy behavior.")
|
|
1933
1966
|
};
|
|
@@ -1942,7 +1975,7 @@ var DirectoryWorkflowInputSchema = {
|
|
|
1942
1975
|
includeZipGroups: import_zod2.z.boolean().default(true).describe("Attach ZIP groups from a configured US ZIPS CSV when available. Set MCP_SCRAPER_USZIPS_CSV_PATH on the API server or pass usZipsCsvPath in local/test mode."),
|
|
1943
1976
|
usZipsCsvPath: import_zod2.z.string().optional().describe("Local/test-only path to a US ZIPS CSV with state_abbr, zipcode, county, city columns, such as Lead Magician tools/analytics/data/uszips.csv. Deployed APIs should use MCP_SCRAPER_USZIPS_CSV_PATH instead."),
|
|
1944
1977
|
saveCsv: import_zod2.z.boolean().default(true).describe("Save a directory-ready CSV to the MCP Scraper output directory and return its path. CSV rows include source_location, result_position, business_name, review_stars, category, address, phone, hours_status, website_url, directions_url, place_url, CID fields, population, and ZIP groups."),
|
|
1945
|
-
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode for every city Maps search. Use location by default for US city/state batches;
|
|
1978
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode for every city Maps search. Use location by default for US city/state batches; retryable failures create a new residential proxy ID and new browser session for up to 5 attempts per city. Use configured for the server proxy ID, and none only for local direct-network debugging."),
|
|
1946
1979
|
proxyZip: import_zod2.z.string().regex(/^\d{5}$/).optional().describe("Optional ZIP override for proxy targeting. Normally omit it so each city can use its Lead Magician ZIP group or city/state location."),
|
|
1947
1980
|
debug: import_zod2.z.boolean().default(false).describe("Include sanitized browser/proxy diagnostics in each Maps browser session when supported.")
|
|
1948
1981
|
};
|
|
@@ -1965,6 +1998,26 @@ var RankTrackerBlueprintInputSchema = {
|
|
|
1965
1998
|
notes: import_zod2.z.string().max(4e3).optional().describe("Extra product, client, stack, or hosting requirements to include in the implementation prompt.")
|
|
1966
1999
|
};
|
|
1967
2000
|
var NullableString = import_zod2.z.string().nullable();
|
|
2001
|
+
var MapsSearchAttemptOutput = import_zod2.z.object({
|
|
2002
|
+
attemptNumber: import_zod2.z.number().int().min(1),
|
|
2003
|
+
maxAttempts: import_zod2.z.number().int().min(1),
|
|
2004
|
+
status: import_zod2.z.enum(["ok", "failed"]),
|
|
2005
|
+
outcome: import_zod2.z.string(),
|
|
2006
|
+
willRetry: import_zod2.z.boolean(),
|
|
2007
|
+
durationMs: import_zod2.z.number().int().min(0),
|
|
2008
|
+
resultCount: import_zod2.z.number().int().min(0),
|
|
2009
|
+
error: NullableString,
|
|
2010
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]),
|
|
2011
|
+
proxyResolutionSource: import_zod2.z.enum(["disabled", "location_reused", "location_created", "configured_fallback", "unavailable"]).nullable(),
|
|
2012
|
+
proxyIdSuffix: NullableString,
|
|
2013
|
+
proxyTargetLevel: import_zod2.z.enum(["zip", "city", "state"]).nullable(),
|
|
2014
|
+
proxyTargetLocation: NullableString,
|
|
2015
|
+
proxyTargetZip: NullableString,
|
|
2016
|
+
browserSessionIdSuffix: NullableString,
|
|
2017
|
+
observedIp: NullableString,
|
|
2018
|
+
observedCity: NullableString,
|
|
2019
|
+
observedRegion: NullableString
|
|
2020
|
+
});
|
|
1968
2021
|
var MapsSearchOutputSchema = {
|
|
1969
2022
|
query: import_zod2.z.string(),
|
|
1970
2023
|
location: import_zod2.z.string().nullable(),
|
|
@@ -1989,6 +2042,7 @@ var MapsSearchOutputSchema = {
|
|
|
1989
2042
|
directionsUrl: NullableString,
|
|
1990
2043
|
metadata: import_zod2.z.array(import_zod2.z.string())
|
|
1991
2044
|
})),
|
|
2045
|
+
attempts: import_zod2.z.array(MapsSearchAttemptOutput),
|
|
1992
2046
|
durationMs: import_zod2.z.number().int().min(0)
|
|
1993
2047
|
};
|
|
1994
2048
|
var DirectoryMapsBusinessOutput = import_zod2.z.object({
|
|
@@ -2035,6 +2089,7 @@ var DirectoryWorkflowOutputSchema = {
|
|
|
2035
2089
|
error: NullableString,
|
|
2036
2090
|
resultCount: import_zod2.z.number().int().min(0),
|
|
2037
2091
|
durationMs: import_zod2.z.number().int().min(0),
|
|
2092
|
+
attempts: import_zod2.z.array(MapsSearchAttemptOutput),
|
|
2038
2093
|
results: import_zod2.z.array(DirectoryMapsBusinessOutput)
|
|
2039
2094
|
})),
|
|
2040
2095
|
durationMs: import_zod2.z.number().int().min(0)
|
|
@@ -2251,7 +2306,7 @@ var SearchSerpInputSchema = {
|
|
|
2251
2306
|
gl: import_zod2.z.string().length(2).default("us").describe("Google country code inferred from location or user language."),
|
|
2252
2307
|
hl: import_zod2.z.string().default("en").describe("Google interface/content language inferred from user request."),
|
|
2253
2308
|
device: import_zod2.z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use desktop by default; use mobile only when the user asks for mobile rankings."),
|
|
2254
|
-
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state SERPs; it creates a fresh residential proxy ID per attempt
|
|
2309
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy targeting mode. Use location by default for US city/state SERPs; it creates a fresh residential proxy ID per attempt. If Google shows a CAPTCHA/challenge, browser-service sessions briefly wait for the automatic solver first, then retry with a fresh proxy/session if the challenge does not clear. Also retries proxy tunnel failure and wrong-location evidence before returning. Use configured only for the static configured proxy. Use none only for direct-network debugging."),
|
|
2255
2310
|
proxyZip: import_zod2.z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting. Use when the user gives a specific ZIP or when city-center targeting needs to be forced. With proxyMode location this ZIP is used for each fresh proxy attempt."),
|
|
2256
2311
|
debug: import_zod2.z.boolean().default(false).describe("Include sanitized browser/session/location diagnostics in the response. Use true when debugging localization, CAPTCHA, or proxy behavior."),
|
|
2257
2312
|
pages: import_zod2.z.number().int().min(1).max(2).default(1).describe("Number of result pages to fetch (1\u20132)")
|
|
@@ -2262,7 +2317,7 @@ var CaptureSerpSnapshotInputSchema = {
|
|
|
2262
2317
|
gl: import_zod2.z.string().length(2).default("us").describe("Google country code inferred from the requested market, e.g. us, gb, ca, au."),
|
|
2263
2318
|
hl: import_zod2.z.string().default("en").describe("Google interface/content language inferred from the user request."),
|
|
2264
2319
|
device: import_zod2.z.enum(["desktop", "mobile"]).default("desktop").describe("SERP device context. Use mobile only when the user asks for mobile rankings or mobile SERP evidence."),
|
|
2265
|
-
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy behavior for capture. Use location for localized US residential evidence; it creates a fresh proxy ID per attempt
|
|
2320
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default("location").describe("Proxy behavior for capture. Use location for localized US residential evidence; it creates a fresh proxy ID per attempt. If Google shows a CAPTCHA/challenge, browser-service sessions briefly wait for the automatic solver first, then retry with a fresh proxy/session if the challenge does not clear. Also retries proxy tunnel failure and wrong-location evidence before returning. Use configured only for the static residential proxy, and none only for direct-network debugging."),
|
|
2266
2321
|
proxyZip: import_zod2.z.string().regex(/^\d{5}$/).optional().describe("Optional US ZIP override for residential location proxy targeting when a precise city-center or ZIP proxy is needed. With proxyMode location this ZIP is used for each fresh proxy attempt."),
|
|
2267
2322
|
pages: import_zod2.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."),
|
|
2268
2323
|
debug: import_zod2.z.boolean().default(false).describe("Include sanitized browser, proxy, and location diagnostics. Use true when debugging localization, CAPTCHA, proxy selection, or capture reliability."),
|
|
@@ -2669,14 +2724,14 @@ function registerPaaExtractorMcpTools(server2, executor, options = {}) {
|
|
|
2669
2724
|
if (savesReports) registerSavedReportResources(server2);
|
|
2670
2725
|
server2.registerTool("harvest_paa", {
|
|
2671
2726
|
title: "Google PAA + SERP Harvest",
|
|
2672
|
-
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"). For US local SERPs, leave proxyMode as location so the service uses fresh residential proxy IDs across retries
|
|
2727
|
+
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"). For US local SERPs, leave proxyMode as location so the service uses fresh residential proxy IDs across retries, briefly waits for browser-service automatic CAPTCHA/challenge solving when Google blocks a page, and rotates to a new proxy/session if the challenge does not clear or the market evidence is wrong. 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.'),
|
|
2673
2728
|
inputSchema: HarvestPaaInputSchema,
|
|
2674
2729
|
outputSchema: HarvestPaaOutputSchema,
|
|
2675
2730
|
annotations: liveWebToolAnnotations("Google PAA + SERP Harvest")
|
|
2676
2731
|
}, async (input) => formatHarvestPaa(await executor.harvestPaa(input), input));
|
|
2677
2732
|
server2.registerTool("search_serp", {
|
|
2678
2733
|
title: "Google SERP Lookup",
|
|
2679
|
-
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. For US city/state rankings, keep proxyMode as location and pass proxyZip when a city-center ZIP is known; location mode uses fresh residential proxy IDs
|
|
2734
|
+
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. For US city/state rankings, keep proxyMode as location and pass proxyZip when a city-center ZIP is known; location mode uses fresh residential proxy IDs, briefly waits for browser-service automatic CAPTCHA/challenge solving when Google blocks a page, and rotates to a new proxy/session if the challenge does not clear, the proxy tunnel fails, or location evidence is wrong."),
|
|
2680
2735
|
inputSchema: SearchSerpInputSchema,
|
|
2681
2736
|
outputSchema: SearchSerpOutputSchema,
|
|
2682
2737
|
annotations: liveWebToolAnnotations("Google SERP Lookup")
|
|
@@ -2744,14 +2799,14 @@ function registerPaaExtractorMcpTools(server2, executor, options = {}) {
|
|
|
2744
2799
|
}, async (input) => formatMapsPlaceIntel(await executor.mapsPlaceIntel(input), input));
|
|
2745
2800
|
server2.registerTool("maps_search", {
|
|
2746
2801
|
title: "Google Maps Business Search",
|
|
2747
|
-
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." For US city/state Maps searches, keep proxyMode as location so the browser service
|
|
2802
|
+
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." For US city/state Maps searches, keep proxyMode as location so the browser service creates a fresh residential proxy ID and browser session for the market; retryable failures rotate to a new proxy and new browser session for up to 5 attempts. Pass proxyZip only when a specific ZIP or city-center ZIP is known. 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.'),
|
|
2748
2803
|
inputSchema: MapsSearchInputSchema,
|
|
2749
2804
|
outputSchema: MapsSearchOutputSchema,
|
|
2750
2805
|
annotations: liveWebToolAnnotations("Google Maps Business Search")
|
|
2751
2806
|
}, async (input) => formatMapsSearch(await executor.mapsSearch(input), input));
|
|
2752
2807
|
server2.registerTool("directory_workflow", {
|
|
2753
2808
|
title: "Directory Workflow: Markets + Maps",
|
|
2754
|
-
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. Use concurrency up to 5 for parallel city sessions. Keep proxyMode as location so each city
|
|
2809
|
+
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. Use concurrency up to 5 for parallel city sessions. Keep proxyMode as location so each city search creates fresh residential proxy IDs and browser sessions; retryable city failures rotate to a new proxy and new browser session for up to 5 attempts. Saved CSV rows include source_location, result_position, business_name, review_stars, 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. This workflow captures star ratings from Maps list cards, not profile review counts; use maps_place_intel only when a selected profile needs deeper review details. For local Lead Magician ZIP enrichment, set MCP_SCRAPER_USZIPS_CSV_PATH on the API server or pass usZipsCsvPath only in local/test mode.'),
|
|
2755
2810
|
inputSchema: DirectoryWorkflowInputSchema,
|
|
2756
2811
|
outputSchema: DirectoryWorkflowOutputSchema,
|
|
2757
2812
|
annotations: liveWebToolAnnotations("Directory Workflow: Markets + Maps")
|