mcp-scraper 0.2.18 → 0.2.20
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 +17 -11
- package/dist/bin/api-server.cjs +1020 -348
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/browser-agent-stdio-server.cjs +314 -55
- 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-cli.cjs +321 -167
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +3 -2
- package/dist/bin/mcp-scraper-cli.js.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +1230 -528
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +19 -3
- package/dist/bin/mcp-scraper-combined-stdio-server.js.map +1 -1
- package/dist/bin/mcp-scraper-install.cjs +8 -6
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +5 -92
- package/dist/bin/mcp-scraper-install.js.map +1 -1
- package/dist/bin/mcp-stdio-server.cjs +726 -387
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +3 -2
- package/dist/bin/mcp-stdio-server.js.map +1 -1
- package/dist/bin/paa-harvest.cjs +5 -3
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +2 -1
- package/dist/bin/paa-harvest.js.map +1 -1
- package/dist/{chunk-TL7YTFLH.js → chunk-DBQDG7EH.js} +99 -27
- package/dist/chunk-DBQDG7EH.js.map +1 -0
- package/dist/{chunk-F44RBOJ5.js → chunk-H2R232HK.js} +216 -108
- package/dist/chunk-H2R232HK.js.map +1 -0
- package/dist/{chunk-P5PLQU3H.js → chunk-L4OWOUGR.js} +315 -56
- package/dist/chunk-L4OWOUGR.js.map +1 -0
- package/dist/chunk-LFATOGDF.js +111 -0
- package/dist/chunk-LFATOGDF.js.map +1 -0
- package/dist/chunk-MLNCKQ2B.js +96 -0
- package/dist/chunk-MLNCKQ2B.js.map +1 -0
- package/dist/{chunk-RJMCASQH.js → chunk-O5J7E4VX.js} +394 -57
- package/dist/chunk-O5J7E4VX.js.map +1 -0
- package/dist/{chunk-BSYPATSM.js → chunk-SXTXMFEQ.js} +2 -2
- package/dist/{chunk-CQTAKXBN.js → chunk-XGUDTDZ2.js} +7 -104
- package/dist/chunk-XGUDTDZ2.js.map +1 -0
- package/dist/chunk-XKUDVN2E.js +7 -0
- package/dist/chunk-XKUDVN2E.js.map +1 -0
- package/dist/{db-P5X6UQ3E.js → db-BE4JVB3V.js} +8 -2
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/{server-VVY5K44O.js → server-7NE45K2A.js} +211 -37
- package/dist/server-7NE45K2A.js.map +1 -0
- package/dist/{worker-OZSWIS3F.js → worker-MIYG2B2I.js} +5 -4
- package/dist/{worker-OZSWIS3F.js.map → worker-MIYG2B2I.js.map} +1 -1
- package/docs/adr/0001-in-page-graphql-interception-for-anti-bot-scraping.md +2 -2
- package/docs/mcp-tool-craft-lint.generated.md +51 -0
- package/docs/mcp-tool-manifest.generated.json +1406 -0
- package/docs/specs/local-competitive-audit-spec.md +1 -2
- package/docs/specs/mcp-tool-definition-quality-audit-spec.md +1602 -0
- package/package.json +4 -1
- package/dist/chunk-CQTAKXBN.js.map +0 -1
- package/dist/chunk-F44RBOJ5.js.map +0 -1
- package/dist/chunk-M5SAUO4K.js +0 -7
- package/dist/chunk-M5SAUO4K.js.map +0 -1
- package/dist/chunk-P5PLQU3H.js.map +0 -1
- package/dist/chunk-RJMCASQH.js.map +0 -1
- package/dist/chunk-TL7YTFLH.js.map +0 -1
- package/dist/server-VVY5K44O.js.map +0 -1
- /package/dist/{chunk-BSYPATSM.js.map → chunk-SXTXMFEQ.js.map} +0 -0
- /package/dist/{db-P5X6UQ3E.js.map → db-BE4JVB3V.js.map} +0 -0
|
@@ -22,6 +22,27 @@ function harvestTimeoutBudget(maxQuestions, serpOnly = false) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// src/mcp/http-mcp-tool-executor.ts
|
|
25
|
+
function youtubeVideoIdFromUrl(url) {
|
|
26
|
+
if (!url) return null;
|
|
27
|
+
try {
|
|
28
|
+
const parsed = new URL(url);
|
|
29
|
+
const host = parsed.hostname.replace(/^www\./, "").replace(/^m\./, "");
|
|
30
|
+
if (host === "youtu.be") {
|
|
31
|
+
const id = parsed.pathname.split("/").filter(Boolean)[0];
|
|
32
|
+
return id || null;
|
|
33
|
+
}
|
|
34
|
+
if (host === "youtube.com" || host === "music.youtube.com") {
|
|
35
|
+
const watchId = parsed.searchParams.get("v");
|
|
36
|
+
if (watchId) return watchId;
|
|
37
|
+
const parts = parsed.pathname.split("/").filter(Boolean);
|
|
38
|
+
const markerIndex = parts.findIndex((part) => ["shorts", "embed", "live"].includes(part));
|
|
39
|
+
if (markerIndex >= 0 && parts[markerIndex + 1]) return parts[markerIndex + 1];
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
25
46
|
var HttpMcpToolExecutor = class {
|
|
26
47
|
baseUrl;
|
|
27
48
|
apiKey;
|
|
@@ -147,7 +168,21 @@ var HttpMcpToolExecutor = class {
|
|
|
147
168
|
return this.call("/youtube/harvest", input);
|
|
148
169
|
}
|
|
149
170
|
youtubeTranscribe(input) {
|
|
150
|
-
|
|
171
|
+
const videoId = input.videoId?.trim() || youtubeVideoIdFromUrl(input.url);
|
|
172
|
+
if (!videoId) {
|
|
173
|
+
return Promise.resolve({
|
|
174
|
+
content: [{
|
|
175
|
+
type: "text",
|
|
176
|
+
text: JSON.stringify({
|
|
177
|
+
error_code: "youtube_video_id_required",
|
|
178
|
+
error: "Pass videoId from youtube_harvest or a YouTube url that contains a video id.",
|
|
179
|
+
retryable: false
|
|
180
|
+
})
|
|
181
|
+
}],
|
|
182
|
+
isError: true
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return this.call("/youtube/transcribe", { videoId });
|
|
151
186
|
}
|
|
152
187
|
facebookPageIntel(input) {
|
|
153
188
|
return this.call("/facebook/page-intel", input);
|
|
@@ -184,6 +219,10 @@ var HttpMcpToolExecutor = class {
|
|
|
184
219
|
webhookUrl: input.webhookUrl
|
|
185
220
|
}, Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 9e5);
|
|
186
221
|
}
|
|
222
|
+
workflowStep(input) {
|
|
223
|
+
const timeoutMs = this.httpTimeoutOverrideMs ?? Number(process.env.MCP_SCRAPER_WORKFLOW_TIMEOUT_MS ?? 9e5);
|
|
224
|
+
return this.call(`/workflows/runs/${encodeURIComponent(input.runId)}/step`, {}, Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 9e5);
|
|
225
|
+
}
|
|
187
226
|
workflowStatus(input) {
|
|
188
227
|
return this.getJson(`/workflows/runs/${encodeURIComponent(input.runId)}`);
|
|
189
228
|
}
|
|
@@ -210,7 +249,7 @@ var import_node_fs2 = require("fs");
|
|
|
210
249
|
var import_node_path2 = require("path");
|
|
211
250
|
|
|
212
251
|
// src/version.ts
|
|
213
|
-
var PACKAGE_VERSION = "0.2.
|
|
252
|
+
var PACKAGE_VERSION = "0.2.20";
|
|
214
253
|
|
|
215
254
|
// src/mcp/mcp-response-formatter.ts
|
|
216
255
|
var import_node_fs = require("fs");
|
|
@@ -832,13 +871,25 @@ ${videoRows}`,
|
|
|
832
871
|
}
|
|
833
872
|
};
|
|
834
873
|
}
|
|
874
|
+
function structuredTranscriptChunks(chunks) {
|
|
875
|
+
return chunks.map((c) => ({
|
|
876
|
+
startSec: Number.isFinite(c.timestamp?.[0]) ? c.timestamp[0] : 0,
|
|
877
|
+
endSec: Number.isFinite(c.timestamp?.[1]) ? c.timestamp[1] : 0,
|
|
878
|
+
text: c.text
|
|
879
|
+
}));
|
|
880
|
+
}
|
|
881
|
+
function wordCount(text) {
|
|
882
|
+
return text.trim() ? text.trim().split(/\s+/).length : 0;
|
|
883
|
+
}
|
|
835
884
|
function formatYoutubeTranscribe(raw, input) {
|
|
836
885
|
const parsed = parseData(raw);
|
|
837
886
|
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
838
887
|
const d = parsed.data;
|
|
839
888
|
const text = d.text ?? "";
|
|
840
889
|
const chunks = d.chunks ?? [];
|
|
890
|
+
const videoId = d.videoId ?? input.videoId ?? null;
|
|
841
891
|
const durSec = d.durationMs ? (d.durationMs / 1e3).toFixed(0) : "\u2014";
|
|
892
|
+
const words = wordCount(text);
|
|
842
893
|
const chunkRows = chunks.slice(0, 50).map((c) => {
|
|
843
894
|
const sec = Number.isFinite(c.timestamp[0]) ? Math.floor(c.timestamp[0]) : 0;
|
|
844
895
|
const mm = String(Math.floor(sec / 60)).padStart(2, "0");
|
|
@@ -846,8 +897,8 @@ function formatYoutubeTranscribe(raw, input) {
|
|
|
846
897
|
return `| ${mm}:${ss} | ${cell(truncate(c.text, 120))} |`;
|
|
847
898
|
}).join("\n");
|
|
848
899
|
const full = [
|
|
849
|
-
`# YouTube Transcript: \`${input.
|
|
850
|
-
`**Duration:** ${durSec}s \xB7 **${
|
|
900
|
+
`# YouTube Transcript: \`${videoId ?? input.url ?? "video"}\``,
|
|
901
|
+
`**Duration:** ${durSec}s \xB7 **${words} words**`,
|
|
851
902
|
`
|
|
852
903
|
## Full Transcript
|
|
853
904
|
${text}`,
|
|
@@ -860,7 +911,22 @@ ${chunkRows}` : "",
|
|
|
860
911
|
---
|
|
861
912
|
\u{1F4A1} Harvest more from this channel: use \`youtube_harvest\` with \`mode: "channel"\``
|
|
862
913
|
].filter(Boolean).join("\n");
|
|
863
|
-
return
|
|
914
|
+
return {
|
|
915
|
+
...oneBlock(full),
|
|
916
|
+
structuredContent: {
|
|
917
|
+
videoId,
|
|
918
|
+
url: videoId ? `https://www.youtube.com/watch?v=${videoId}` : input.url ?? null,
|
|
919
|
+
wordCount: words,
|
|
920
|
+
chunkCount: chunks.length,
|
|
921
|
+
durationMs: typeof d.durationMs === "number" ? d.durationMs : null,
|
|
922
|
+
transcriptText: text,
|
|
923
|
+
chunks: structuredTranscriptChunks(chunks),
|
|
924
|
+
resolvedInputs: {
|
|
925
|
+
videoId,
|
|
926
|
+
url: input.url ?? null
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
};
|
|
864
930
|
}
|
|
865
931
|
function formatFacebookPageIntel(raw, input) {
|
|
866
932
|
const parsed = parseData(raw);
|
|
@@ -870,12 +936,13 @@ function formatFacebookPageIntel(raw, input) {
|
|
|
870
936
|
const ads = d.ads ?? [];
|
|
871
937
|
const s = d.summary ?? { totalAds: 0, activeCount: 0, videoCount: 0, imageCount: 0 };
|
|
872
938
|
const adBlocks = ads.map((ad, i) => [
|
|
873
|
-
`### Ad ${i + 1}${ad.libraryId ? ` \xB7 \`${ad.libraryId}\`` : ""} \u2014 ${ad.status ?? "\u2014"} \xB7 ${ad.creativeType ?? "\u2014"} \xB7 ${ad.startDate ?? "\u2014"}`,
|
|
939
|
+
`### Ad ${i + 1}${ad.libraryId ? ` \xB7 \`${ad.libraryId}\`` : ""} \u2014 ${ad.status ?? "\u2014"} \xB7 ${ad.creativeType ?? "\u2014"} \xB7 ${ad.startDate ?? ad.started ?? "\u2014"}`,
|
|
874
940
|
ad.headline ? `**Headline:** ${ad.headline}` : "",
|
|
875
941
|
ad.primaryText ? `**Copy:** ${truncate(ad.primaryText, 200)}` : "",
|
|
876
942
|
ad.cta ? `**CTA:** ${ad.cta}` : "",
|
|
877
|
-
ad.
|
|
878
|
-
ad.
|
|
943
|
+
ad.landingUrl ? `**Landing URL:** ${ad.landingUrl}` : "",
|
|
944
|
+
ad.videoUrl ?? ad.videoSrc ? `**Video URL:** \`${ad.videoUrl ?? ad.videoSrc}\`` : "",
|
|
945
|
+
ad.variations ?? ad.clusterCount ? `**Variations:** ${ad.variations ?? ad.clusterCount}` : ""
|
|
879
946
|
].filter(Boolean).join("\n")).join("\n\n---\n\n");
|
|
880
947
|
const full = [
|
|
881
948
|
`# Facebook Ad Intel: ${advertiser}`,
|
|
@@ -901,11 +968,16 @@ ${adBlocks}`,
|
|
|
901
968
|
libraryId: ad.libraryId ?? null,
|
|
902
969
|
status: ad.status ?? null,
|
|
903
970
|
creativeType: ad.creativeType ?? null,
|
|
971
|
+
primaryText: ad.primaryText ?? null,
|
|
904
972
|
headline: ad.headline ?? null,
|
|
905
973
|
cta: ad.cta ?? null,
|
|
906
|
-
startDate: ad.startDate ?? null,
|
|
907
|
-
|
|
908
|
-
|
|
974
|
+
startDate: ad.startDate ?? ad.started ?? null,
|
|
975
|
+
landingUrl: ad.landingUrl ?? null,
|
|
976
|
+
domain: ad.domain ?? null,
|
|
977
|
+
videoUrl: ad.videoUrl ?? ad.videoSrc ?? null,
|
|
978
|
+
imageUrl: ad.imageUrl ?? ad.imageSrc ?? null,
|
|
979
|
+
videoPoster: ad.videoPoster ?? null,
|
|
980
|
+
variations: typeof ad.variations === "number" ? ad.variations : typeof ad.clusterCount === "number" ? ad.clusterCount : null
|
|
909
981
|
}))
|
|
910
982
|
}
|
|
911
983
|
};
|
|
@@ -939,8 +1011,11 @@ ${rows}`,
|
|
|
939
1011
|
advertiserCount: advertisers.length,
|
|
940
1012
|
advertisers: advertisers.map((a) => ({
|
|
941
1013
|
name: a.pageName ?? a.name ?? null,
|
|
1014
|
+
pageId: a.pageId ?? null,
|
|
1015
|
+
pageUrl: a.pageUrl ?? null,
|
|
942
1016
|
adCount: typeof a.adCount === "number" ? a.adCount : null,
|
|
943
|
-
libraryId: a.sampleLibraryId ?? a.libraryId ?? null
|
|
1017
|
+
libraryId: a.sampleLibraryId ?? a.libraryId ?? null,
|
|
1018
|
+
sampleLibraryId: a.sampleLibraryId ?? null
|
|
944
1019
|
}))
|
|
945
1020
|
}
|
|
946
1021
|
};
|
|
@@ -1038,6 +1113,34 @@ function formatWorkflowSuggest(input) {
|
|
|
1038
1113
|
}
|
|
1039
1114
|
};
|
|
1040
1115
|
}
|
|
1116
|
+
function workflowStepLines(data) {
|
|
1117
|
+
const step = data.step;
|
|
1118
|
+
const nextStep = data.nextStep;
|
|
1119
|
+
const done = data.done === true;
|
|
1120
|
+
const lines = [];
|
|
1121
|
+
if (step) {
|
|
1122
|
+
const index = Number(step.index ?? 0);
|
|
1123
|
+
const totalSteps = step.totalSteps != null ? Number(step.totalSteps) : void 0;
|
|
1124
|
+
const stepLabel = totalSteps ? `${index + 1}/${totalSteps}` : `${index + 1}`;
|
|
1125
|
+
lines.push(`
|
|
1126
|
+
## Step ${stepLabel}: ${step.title ?? step.id ?? ""}`);
|
|
1127
|
+
const output = step.output;
|
|
1128
|
+
if (output && Object.keys(output).length) {
|
|
1129
|
+
lines.push(Object.entries(output).map(([k, v]) => `- ${k}: ${typeof v === "object" ? JSON.stringify(v) : String(v)}`).join("\n"));
|
|
1130
|
+
}
|
|
1131
|
+
const warnings = Array.isArray(step.warnings) ? step.warnings : [];
|
|
1132
|
+
if (warnings.length) lines.push(`
|
|
1133
|
+
**Warnings:**
|
|
1134
|
+
${warnings.map((w) => `- ${w}`).join("\n")}`);
|
|
1135
|
+
}
|
|
1136
|
+
if (done) {
|
|
1137
|
+
lines.push("\n**Done.** All steps complete.");
|
|
1138
|
+
} else if (nextStep && typeof nextStep === "object") {
|
|
1139
|
+
lines.push(`
|
|
1140
|
+
**Next step:** \`${nextStep.id ?? nextStep.index}\` \u2014 call \`workflow_step\` with this run id to continue.`);
|
|
1141
|
+
}
|
|
1142
|
+
return lines;
|
|
1143
|
+
}
|
|
1041
1144
|
function formatWorkflowRun(raw, input) {
|
|
1042
1145
|
const parsed = parseData(raw);
|
|
1043
1146
|
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
@@ -1051,6 +1154,7 @@ function formatWorkflowRun(raw, input) {
|
|
|
1051
1154
|
`**Run ID:** \`${runId || "unknown"}\``,
|
|
1052
1155
|
`**Status:** ${status}`,
|
|
1053
1156
|
summary?.title ? `**Title:** ${summary.title}` : "",
|
|
1157
|
+
...workflowStepLines(parsed.data),
|
|
1054
1158
|
summary?.summary ? `
|
|
1055
1159
|
## Summary
|
|
1056
1160
|
${summary.summary}` : "",
|
|
@@ -1066,6 +1170,42 @@ ${workflowArtifactRows(artifacts)}` : "",
|
|
|
1066
1170
|
input: input.input ?? {},
|
|
1067
1171
|
run,
|
|
1068
1172
|
summary,
|
|
1173
|
+
step: parsed.data.step,
|
|
1174
|
+
nextStep: parsed.data.nextStep ?? null,
|
|
1175
|
+
done: parsed.data.done === true,
|
|
1176
|
+
artifacts
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
function formatWorkflowStep(raw, input) {
|
|
1181
|
+
const parsed = parseData(raw);
|
|
1182
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
1183
|
+
const run = parsed.data.run;
|
|
1184
|
+
const summary = parsed.data.summary;
|
|
1185
|
+
const artifacts = workflowArtifactsFrom(run);
|
|
1186
|
+
const done = parsed.data.done === true;
|
|
1187
|
+
const full = [
|
|
1188
|
+
`# Workflow Step`,
|
|
1189
|
+
`**Run ID:** \`${input.runId}\``,
|
|
1190
|
+
`**Status:** ${run?.status ?? (done ? "done" : "running")}`,
|
|
1191
|
+
...workflowStepLines(parsed.data),
|
|
1192
|
+
done && summary?.summary ? `
|
|
1193
|
+
## Summary
|
|
1194
|
+
${summary.summary}` : "",
|
|
1195
|
+
artifacts.length ? `
|
|
1196
|
+
## Artifacts
|
|
1197
|
+
${workflowArtifactRows(artifacts)}` : "",
|
|
1198
|
+
done && artifacts.length ? "\nUse `workflow_artifact_read` with the run id and artifact id to pull CSV, JSON, Markdown, or report content into context." : ""
|
|
1199
|
+
].filter(Boolean).join("\n");
|
|
1200
|
+
return {
|
|
1201
|
+
...oneBlock(full),
|
|
1202
|
+
structuredContent: {
|
|
1203
|
+
runId: input.runId,
|
|
1204
|
+
run,
|
|
1205
|
+
summary: summary ?? null,
|
|
1206
|
+
step: parsed.data.step,
|
|
1207
|
+
nextStep: parsed.data.nextStep ?? null,
|
|
1208
|
+
done,
|
|
1069
1209
|
artifacts
|
|
1070
1210
|
}
|
|
1071
1211
|
};
|
|
@@ -1470,6 +1610,7 @@ function formatFacebookAdTranscribe(raw, input) {
|
|
|
1470
1610
|
const text = d.text ?? "";
|
|
1471
1611
|
const chunks = d.chunks ?? [];
|
|
1472
1612
|
const durSec = d.durationMs ? (d.durationMs / 1e3).toFixed(0) : "\u2014";
|
|
1613
|
+
const words = wordCount(text);
|
|
1473
1614
|
const chunkRows = chunks.slice(0, 50).map((c) => {
|
|
1474
1615
|
const sec = Number.isFinite(c.timestamp[0]) ? Math.floor(c.timestamp[0]) : 0;
|
|
1475
1616
|
const mm = String(Math.floor(sec / 60)).padStart(2, "0");
|
|
@@ -1478,7 +1619,7 @@ function formatFacebookAdTranscribe(raw, input) {
|
|
|
1478
1619
|
}).join("\n");
|
|
1479
1620
|
const full = [
|
|
1480
1621
|
`# Facebook Ad Transcript`,
|
|
1481
|
-
`**Duration:** ${durSec}s \xB7 **${
|
|
1622
|
+
`**Duration:** ${durSec}s \xB7 **${words} words**`,
|
|
1482
1623
|
`
|
|
1483
1624
|
## Full Transcript
|
|
1484
1625
|
${text}`,
|
|
@@ -1491,7 +1632,20 @@ ${chunkRows}` : "",
|
|
|
1491
1632
|
---
|
|
1492
1633
|
\u{1F4A1} Get more ads from this advertiser: use \`facebook_page_intel\`. For public Facebook reel/post URLs, use \`facebook_video_transcribe\`.`
|
|
1493
1634
|
].filter(Boolean).join("\n");
|
|
1494
|
-
return
|
|
1635
|
+
return {
|
|
1636
|
+
...oneBlock(full),
|
|
1637
|
+
structuredContent: {
|
|
1638
|
+
videoUrl: input.videoUrl,
|
|
1639
|
+
wordCount: words,
|
|
1640
|
+
chunkCount: chunks.length,
|
|
1641
|
+
durationMs: typeof d.durationMs === "number" ? d.durationMs : null,
|
|
1642
|
+
transcriptText: text,
|
|
1643
|
+
chunks: structuredTranscriptChunks(chunks),
|
|
1644
|
+
resolvedInputs: {
|
|
1645
|
+
videoUrl: input.videoUrl
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
};
|
|
1495
1649
|
}
|
|
1496
1650
|
function formatFacebookVideoTranscribe(raw, input) {
|
|
1497
1651
|
const parsed = parseData(raw);
|
|
@@ -1499,7 +1653,7 @@ function formatFacebookVideoTranscribe(raw, input) {
|
|
|
1499
1653
|
const d = parsed.data;
|
|
1500
1654
|
const text = d.text ?? "";
|
|
1501
1655
|
const chunks = d.chunks ?? [];
|
|
1502
|
-
const
|
|
1656
|
+
const wordCount2 = text.trim() ? text.trim().split(/\s+/).length : 0;
|
|
1503
1657
|
const durSec = d.durationMs ? (d.durationMs / 1e3).toFixed(0) : "\u2014";
|
|
1504
1658
|
const videoDuration = typeof d.videoDurationSec === "number" ? `${Math.round(d.videoDurationSec)}s` : "\u2014";
|
|
1505
1659
|
const label = d.videoId ? `Facebook Organic Video \`${d.videoId}\`` : "Facebook Organic Video";
|
|
@@ -1512,7 +1666,7 @@ function formatFacebookVideoTranscribe(raw, input) {
|
|
|
1512
1666
|
const full = [
|
|
1513
1667
|
`# ${label} Transcript`,
|
|
1514
1668
|
d.ownerName ? `**Owner:** ${d.ownerName}` : "",
|
|
1515
|
-
`**Video duration:** ${videoDuration} \xB7 **Transcribed in:** ${durSec}s \xB7 **${
|
|
1669
|
+
`**Video duration:** ${videoDuration} \xB7 **Transcribed in:** ${durSec}s \xB7 **${wordCount2} words**`,
|
|
1516
1670
|
d.pageUrl ? `**Page URL:** ${d.pageUrl}` : `**Page URL:** ${input.url}`,
|
|
1517
1671
|
d.videoUrl ? `**Extracted MP4:** \`${d.videoUrl}\`` : "",
|
|
1518
1672
|
`
|
|
@@ -1538,7 +1692,7 @@ ${chunkRows}` : "",
|
|
|
1538
1692
|
bitrate: typeof d.bitrate === "number" ? d.bitrate : null,
|
|
1539
1693
|
videoDurationSec: typeof d.videoDurationSec === "number" ? d.videoDurationSec : null,
|
|
1540
1694
|
videoUrl: d.videoUrl ?? "",
|
|
1541
|
-
wordCount,
|
|
1695
|
+
wordCount: wordCount2,
|
|
1542
1696
|
chunkCount: chunks.length,
|
|
1543
1697
|
transcriptText: text,
|
|
1544
1698
|
chunks: chunks.map((c) => ({
|
|
@@ -1551,128 +1705,229 @@ ${chunkRows}` : "",
|
|
|
1551
1705
|
}
|
|
1552
1706
|
|
|
1553
1707
|
// src/mcp/mcp-tool-schemas.ts
|
|
1708
|
+
var import_zod2 = require("zod");
|
|
1709
|
+
|
|
1710
|
+
// src/schemas.ts
|
|
1554
1711
|
var import_zod = require("zod");
|
|
1712
|
+
var DEFAULT_PROXY_MODE = "configured";
|
|
1713
|
+
var DEFAULT_MAPS_PROXY_MODE = "location";
|
|
1714
|
+
var HarvestOptionsSchema = import_zod.z.object({
|
|
1715
|
+
query: import_zod.z.string().min(1),
|
|
1716
|
+
location: import_zod.z.string().optional(),
|
|
1717
|
+
gl: import_zod.z.string().length(2).default("us"),
|
|
1718
|
+
hl: import_zod.z.string().length(2).default("en"),
|
|
1719
|
+
device: import_zod.z.enum(["desktop", "mobile"]).default("desktop"),
|
|
1720
|
+
proxyMode: import_zod.z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE),
|
|
1721
|
+
proxyZip: import_zod.z.string().regex(/^\d{5}$/).optional(),
|
|
1722
|
+
debug: import_zod.z.boolean().default(false),
|
|
1723
|
+
depth: import_zod.z.number().int().min(1).max(30).default(3),
|
|
1724
|
+
maxQuestions: import_zod.z.number().int().min(1).max(1e3).default(100),
|
|
1725
|
+
headless: import_zod.z.boolean().default(false),
|
|
1726
|
+
profileDir: import_zod.z.string().optional(),
|
|
1727
|
+
proxy: import_zod.z.string().url().optional(),
|
|
1728
|
+
kernelApiKey: import_zod.z.string().optional(),
|
|
1729
|
+
kernelProxyId: import_zod.z.string().optional(),
|
|
1730
|
+
kernelProxyResolution: import_zod.z.unknown().optional(),
|
|
1731
|
+
outputDir: import_zod.z.string().default("./paa-output"),
|
|
1732
|
+
format: import_zod.z.enum(["json", "csv", "both"]).default("both"),
|
|
1733
|
+
serpOnly: import_zod.z.boolean().default(false),
|
|
1734
|
+
pages: import_zod.z.number().int().min(1).max(2).default(1)
|
|
1735
|
+
});
|
|
1736
|
+
var MapsPlaceOptionsSchema = import_zod.z.object({
|
|
1737
|
+
businessName: import_zod.z.string().min(1),
|
|
1738
|
+
location: import_zod.z.string().min(1),
|
|
1739
|
+
gl: import_zod.z.string().length(2).default("us"),
|
|
1740
|
+
hl: import_zod.z.string().length(2).default("en"),
|
|
1741
|
+
includeReviews: import_zod.z.boolean().default(false),
|
|
1742
|
+
maxReviews: import_zod.z.number().int().min(1).max(500).default(50),
|
|
1743
|
+
kernelApiKey: import_zod.z.string().optional(),
|
|
1744
|
+
kernelProxyId: import_zod.z.string().optional(),
|
|
1745
|
+
headless: import_zod.z.boolean().default(true)
|
|
1746
|
+
});
|
|
1747
|
+
var MapsSearchOptionsSchema = import_zod.z.object({
|
|
1748
|
+
query: import_zod.z.string().min(1),
|
|
1749
|
+
location: import_zod.z.string().optional(),
|
|
1750
|
+
gl: import_zod.z.string().length(2).default("us"),
|
|
1751
|
+
hl: import_zod.z.string().length(2).default("en"),
|
|
1752
|
+
maxResults: import_zod.z.number().int().min(1).max(50).default(10),
|
|
1753
|
+
proxyMode: import_zod.z.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE),
|
|
1754
|
+
proxyZip: import_zod.z.string().regex(/^\d{5}$/).optional(),
|
|
1755
|
+
debug: import_zod.z.boolean().default(false),
|
|
1756
|
+
kernelApiKey: import_zod.z.string().optional(),
|
|
1757
|
+
kernelProxyId: import_zod.z.string().optional(),
|
|
1758
|
+
kernelProxyResolution: import_zod.z.unknown().optional(),
|
|
1759
|
+
headless: import_zod.z.boolean().default(true)
|
|
1760
|
+
});
|
|
1761
|
+
var RawPAAItemSchema = import_zod.z.object({
|
|
1762
|
+
question: import_zod.z.string().min(1),
|
|
1763
|
+
answer: import_zod.z.string().optional(),
|
|
1764
|
+
sourceTitle: import_zod.z.string().optional(),
|
|
1765
|
+
sourceSite: import_zod.z.string().optional(),
|
|
1766
|
+
sourceCite: import_zod.z.string().optional()
|
|
1767
|
+
});
|
|
1768
|
+
var RawMapsOverviewSchema = import_zod.z.object({
|
|
1769
|
+
name: import_zod.z.string().nullable(),
|
|
1770
|
+
rating: import_zod.z.string().nullable(),
|
|
1771
|
+
reviewCount: import_zod.z.string().nullable(),
|
|
1772
|
+
category: import_zod.z.string().nullable(),
|
|
1773
|
+
address: import_zod.z.string().nullable(),
|
|
1774
|
+
hoursSummary: import_zod.z.string().nullable(),
|
|
1775
|
+
phone: import_zod.z.string().nullable(),
|
|
1776
|
+
phoneDisplay: import_zod.z.string().nullable(),
|
|
1777
|
+
website: import_zod.z.string().nullable(),
|
|
1778
|
+
plusCode: import_zod.z.string().nullable(),
|
|
1779
|
+
bookingUrl: import_zod.z.string().nullable()
|
|
1780
|
+
});
|
|
1781
|
+
var RawMapsHoursRowSchema = import_zod.z.object({
|
|
1782
|
+
day: import_zod.z.string(),
|
|
1783
|
+
hours: import_zod.z.string()
|
|
1784
|
+
});
|
|
1785
|
+
var RawMapsReviewStatsSchema = import_zod.z.object({
|
|
1786
|
+
reviewHistogram: import_zod.z.array(import_zod.z.object({
|
|
1787
|
+
stars: import_zod.z.number(),
|
|
1788
|
+
count: import_zod.z.string()
|
|
1789
|
+
})),
|
|
1790
|
+
reviewTopics: import_zod.z.array(import_zod.z.object({
|
|
1791
|
+
label: import_zod.z.string(),
|
|
1792
|
+
count: import_zod.z.string()
|
|
1793
|
+
}))
|
|
1794
|
+
});
|
|
1795
|
+
var RawMapsReviewCardSchema = import_zod.z.object({
|
|
1796
|
+
reviewId: import_zod.z.string(),
|
|
1797
|
+
author: import_zod.z.string().nullable(),
|
|
1798
|
+
stars: import_zod.z.string().nullable(),
|
|
1799
|
+
date: import_zod.z.string().nullable(),
|
|
1800
|
+
text: import_zod.z.string().nullable(),
|
|
1801
|
+
ownerResponse: import_zod.z.string().nullable()
|
|
1802
|
+
});
|
|
1803
|
+
var RawMapsAboutAttributeSchema = import_zod.z.object({
|
|
1804
|
+
section: import_zod.z.string(),
|
|
1805
|
+
attribute: import_zod.z.string()
|
|
1806
|
+
});
|
|
1807
|
+
|
|
1808
|
+
// src/mcp/mcp-tool-schemas.ts
|
|
1555
1809
|
var HarvestPaaInputSchema = {
|
|
1556
|
-
query:
|
|
1557
|
-
location:
|
|
1558
|
-
maxQuestions:
|
|
1559
|
-
gl:
|
|
1560
|
-
hl:
|
|
1561
|
-
device:
|
|
1562
|
-
proxyMode:
|
|
1563
|
-
proxyZip:
|
|
1564
|
-
debug:
|
|
1810
|
+
query: import_zod2.z.string().min(1).describe('Core search topic only. If the user says "best hvac company in Denver CO", use query="best hvac company" and location="Denver, CO". Do not include the location in query when it can be separated.'),
|
|
1811
|
+
location: import_zod2.z.string().optional().describe('City, region, or country for geo-targeted results, inferred from the user request when present, e.g. "Denver, CO", "Tokyo, Japan", "London, UK".'),
|
|
1812
|
+
maxQuestions: import_zod2.z.number().int().min(1).max(200).default(30).describe("Number of PAA questions to extract. Default 30. Maximum 200. Use 10 for quick probes, 30 for normal research, 100-200 when the user asks for everything/full/deep research. Larger harvests get a longer server time budget (151-200 questions \u2192 up to 280s). Credits are charged by extracted question; unused request hold is refunded."),
|
|
1813
|
+
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."),
|
|
1814
|
+
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."),
|
|
1815
|
+
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."),
|
|
1816
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe("Proxy targeting mode. Default configured uses the service proxy without city/ZIP targeting for the highest general success rate. Use location only when the user explicitly needs city/ZIP-targeted residential proxy evidence. Use none only for direct-network debugging."),
|
|
1817
|
+
proxyZip: import_zod2.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."),
|
|
1818
|
+
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.")
|
|
1565
1819
|
};
|
|
1566
1820
|
var ExtractUrlInputSchema = {
|
|
1567
|
-
url:
|
|
1568
|
-
screenshot:
|
|
1569
|
-
screenshotDevice:
|
|
1570
|
-
extractBranding:
|
|
1571
|
-
downloadMedia:
|
|
1572
|
-
mediaTypes:
|
|
1573
|
-
allowLocal:
|
|
1821
|
+
url: import_zod2.z.string().url().describe("Public http/https URL to extract. Use this when the user provides one specific page URL."),
|
|
1822
|
+
screenshot: import_zod2.z.boolean().default(false).describe("Also capture a full-page screenshot of the URL. Saved to ~/Downloads/mcp-scraper/screenshots/ and returned inline. Use when the user asks to see or capture the page visually."),
|
|
1823
|
+
screenshotDevice: import_zod2.z.enum(["desktop", "mobile"]).default("desktop").describe("Viewport for screenshot. desktop = 1440\xD7900. mobile = 390\xD7844. Default desktop."),
|
|
1824
|
+
extractBranding: import_zod2.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."),
|
|
1825
|
+
downloadMedia: import_zod2.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."),
|
|
1826
|
+
mediaTypes: import_zod2.z.array(import_zod2.z.enum(["image", "video", "audio"])).default(["image", "video", "audio"]).describe("Which media types to download. Default all three."),
|
|
1827
|
+
allowLocal: import_zod2.z.boolean().default(false).describe("Allow localhost and private-network URLs. For local development only.")
|
|
1574
1828
|
};
|
|
1575
1829
|
var MapSiteUrlsInputSchema = {
|
|
1576
|
-
url:
|
|
1577
|
-
maxUrls:
|
|
1830
|
+
url: import_zod2.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."),
|
|
1831
|
+
maxUrls: import_zod2.z.number().int().min(1).max(500).optional().describe("Maximum URLs to discover. Use 100 for normal maps, higher when the user asks for a full inventory.")
|
|
1578
1832
|
};
|
|
1579
1833
|
var ExtractSiteInputSchema = {
|
|
1580
|
-
url:
|
|
1581
|
-
maxPages:
|
|
1834
|
+
url: import_zod2.z.string().url().describe("Public website URL or domain to extract across multiple pages. Use when the user asks for a site audit, website crawl, or full-site content/schema extraction."),
|
|
1835
|
+
maxPages: import_zod2.z.number().int().min(1).max(50).optional().describe("Maximum pages to extract. Use 50 when the user asks for full results or a complete crawl within MCP limits.")
|
|
1582
1836
|
};
|
|
1583
1837
|
var YoutubeHarvestInputSchema = {
|
|
1584
|
-
mode:
|
|
1585
|
-
query:
|
|
1586
|
-
channelHandle:
|
|
1587
|
-
maxVideos:
|
|
1838
|
+
mode: import_zod2.z.enum(["search", "channel"]).describe("Use search for topic/keyword requests. Use channel when the user provides @handle, channel ID, or channel URL."),
|
|
1839
|
+
query: import_zod2.z.string().optional().describe("Required when mode is search. The YouTube search topic in the user\u2019s words."),
|
|
1840
|
+
channelHandle: import_zod2.z.string().optional().describe("YouTube channel handle, channel ID, or URL. Examples: @mkbhd, UC..., https://youtube.com/@mkbhd."),
|
|
1841
|
+
maxVideos: import_zod2.z.number().int().min(1).max(500).default(50).describe("Number of videos to return. Default 50, maximum 500. Use 10-25 for quick topic discovery, 50 for normal channel/search harvests, and larger values only when the user asks for full channel/history because large responses consume more context.")
|
|
1588
1842
|
};
|
|
1589
1843
|
var YoutubeTranscribeInputSchema = {
|
|
1590
|
-
videoId:
|
|
1844
|
+
videoId: import_zod2.z.string().min(1).optional().describe("YouTube video ID, e.g. dQw4w9WgXcQ. Use only an ID returned by youtube_harvest or visible in a YouTube URL; do not invent one."),
|
|
1845
|
+
url: import_zod2.z.string().url().optional().describe("Full YouTube URL, e.g. https://www.youtube.com/watch?v=dQw4w9WgXcQ or https://youtu.be/dQw4w9WgXcQ. Use this when the user pasted a URL instead of an ID. Provide videoId or url.")
|
|
1591
1846
|
};
|
|
1592
1847
|
var FacebookPageIntelInputSchema = {
|
|
1593
|
-
pageId:
|
|
1594
|
-
libraryId:
|
|
1595
|
-
query:
|
|
1596
|
-
maxAds:
|
|
1597
|
-
country:
|
|
1848
|
+
pageId: import_zod2.z.string().optional().describe("Facebook advertiser/page ID. Use only a pageId returned by facebook_ad_search/facebook_page_intel or copied from Facebook Ad Library; do not construct one yourself."),
|
|
1849
|
+
libraryId: import_zod2.z.string().optional().describe("Facebook Ad Library archive ID for a known ad or advertiser sample. Use a libraryId returned by facebook_ad_search, or a libraryId/adArchiveId visible in Ad Library."),
|
|
1850
|
+
query: import_zod2.z.string().optional().describe("Advertiser or brand name when pageId/libraryId is not known. One of pageId, libraryId, or query is required."),
|
|
1851
|
+
maxAds: import_zod2.z.number().int().min(1).max(200).default(50).describe("Maximum ads to inspect. Default 50, maximum 200. Prefer 25-50 for focused advertiser scans; use 100-200 only when the user asks for a broad ad archive sweep."),
|
|
1852
|
+
country: import_zod2.z.string().length(2).default("US").describe("Two-letter Ad Library country code. Default US. Examples: US, CA, GB, AU. Infer from the user request when they name a country.")
|
|
1598
1853
|
};
|
|
1599
1854
|
var FacebookAdSearchInputSchema = {
|
|
1600
|
-
query:
|
|
1601
|
-
country:
|
|
1602
|
-
maxResults:
|
|
1855
|
+
query: import_zod2.z.string().min(1).describe("Advertiser, brand, competitor, niche, or keyword to search in Facebook Ad Library."),
|
|
1856
|
+
country: import_zod2.z.string().length(2).default("US").describe("Two-letter Ad Library country code. Default US. Examples: US, CA, GB, AU."),
|
|
1857
|
+
maxResults: import_zod2.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.")
|
|
1603
1858
|
};
|
|
1604
1859
|
var FacebookAdTranscribeInputSchema = {
|
|
1605
|
-
videoUrl:
|
|
1860
|
+
videoUrl: import_zod2.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.")
|
|
1606
1861
|
};
|
|
1607
1862
|
var FacebookVideoTranscribeInputSchema = {
|
|
1608
|
-
url:
|
|
1609
|
-
quality:
|
|
1863
|
+
url: import_zod2.z.string().url().describe("Organic Facebook reel, video, watch, post, or share URL from facebook.com, m.facebook.com, or fb.watch. The tool renders the page, extracts the best matching public Facebook CDN MP4 URL, then transcribes it. Use this when the user pastes a normal Facebook video page URL and asks for the transcript or downloadable MP4."),
|
|
1864
|
+
quality: import_zod2.z.enum(["best", "hd", "sd"]).default("best").describe("Preferred progressive MP4 quality. Use best by default; hd prefers the highest HD progressive URL; sd forces the SD URL.")
|
|
1610
1865
|
};
|
|
1611
1866
|
var MapsPlaceIntelInputSchema = {
|
|
1612
|
-
businessName:
|
|
1613
|
-
location:
|
|
1614
|
-
gl:
|
|
1615
|
-
hl:
|
|
1616
|
-
includeReviews:
|
|
1617
|
-
maxReviews:
|
|
1867
|
+
businessName: import_zod2.z.string().min(1).describe('Business name only. If user says "Elite Roofing Denver CO", use businessName="Elite Roofing" and location="Denver, CO".'),
|
|
1868
|
+
location: import_zod2.z.string().min(1).describe('City/region/country where the business should be searched, e.g. "Denver, CO". Infer from the user request when possible.'),
|
|
1869
|
+
gl: import_zod2.z.string().length(2).default("us").describe("Google country code inferred from location."),
|
|
1870
|
+
hl: import_zod2.z.string().length(2).default("en").describe("Language inferred from user request."),
|
|
1871
|
+
includeReviews: import_zod2.z.boolean().default(false).describe("Whether to fetch individual review cards. Use true when the user asks for reviews, customer pain, complaints, praise, themes, or review evidence."),
|
|
1872
|
+
maxReviews: import_zod2.z.number().int().min(1).max(500).default(50).describe("Max review cards to return when includeReviews is true. Default 50, maximum 500. Use 50 for normal review analysis and larger values only for deep review mining.")
|
|
1618
1873
|
};
|
|
1619
1874
|
var MapsSearchInputSchema = {
|
|
1620
|
-
query:
|
|
1621
|
-
location:
|
|
1622
|
-
gl:
|
|
1623
|
-
hl:
|
|
1624
|
-
maxResults:
|
|
1625
|
-
proxyMode:
|
|
1626
|
-
proxyZip:
|
|
1627
|
-
debug:
|
|
1875
|
+
query: import_zod2.z.string().min(1).describe('Business category, niche, keyword, or search term. If the user says "roofers in Denver CO", use query="roofers" and location="Denver, CO". Do not put the location here when it can be separated.'),
|
|
1876
|
+
location: import_zod2.z.string().optional().describe('City, region, country, or service area for the Maps search, e.g. "Denver, CO". Infer from the user request when present.'),
|
|
1877
|
+
gl: import_zod2.z.string().length(2).default("us").describe("Google country code inferred from location."),
|
|
1878
|
+
hl: import_zod2.z.string().length(2).default("en").describe("Language inferred from user request."),
|
|
1879
|
+
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."),
|
|
1880
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE).describe("Proxy targeting mode. Maps defaults to location so local market searches get city/state residential proxy targeting and rotation. Use configured to force the service proxy without city/ZIP targeting, and none only for local direct-network debugging."),
|
|
1881
|
+
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."),
|
|
1882
|
+
debug: import_zod2.z.boolean().default(false).describe("Include sanitized browser/proxy diagnostics when debugging Maps localization, CAPTCHA, or proxy behavior.")
|
|
1628
1883
|
};
|
|
1629
1884
|
var DirectoryWorkflowInputSchema = {
|
|
1630
|
-
query:
|
|
1631
|
-
state:
|
|
1632
|
-
minPopulation:
|
|
1633
|
-
populationYear:
|
|
1634
|
-
maxCities:
|
|
1635
|
-
maxResultsPerCity:
|
|
1636
|
-
concurrency:
|
|
1637
|
-
includeZipGroups:
|
|
1638
|
-
usZipsCsvPath:
|
|
1639
|
-
saveCsv:
|
|
1640
|
-
proxyMode:
|
|
1641
|
-
proxyZip:
|
|
1642
|
-
debug:
|
|
1885
|
+
query: import_zod2.z.string().min(1).describe("Business category, niche, or keyword to search on Google Maps for every selected market, e.g. roofers, dentists, med spas. Do not include the city here."),
|
|
1886
|
+
state: import_zod2.z.string().min(2).default("TN").describe("US state abbreviation or state name used to select Census places, e.g. TN or Tennessee."),
|
|
1887
|
+
minPopulation: import_zod2.z.number().int().min(0).default(1e5).describe('Minimum Census place population for market selection. Use 100000 for "cities above 100k population".'),
|
|
1888
|
+
populationYear: import_zod2.z.number().int().min(2020).max(2025).default(2025).describe("Census population estimate year from the 2020-2025 Population Estimates Program city/place dataset."),
|
|
1889
|
+
maxCities: import_zod2.z.number().int().min(1).max(100).default(25).describe("Maximum number of markets to process after sorting by population descending."),
|
|
1890
|
+
maxResultsPerCity: import_zod2.z.number().int().min(1).max(50).default(50).describe("Google Maps business/profile candidates to collect for each city. Maximum 50."),
|
|
1891
|
+
concurrency: import_zod2.z.number().int().min(1).max(5).default(5).describe("How many city Maps searches to run in parallel. Use 5 for broad directory batches unless debugging."),
|
|
1892
|
+
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."),
|
|
1893
|
+
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."),
|
|
1894
|
+
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, review_count, category, address, phone, hours_status, website_url, directions_url, place_url, CID fields, population, and ZIP groups."),
|
|
1895
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE).describe("Proxy targeting mode for every city Maps search. Maps workflows default to location so each city can use city/state or ZIP-group residential proxy targeting. Use configured to force the service proxy without city/ZIP targeting, and none only for local direct-network debugging."),
|
|
1896
|
+
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 ZIP group or city/state location."),
|
|
1897
|
+
debug: import_zod2.z.boolean().default(false).describe("Include sanitized browser/proxy diagnostics in each Maps browser session when supported.")
|
|
1643
1898
|
};
|
|
1644
|
-
var RankTrackerModeSchema =
|
|
1899
|
+
var RankTrackerModeSchema = import_zod2.z.enum(["maps", "organic", "ai_overview", "paa"]);
|
|
1645
1900
|
var RankTrackerBlueprintInputSchema = {
|
|
1646
|
-
projectName:
|
|
1647
|
-
targetDomain:
|
|
1648
|
-
targetBusinessName:
|
|
1649
|
-
trackingModes:
|
|
1650
|
-
keywords:
|
|
1651
|
-
locations:
|
|
1652
|
-
competitors:
|
|
1653
|
-
database:
|
|
1654
|
-
scheduleCadence:
|
|
1655
|
-
customCron:
|
|
1656
|
-
timezone:
|
|
1657
|
-
includeCron:
|
|
1658
|
-
includeDashboard:
|
|
1659
|
-
includeAlerts:
|
|
1660
|
-
notes:
|
|
1901
|
+
projectName: import_zod2.z.string().min(1).optional().describe("Optional name for the rank tracker project, client, or campaign."),
|
|
1902
|
+
targetDomain: import_zod2.z.string().min(1).optional().describe("Primary domain to track in organic results, AI Overview citations, and PAA sources, e.g. example.com."),
|
|
1903
|
+
targetBusinessName: import_zod2.z.string().min(1).optional().describe("Primary Google Business Profile or brand/business name to match in Maps results. Required for reliable Maps rank tracking."),
|
|
1904
|
+
trackingModes: import_zod2.z.array(RankTrackerModeSchema).min(1).max(4).default(["maps", "organic", "ai_overview", "paa"]).describe("Rank tracker surfaces to build. maps uses directory_workflow/maps_search. organic uses search_serp. ai_overview uses search_serp/harvest_paa. paa uses harvest_paa source presence."),
|
|
1905
|
+
keywords: import_zod2.z.array(import_zod2.z.string().min(1)).max(200).default([]).describe("Seed keywords or service queries to track. Leave empty when the downstream AI should create the keyword table from user input."),
|
|
1906
|
+
locations: import_zod2.z.array(import_zod2.z.string().min(1)).max(100).default([]).describe("Markets, cities, ZIPs, or service areas to track. Use city/state strings like Denver, CO for localized SERP and Maps checks."),
|
|
1907
|
+
competitors: import_zod2.z.array(import_zod2.z.string().min(1)).max(100).default([]).describe("Optional competitor domains or business names to persist as comparison targets."),
|
|
1908
|
+
database: import_zod2.z.enum(["postgres", "neon", "supabase", "sqlite", "mysql"]).default("postgres").describe("Database family the downstream AI should target when generating migrations."),
|
|
1909
|
+
scheduleCadence: import_zod2.z.enum(["daily", "weekly", "monthly", "custom"]).default("weekly").describe("Default recurring rank check cadence for the generated cron/heartbeat plan."),
|
|
1910
|
+
customCron: import_zod2.z.string().min(1).optional().describe("Cron expression to use when scheduleCadence is custom."),
|
|
1911
|
+
timezone: import_zod2.z.string().min(1).default("UTC").describe("IANA timezone for scheduled rank checks, e.g. America/Denver."),
|
|
1912
|
+
includeCron: import_zod2.z.boolean().default(true).describe("Include a cron or heartbeat worker plan. Keep true for production rank trackers."),
|
|
1913
|
+
includeDashboard: import_zod2.z.boolean().default(true).describe("Include dashboard/reporting requirements in the generated prompt."),
|
|
1914
|
+
includeAlerts: import_zod2.z.boolean().default(true).describe("Include alert rules for rank movement and SERP feature gains/losses."),
|
|
1915
|
+
notes: import_zod2.z.string().max(4e3).optional().describe("Extra product, client, stack, or hosting requirements to include in the implementation prompt.")
|
|
1661
1916
|
};
|
|
1662
|
-
var NullableString =
|
|
1663
|
-
var MapsSearchAttemptOutput =
|
|
1664
|
-
attemptNumber:
|
|
1665
|
-
maxAttempts:
|
|
1666
|
-
status:
|
|
1667
|
-
outcome:
|
|
1668
|
-
willRetry:
|
|
1669
|
-
durationMs:
|
|
1670
|
-
resultCount:
|
|
1917
|
+
var NullableString = import_zod2.z.string().nullable();
|
|
1918
|
+
var MapsSearchAttemptOutput = import_zod2.z.object({
|
|
1919
|
+
attemptNumber: import_zod2.z.number().int().min(1),
|
|
1920
|
+
maxAttempts: import_zod2.z.number().int().min(1),
|
|
1921
|
+
status: import_zod2.z.enum(["ok", "failed"]),
|
|
1922
|
+
outcome: import_zod2.z.string(),
|
|
1923
|
+
willRetry: import_zod2.z.boolean(),
|
|
1924
|
+
durationMs: import_zod2.z.number().int().min(0),
|
|
1925
|
+
resultCount: import_zod2.z.number().int().min(0),
|
|
1671
1926
|
error: NullableString,
|
|
1672
|
-
proxyMode:
|
|
1673
|
-
proxyResolutionSource:
|
|
1927
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]),
|
|
1928
|
+
proxyResolutionSource: import_zod2.z.enum(["disabled", "location_reused", "location_created", "configured_fallback", "unavailable"]).nullable(),
|
|
1674
1929
|
proxyIdSuffix: NullableString,
|
|
1675
|
-
proxyTargetLevel:
|
|
1930
|
+
proxyTargetLevel: import_zod2.z.enum(["zip", "city", "state"]).nullable(),
|
|
1676
1931
|
proxyTargetLocation: NullableString,
|
|
1677
1932
|
proxyTargetZip: NullableString,
|
|
1678
1933
|
browserSessionIdSuffix: NullableString,
|
|
@@ -1681,17 +1936,17 @@ var MapsSearchAttemptOutput = import_zod.z.object({
|
|
|
1681
1936
|
observedRegion: NullableString
|
|
1682
1937
|
});
|
|
1683
1938
|
var MapsSearchOutputSchema = {
|
|
1684
|
-
query:
|
|
1685
|
-
location:
|
|
1686
|
-
searchQuery:
|
|
1687
|
-
searchUrl:
|
|
1688
|
-
extractedAt:
|
|
1689
|
-
requestedMaxResults:
|
|
1690
|
-
resultCount:
|
|
1691
|
-
results:
|
|
1692
|
-
position:
|
|
1693
|
-
name:
|
|
1694
|
-
placeUrl:
|
|
1939
|
+
query: import_zod2.z.string(),
|
|
1940
|
+
location: import_zod2.z.string().nullable(),
|
|
1941
|
+
searchQuery: import_zod2.z.string(),
|
|
1942
|
+
searchUrl: import_zod2.z.string().url(),
|
|
1943
|
+
extractedAt: import_zod2.z.string(),
|
|
1944
|
+
requestedMaxResults: import_zod2.z.number().int().min(1).max(50),
|
|
1945
|
+
resultCount: import_zod2.z.number().int().min(0).max(50),
|
|
1946
|
+
results: import_zod2.z.array(import_zod2.z.object({
|
|
1947
|
+
position: import_zod2.z.number().int().min(1),
|
|
1948
|
+
name: import_zod2.z.string(),
|
|
1949
|
+
placeUrl: import_zod2.z.string().url(),
|
|
1695
1950
|
cid: NullableString,
|
|
1696
1951
|
cidDecimal: NullableString,
|
|
1697
1952
|
rating: NullableString,
|
|
@@ -1702,15 +1957,15 @@ var MapsSearchOutputSchema = {
|
|
|
1702
1957
|
hoursStatus: NullableString,
|
|
1703
1958
|
websiteUrl: NullableString,
|
|
1704
1959
|
directionsUrl: NullableString,
|
|
1705
|
-
metadata:
|
|
1960
|
+
metadata: import_zod2.z.array(import_zod2.z.string())
|
|
1706
1961
|
})),
|
|
1707
|
-
attempts:
|
|
1708
|
-
durationMs:
|
|
1962
|
+
attempts: import_zod2.z.array(MapsSearchAttemptOutput),
|
|
1963
|
+
durationMs: import_zod2.z.number().int().min(0)
|
|
1709
1964
|
};
|
|
1710
|
-
var DirectoryMapsBusinessOutput =
|
|
1711
|
-
position:
|
|
1712
|
-
name:
|
|
1713
|
-
placeUrl:
|
|
1965
|
+
var DirectoryMapsBusinessOutput = import_zod2.z.object({
|
|
1966
|
+
position: import_zod2.z.number().int().min(1),
|
|
1967
|
+
name: import_zod2.z.string(),
|
|
1968
|
+
placeUrl: import_zod2.z.string().url(),
|
|
1714
1969
|
cid: NullableString,
|
|
1715
1970
|
cidDecimal: NullableString,
|
|
1716
1971
|
rating: NullableString,
|
|
@@ -1721,113 +1976,113 @@ var DirectoryMapsBusinessOutput = import_zod.z.object({
|
|
|
1721
1976
|
hoursStatus: NullableString,
|
|
1722
1977
|
websiteUrl: NullableString,
|
|
1723
1978
|
directionsUrl: NullableString,
|
|
1724
|
-
metadata:
|
|
1979
|
+
metadata: import_zod2.z.array(import_zod2.z.string())
|
|
1725
1980
|
});
|
|
1726
1981
|
var DirectoryWorkflowOutputSchema = {
|
|
1727
|
-
query:
|
|
1728
|
-
state:
|
|
1729
|
-
minPopulation:
|
|
1730
|
-
populationYear:
|
|
1731
|
-
maxResultsPerCity:
|
|
1732
|
-
concurrency:
|
|
1733
|
-
censusSourceUrl:
|
|
1982
|
+
query: import_zod2.z.string(),
|
|
1983
|
+
state: import_zod2.z.string(),
|
|
1984
|
+
minPopulation: import_zod2.z.number().int().min(0),
|
|
1985
|
+
populationYear: import_zod2.z.number().int().min(2020).max(2025),
|
|
1986
|
+
maxResultsPerCity: import_zod2.z.number().int().min(1).max(50),
|
|
1987
|
+
concurrency: import_zod2.z.number().int().min(1).max(5),
|
|
1988
|
+
censusSourceUrl: import_zod2.z.string().url(),
|
|
1734
1989
|
usZipsSourcePath: NullableString,
|
|
1735
|
-
warnings:
|
|
1736
|
-
extractedAt:
|
|
1737
|
-
selectedCityCount:
|
|
1738
|
-
totalResultCount:
|
|
1990
|
+
warnings: import_zod2.z.array(import_zod2.z.string()),
|
|
1991
|
+
extractedAt: import_zod2.z.string(),
|
|
1992
|
+
selectedCityCount: import_zod2.z.number().int().min(0),
|
|
1993
|
+
totalResultCount: import_zod2.z.number().int().min(0),
|
|
1739
1994
|
csvPath: NullableString,
|
|
1740
|
-
cities:
|
|
1741
|
-
city:
|
|
1742
|
-
state:
|
|
1743
|
-
location:
|
|
1744
|
-
cityKey:
|
|
1745
|
-
censusName:
|
|
1746
|
-
population:
|
|
1747
|
-
populationYear:
|
|
1748
|
-
zips:
|
|
1749
|
-
counties:
|
|
1750
|
-
status:
|
|
1995
|
+
cities: import_zod2.z.array(import_zod2.z.object({
|
|
1996
|
+
city: import_zod2.z.string(),
|
|
1997
|
+
state: import_zod2.z.string(),
|
|
1998
|
+
location: import_zod2.z.string(),
|
|
1999
|
+
cityKey: import_zod2.z.string(),
|
|
2000
|
+
censusName: import_zod2.z.string(),
|
|
2001
|
+
population: import_zod2.z.number().int().min(0),
|
|
2002
|
+
populationYear: import_zod2.z.number().int().min(2020).max(2025),
|
|
2003
|
+
zips: import_zod2.z.array(import_zod2.z.string()),
|
|
2004
|
+
counties: import_zod2.z.array(import_zod2.z.string()),
|
|
2005
|
+
status: import_zod2.z.enum(["ok", "empty", "failed"]),
|
|
1751
2006
|
error: NullableString,
|
|
1752
|
-
resultCount:
|
|
1753
|
-
durationMs:
|
|
1754
|
-
attempts:
|
|
1755
|
-
results:
|
|
2007
|
+
resultCount: import_zod2.z.number().int().min(0),
|
|
2008
|
+
durationMs: import_zod2.z.number().int().min(0),
|
|
2009
|
+
attempts: import_zod2.z.array(MapsSearchAttemptOutput),
|
|
2010
|
+
results: import_zod2.z.array(DirectoryMapsBusinessOutput)
|
|
1756
2011
|
})),
|
|
1757
|
-
durationMs:
|
|
2012
|
+
durationMs: import_zod2.z.number().int().min(0)
|
|
1758
2013
|
};
|
|
1759
|
-
var RankTrackerToolPlanOutput =
|
|
1760
|
-
tool:
|
|
1761
|
-
purpose:
|
|
2014
|
+
var RankTrackerToolPlanOutput = import_zod2.z.object({
|
|
2015
|
+
tool: import_zod2.z.string(),
|
|
2016
|
+
purpose: import_zod2.z.string()
|
|
1762
2017
|
});
|
|
1763
|
-
var RankTrackerTableOutput =
|
|
1764
|
-
name:
|
|
1765
|
-
purpose:
|
|
1766
|
-
keyColumns:
|
|
2018
|
+
var RankTrackerTableOutput = import_zod2.z.object({
|
|
2019
|
+
name: import_zod2.z.string(),
|
|
2020
|
+
purpose: import_zod2.z.string(),
|
|
2021
|
+
keyColumns: import_zod2.z.array(import_zod2.z.string())
|
|
1767
2022
|
});
|
|
1768
|
-
var RankTrackerCronJobOutput =
|
|
1769
|
-
name:
|
|
1770
|
-
purpose:
|
|
1771
|
-
modes:
|
|
1772
|
-
recommendedTools:
|
|
2023
|
+
var RankTrackerCronJobOutput = import_zod2.z.object({
|
|
2024
|
+
name: import_zod2.z.string(),
|
|
2025
|
+
purpose: import_zod2.z.string(),
|
|
2026
|
+
modes: import_zod2.z.array(RankTrackerModeSchema),
|
|
2027
|
+
recommendedTools: import_zod2.z.array(import_zod2.z.string())
|
|
1773
2028
|
});
|
|
1774
2029
|
var RankTrackerBlueprintOutputSchema = {
|
|
1775
|
-
projectName:
|
|
2030
|
+
projectName: import_zod2.z.string(),
|
|
1776
2031
|
targetDomain: NullableString,
|
|
1777
2032
|
targetBusinessName: NullableString,
|
|
1778
|
-
trackingModes:
|
|
1779
|
-
database:
|
|
1780
|
-
recommendedTools:
|
|
1781
|
-
tables:
|
|
1782
|
-
cron:
|
|
1783
|
-
enabled:
|
|
1784
|
-
cadence:
|
|
1785
|
-
expression:
|
|
1786
|
-
timezone:
|
|
1787
|
-
jobs:
|
|
2033
|
+
trackingModes: import_zod2.z.array(RankTrackerModeSchema),
|
|
2034
|
+
database: import_zod2.z.string(),
|
|
2035
|
+
recommendedTools: import_zod2.z.array(RankTrackerToolPlanOutput),
|
|
2036
|
+
tables: import_zod2.z.array(RankTrackerTableOutput),
|
|
2037
|
+
cron: import_zod2.z.object({
|
|
2038
|
+
enabled: import_zod2.z.boolean(),
|
|
2039
|
+
cadence: import_zod2.z.string(),
|
|
2040
|
+
expression: import_zod2.z.string(),
|
|
2041
|
+
timezone: import_zod2.z.string(),
|
|
2042
|
+
jobs: import_zod2.z.array(RankTrackerCronJobOutput)
|
|
1788
2043
|
}),
|
|
1789
|
-
metrics:
|
|
1790
|
-
implementationPrompt:
|
|
2044
|
+
metrics: import_zod2.z.array(import_zod2.z.string()),
|
|
2045
|
+
implementationPrompt: import_zod2.z.string()
|
|
1791
2046
|
};
|
|
1792
|
-
var OrganicResultOutput =
|
|
1793
|
-
position:
|
|
1794
|
-
title:
|
|
1795
|
-
url:
|
|
1796
|
-
domain:
|
|
2047
|
+
var OrganicResultOutput = import_zod2.z.object({
|
|
2048
|
+
position: import_zod2.z.number().int(),
|
|
2049
|
+
title: import_zod2.z.string(),
|
|
2050
|
+
url: import_zod2.z.string(),
|
|
2051
|
+
domain: import_zod2.z.string(),
|
|
1797
2052
|
snippet: NullableString
|
|
1798
2053
|
});
|
|
1799
|
-
var AiOverviewOutput =
|
|
1800
|
-
detected:
|
|
2054
|
+
var AiOverviewOutput = import_zod2.z.object({
|
|
2055
|
+
detected: import_zod2.z.boolean(),
|
|
1801
2056
|
text: NullableString
|
|
1802
2057
|
}).nullable();
|
|
1803
|
-
var EntityIdsOutput =
|
|
1804
|
-
kgIds:
|
|
1805
|
-
cids:
|
|
1806
|
-
gcids:
|
|
2058
|
+
var EntityIdsOutput = import_zod2.z.object({
|
|
2059
|
+
kgIds: import_zod2.z.array(import_zod2.z.string()),
|
|
2060
|
+
cids: import_zod2.z.array(import_zod2.z.string()),
|
|
2061
|
+
gcids: import_zod2.z.array(import_zod2.z.string())
|
|
1807
2062
|
}).nullable();
|
|
1808
2063
|
var HarvestPaaOutputSchema = {
|
|
1809
|
-
query:
|
|
2064
|
+
query: import_zod2.z.string(),
|
|
1810
2065
|
location: NullableString,
|
|
1811
|
-
questionCount:
|
|
2066
|
+
questionCount: import_zod2.z.number().int().min(0),
|
|
1812
2067
|
completionStatus: NullableString,
|
|
1813
|
-
questions:
|
|
1814
|
-
question:
|
|
2068
|
+
questions: import_zod2.z.array(import_zod2.z.object({
|
|
2069
|
+
question: import_zod2.z.string(),
|
|
1815
2070
|
answer: NullableString,
|
|
1816
2071
|
sourceTitle: NullableString,
|
|
1817
2072
|
sourceSite: NullableString
|
|
1818
2073
|
})),
|
|
1819
|
-
organicResults:
|
|
2074
|
+
organicResults: import_zod2.z.array(OrganicResultOutput),
|
|
1820
2075
|
aiOverview: AiOverviewOutput,
|
|
1821
2076
|
entityIds: EntityIdsOutput,
|
|
1822
|
-
durationMs:
|
|
2077
|
+
durationMs: import_zod2.z.number().min(0).nullable()
|
|
1823
2078
|
};
|
|
1824
2079
|
var SearchSerpOutputSchema = {
|
|
1825
|
-
query:
|
|
2080
|
+
query: import_zod2.z.string(),
|
|
1826
2081
|
location: NullableString,
|
|
1827
|
-
organicResults:
|
|
1828
|
-
localPack:
|
|
1829
|
-
position:
|
|
1830
|
-
name:
|
|
2082
|
+
organicResults: import_zod2.z.array(OrganicResultOutput),
|
|
2083
|
+
localPack: import_zod2.z.array(import_zod2.z.object({
|
|
2084
|
+
position: import_zod2.z.number().int(),
|
|
2085
|
+
name: import_zod2.z.string(),
|
|
1831
2086
|
rating: NullableString,
|
|
1832
2087
|
reviewCount: NullableString,
|
|
1833
2088
|
websiteUrl: NullableString
|
|
@@ -1836,31 +2091,31 @@ var SearchSerpOutputSchema = {
|
|
|
1836
2091
|
entityIds: EntityIdsOutput
|
|
1837
2092
|
};
|
|
1838
2093
|
var ExtractUrlOutputSchema = {
|
|
1839
|
-
url:
|
|
2094
|
+
url: import_zod2.z.string(),
|
|
1840
2095
|
title: NullableString,
|
|
1841
|
-
headings:
|
|
1842
|
-
level:
|
|
1843
|
-
text:
|
|
2096
|
+
headings: import_zod2.z.array(import_zod2.z.object({
|
|
2097
|
+
level: import_zod2.z.number().int(),
|
|
2098
|
+
text: import_zod2.z.string()
|
|
1844
2099
|
})),
|
|
1845
|
-
schemaBlockCount:
|
|
2100
|
+
schemaBlockCount: import_zod2.z.number().int().min(0),
|
|
1846
2101
|
entityName: NullableString,
|
|
1847
|
-
entityTypes:
|
|
1848
|
-
napScore:
|
|
1849
|
-
missingSchemaFields:
|
|
2102
|
+
entityTypes: import_zod2.z.array(import_zod2.z.string()),
|
|
2103
|
+
napScore: import_zod2.z.number().nullable(),
|
|
2104
|
+
missingSchemaFields: import_zod2.z.array(import_zod2.z.string()),
|
|
1850
2105
|
screenshotSaved: NullableString
|
|
1851
2106
|
};
|
|
1852
2107
|
var ExtractSiteOutputSchema = {
|
|
1853
|
-
url:
|
|
1854
|
-
pageCount:
|
|
1855
|
-
pages:
|
|
1856
|
-
url:
|
|
2108
|
+
url: import_zod2.z.string(),
|
|
2109
|
+
pageCount: import_zod2.z.number().int().min(0),
|
|
2110
|
+
pages: import_zod2.z.array(import_zod2.z.object({
|
|
2111
|
+
url: import_zod2.z.string(),
|
|
1857
2112
|
title: NullableString,
|
|
1858
|
-
schemaTypes:
|
|
2113
|
+
schemaTypes: import_zod2.z.array(import_zod2.z.string())
|
|
1859
2114
|
})),
|
|
1860
|
-
durationMs:
|
|
2115
|
+
durationMs: import_zod2.z.number().min(0)
|
|
1861
2116
|
};
|
|
1862
2117
|
var MapsPlaceIntelOutputSchema = {
|
|
1863
|
-
name:
|
|
2118
|
+
name: import_zod2.z.string(),
|
|
1864
2119
|
rating: NullableString,
|
|
1865
2120
|
reviewCount: NullableString,
|
|
1866
2121
|
category: NullableString,
|
|
@@ -1872,75 +2127,75 @@ var MapsPlaceIntelOutputSchema = {
|
|
|
1872
2127
|
kgmid: NullableString,
|
|
1873
2128
|
cidDecimal: NullableString,
|
|
1874
2129
|
cidUrl: NullableString,
|
|
1875
|
-
lat:
|
|
1876
|
-
lng:
|
|
1877
|
-
reviewsStatus:
|
|
1878
|
-
reviewsCollected:
|
|
1879
|
-
reviewTopics:
|
|
1880
|
-
label:
|
|
1881
|
-
count:
|
|
2130
|
+
lat: import_zod2.z.number().nullable(),
|
|
2131
|
+
lng: import_zod2.z.number().nullable(),
|
|
2132
|
+
reviewsStatus: import_zod2.z.string(),
|
|
2133
|
+
reviewsCollected: import_zod2.z.number().int().min(0),
|
|
2134
|
+
reviewTopics: import_zod2.z.array(import_zod2.z.object({
|
|
2135
|
+
label: import_zod2.z.string(),
|
|
2136
|
+
count: import_zod2.z.string()
|
|
1882
2137
|
}))
|
|
1883
2138
|
};
|
|
1884
2139
|
var CreditsInfoOutputSchema = {
|
|
1885
|
-
balanceCredits:
|
|
1886
|
-
matchedCost:
|
|
1887
|
-
label:
|
|
1888
|
-
credits:
|
|
1889
|
-
unit:
|
|
2140
|
+
balanceCredits: import_zod2.z.number().nullable(),
|
|
2141
|
+
matchedCost: import_zod2.z.object({
|
|
2142
|
+
label: import_zod2.z.string(),
|
|
2143
|
+
credits: import_zod2.z.number(),
|
|
2144
|
+
unit: import_zod2.z.string(),
|
|
1890
2145
|
notes: NullableString
|
|
1891
2146
|
}).nullable(),
|
|
1892
|
-
costs:
|
|
1893
|
-
key:
|
|
1894
|
-
label:
|
|
1895
|
-
credits:
|
|
1896
|
-
unit:
|
|
2147
|
+
costs: import_zod2.z.array(import_zod2.z.object({
|
|
2148
|
+
key: import_zod2.z.string(),
|
|
2149
|
+
label: import_zod2.z.string(),
|
|
2150
|
+
credits: import_zod2.z.number(),
|
|
2151
|
+
unit: import_zod2.z.string(),
|
|
1897
2152
|
notes: NullableString
|
|
1898
2153
|
})),
|
|
1899
|
-
ledger:
|
|
1900
|
-
createdAt:
|
|
1901
|
-
operation:
|
|
1902
|
-
credits:
|
|
2154
|
+
ledger: import_zod2.z.array(import_zod2.z.object({
|
|
2155
|
+
createdAt: import_zod2.z.string(),
|
|
2156
|
+
operation: import_zod2.z.string(),
|
|
2157
|
+
credits: import_zod2.z.number(),
|
|
1903
2158
|
description: NullableString
|
|
1904
2159
|
})),
|
|
1905
|
-
concurrency:
|
|
1906
|
-
currentExtraSlots:
|
|
1907
|
-
currentLimit:
|
|
1908
|
-
hasSubscription:
|
|
1909
|
-
upgrade:
|
|
1910
|
-
product:
|
|
1911
|
-
priceLabel:
|
|
1912
|
-
unitAmountUsd:
|
|
1913
|
-
currency:
|
|
1914
|
-
interval:
|
|
1915
|
-
billingUrl:
|
|
1916
|
-
terminalCommand:
|
|
1917
|
-
terminalCommandWithApiKeyEnv:
|
|
2160
|
+
concurrency: import_zod2.z.object({
|
|
2161
|
+
currentExtraSlots: import_zod2.z.number().int().min(0),
|
|
2162
|
+
currentLimit: import_zod2.z.number().int().min(1),
|
|
2163
|
+
hasSubscription: import_zod2.z.boolean(),
|
|
2164
|
+
upgrade: import_zod2.z.object({
|
|
2165
|
+
product: import_zod2.z.string(),
|
|
2166
|
+
priceLabel: import_zod2.z.string(),
|
|
2167
|
+
unitAmountUsd: import_zod2.z.number(),
|
|
2168
|
+
currency: import_zod2.z.string(),
|
|
2169
|
+
interval: import_zod2.z.string(),
|
|
2170
|
+
billingUrl: import_zod2.z.string().url(),
|
|
2171
|
+
terminalCommand: import_zod2.z.string(),
|
|
2172
|
+
terminalCommandWithApiKeyEnv: import_zod2.z.string()
|
|
1918
2173
|
})
|
|
1919
2174
|
}).nullable()
|
|
1920
2175
|
};
|
|
1921
2176
|
var MapSiteUrlsOutputSchema = {
|
|
1922
|
-
startUrl:
|
|
1923
|
-
totalFound:
|
|
1924
|
-
truncated:
|
|
1925
|
-
okCount:
|
|
1926
|
-
redirectCount:
|
|
1927
|
-
brokenCount:
|
|
1928
|
-
urls:
|
|
1929
|
-
url:
|
|
1930
|
-
status:
|
|
2177
|
+
startUrl: import_zod2.z.string(),
|
|
2178
|
+
totalFound: import_zod2.z.number().int().min(0),
|
|
2179
|
+
truncated: import_zod2.z.boolean(),
|
|
2180
|
+
okCount: import_zod2.z.number().int().min(0),
|
|
2181
|
+
redirectCount: import_zod2.z.number().int().min(0),
|
|
2182
|
+
brokenCount: import_zod2.z.number().int().min(0),
|
|
2183
|
+
urls: import_zod2.z.array(import_zod2.z.object({
|
|
2184
|
+
url: import_zod2.z.string(),
|
|
2185
|
+
status: import_zod2.z.number().int().nullable()
|
|
1931
2186
|
})),
|
|
1932
|
-
durationMs:
|
|
2187
|
+
durationMs: import_zod2.z.number().min(0)
|
|
1933
2188
|
};
|
|
1934
2189
|
var YoutubeHarvestOutputSchema = {
|
|
1935
|
-
mode:
|
|
1936
|
-
videoCount:
|
|
1937
|
-
channel:
|
|
2190
|
+
mode: import_zod2.z.string(),
|
|
2191
|
+
videoCount: import_zod2.z.number().int().min(0),
|
|
2192
|
+
channel: import_zod2.z.object({
|
|
1938
2193
|
title: NullableString,
|
|
1939
2194
|
subscriberCount: NullableString
|
|
1940
2195
|
}).nullable(),
|
|
1941
|
-
videos:
|
|
1942
|
-
videoId:
|
|
1943
|
-
title:
|
|
2196
|
+
videos: import_zod2.z.array(import_zod2.z.object({
|
|
2197
|
+
videoId: import_zod2.z.string(),
|
|
2198
|
+
title: import_zod2.z.string(),
|
|
1944
2199
|
channelName: NullableString,
|
|
1945
2200
|
views: NullableString,
|
|
1946
2201
|
duration: NullableString,
|
|
@@ -1948,54 +2203,114 @@ var YoutubeHarvestOutputSchema = {
|
|
|
1948
2203
|
}))
|
|
1949
2204
|
};
|
|
1950
2205
|
var FacebookAdSearchOutputSchema = {
|
|
1951
|
-
query:
|
|
1952
|
-
advertiserCount:
|
|
1953
|
-
advertisers:
|
|
2206
|
+
query: import_zod2.z.string(),
|
|
2207
|
+
advertiserCount: import_zod2.z.number().int().min(0),
|
|
2208
|
+
advertisers: import_zod2.z.array(import_zod2.z.object({
|
|
1954
2209
|
name: NullableString,
|
|
1955
|
-
|
|
1956
|
-
|
|
2210
|
+
pageId: NullableString,
|
|
2211
|
+
pageUrl: NullableString,
|
|
2212
|
+
adCount: import_zod2.z.number().int().nullable(),
|
|
2213
|
+
libraryId: NullableString,
|
|
2214
|
+
sampleLibraryId: NullableString
|
|
1957
2215
|
}))
|
|
1958
2216
|
};
|
|
1959
2217
|
var FacebookPageIntelOutputSchema = {
|
|
1960
2218
|
advertiserName: NullableString,
|
|
1961
|
-
totalAds:
|
|
1962
|
-
activeCount:
|
|
1963
|
-
videoCount:
|
|
1964
|
-
imageCount:
|
|
1965
|
-
ads:
|
|
2219
|
+
totalAds: import_zod2.z.number().int().min(0),
|
|
2220
|
+
activeCount: import_zod2.z.number().int().min(0),
|
|
2221
|
+
videoCount: import_zod2.z.number().int().min(0),
|
|
2222
|
+
imageCount: import_zod2.z.number().int().min(0),
|
|
2223
|
+
ads: import_zod2.z.array(import_zod2.z.object({
|
|
1966
2224
|
libraryId: NullableString,
|
|
1967
2225
|
status: NullableString,
|
|
1968
2226
|
creativeType: NullableString,
|
|
2227
|
+
primaryText: NullableString,
|
|
1969
2228
|
headline: NullableString,
|
|
1970
2229
|
cta: NullableString,
|
|
1971
2230
|
startDate: NullableString,
|
|
2231
|
+
landingUrl: NullableString,
|
|
2232
|
+
domain: NullableString,
|
|
1972
2233
|
videoUrl: NullableString,
|
|
1973
|
-
|
|
2234
|
+
imageUrl: NullableString,
|
|
2235
|
+
videoPoster: NullableString,
|
|
2236
|
+
variations: import_zod2.z.number().int().nullable()
|
|
1974
2237
|
}))
|
|
1975
2238
|
};
|
|
1976
2239
|
var FacebookVideoTranscribeOutputSchema = {
|
|
1977
|
-
sourceUrl:
|
|
1978
|
-
pageUrl:
|
|
2240
|
+
sourceUrl: import_zod2.z.string().url(),
|
|
2241
|
+
pageUrl: import_zod2.z.string().url(),
|
|
1979
2242
|
videoId: NullableString,
|
|
1980
2243
|
ownerName: NullableString,
|
|
1981
|
-
selectedQuality:
|
|
1982
|
-
bitrate:
|
|
1983
|
-
videoDurationSec:
|
|
1984
|
-
videoUrl:
|
|
1985
|
-
wordCount:
|
|
1986
|
-
chunkCount:
|
|
1987
|
-
transcriptText:
|
|
1988
|
-
chunks:
|
|
1989
|
-
startSec:
|
|
1990
|
-
endSec:
|
|
1991
|
-
text:
|
|
2244
|
+
selectedQuality: import_zod2.z.string(),
|
|
2245
|
+
bitrate: import_zod2.z.number().int().nullable(),
|
|
2246
|
+
videoDurationSec: import_zod2.z.number().nullable(),
|
|
2247
|
+
videoUrl: import_zod2.z.string().url(),
|
|
2248
|
+
wordCount: import_zod2.z.number().int().min(0),
|
|
2249
|
+
chunkCount: import_zod2.z.number().int().min(0),
|
|
2250
|
+
transcriptText: import_zod2.z.string(),
|
|
2251
|
+
chunks: import_zod2.z.array(import_zod2.z.object({
|
|
2252
|
+
startSec: import_zod2.z.number(),
|
|
2253
|
+
endSec: import_zod2.z.number(),
|
|
2254
|
+
text: import_zod2.z.string()
|
|
1992
2255
|
}))
|
|
1993
2256
|
};
|
|
2257
|
+
var TranscriptChunkOutput = import_zod2.z.object({
|
|
2258
|
+
startSec: import_zod2.z.number(),
|
|
2259
|
+
endSec: import_zod2.z.number(),
|
|
2260
|
+
text: import_zod2.z.string()
|
|
2261
|
+
});
|
|
2262
|
+
var YoutubeTranscribeOutputSchema = {
|
|
2263
|
+
videoId: NullableString,
|
|
2264
|
+
url: NullableString,
|
|
2265
|
+
wordCount: import_zod2.z.number().int().min(0),
|
|
2266
|
+
chunkCount: import_zod2.z.number().int().min(0),
|
|
2267
|
+
durationMs: import_zod2.z.number().nullable(),
|
|
2268
|
+
transcriptText: import_zod2.z.string(),
|
|
2269
|
+
chunks: import_zod2.z.array(TranscriptChunkOutput),
|
|
2270
|
+
resolvedInputs: import_zod2.z.object({
|
|
2271
|
+
videoId: NullableString,
|
|
2272
|
+
url: NullableString
|
|
2273
|
+
})
|
|
2274
|
+
};
|
|
2275
|
+
var FacebookAdTranscribeOutputSchema = {
|
|
2276
|
+
videoUrl: import_zod2.z.string().url(),
|
|
2277
|
+
wordCount: import_zod2.z.number().int().min(0),
|
|
2278
|
+
chunkCount: import_zod2.z.number().int().min(0),
|
|
2279
|
+
durationMs: import_zod2.z.number().nullable(),
|
|
2280
|
+
transcriptText: import_zod2.z.string(),
|
|
2281
|
+
chunks: import_zod2.z.array(TranscriptChunkOutput),
|
|
2282
|
+
resolvedInputs: import_zod2.z.object({
|
|
2283
|
+
videoUrl: import_zod2.z.string().url()
|
|
2284
|
+
})
|
|
2285
|
+
};
|
|
2286
|
+
var CaptureSerpSnapshotOutputSchema = {
|
|
2287
|
+
schemaVersion: import_zod2.z.literal("serp-intelligence.capture.v1"),
|
|
2288
|
+
status: import_zod2.z.string(),
|
|
2289
|
+
query: NullableString,
|
|
2290
|
+
location: NullableString,
|
|
2291
|
+
capturedAt: NullableString,
|
|
2292
|
+
resultCount: import_zod2.z.number().int().min(0).nullable(),
|
|
2293
|
+
snapshotId: NullableString,
|
|
2294
|
+
resolvedInputs: import_zod2.z.record(import_zod2.z.unknown()),
|
|
2295
|
+
artifacts: import_zod2.z.array(import_zod2.z.record(import_zod2.z.unknown())),
|
|
2296
|
+
diagnostics: import_zod2.z.record(import_zod2.z.unknown()).nullable(),
|
|
2297
|
+
providerPayload: import_zod2.z.record(import_zod2.z.unknown())
|
|
2298
|
+
};
|
|
2299
|
+
var CaptureSerpPageSnapshotsOutputSchema = {
|
|
2300
|
+
schemaVersion: import_zod2.z.literal("serp-intelligence.page-snapshots.v1"),
|
|
2301
|
+
status: import_zod2.z.string(),
|
|
2302
|
+
count: import_zod2.z.number().int().min(0),
|
|
2303
|
+
failedCount: import_zod2.z.number().int().min(0),
|
|
2304
|
+
captures: import_zod2.z.array(import_zod2.z.record(import_zod2.z.unknown())),
|
|
2305
|
+
resolvedInputs: import_zod2.z.record(import_zod2.z.unknown()),
|
|
2306
|
+
diagnostics: import_zod2.z.record(import_zod2.z.unknown()).nullable(),
|
|
2307
|
+
providerPayload: import_zod2.z.record(import_zod2.z.unknown())
|
|
2308
|
+
};
|
|
1994
2309
|
var CreditsInfoInputSchema = {
|
|
1995
|
-
item:
|
|
1996
|
-
includeLedger:
|
|
2310
|
+
item: import_zod2.z.string().optional().describe('Optional tool, action, or feature to look up, e.g. "maps reviews", "extract_url", "YouTube transcription", or "concurrency"'),
|
|
2311
|
+
includeLedger: import_zod2.z.boolean().default(false).describe("Whether to include recent credit ledger entries")
|
|
1997
2312
|
};
|
|
1998
|
-
var WorkflowIdSchema =
|
|
2313
|
+
var WorkflowIdSchema = import_zod2.z.enum([
|
|
1999
2314
|
"directory",
|
|
2000
2315
|
"agent-packet",
|
|
2001
2316
|
"local-competitive-audit",
|
|
@@ -2005,114 +2320,129 @@ var WorkflowIdSchema = import_zod.z.enum([
|
|
|
2005
2320
|
"ai-overview-language"
|
|
2006
2321
|
]);
|
|
2007
2322
|
var WorkflowListInputSchema = {
|
|
2008
|
-
includeRecipes:
|
|
2323
|
+
includeRecipes: import_zod2.z.boolean().default(true).describe("Include high-level AI-facing recipes such as market analysis, ICP research, forum/review research, brand design briefings, CRO audits, positioning briefs, content gaps, and AI search visibility audits.")
|
|
2009
2324
|
};
|
|
2010
2325
|
var WorkflowSuggestInputSchema = {
|
|
2011
|
-
goal:
|
|
2012
|
-
query:
|
|
2013
|
-
keyword:
|
|
2014
|
-
domain:
|
|
2015
|
-
url:
|
|
2016
|
-
location:
|
|
2017
|
-
state:
|
|
2018
|
-
maxSuggestions:
|
|
2326
|
+
goal: import_zod2.z.string().min(1).describe('The user goal or job to route, e.g. "market analysis for roofers in Tennessee", "ICP research for med spas", "CRO audit for this URL", or "brand design briefing".'),
|
|
2327
|
+
query: import_zod2.z.string().optional().describe("Business category, niche, or Maps query when known."),
|
|
2328
|
+
keyword: import_zod2.z.string().optional().describe("Search keyword, audience problem, or content topic when known."),
|
|
2329
|
+
domain: import_zod2.z.string().optional().describe("Target domain or brand domain when known."),
|
|
2330
|
+
url: import_zod2.z.string().url().optional().describe("Target URL when the workflow should inspect a specific page."),
|
|
2331
|
+
location: import_zod2.z.string().optional().describe("City/region/country for localized research, e.g. Denver, CO."),
|
|
2332
|
+
state: import_zod2.z.string().optional().describe("US state abbreviation or name for state-wide market research."),
|
|
2333
|
+
maxSuggestions: import_zod2.z.number().int().min(1).max(8).default(3).describe("Number of matching workflow recipes to return.")
|
|
2019
2334
|
};
|
|
2020
2335
|
var WorkflowRunInputSchema = {
|
|
2021
|
-
workflowId: WorkflowIdSchema.describe("Workflow to run. Use workflow_list or workflow_suggest first when unsure."),
|
|
2022
|
-
input:
|
|
2023
|
-
webhookUrl:
|
|
2336
|
+
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."),
|
|
2337
|
+
input: import_zod2.z.record(import_zod2.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?}."),
|
|
2338
|
+
webhookUrl: import_zod2.z.string().url().optional().describe("Optional HTTPS webhook to receive the completed hosted workflow run event.")
|
|
2339
|
+
};
|
|
2340
|
+
var WorkflowStepInputSchema = {
|
|
2341
|
+
runId: import_zod2.z.string().min(1).describe("Workflow run id returned by workflow_run, workflow_step, or workflow_status. Use only a returned runId; do not construct one yourself. Advances the run by exactly one step (one logical leg, e.g. one live harvest).")
|
|
2024
2342
|
};
|
|
2025
2343
|
var WorkflowStatusInputSchema = {
|
|
2026
|
-
runId:
|
|
2344
|
+
runId: import_zod2.z.string().min(1).describe("Workflow run id returned by workflow_run, workflow_step, or workflow_status. Use only a returned runId; do not construct one yourself.")
|
|
2027
2345
|
};
|
|
2028
2346
|
var WorkflowArtifactReadInputSchema = {
|
|
2029
|
-
runId:
|
|
2030
|
-
artifactId:
|
|
2031
|
-
maxBytes:
|
|
2347
|
+
runId: import_zod2.z.string().min(1).describe("Workflow run id returned by workflow_run, workflow_step, or workflow_status. Use only a returned runId; do not construct one yourself."),
|
|
2348
|
+
artifactId: import_zod2.z.string().min(1).describe("Artifact id from the run artifact list returned by workflow_run, workflow_step, or workflow_status. Use only a returned artifactId; do not construct one yourself."),
|
|
2349
|
+
maxBytes: import_zod2.z.number().int().min(1e3).max(1e6).default(2e5).describe("Maximum bytes of artifact text to return inline. Use lower values for large CSV/JSON artifacts; call again with the downloadUrl if needed outside MCP.")
|
|
2032
2350
|
};
|
|
2033
|
-
var WorkflowRecipeOutput =
|
|
2034
|
-
id:
|
|
2035
|
-
title:
|
|
2036
|
-
description:
|
|
2037
|
-
primaryWorkflowId:
|
|
2038
|
-
recommendedTools:
|
|
2039
|
-
requiredInputs:
|
|
2040
|
-
optionalInputs:
|
|
2041
|
-
produces:
|
|
2042
|
-
runHint:
|
|
2351
|
+
var WorkflowRecipeOutput = import_zod2.z.object({
|
|
2352
|
+
id: import_zod2.z.string(),
|
|
2353
|
+
title: import_zod2.z.string(),
|
|
2354
|
+
description: import_zod2.z.string(),
|
|
2355
|
+
primaryWorkflowId: import_zod2.z.string().nullable(),
|
|
2356
|
+
recommendedTools: import_zod2.z.array(import_zod2.z.string()),
|
|
2357
|
+
requiredInputs: import_zod2.z.array(import_zod2.z.string()),
|
|
2358
|
+
optionalInputs: import_zod2.z.array(import_zod2.z.string()),
|
|
2359
|
+
produces: import_zod2.z.array(import_zod2.z.string()),
|
|
2360
|
+
runHint: import_zod2.z.string()
|
|
2043
2361
|
});
|
|
2044
|
-
var WorkflowDefinitionOutput =
|
|
2045
|
-
id:
|
|
2046
|
-
title:
|
|
2047
|
-
description:
|
|
2362
|
+
var WorkflowDefinitionOutput = import_zod2.z.object({
|
|
2363
|
+
id: import_zod2.z.string(),
|
|
2364
|
+
title: import_zod2.z.string(),
|
|
2365
|
+
description: import_zod2.z.string()
|
|
2048
2366
|
});
|
|
2049
|
-
var WorkflowArtifactOutput =
|
|
2367
|
+
var WorkflowArtifactOutput = import_zod2.z.record(import_zod2.z.unknown());
|
|
2050
2368
|
var WorkflowListOutputSchema = {
|
|
2051
|
-
workflows:
|
|
2052
|
-
recipes:
|
|
2369
|
+
workflows: import_zod2.z.array(WorkflowDefinitionOutput),
|
|
2370
|
+
recipes: import_zod2.z.array(WorkflowRecipeOutput)
|
|
2053
2371
|
};
|
|
2054
2372
|
var WorkflowSuggestOutputSchema = {
|
|
2055
|
-
goal:
|
|
2056
|
-
suggestions:
|
|
2373
|
+
goal: import_zod2.z.string(),
|
|
2374
|
+
suggestions: import_zod2.z.array(WorkflowRecipeOutput)
|
|
2057
2375
|
};
|
|
2058
2376
|
var WorkflowRunOutputSchema = {
|
|
2059
|
-
workflowId:
|
|
2060
|
-
input:
|
|
2061
|
-
run:
|
|
2062
|
-
summary:
|
|
2063
|
-
|
|
2377
|
+
workflowId: import_zod2.z.string(),
|
|
2378
|
+
input: import_zod2.z.record(import_zod2.z.unknown()),
|
|
2379
|
+
run: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
2380
|
+
summary: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
2381
|
+
step: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
2382
|
+
nextStep: import_zod2.z.record(import_zod2.z.unknown()).nullable().optional(),
|
|
2383
|
+
done: import_zod2.z.boolean().optional(),
|
|
2384
|
+
artifacts: import_zod2.z.array(WorkflowArtifactOutput)
|
|
2385
|
+
};
|
|
2386
|
+
var WorkflowStepOutputSchema = {
|
|
2387
|
+
runId: import_zod2.z.string(),
|
|
2388
|
+
run: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
2389
|
+
summary: import_zod2.z.record(import_zod2.z.unknown()).nullable().optional(),
|
|
2390
|
+
step: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
2391
|
+
nextStep: import_zod2.z.record(import_zod2.z.unknown()).nullable().optional(),
|
|
2392
|
+
done: import_zod2.z.boolean(),
|
|
2393
|
+
artifacts: import_zod2.z.array(WorkflowArtifactOutput)
|
|
2064
2394
|
};
|
|
2065
2395
|
var WorkflowStatusOutputSchema = {
|
|
2066
|
-
run:
|
|
2067
|
-
artifacts:
|
|
2396
|
+
run: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
2397
|
+
artifacts: import_zod2.z.array(WorkflowArtifactOutput)
|
|
2068
2398
|
};
|
|
2069
2399
|
var WorkflowArtifactReadOutputSchema = {
|
|
2070
|
-
runId:
|
|
2071
|
-
artifactId:
|
|
2072
|
-
contentType:
|
|
2073
|
-
bytes:
|
|
2074
|
-
truncated:
|
|
2075
|
-
text:
|
|
2400
|
+
runId: import_zod2.z.string(),
|
|
2401
|
+
artifactId: import_zod2.z.string(),
|
|
2402
|
+
contentType: import_zod2.z.string(),
|
|
2403
|
+
bytes: import_zod2.z.number().int().min(0),
|
|
2404
|
+
truncated: import_zod2.z.boolean(),
|
|
2405
|
+
text: import_zod2.z.string()
|
|
2076
2406
|
};
|
|
2077
2407
|
var SearchSerpInputSchema = {
|
|
2078
|
-
query:
|
|
2079
|
-
location:
|
|
2080
|
-
gl:
|
|
2081
|
-
hl:
|
|
2082
|
-
device:
|
|
2083
|
-
proxyMode:
|
|
2084
|
-
proxyZip:
|
|
2085
|
-
debug:
|
|
2086
|
-
pages:
|
|
2408
|
+
query: import_zod2.z.string().min(1).describe('Core search topic only. Separate location when possible. If user says "best dentist in Brooklyn NY serp", use query="best dentist" and location="Brooklyn, NY".'),
|
|
2409
|
+
location: import_zod2.z.string().optional().describe("City, region, or country for geo-targeted results, inferred from user request when present."),
|
|
2410
|
+
gl: import_zod2.z.string().length(2).default("us").describe("Google country code inferred from location or user language."),
|
|
2411
|
+
hl: import_zod2.z.string().default("en").describe("Google interface/content language inferred from user request."),
|
|
2412
|
+
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."),
|
|
2413
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe("Proxy targeting mode. Default configured uses the service proxy without city/ZIP targeting for the highest general SERP success rate. Use location only when the user explicitly needs city/ZIP-targeted residential proxy evidence. Use none only for direct-network debugging."),
|
|
2414
|
+
proxyZip: import_zod2.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."),
|
|
2415
|
+
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."),
|
|
2416
|
+
pages: import_zod2.z.number().int().min(1).max(2).default(1).describe("Number of result pages to fetch (1\u20132)")
|
|
2087
2417
|
};
|
|
2088
2418
|
var CaptureSerpSnapshotInputSchema = {
|
|
2089
|
-
query:
|
|
2090
|
-
location:
|
|
2091
|
-
gl:
|
|
2092
|
-
hl:
|
|
2093
|
-
device:
|
|
2094
|
-
proxyMode:
|
|
2095
|
-
proxyZip:
|
|
2096
|
-
pages:
|
|
2097
|
-
debug:
|
|
2098
|
-
includePageSnapshots:
|
|
2099
|
-
pageSnapshotLimit:
|
|
2419
|
+
query: import_zod2.z.string().min(1).describe("Core search query to capture as a structured SERP Intelligence snapshot. Separate the place into location when the user gives a city, region, country, or ZIP."),
|
|
2420
|
+
location: import_zod2.z.string().optional().describe("City, region, country, or service area used for localized Google results. MCP Scraper records location evidence; UULE alone is not proof of localization."),
|
|
2421
|
+
gl: import_zod2.z.string().length(2).default("us").describe("Google country code inferred from the requested market, e.g. us, gb, ca, au."),
|
|
2422
|
+
hl: import_zod2.z.string().default("en").describe("Google interface/content language inferred from the user request."),
|
|
2423
|
+
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."),
|
|
2424
|
+
proxyMode: import_zod2.z.enum(["location", "configured", "none"]).default(DEFAULT_PROXY_MODE).describe("Proxy behavior for capture. Default configured uses the service proxy without city/ZIP targeting for the highest general capture success rate. Use location only when the user explicitly needs city/ZIP-targeted residential proxy evidence. Use none only for direct-network debugging."),
|
|
2425
|
+
proxyZip: import_zod2.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."),
|
|
2426
|
+
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."),
|
|
2427
|
+
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."),
|
|
2428
|
+
includePageSnapshots: import_zod2.z.boolean().default(false).describe("Also capture ranking-page snapshots for selected SERP URLs through the same product capture path."),
|
|
2429
|
+
pageSnapshotLimit: import_zod2.z.number().int().min(0).max(10).default(0).describe("Maximum ranking-page snapshots to capture when includePageSnapshots is true. Use 0 when only SERP evidence is needed.")
|
|
2100
2430
|
};
|
|
2101
2431
|
var ScreenshotInputSchema = {
|
|
2102
|
-
url:
|
|
2103
|
-
device:
|
|
2104
|
-
allowLocal:
|
|
2432
|
+
url: import_zod2.z.string().url().describe("URL to capture as a full-page screenshot. Use http or https. Pass allowLocal: true to capture localhost or private-network URLs during development."),
|
|
2433
|
+
device: import_zod2.z.enum(["desktop", "mobile"]).default("desktop").describe("Viewport profile. desktop = 1440\xD7900. mobile = 390\xD7844. Use desktop by default; use mobile when the user asks for a mobile view."),
|
|
2434
|
+
allowLocal: import_zod2.z.boolean().default(false).describe("Allow localhost and private-network URLs (127.x, 192.168.x, 10.x, etc.). For local development only \u2014 not for production use.")
|
|
2105
2435
|
};
|
|
2106
2436
|
var CaptureSerpPageSnapshotsInputSchema = {
|
|
2107
|
-
urls:
|
|
2108
|
-
targets:
|
|
2109
|
-
url:
|
|
2110
|
-
sourceKind:
|
|
2111
|
-
sourcePosition:
|
|
2437
|
+
urls: import_zod2.z.array(import_zod2.z.string().url()).min(1).max(25).describe("Public HTTP/HTTPS URLs to capture as SERP Intelligence page snapshots. Do not pass localhost, private IPs, file URLs, or internal admin URLs."),
|
|
2438
|
+
targets: import_zod2.z.array(import_zod2.z.object({
|
|
2439
|
+
url: import_zod2.z.string().url().describe("Public HTTP/HTTPS URL to capture."),
|
|
2440
|
+
sourceKind: import_zod2.z.enum(["organic", "ai_citation", "local_pack_website", "configured_target", "site_subject"]).default("configured_target").describe("Why this page is being captured for SERP Intelligence evidence."),
|
|
2441
|
+
sourcePosition: import_zod2.z.number().int().min(1).optional().describe("Ranking or citation position when the page came from SERP evidence.")
|
|
2112
2442
|
}).strict()).min(1).max(25).optional().describe("Structured page snapshot targets. Use this instead of urls when source kind or position should be preserved."),
|
|
2113
|
-
maxConcurrency:
|
|
2114
|
-
timeoutMs:
|
|
2115
|
-
debug:
|
|
2443
|
+
maxConcurrency: import_zod2.z.number().int().min(1).max(5).default(2).describe("Parallel page captures. Use 2 normally; higher values can increase proxy/browser pressure."),
|
|
2444
|
+
timeoutMs: import_zod2.z.number().int().min(1e3).max(6e4).default(15e3).describe("Per-page capture timeout in milliseconds. Increase for slow pages; timeout artifacts are returned as structured capture failures."),
|
|
2445
|
+
debug: import_zod2.z.boolean().default(false).describe("Include sanitized browser/proxy diagnostics for page snapshot debugging. Use true for capture, network, or proxy troubleshooting.")
|
|
2116
2446
|
};
|
|
2117
2447
|
|
|
2118
2448
|
// src/mcp/rank-tracker-blueprint.ts
|
|
@@ -2503,70 +2833,72 @@ function registerPaaExtractorMcpTools(server2, executor2, options = {}) {
|
|
|
2503
2833
|
if (savesReports) registerSavedReportResources(server2);
|
|
2504
2834
|
server2.registerTool("harvest_paa", {
|
|
2505
2835
|
title: "Google PAA + SERP Harvest",
|
|
2506
|
-
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").
|
|
2836
|
+
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"). Omit proxyMode for normal use; the service defaults to the configured browser-service proxy without city/ZIP targeting for the highest general success rate. Use proxyMode location only when the user explicitly needs city/ZIP-targeted residential proxy evidence. 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.'),
|
|
2507
2837
|
inputSchema: HarvestPaaInputSchema,
|
|
2508
2838
|
outputSchema: HarvestPaaOutputSchema,
|
|
2509
2839
|
annotations: liveWebToolAnnotations("Google PAA + SERP Harvest")
|
|
2510
2840
|
}, async (input) => formatHarvestPaa(await executor2.harvestPaa(input), input));
|
|
2511
2841
|
server2.registerTool("search_serp", {
|
|
2512
2842
|
title: "Google SERP Lookup",
|
|
2513
|
-
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.
|
|
2843
|
+
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. Omit proxyMode for normal use; the service defaults to the configured browser-service proxy without city/ZIP targeting. Use proxyMode location and proxyZip only when the user explicitly needs city/ZIP-targeted residential proxy evidence."),
|
|
2514
2844
|
inputSchema: SearchSerpInputSchema,
|
|
2515
2845
|
outputSchema: SearchSerpOutputSchema,
|
|
2516
2846
|
annotations: liveWebToolAnnotations("Google SERP Lookup")
|
|
2517
2847
|
}, async (input) => formatSearchSerp(await executor2.searchSerp(input), input));
|
|
2518
2848
|
server2.registerTool("extract_url", {
|
|
2519
2849
|
title: "Single URL Extract",
|
|
2520
|
-
description: withReportNote("Extract structured data from one public URL
|
|
2850
|
+
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."),
|
|
2521
2851
|
inputSchema: ExtractUrlInputSchema,
|
|
2522
2852
|
outputSchema: ExtractUrlOutputSchema,
|
|
2523
2853
|
annotations: liveWebToolAnnotations("Single URL Extract")
|
|
2524
2854
|
}, async (input) => formatExtractUrl(await executor2.extractUrl(input), input));
|
|
2525
2855
|
server2.registerTool("map_site_urls", {
|
|
2526
2856
|
title: "Site URL Map",
|
|
2527
|
-
description: withReportNote("Map/crawl a public website
|
|
2857
|
+
description: withReportNote("Map/crawl a public website when the user asks for a sitemap, URL inventory, broken-link scan, redirect scan, or crawl planning. Returns internal URLs with HTTP status and truncation metadata. Use this before extract_site when choosing pages for an audit; use extract_url for one known page."),
|
|
2528
2858
|
inputSchema: MapSiteUrlsInputSchema,
|
|
2529
2859
|
outputSchema: MapSiteUrlsOutputSchema,
|
|
2530
2860
|
annotations: liveWebToolAnnotations("Site URL Map")
|
|
2531
2861
|
}, async (input) => formatMapSiteUrls(await executor2.mapSiteUrls(input), input));
|
|
2532
2862
|
server2.registerTool("extract_site", {
|
|
2533
2863
|
title: "Multi-Page Site Extract",
|
|
2534
|
-
description: withReportNote("Run multi-page extraction across a public website. Returns per-page titles, H1s, metadata, headings, schema/entity data, canonical URLs, and content. Use
|
|
2864
|
+
description: withReportNote("Run multi-page extraction across a public website when the user asks for a website audit, competitor audit, full-site content crawl, schema inventory, or page metadata review. Returns per-page titles, H1s, metadata, headings, schema/entity data, canonical URLs, and content. Use map_site_urls first when URL selection matters; use extract_url for one page."),
|
|
2535
2865
|
inputSchema: ExtractSiteInputSchema,
|
|
2536
2866
|
outputSchema: ExtractSiteOutputSchema,
|
|
2537
2867
|
annotations: liveWebToolAnnotations("Multi-Page Site Extract")
|
|
2538
2868
|
}, async (input) => formatExtractSite(await executor2.extractSite(input), input));
|
|
2539
2869
|
server2.registerTool("youtube_harvest", {
|
|
2540
2870
|
title: "YouTube Video Harvest",
|
|
2541
|
-
description: withReportNote('Harvest YouTube video metadata by
|
|
2871
|
+
description: withReportNote('Harvest YouTube video metadata when the user wants to find videos by topic, inspect a channel library, compare video angles, or get videoIds for later transcription. Use mode "search" for keyword/topic requests and mode "channel" for @handles, channel IDs, or channel URLs. Returns titles, views, durations, URLs, and videoIds for follow-up transcription. Use youtube_transcribe after selecting one video.'),
|
|
2542
2872
|
inputSchema: YoutubeHarvestInputSchema,
|
|
2543
2873
|
outputSchema: YoutubeHarvestOutputSchema,
|
|
2544
2874
|
annotations: liveWebToolAnnotations("YouTube Video Harvest")
|
|
2545
2875
|
}, async (input) => formatYoutubeHarvest(await executor2.youtubeHarvest(input), input));
|
|
2546
2876
|
server2.registerTool("youtube_transcribe", {
|
|
2547
2877
|
title: "YouTube Transcription",
|
|
2548
|
-
description: withReportNote("Fetch and transcribe captions from a YouTube video. Returns full transcript, timestamped chunks,
|
|
2878
|
+
description: withReportNote("Fetch and transcribe captions from a YouTube video. Use this when the user asks what was said in a YouTube video, wants claims/offers/lessons extracted from a video, or provides a YouTube URL for transcript work. Returns full transcript, timestamped chunks, word count, and resolvedInputs. Pass videoId from youtube_harvest results, or pass url when the user pasted a YouTube URL. Use youtube_harvest first when you need to discover videos by topic/channel."),
|
|
2549
2879
|
inputSchema: YoutubeTranscribeInputSchema,
|
|
2880
|
+
outputSchema: YoutubeTranscribeOutputSchema,
|
|
2550
2881
|
annotations: liveWebToolAnnotations("YouTube Transcription")
|
|
2551
2882
|
}, async (input) => formatYoutubeTranscribe(await executor2.youtubeTranscribe(input), input));
|
|
2552
2883
|
server2.registerTool("facebook_page_intel", {
|
|
2553
2884
|
title: "Facebook Advertiser Ad Intel",
|
|
2554
|
-
description: withReportNote("Harvest ads from a Facebook advertiser. Returns ad copy, headlines, CTAs, creative type, status, landing URLs, and direct ad video URLs ready for facebook_ad_transcribe. Accepts pageId, libraryId, or a brand/advertiser name as query. Use
|
|
2885
|
+
description: withReportNote("Harvest ads from a Facebook advertiser when the user wants current ad copy, creative angles, CTAs, video URLs, or competitive ad intelligence for one brand/page. Returns ad copy, headlines, CTAs, creative type, status, landing URLs, and direct ad video URLs ready for facebook_ad_transcribe. Accepts pageId, libraryId, or a brand/advertiser name as query. Use facebook_ad_search first when the advertiser handle is unknown. For normal public Facebook reels/posts/watch/share URLs, use facebook_video_transcribe instead."),
|
|
2555
2886
|
inputSchema: FacebookPageIntelInputSchema,
|
|
2556
2887
|
outputSchema: FacebookPageIntelOutputSchema,
|
|
2557
2888
|
annotations: liveWebToolAnnotations("Facebook Advertiser Ad Intel")
|
|
2558
2889
|
}, async (input) => formatFacebookPageIntel(await executor2.facebookPageIntel(input), input));
|
|
2559
2890
|
server2.registerTool("facebook_ad_search", {
|
|
2560
2891
|
title: "Facebook Ad Library Search",
|
|
2561
|
-
description: withReportNote("Search Facebook Ad Library by brand,
|
|
2892
|
+
description: withReportNote("Search Facebook Ad Library when the user wants to find advertisers by brand, competitor, niche, or keyword. Returns advertisers with ad counts and library IDs. Use this to discover competitor/page handles, then pass libraryId or pageId to facebook_page_intel for ad details."),
|
|
2562
2893
|
inputSchema: FacebookAdSearchInputSchema,
|
|
2563
2894
|
outputSchema: FacebookAdSearchOutputSchema,
|
|
2564
2895
|
annotations: liveWebToolAnnotations("Facebook Ad Library Search")
|
|
2565
2896
|
}, async (input) => formatFacebookAdSearch(await executor2.facebookAdSearch(input), input));
|
|
2566
2897
|
server2.registerTool("facebook_ad_transcribe", {
|
|
2567
2898
|
title: "Facebook Ad Transcription",
|
|
2568
|
-
description: "Transcribe audio from a Facebook ad video CDN URL. Returns full transcript
|
|
2899
|
+
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.",
|
|
2569
2900
|
inputSchema: FacebookAdTranscribeInputSchema,
|
|
2901
|
+
outputSchema: FacebookAdTranscribeOutputSchema,
|
|
2570
2902
|
annotations: liveWebToolAnnotations("Facebook Ad Transcription")
|
|
2571
2903
|
}, async (input) => formatFacebookAdTranscribe(await executor2.facebookAdTranscribe(input), input));
|
|
2572
2904
|
server2.registerTool("facebook_video_transcribe", {
|
|
@@ -2585,14 +2917,14 @@ function registerPaaExtractorMcpTools(server2, executor2, options = {}) {
|
|
|
2585
2917
|
}, async (input) => formatMapsPlaceIntel(await executor2.mapsPlaceIntel(input), input));
|
|
2586
2918
|
server2.registerTool("maps_search", {
|
|
2587
2919
|
title: "Google Maps Business Search",
|
|
2588
|
-
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."
|
|
2920
|
+
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." Maps is the default location-targeted surface: omit proxyMode for normal Maps use so the service creates residential proxy evidence for the requested market and rotates on retryable failures. Pass proxyZip only when a specific ZIP or city-center ZIP is known. Use proxyMode configured only when you explicitly do not want city/ZIP proxy targeting. 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.'),
|
|
2589
2921
|
inputSchema: MapsSearchInputSchema,
|
|
2590
2922
|
outputSchema: MapsSearchOutputSchema,
|
|
2591
2923
|
annotations: liveWebToolAnnotations("Google Maps Business Search")
|
|
2592
2924
|
}, async (input) => formatMapsSearch(await executor2.mapsSearch(input), input));
|
|
2593
2925
|
server2.registerTool("directory_workflow", {
|
|
2594
2926
|
title: "Directory Workflow: Markets + Maps",
|
|
2595
|
-
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.
|
|
2927
|
+
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. Maps workflows default to location-targeted proxying so each city can use city/state or ZIP-group residential proxy evidence; use proxyMode configured only when you explicitly do not want city/ZIP proxy targeting. 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.'),
|
|
2596
2928
|
inputSchema: DirectoryWorkflowInputSchema,
|
|
2597
2929
|
outputSchema: DirectoryWorkflowOutputSchema,
|
|
2598
2930
|
annotations: liveWebToolAnnotations("Directory Workflow: Markets + Maps")
|
|
@@ -2613,21 +2945,28 @@ function registerPaaExtractorMcpTools(server2, executor2, options = {}) {
|
|
|
2613
2945
|
}, async (input) => formatWorkflowSuggest(input));
|
|
2614
2946
|
server2.registerTool("workflow_run", {
|
|
2615
2947
|
title: "Run Workflow",
|
|
2616
|
-
description: withReportNote("
|
|
2948
|
+
description: withReportNote("Start a higher-level MCP Scraper workflow. Use after workflow_suggest or workflow_list. Runnable workflow ids: directory, agent-packet, local-competitive-audit, map-comparison, serp-comparison, paa-expansion-brief, ai-overview-language. This is the main MCP tool for market analysis, ICP evidence packets, local competitive audits, Maps/SERP comparisons, content gap briefs, and AI Overview language guidance. Stepwise workflows (e.g. agent-packet) run ONE leg per call and return runId, the step output, and nextStep \u2014 when nextStep is present, call workflow_step with the runId to run the next leg, and keep calling it until done is true. This keeps each call short instead of one long blocking run, so report each step result to the user as it arrives."),
|
|
2617
2949
|
inputSchema: WorkflowRunInputSchema,
|
|
2618
2950
|
outputSchema: WorkflowRunOutputSchema,
|
|
2619
2951
|
annotations: liveWebToolAnnotations("Run Workflow")
|
|
2620
2952
|
}, async (input) => formatWorkflowRun(await executor2.workflowRun(input), input));
|
|
2953
|
+
server2.registerTool("workflow_step", {
|
|
2954
|
+
title: "Advance Workflow Step",
|
|
2955
|
+
description: withReportNote("Run the next leg of a stepwise MCP Scraper workflow started with workflow_run. Pass the runId. Each call executes exactly one logical step (typically one live harvest), persists that step's artifacts, and returns the step output plus nextStep. Keep calling workflow_step with the same runId until done is true, reporting each step result to the user as it lands. Use this instead of waiting on one long workflow call \u2014 it avoids client timeouts on long multi-step jobs."),
|
|
2956
|
+
inputSchema: WorkflowStepInputSchema,
|
|
2957
|
+
outputSchema: WorkflowStepOutputSchema,
|
|
2958
|
+
annotations: liveWebToolAnnotations("Advance Workflow Step")
|
|
2959
|
+
}, async (input) => formatWorkflowStep(await executor2.workflowStep(input), input));
|
|
2621
2960
|
server2.registerTool("workflow_status", {
|
|
2622
2961
|
title: "Workflow Status",
|
|
2623
|
-
description: "Fetch a hosted workflow run by id and list its current status and artifacts. Use
|
|
2962
|
+
description: "Fetch a hosted workflow run by id and list its current status and artifacts. Use when a workflow may still be running, when the model needs to re-open a run, inspect artifact ids, recover from a long workflow, or decide whether to call workflow_step or workflow_artifact_read next. Use only a runId returned by workflow_run/workflow_step/workflow_status; do not construct one yourself.",
|
|
2624
2963
|
inputSchema: WorkflowStatusInputSchema,
|
|
2625
2964
|
outputSchema: WorkflowStatusOutputSchema,
|
|
2626
2965
|
annotations: liveWebToolAnnotations("Workflow Status")
|
|
2627
2966
|
}, async (input) => formatWorkflowStatus(await executor2.workflowStatus(input), input));
|
|
2628
2967
|
server2.registerTool("workflow_artifact_read", {
|
|
2629
2968
|
title: "Read Workflow Artifact",
|
|
2630
|
-
description: "Read a workflow artifact back into MCP context by run id and artifact id. Use this before writing final deliverables so the answer is grounded in generated evidence.json, CSVs, Markdown briefs, reports, and task files instead of memory. Use maxBytes to limit large CSV/JSON artifacts.",
|
|
2969
|
+
description: "Read a workflow artifact back into MCP context by run id and artifact id. Use this before writing final deliverables so the answer is grounded in generated evidence.json, CSVs, Markdown briefs, reports, and task files instead of memory. Use workflow_status first when artifact ids are unknown. Use only artifactId values returned by workflow_run/workflow_step/workflow_status; do not construct one yourself. Use maxBytes to limit large CSV/JSON artifacts.",
|
|
2631
2970
|
inputSchema: WorkflowArtifactReadInputSchema,
|
|
2632
2971
|
outputSchema: WorkflowArtifactReadOutputSchema,
|
|
2633
2972
|
annotations: liveWebToolAnnotations("Read Workflow Artifact")
|