mcp-scraper 0.2.15 → 0.2.17
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 +11 -6
- package/dist/bin/api-server.cjs +489 -16
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/browser-agent-stdio-server.cjs +1 -1
- package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
- package/dist/bin/browser-agent-stdio-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +475 -9
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +3 -3
- package/dist/bin/mcp-scraper-install.cjs +7 -6
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +7 -6
- package/dist/bin/mcp-scraper-install.js.map +1 -1
- package/dist/bin/mcp-stdio-server.cjs +475 -9
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/{chunk-Q4DFONIK.js → chunk-BYF7HTLJ.js} +478 -10
- package/dist/chunk-BYF7HTLJ.js.map +1 -0
- package/dist/chunk-NZG2K6CJ.js +7 -0
- package/dist/chunk-NZG2K6CJ.js.map +1 -0
- package/dist/{chunk-TIPUIEJN.js → chunk-ZA7IZZKN.js} +2 -2
- package/dist/{server-VD2TD3AD.js → server-AK3UME3Y.js} +4 -4
- package/dist/server-AK3UME3Y.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-2CQXHSWC.js +0 -7
- package/dist/chunk-2CQXHSWC.js.map +0 -1
- package/dist/chunk-Q4DFONIK.js.map +0 -1
- package/dist/server-VD2TD3AD.js.map +0 -1
- /package/dist/{chunk-TIPUIEJN.js.map → chunk-ZA7IZZKN.js.map} +0 -0
package/dist/bin/api-server.cjs
CHANGED
|
@@ -13814,6 +13814,127 @@ var init_maps_routes = __esm({
|
|
|
13814
13814
|
}
|
|
13815
13815
|
});
|
|
13816
13816
|
|
|
13817
|
+
// src/mcp/workflow-catalog.ts
|
|
13818
|
+
function normalize(value) {
|
|
13819
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
13820
|
+
}
|
|
13821
|
+
function suggestWorkflowRecipes(goal, limit = 3) {
|
|
13822
|
+
const normalized = normalize(goal);
|
|
13823
|
+
const scored = WORKFLOW_RECIPES.map((recipe) => {
|
|
13824
|
+
const haystack = normalize([
|
|
13825
|
+
recipe.id,
|
|
13826
|
+
recipe.title,
|
|
13827
|
+
recipe.description,
|
|
13828
|
+
recipe.requiredInputs.join(" "),
|
|
13829
|
+
recipe.optionalInputs.join(" "),
|
|
13830
|
+
recipe.produces.join(" ")
|
|
13831
|
+
].join(" "));
|
|
13832
|
+
let score = 0;
|
|
13833
|
+
for (const token of normalized.split(/\s+/).filter(Boolean)) {
|
|
13834
|
+
if (haystack.includes(token)) score += 1;
|
|
13835
|
+
}
|
|
13836
|
+
if (haystack.includes(normalized)) score += 5;
|
|
13837
|
+
return { recipe, score };
|
|
13838
|
+
});
|
|
13839
|
+
return scored.sort((a, b) => b.score - a.score || a.recipe.title.localeCompare(b.recipe.title)).slice(0, Math.max(1, limit)).map((item) => item.recipe);
|
|
13840
|
+
}
|
|
13841
|
+
var WORKFLOW_RECIPES;
|
|
13842
|
+
var init_workflow_catalog = __esm({
|
|
13843
|
+
"src/mcp/workflow-catalog.ts"() {
|
|
13844
|
+
"use strict";
|
|
13845
|
+
WORKFLOW_RECIPES = [
|
|
13846
|
+
{
|
|
13847
|
+
id: "market_analysis",
|
|
13848
|
+
title: "Market Analysis",
|
|
13849
|
+
description: "Compare a niche across a city, state, or market set using Google Maps competitors, SERP evidence, review counts, categories, and opportunity signals.",
|
|
13850
|
+
primaryWorkflowId: "local-competitive-audit",
|
|
13851
|
+
recommendedTools: ["workflow_run", "directory_workflow", "maps_search", "maps_place_intel", "search_serp", "harvest_paa"],
|
|
13852
|
+
requiredInputs: ["query", "state or location"],
|
|
13853
|
+
optionalInputs: ["domain", "minPopulation", "maxCities", "maxResultsPerCity", "hydrateTop", "maxReviews"],
|
|
13854
|
+
produces: ["city summary", "competitor CSV", "review insight CSV", "market difficulty/opportunity notes", "HTML report"],
|
|
13855
|
+
runHint: "Use workflow_run with workflowId local-competitive-audit for state-wide market batches, or map-comparison for one city/location comparison."
|
|
13856
|
+
},
|
|
13857
|
+
{
|
|
13858
|
+
id: "icp_research",
|
|
13859
|
+
title: "ICP Research",
|
|
13860
|
+
description: "Build an evidence packet for ideal-customer-profile research from real search demand, PAA language, competing pages, AI Overview citations, and source domains.",
|
|
13861
|
+
primaryWorkflowId: "agent-packet",
|
|
13862
|
+
recommendedTools: ["workflow_run", "harvest_paa", "search_serp", "extract_url", "youtube_harvest", "facebook_ad_search"],
|
|
13863
|
+
requiredInputs: ["keyword or audience problem"],
|
|
13864
|
+
optionalInputs: ["domain", "location", "maxQuestions"],
|
|
13865
|
+
produces: ["evidence JSON", "sources CSV", "competitor CSV", "brief markdown", "agent task list"],
|
|
13866
|
+
runHint: "Use workflow_run with workflowId agent-packet. Treat keyword as the buyer problem, job-to-be-done, niche, or service category."
|
|
13867
|
+
},
|
|
13868
|
+
{
|
|
13869
|
+
id: "forum_review_research",
|
|
13870
|
+
title: "Forum And Review Research",
|
|
13871
|
+
description: "Acquire customer language from Google PAA, forum/Perspectives SERP surfaces, Google Maps reviews, review topics, Facebook ads, and video transcripts.",
|
|
13872
|
+
primaryWorkflowId: "local-competitive-audit",
|
|
13873
|
+
recommendedTools: ["workflow_run", "harvest_paa", "maps_search", "maps_place_intel", "facebook_page_intel", "facebook_video_transcribe", "youtube_transcribe"],
|
|
13874
|
+
requiredInputs: ["query", "state or location"],
|
|
13875
|
+
optionalInputs: ["maxReviews", "maxQuestions", "competitors", "facebookUrl", "youtubeVideoId"],
|
|
13876
|
+
produces: ["review insight CSV", "PAA/source language", "competitor review topics", "transcripts where supplied", "pain/theme summary"],
|
|
13877
|
+
runHint: "Use local-competitive-audit for review acquisition, then harvest_paa for forum/Perspectives language and transcript tools for supplied videos."
|
|
13878
|
+
},
|
|
13879
|
+
{
|
|
13880
|
+
id: "brand_design_brief",
|
|
13881
|
+
title: "Brand Design Brief",
|
|
13882
|
+
description: "Create a design briefing grounded in a brand site, competitor pages, SERP language, audience pains, visual assets, colors, fonts, screenshots, and positioning evidence.",
|
|
13883
|
+
primaryWorkflowId: "serp-comparison",
|
|
13884
|
+
recommendedTools: ["workflow_run", "extract_url", "extract_site", "capture_serp_page_snapshots", "search_serp", "harvest_paa", "browser_open"],
|
|
13885
|
+
requiredInputs: ["url or domain", "keyword or market category"],
|
|
13886
|
+
optionalInputs: ["competitorUrls", "location", "extractTop"],
|
|
13887
|
+
produces: ["page comparison CSV", "content gaps", "branding/asset evidence", "SERP positioning evidence", "designer-ready brief"],
|
|
13888
|
+
runHint: "Use workflow_run with workflowId serp-comparison for market/page evidence, then extract_url with extractBranding:true for brand assets and colors."
|
|
13889
|
+
},
|
|
13890
|
+
{
|
|
13891
|
+
id: "cro_audit",
|
|
13892
|
+
title: "CRO Audit",
|
|
13893
|
+
description: "Audit conversion clarity using page extraction, screenshots, browser DOM inspection, competitor SERP/page comparisons, forms/buttons, proof, offer, trust, and friction evidence.",
|
|
13894
|
+
primaryWorkflowId: "serp-comparison",
|
|
13895
|
+
recommendedTools: ["workflow_run", "extract_url", "extract_site", "capture_serp_page_snapshots", "browser_open", "browser_screenshot", "browser_locate"],
|
|
13896
|
+
requiredInputs: ["url"],
|
|
13897
|
+
optionalInputs: ["keyword", "domain", "location", "competitorUrls"],
|
|
13898
|
+
produces: ["page extraction", "SERP/page comparison", "screenshot evidence", "friction checklist", "CRO recommendations"],
|
|
13899
|
+
runHint: "Use workflow_run with workflowId serp-comparison when a keyword/domain is known; use extract_url and browser tools for page-level CRO evidence."
|
|
13900
|
+
},
|
|
13901
|
+
{
|
|
13902
|
+
id: "competitive_positioning_brief",
|
|
13903
|
+
title: "Competitive Positioning Brief",
|
|
13904
|
+
description: "Compare competitors across SERP, PAA, AI Overview citations, page headings, Facebook ads, YouTube angles, Maps proof, and website claims.",
|
|
13905
|
+
primaryWorkflowId: "serp-comparison",
|
|
13906
|
+
recommendedTools: ["workflow_run", "facebook_ad_search", "facebook_page_intel", "youtube_harvest", "youtube_transcribe", "maps_search", "extract_url"],
|
|
13907
|
+
requiredInputs: ["keyword or query", "domain or url"],
|
|
13908
|
+
optionalInputs: ["location", "extractTop", "competitorUrls", "brandNames"],
|
|
13909
|
+
produces: ["SERP comparison", "content gap CSV", "AI Overview citation table", "competitor message map", "positioning brief"],
|
|
13910
|
+
runHint: "Use workflow_run with workflowId serp-comparison, then enrich with Facebook, YouTube, and Maps tools when the user asks for campaign or offer positioning."
|
|
13911
|
+
},
|
|
13912
|
+
{
|
|
13913
|
+
id: "content_gap_brief",
|
|
13914
|
+
title: "Content Gap Brief",
|
|
13915
|
+
description: "Turn live SERP, page extraction, PAA questions, AI Overview citations, and source-domain evidence into a writer-ready content brief.",
|
|
13916
|
+
primaryWorkflowId: "serp-comparison",
|
|
13917
|
+
recommendedTools: ["workflow_run", "paa-expansion-brief", "harvest_paa", "extract_url"],
|
|
13918
|
+
requiredInputs: ["keyword"],
|
|
13919
|
+
optionalInputs: ["domain", "url", "location", "maxQuestions", "extractTop"],
|
|
13920
|
+
produces: ["content gaps", "page comparison CSV", "PAA questions CSV", "section map", "writer brief"],
|
|
13921
|
+
runHint: "Use workflow_run with workflowId serp-comparison for competitor gaps or paa-expansion-brief when the user mainly wants a section map from PAA data."
|
|
13922
|
+
},
|
|
13923
|
+
{
|
|
13924
|
+
id: "ai_search_visibility_audit",
|
|
13925
|
+
title: "AI Search Visibility Audit",
|
|
13926
|
+
description: "Audit whether a brand/domain appears in AI Overview citations, PAA sources, organic results, local pack, and source pages, then produce language guidance.",
|
|
13927
|
+
primaryWorkflowId: "ai-overview-language",
|
|
13928
|
+
recommendedTools: ["workflow_run", "search_serp", "harvest_paa", "extract_url", "capture_serp_snapshot"],
|
|
13929
|
+
requiredInputs: ["keyword", "domain or url"],
|
|
13930
|
+
optionalInputs: ["location", "maxQuestions", "extractTop"],
|
|
13931
|
+
produces: ["AI Overview citations CSV", "claim patterns", "language guidance", "citation hooks", "visibility gaps"],
|
|
13932
|
+
runHint: "Use workflow_run with workflowId ai-overview-language. Use serp-comparison when the user also wants ranking-page gaps."
|
|
13933
|
+
}
|
|
13934
|
+
];
|
|
13935
|
+
}
|
|
13936
|
+
});
|
|
13937
|
+
|
|
13817
13938
|
// src/mcp/mcp-response-formatter.ts
|
|
13818
13939
|
function configureReportSaving(enabled) {
|
|
13819
13940
|
reportSavingEnabled = enabled;
|
|
@@ -13867,6 +13988,14 @@ function oneBlock(content) {
|
|
|
13867
13988
|
\u{1F4C4} Saved: \`${filePath}\`` : content;
|
|
13868
13989
|
return { content: [{ type: "text", text }] };
|
|
13869
13990
|
}
|
|
13991
|
+
function workflowRecipeTable(recipes) {
|
|
13992
|
+
if (!recipes.length) return "";
|
|
13993
|
+
return [
|
|
13994
|
+
"| Recipe | Best workflow | What it produces |",
|
|
13995
|
+
"|---|---|---|",
|
|
13996
|
+
...recipes.map((recipe) => `| ${cell(recipe.title)} | ${recipe.primaryWorkflowId ? `\`${recipe.primaryWorkflowId}\`` : "tool chain"} | ${cell(recipe.produces.slice(0, 4).join(", "))} |`)
|
|
13997
|
+
].join("\n");
|
|
13998
|
+
}
|
|
13870
13999
|
function formatStructuredError(body, fallback) {
|
|
13871
14000
|
if (body.error === "insufficient_balance") {
|
|
13872
14001
|
return `Insufficient credits. Balance: ${body.balance_credits} credits. This call requires ${body.required_credits} credits. Top up at ${body.topup_url}`;
|
|
@@ -14356,7 +14485,8 @@ ${adBlocks}`,
|
|
|
14356
14485
|
`
|
|
14357
14486
|
---
|
|
14358
14487
|
\u{1F4A1} **Tips**
|
|
14359
|
-
- Transcribe video ads: use \`facebook_ad_transcribe\` with the \`videoUrl\` above
|
|
14488
|
+
- Transcribe video ads: use \`facebook_ad_transcribe\` with the direct \`videoUrl\` above
|
|
14489
|
+
- Transcribe organic Facebook reels/posts: use \`facebook_video_transcribe\` with the public Facebook URL
|
|
14360
14490
|
- Find other advertisers: use \`facebook_ad_search\``
|
|
14361
14491
|
].filter(Boolean).join("\n");
|
|
14362
14492
|
return {
|
|
@@ -14438,6 +14568,163 @@ function normalizeMapsAttempts(value) {
|
|
|
14438
14568
|
observedRegion: attempt.observedRegion ?? attempt.observed_region ?? null
|
|
14439
14569
|
}));
|
|
14440
14570
|
}
|
|
14571
|
+
function workflowArtifactsFrom(run) {
|
|
14572
|
+
return Array.isArray(run?.artifacts) ? run.artifacts : [];
|
|
14573
|
+
}
|
|
14574
|
+
function workflowArtifactRows(artifacts) {
|
|
14575
|
+
if (!artifacts.length) return "";
|
|
14576
|
+
return [
|
|
14577
|
+
"| Artifact | Type | ID | Rows/Bytes |",
|
|
14578
|
+
"|---|---|---|---|",
|
|
14579
|
+
...artifacts.map((artifact) => {
|
|
14580
|
+
const rows = artifact.rows_count ?? artifact.rows ?? "";
|
|
14581
|
+
const bytes = artifact.bytes ?? "";
|
|
14582
|
+
const size = [rows ? `${rows} rows` : "", bytes ? `${bytes} bytes` : ""].filter(Boolean).join(" / ");
|
|
14583
|
+
return `| ${cell(String(artifact.label ?? artifact.path ?? "artifact"))} | ${cell(String(artifact.kind ?? artifact.content_type ?? "file"))} | \`${String(artifact.id ?? "")}\` | ${cell(size || "\u2014")} |`;
|
|
14584
|
+
})
|
|
14585
|
+
].join("\n");
|
|
14586
|
+
}
|
|
14587
|
+
function formatWorkflowList(raw, input) {
|
|
14588
|
+
const parsed = parseData(raw);
|
|
14589
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
14590
|
+
const workflows = Array.isArray(parsed.data.workflows) ? parsed.data.workflows : [];
|
|
14591
|
+
const workflowRows = [
|
|
14592
|
+
"| Workflow ID | Title | Use |",
|
|
14593
|
+
"|---|---|---|",
|
|
14594
|
+
...workflows.map((workflow) => `| \`${String(workflow.id ?? "")}\` | ${cell(String(workflow.title ?? ""))} | ${cell(String(workflow.description ?? ""))} |`)
|
|
14595
|
+
].join("\n");
|
|
14596
|
+
const recipes = input.includeRecipes === false ? [] : WORKFLOW_RECIPES;
|
|
14597
|
+
const full = [
|
|
14598
|
+
"# MCP Scraper Workflows",
|
|
14599
|
+
"Use `workflow_suggest` when the user describes a high-level job. Use `workflow_run` when the workflow id and input are known. Use `workflow_status` and `workflow_artifact_read` to inspect completed runs and pull evidence back into context.",
|
|
14600
|
+
workflows.length ? `
|
|
14601
|
+
## Runnable Workflows
|
|
14602
|
+
${workflowRows}` : "",
|
|
14603
|
+
recipes.length ? `
|
|
14604
|
+
## High-Level Recipes
|
|
14605
|
+
${workflowRecipeTable(recipes)}` : "",
|
|
14606
|
+
recipes.length ? "\nThese recipes cover market analysis, ICP research, forum and review acquisition, brand design briefings, CRO audits, competitive positioning, content gaps, and AI search visibility audits." : ""
|
|
14607
|
+
].filter(Boolean).join("\n");
|
|
14608
|
+
return {
|
|
14609
|
+
...oneBlock(full),
|
|
14610
|
+
structuredContent: {
|
|
14611
|
+
workflows: workflows.map((workflow) => ({
|
|
14612
|
+
id: String(workflow.id ?? ""),
|
|
14613
|
+
title: String(workflow.title ?? ""),
|
|
14614
|
+
description: String(workflow.description ?? "")
|
|
14615
|
+
})),
|
|
14616
|
+
recipes
|
|
14617
|
+
}
|
|
14618
|
+
};
|
|
14619
|
+
}
|
|
14620
|
+
function formatWorkflowSuggest(input) {
|
|
14621
|
+
const suggestions = suggestWorkflowRecipes(input.goal, input.maxSuggestions ?? 3);
|
|
14622
|
+
const full = [
|
|
14623
|
+
`# Workflow Suggestions`,
|
|
14624
|
+
`**Goal:** ${input.goal}`,
|
|
14625
|
+
"",
|
|
14626
|
+
workflowRecipeTable(suggestions),
|
|
14627
|
+
"",
|
|
14628
|
+
"## How To Execute",
|
|
14629
|
+
"1. Pick the closest recipe.",
|
|
14630
|
+
"2. If it has a `primaryWorkflowId`, call `workflow_run` with that id and the required inputs.",
|
|
14631
|
+
"3. After the run completes, use `workflow_artifact_read` on the most relevant CSV/JSON/Markdown artifacts before writing the final answer."
|
|
14632
|
+
].join("\n");
|
|
14633
|
+
return {
|
|
14634
|
+
...oneBlock(full),
|
|
14635
|
+
structuredContent: {
|
|
14636
|
+
goal: input.goal,
|
|
14637
|
+
suggestions
|
|
14638
|
+
}
|
|
14639
|
+
};
|
|
14640
|
+
}
|
|
14641
|
+
function formatWorkflowRun(raw, input) {
|
|
14642
|
+
const parsed = parseData(raw);
|
|
14643
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
14644
|
+
const run = parsed.data.run;
|
|
14645
|
+
const summary = parsed.data.summary;
|
|
14646
|
+
const artifacts = workflowArtifactsFrom(run);
|
|
14647
|
+
const runId = String(run?.id ?? "");
|
|
14648
|
+
const status = String(run?.status ?? summary?.status ?? "unknown");
|
|
14649
|
+
const full = [
|
|
14650
|
+
`# Workflow Run: ${input.workflowId}`,
|
|
14651
|
+
`**Run ID:** \`${runId || "unknown"}\``,
|
|
14652
|
+
`**Status:** ${status}`,
|
|
14653
|
+
summary?.title ? `**Title:** ${summary.title}` : "",
|
|
14654
|
+
summary?.summary ? `
|
|
14655
|
+
## Summary
|
|
14656
|
+
${summary.summary}` : "",
|
|
14657
|
+
artifacts.length ? `
|
|
14658
|
+
## Artifacts
|
|
14659
|
+
${workflowArtifactRows(artifacts)}` : "",
|
|
14660
|
+
artifacts.length ? "\nUse `workflow_artifact_read` with the run id and artifact id to pull CSV, JSON, Markdown, or report content into context." : ""
|
|
14661
|
+
].filter(Boolean).join("\n");
|
|
14662
|
+
return {
|
|
14663
|
+
...oneBlock(full),
|
|
14664
|
+
structuredContent: {
|
|
14665
|
+
workflowId: input.workflowId,
|
|
14666
|
+
input: input.input ?? {},
|
|
14667
|
+
run,
|
|
14668
|
+
summary,
|
|
14669
|
+
artifacts
|
|
14670
|
+
}
|
|
14671
|
+
};
|
|
14672
|
+
}
|
|
14673
|
+
function formatWorkflowStatus(raw, input) {
|
|
14674
|
+
const parsed = parseData(raw);
|
|
14675
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
14676
|
+
const run = parsed.data.run;
|
|
14677
|
+
const artifacts = workflowArtifactsFrom(run);
|
|
14678
|
+
const full = [
|
|
14679
|
+
`# Workflow Status`,
|
|
14680
|
+
`**Run ID:** \`${input.runId}\``,
|
|
14681
|
+
`**Workflow:** ${run?.workflow_id ?? "unknown"}`,
|
|
14682
|
+
`**Status:** ${run?.status ?? "unknown"}`,
|
|
14683
|
+
run?.error_message ? `
|
|
14684
|
+
## Error
|
|
14685
|
+
${run.error_message}` : "",
|
|
14686
|
+
artifacts.length ? `
|
|
14687
|
+
## Artifacts
|
|
14688
|
+
${workflowArtifactRows(artifacts)}` : ""
|
|
14689
|
+
].filter(Boolean).join("\n");
|
|
14690
|
+
return {
|
|
14691
|
+
...oneBlock(full),
|
|
14692
|
+
structuredContent: {
|
|
14693
|
+
run,
|
|
14694
|
+
artifacts
|
|
14695
|
+
}
|
|
14696
|
+
};
|
|
14697
|
+
}
|
|
14698
|
+
function formatWorkflowArtifactRead(raw, input) {
|
|
14699
|
+
const parsed = parseData(raw);
|
|
14700
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
14701
|
+
const text = typeof parsed.data.text === "string" ? parsed.data.text : "";
|
|
14702
|
+
const contentType = String(parsed.data.contentType ?? "text/plain");
|
|
14703
|
+
const bytes = Number(parsed.data.bytes ?? 0);
|
|
14704
|
+
const truncated = parsed.data.truncated === true;
|
|
14705
|
+
const full = [
|
|
14706
|
+
`# Workflow Artifact`,
|
|
14707
|
+
`**Run ID:** \`${input.runId}\``,
|
|
14708
|
+
`**Artifact ID:** \`${input.artifactId}\``,
|
|
14709
|
+
`**Content-Type:** ${contentType}`,
|
|
14710
|
+
`**Bytes:** ${bytes}${truncated ? ` (truncated to ${input.maxBytes ?? 2e5})` : ""}`,
|
|
14711
|
+
"",
|
|
14712
|
+
"```",
|
|
14713
|
+
text,
|
|
14714
|
+
"```"
|
|
14715
|
+
].join("\n");
|
|
14716
|
+
return {
|
|
14717
|
+
...oneBlock(full),
|
|
14718
|
+
structuredContent: {
|
|
14719
|
+
runId: input.runId,
|
|
14720
|
+
artifactId: input.artifactId,
|
|
14721
|
+
contentType,
|
|
14722
|
+
bytes,
|
|
14723
|
+
truncated,
|
|
14724
|
+
text
|
|
14725
|
+
}
|
|
14726
|
+
};
|
|
14727
|
+
}
|
|
14441
14728
|
function formatCreditsInfo(raw, input) {
|
|
14442
14729
|
const parsed = parseData(raw);
|
|
14443
14730
|
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
@@ -14802,7 +15089,7 @@ ${text}`,
|
|
|
14802
15089
|
${chunkRows}` : "",
|
|
14803
15090
|
`
|
|
14804
15091
|
---
|
|
14805
|
-
\u{1F4A1} Get more ads from this advertiser: use \`facebook_page_intel
|
|
15092
|
+
\u{1F4A1} Get more ads from this advertiser: use \`facebook_page_intel\`. For public Facebook reel/post URLs, use \`facebook_video_transcribe\`.`
|
|
14806
15093
|
].filter(Boolean).join("\n");
|
|
14807
15094
|
return oneBlock(full);
|
|
14808
15095
|
}
|
|
@@ -14838,7 +15125,7 @@ ${text}`,
|
|
|
14838
15125
|
${chunkRows}` : "",
|
|
14839
15126
|
`
|
|
14840
15127
|
---
|
|
14841
|
-
\u{1F4A1}
|
|
15128
|
+
\u{1F4A1} Use \`videoUrl\` as the extracted MP4 for download or follow-up processing. Use \`facebook_ad_transcribe\` only for Ad Library videoUrl values.`
|
|
14842
15129
|
].filter(Boolean).join("\n");
|
|
14843
15130
|
return {
|
|
14844
15131
|
...oneBlock(full),
|
|
@@ -14870,6 +15157,7 @@ var init_mcp_response_formatter = __esm({
|
|
|
14870
15157
|
import_node_os3 = require("os");
|
|
14871
15158
|
import_node_path5 = require("path");
|
|
14872
15159
|
init_errors();
|
|
15160
|
+
init_workflow_catalog();
|
|
14873
15161
|
reportSavingEnabled = true;
|
|
14874
15162
|
}
|
|
14875
15163
|
});
|
|
@@ -20145,12 +20433,12 @@ var PACKAGE_VERSION;
|
|
|
20145
20433
|
var init_version = __esm({
|
|
20146
20434
|
"src/version.ts"() {
|
|
20147
20435
|
"use strict";
|
|
20148
|
-
PACKAGE_VERSION = "0.2.
|
|
20436
|
+
PACKAGE_VERSION = "0.2.17";
|
|
20149
20437
|
}
|
|
20150
20438
|
});
|
|
20151
20439
|
|
|
20152
20440
|
// src/mcp/mcp-tool-schemas.ts
|
|
20153
|
-
var import_zod26, HarvestPaaInputSchema, ExtractUrlInputSchema, MapSiteUrlsInputSchema, ExtractSiteInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, MapsPlaceIntelInputSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryWorkflowOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, ExtractUrlOutputSchema, ExtractSiteOutputSchema, MapsPlaceIntelOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, FacebookPageIntelOutputSchema, FacebookVideoTranscribeOutputSchema, CreditsInfoInputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema;
|
|
20441
|
+
var import_zod26, HarvestPaaInputSchema, ExtractUrlInputSchema, MapSiteUrlsInputSchema, ExtractSiteInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, MapsPlaceIntelInputSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryWorkflowOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, ExtractUrlOutputSchema, ExtractSiteOutputSchema, MapsPlaceIntelOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, FacebookPageIntelOutputSchema, FacebookVideoTranscribeOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema;
|
|
20154
20442
|
var init_mcp_tool_schemas = __esm({
|
|
20155
20443
|
"src/mcp/mcp-tool-schemas.ts"() {
|
|
20156
20444
|
"use strict";
|
|
@@ -20205,10 +20493,10 @@ var init_mcp_tool_schemas = __esm({
|
|
|
20205
20493
|
maxResults: import_zod26.z.number().int().min(1).max(20).default(10)
|
|
20206
20494
|
};
|
|
20207
20495
|
FacebookAdTranscribeInputSchema = {
|
|
20208
|
-
videoUrl: import_zod26.z.string().url().describe("Facebook CDN video URL from a facebook_page_intel result")
|
|
20496
|
+
videoUrl: import_zod26.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.")
|
|
20209
20497
|
};
|
|
20210
20498
|
FacebookVideoTranscribeInputSchema = {
|
|
20211
|
-
url: import_zod26.z.string().url().describe("Organic Facebook reel, video, watch, or share URL. The tool renders the page, extracts the best public Facebook CDN MP4 URL, then transcribes it."),
|
|
20499
|
+
url: import_zod26.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."),
|
|
20212
20500
|
quality: import_zod26.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.")
|
|
20213
20501
|
};
|
|
20214
20502
|
MapsPlaceIntelInputSchema = {
|
|
@@ -20598,6 +20886,85 @@ var init_mcp_tool_schemas = __esm({
|
|
|
20598
20886
|
item: import_zod26.z.string().optional().describe('Optional tool, action, or feature to look up, e.g. "maps reviews", "extract_url", "YouTube transcription", or "concurrency"'),
|
|
20599
20887
|
includeLedger: import_zod26.z.boolean().default(false).describe("Whether to include recent credit ledger entries")
|
|
20600
20888
|
};
|
|
20889
|
+
WorkflowIdSchema2 = import_zod26.z.enum([
|
|
20890
|
+
"directory",
|
|
20891
|
+
"agent-packet",
|
|
20892
|
+
"local-competitive-audit",
|
|
20893
|
+
"map-comparison",
|
|
20894
|
+
"serp-comparison",
|
|
20895
|
+
"paa-expansion-brief",
|
|
20896
|
+
"ai-overview-language"
|
|
20897
|
+
]);
|
|
20898
|
+
WorkflowListInputSchema = {
|
|
20899
|
+
includeRecipes: import_zod26.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.")
|
|
20900
|
+
};
|
|
20901
|
+
WorkflowSuggestInputSchema = {
|
|
20902
|
+
goal: import_zod26.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".'),
|
|
20903
|
+
query: import_zod26.z.string().optional().describe("Business category, niche, or Maps query when known."),
|
|
20904
|
+
keyword: import_zod26.z.string().optional().describe("Search keyword, audience problem, or content topic when known."),
|
|
20905
|
+
domain: import_zod26.z.string().optional().describe("Target domain or brand domain when known."),
|
|
20906
|
+
url: import_zod26.z.string().url().optional().describe("Target URL when the workflow should inspect a specific page."),
|
|
20907
|
+
location: import_zod26.z.string().optional().describe("City/region/country for localized research, e.g. Denver, CO."),
|
|
20908
|
+
state: import_zod26.z.string().optional().describe("US state abbreviation or name for state-wide market research."),
|
|
20909
|
+
maxSuggestions: import_zod26.z.number().int().min(1).max(8).default(3).describe("Number of matching workflow recipes to return.")
|
|
20910
|
+
};
|
|
20911
|
+
WorkflowRunInputSchema = {
|
|
20912
|
+
workflowId: WorkflowIdSchema2.describe("Workflow to run. Use workflow_list or workflow_suggest first when unsure."),
|
|
20913
|
+
input: import_zod26.z.record(import_zod26.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?}."),
|
|
20914
|
+
webhookUrl: import_zod26.z.string().url().optional().describe("Optional HTTPS webhook to receive the completed hosted workflow run event.")
|
|
20915
|
+
};
|
|
20916
|
+
WorkflowStatusInputSchema = {
|
|
20917
|
+
runId: import_zod26.z.string().min(1).describe("Workflow run id returned by workflow_run.")
|
|
20918
|
+
};
|
|
20919
|
+
WorkflowArtifactReadInputSchema = {
|
|
20920
|
+
runId: import_zod26.z.string().min(1).describe("Workflow run id returned by workflow_run or workflow_status."),
|
|
20921
|
+
artifactId: import_zod26.z.string().min(1).describe("Artifact id from the run artifact list."),
|
|
20922
|
+
maxBytes: import_zod26.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.")
|
|
20923
|
+
};
|
|
20924
|
+
WorkflowRecipeOutput = import_zod26.z.object({
|
|
20925
|
+
id: import_zod26.z.string(),
|
|
20926
|
+
title: import_zod26.z.string(),
|
|
20927
|
+
description: import_zod26.z.string(),
|
|
20928
|
+
primaryWorkflowId: import_zod26.z.string().nullable(),
|
|
20929
|
+
recommendedTools: import_zod26.z.array(import_zod26.z.string()),
|
|
20930
|
+
requiredInputs: import_zod26.z.array(import_zod26.z.string()),
|
|
20931
|
+
optionalInputs: import_zod26.z.array(import_zod26.z.string()),
|
|
20932
|
+
produces: import_zod26.z.array(import_zod26.z.string()),
|
|
20933
|
+
runHint: import_zod26.z.string()
|
|
20934
|
+
});
|
|
20935
|
+
WorkflowDefinitionOutput = import_zod26.z.object({
|
|
20936
|
+
id: import_zod26.z.string(),
|
|
20937
|
+
title: import_zod26.z.string(),
|
|
20938
|
+
description: import_zod26.z.string()
|
|
20939
|
+
});
|
|
20940
|
+
WorkflowArtifactOutput = import_zod26.z.record(import_zod26.z.unknown());
|
|
20941
|
+
WorkflowListOutputSchema = {
|
|
20942
|
+
workflows: import_zod26.z.array(WorkflowDefinitionOutput),
|
|
20943
|
+
recipes: import_zod26.z.array(WorkflowRecipeOutput)
|
|
20944
|
+
};
|
|
20945
|
+
WorkflowSuggestOutputSchema = {
|
|
20946
|
+
goal: import_zod26.z.string(),
|
|
20947
|
+
suggestions: import_zod26.z.array(WorkflowRecipeOutput)
|
|
20948
|
+
};
|
|
20949
|
+
WorkflowRunOutputSchema = {
|
|
20950
|
+
workflowId: import_zod26.z.string(),
|
|
20951
|
+
input: import_zod26.z.record(import_zod26.z.unknown()),
|
|
20952
|
+
run: import_zod26.z.record(import_zod26.z.unknown()).optional(),
|
|
20953
|
+
summary: import_zod26.z.record(import_zod26.z.unknown()).optional(),
|
|
20954
|
+
artifacts: import_zod26.z.array(WorkflowArtifactOutput)
|
|
20955
|
+
};
|
|
20956
|
+
WorkflowStatusOutputSchema = {
|
|
20957
|
+
run: import_zod26.z.record(import_zod26.z.unknown()).optional(),
|
|
20958
|
+
artifacts: import_zod26.z.array(WorkflowArtifactOutput)
|
|
20959
|
+
};
|
|
20960
|
+
WorkflowArtifactReadOutputSchema = {
|
|
20961
|
+
runId: import_zod26.z.string(),
|
|
20962
|
+
artifactId: import_zod26.z.string(),
|
|
20963
|
+
contentType: import_zod26.z.string(),
|
|
20964
|
+
bytes: import_zod26.z.number().int().min(0),
|
|
20965
|
+
truncated: import_zod26.z.boolean(),
|
|
20966
|
+
text: import_zod26.z.string()
|
|
20967
|
+
};
|
|
20601
20968
|
SearchSerpInputSchema = {
|
|
20602
20969
|
query: import_zod26.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".'),
|
|
20603
20970
|
location: import_zod26.z.string().optional().describe("City, region, or country for geo-targeted results, inferred from user request when present."),
|
|
@@ -21083,7 +21450,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
21083
21450
|
}, async (input) => formatYoutubeTranscribe(await executor.youtubeTranscribe(input), input));
|
|
21084
21451
|
server.registerTool("facebook_page_intel", {
|
|
21085
21452
|
title: "Facebook Advertiser Ad Intel",
|
|
21086
|
-
description: withReportNote("Harvest ads from a Facebook advertiser. Returns ad copy, headlines, CTAs, creative type, status, landing URLs, and video URLs ready for
|
|
21453
|
+
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 after facebook_ad_search when possible. For normal public Facebook reels/posts/watch/share URLs, use facebook_video_transcribe instead."),
|
|
21087
21454
|
inputSchema: FacebookPageIntelInputSchema,
|
|
21088
21455
|
outputSchema: FacebookPageIntelOutputSchema,
|
|
21089
21456
|
annotations: liveWebToolAnnotations("Facebook Advertiser Ad Intel")
|
|
@@ -21097,13 +21464,13 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
21097
21464
|
}, async (input) => formatFacebookAdSearch(await executor.facebookAdSearch(input), input));
|
|
21098
21465
|
server.registerTool("facebook_ad_transcribe", {
|
|
21099
21466
|
title: "Facebook Ad Transcription",
|
|
21100
|
-
description: "Transcribe audio from a Facebook ad video. Returns full transcript and timestamped chunks. Use the videoUrl value from facebook_page_intel results.",
|
|
21467
|
+
description: "Transcribe audio from a Facebook ad video CDN URL. Returns full transcript and timestamped chunks. Use only with the direct videoUrl value from facebook_page_intel results, not public Facebook post/reel/share URLs.",
|
|
21101
21468
|
inputSchema: FacebookAdTranscribeInputSchema,
|
|
21102
21469
|
annotations: liveWebToolAnnotations("Facebook Ad Transcription")
|
|
21103
21470
|
}, async (input) => formatFacebookAdTranscribe(await executor.facebookAdTranscribe(input), input));
|
|
21104
21471
|
server.registerTool("facebook_video_transcribe", {
|
|
21105
21472
|
title: "Facebook Organic Video Transcription",
|
|
21106
|
-
description: withReportNote("Transcribe audio from an organic Facebook reel, video, watch, or share URL. Renders the Facebook page,
|
|
21473
|
+
description: withReportNote("Transcribe audio from an organic Facebook reel, video, watch, post, or share URL, including fb.watch links. Use this when the user pastes a normal Facebook video page URL and wants the transcript or downloadable MP4. Renders the Facebook page in a browser, selects the best matching public Facebook CDN MP4 URL from page state, then returns sourceUrl, resolved pageUrl, videoId, ownerName, selectedQuality, bitrate, videoDurationSec, extracted MP4 URL, full transcript, and timestamped chunks."),
|
|
21107
21474
|
inputSchema: FacebookVideoTranscribeInputSchema,
|
|
21108
21475
|
outputSchema: FacebookVideoTranscribeOutputSchema,
|
|
21109
21476
|
annotations: liveWebToolAnnotations("Facebook Organic Video Transcription")
|
|
@@ -21129,6 +21496,41 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
21129
21496
|
outputSchema: DirectoryWorkflowOutputSchema,
|
|
21130
21497
|
annotations: liveWebToolAnnotations("Directory Workflow: Markets + Maps")
|
|
21131
21498
|
}, async (input) => formatDirectoryWorkflow(await executor.directoryWorkflow(input), input));
|
|
21499
|
+
server.registerTool("workflow_list", {
|
|
21500
|
+
title: "Workflow Catalog",
|
|
21501
|
+
description: "List MCP Scraper higher-level workflows and AI-facing recipes. Use this when the user asks what MCP Scraper can do beyond single tools, or asks for market analysis, ICP research, forum/review acquisition, full brand design briefings, CRO audits, competitive positioning, content gap briefs, or AI search visibility audits. Returns runnable workflow ids plus recipe guidance for the best tool chain.",
|
|
21502
|
+
inputSchema: WorkflowListInputSchema,
|
|
21503
|
+
outputSchema: WorkflowListOutputSchema,
|
|
21504
|
+
annotations: localPlanningToolAnnotations("Workflow Catalog")
|
|
21505
|
+
}, async (input) => formatWorkflowList(await executor.workflowList(input), input));
|
|
21506
|
+
server.registerTool("workflow_suggest", {
|
|
21507
|
+
title: "Workflow Intent Router",
|
|
21508
|
+
description: 'Route a high-level business or research goal to the right MCP Scraper workflow/tool chain. Use before running work when the user says things like "do market analysis", "research my ICP", "acquire forum and review language", "build a brand design brief", "run a CRO audit", "compare competitors", "make a content gap brief", or "audit AI search visibility". This tool does not spend credits; it tells the AI exactly which workflow/tool chain to run next.',
|
|
21509
|
+
inputSchema: WorkflowSuggestInputSchema,
|
|
21510
|
+
outputSchema: WorkflowSuggestOutputSchema,
|
|
21511
|
+
annotations: localPlanningToolAnnotations("Workflow Intent Router")
|
|
21512
|
+
}, async (input) => formatWorkflowSuggest(input));
|
|
21513
|
+
server.registerTool("workflow_run", {
|
|
21514
|
+
title: "Run Workflow",
|
|
21515
|
+
description: withReportNote("Run a higher-level MCP Scraper workflow and return the run id, summary, and artifacts. 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 executing market analysis, ICP evidence packets, local competitive audits, Maps/SERP comparisons, content gap briefs, and AI Overview language guidance."),
|
|
21516
|
+
inputSchema: WorkflowRunInputSchema,
|
|
21517
|
+
outputSchema: WorkflowRunOutputSchema,
|
|
21518
|
+
annotations: liveWebToolAnnotations("Run Workflow")
|
|
21519
|
+
}, async (input) => formatWorkflowRun(await executor.workflowRun(input), input));
|
|
21520
|
+
server.registerTool("workflow_status", {
|
|
21521
|
+
title: "Workflow Status",
|
|
21522
|
+
description: "Fetch a hosted workflow run by id and list its current status and artifacts. Use after workflow_run when the model needs to re-open a run, inspect artifact ids, or recover from a long workflow.",
|
|
21523
|
+
inputSchema: WorkflowStatusInputSchema,
|
|
21524
|
+
outputSchema: WorkflowStatusOutputSchema,
|
|
21525
|
+
annotations: liveWebToolAnnotations("Workflow Status")
|
|
21526
|
+
}, async (input) => formatWorkflowStatus(await executor.workflowStatus(input), input));
|
|
21527
|
+
server.registerTool("workflow_artifact_read", {
|
|
21528
|
+
title: "Read Workflow Artifact",
|
|
21529
|
+
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.",
|
|
21530
|
+
inputSchema: WorkflowArtifactReadInputSchema,
|
|
21531
|
+
outputSchema: WorkflowArtifactReadOutputSchema,
|
|
21532
|
+
annotations: liveWebToolAnnotations("Read Workflow Artifact")
|
|
21533
|
+
}, async (input) => formatWorkflowArtifactRead(await executor.workflowArtifactRead(input), input));
|
|
21132
21534
|
server.registerTool("rank_tracker_blueprint", {
|
|
21133
21535
|
title: "Rank Tracker Blueprint Builder",
|
|
21134
21536
|
description: "Generate a build-ready database, cron/heartbeat, ingestion, metrics, and AI implementation prompt for a rank tracker powered by MCP Scraper. Supports Maps rankings through directory_workflow/maps_search, organic rankings through search_serp, AI Overview citation tracking, and People Also Ask source presence tracking. This tool is local planning only; it does not call the web or spend credits.",
|
|
@@ -21224,6 +21626,57 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
21224
21626
|
return { content: [{ type: "text", text: msg }], isError: true };
|
|
21225
21627
|
}
|
|
21226
21628
|
}
|
|
21629
|
+
async getJson(path6, timeoutMs = this.timeoutMs) {
|
|
21630
|
+
try {
|
|
21631
|
+
const res = await fetch(`${this.baseUrl}${path6}`, {
|
|
21632
|
+
method: "GET",
|
|
21633
|
+
headers: {
|
|
21634
|
+
"x-api-key": this.apiKey
|
|
21635
|
+
},
|
|
21636
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
21637
|
+
});
|
|
21638
|
+
const data = await res.json();
|
|
21639
|
+
if (!res.ok) {
|
|
21640
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }], isError: true };
|
|
21641
|
+
}
|
|
21642
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
21643
|
+
} catch (err) {
|
|
21644
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21645
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
21646
|
+
}
|
|
21647
|
+
}
|
|
21648
|
+
async getTextArtifact(path6, maxBytes, timeoutMs = this.timeoutMs) {
|
|
21649
|
+
try {
|
|
21650
|
+
const res = await fetch(`${this.baseUrl}${path6}`, {
|
|
21651
|
+
method: "GET",
|
|
21652
|
+
headers: {
|
|
21653
|
+
"x-api-key": this.apiKey
|
|
21654
|
+
},
|
|
21655
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
21656
|
+
});
|
|
21657
|
+
if (!res.ok) {
|
|
21658
|
+
const data = await res.json().catch(async () => ({ error: await res.text().catch(() => `HTTP ${res.status}`) }));
|
|
21659
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }], isError: true };
|
|
21660
|
+
}
|
|
21661
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
21662
|
+
const sliced = bytes.subarray(0, Math.min(maxBytes, bytes.length));
|
|
21663
|
+
return {
|
|
21664
|
+
content: [{
|
|
21665
|
+
type: "text",
|
|
21666
|
+
text: JSON.stringify({
|
|
21667
|
+
contentType: res.headers.get("content-type") ?? "application/octet-stream",
|
|
21668
|
+
bytes: bytes.length,
|
|
21669
|
+
truncated: sliced.length < bytes.length,
|
|
21670
|
+
maxBytes,
|
|
21671
|
+
text: sliced.toString("utf8")
|
|
21672
|
+
})
|
|
21673
|
+
}]
|
|
21674
|
+
};
|
|
21675
|
+
} catch (err) {
|
|
21676
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21677
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
21678
|
+
}
|
|
21679
|
+
}
|
|
21227
21680
|
harvestPaa(input) {
|
|
21228
21681
|
const timeoutMs = this.httpTimeoutOverrideMs ?? harvestTimeoutBudget(input.maxQuestions ?? 30).clientMs;
|
|
21229
21682
|
return this.call("/harvest/sync", input, timeoutMs);
|
|
@@ -21271,6 +21724,26 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
21271
21724
|
const timeoutMs = this.httpTimeoutOverrideMs ?? Math.min(9e5, Math.max(18e4, Math.ceil(cityCount / concurrency) * 12e4));
|
|
21272
21725
|
return this.call("/directory/run", input, timeoutMs);
|
|
21273
21726
|
}
|
|
21727
|
+
workflowList(_input) {
|
|
21728
|
+
return this.getJson("/workflows/definitions");
|
|
21729
|
+
}
|
|
21730
|
+
workflowRun(input) {
|
|
21731
|
+
const timeoutMs = this.httpTimeoutOverrideMs ?? Number(process.env.MCP_SCRAPER_WORKFLOW_TIMEOUT_MS ?? 9e5);
|
|
21732
|
+
return this.call("/workflows/run", {
|
|
21733
|
+
workflowId: input.workflowId,
|
|
21734
|
+
input: input.input ?? {},
|
|
21735
|
+
webhookUrl: input.webhookUrl
|
|
21736
|
+
}, Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 9e5);
|
|
21737
|
+
}
|
|
21738
|
+
workflowStatus(input) {
|
|
21739
|
+
return this.getJson(`/workflows/runs/${encodeURIComponent(input.runId)}`);
|
|
21740
|
+
}
|
|
21741
|
+
workflowArtifactRead(input) {
|
|
21742
|
+
return this.getTextArtifact(
|
|
21743
|
+
`/workflows/runs/${encodeURIComponent(input.runId)}/artifacts/${encodeURIComponent(input.artifactId)}`,
|
|
21744
|
+
input.maxBytes ?? 2e5
|
|
21745
|
+
);
|
|
21746
|
+
}
|
|
21274
21747
|
creditsInfo(input) {
|
|
21275
21748
|
return this.call("/billing/credits", input);
|
|
21276
21749
|
}
|
|
@@ -21676,16 +22149,16 @@ async function locatePageTargets(cdpWsUrl, targets) {
|
|
|
21676
22149
|
}).catch(() => {
|
|
21677
22150
|
});
|
|
21678
22151
|
const data = await page.evaluate((rawTargets) => {
|
|
21679
|
-
const
|
|
22152
|
+
const normalize2 = (value) => value.replace(/\s+/g, " ").trim();
|
|
21680
22153
|
const isVisibleRect = (r) => r.width >= 1 && r.height >= 1 && r.bottom >= 0 && r.right >= 0 && r.top <= window.innerHeight && r.left <= window.innerWidth;
|
|
21681
22154
|
const roleOf = (el2) => el2.getAttribute("role") || el2.tagName.toLowerCase();
|
|
21682
22155
|
const nameOf = (el2) => {
|
|
21683
22156
|
const e = el2;
|
|
21684
|
-
return
|
|
22157
|
+
return normalize2(
|
|
21685
22158
|
e.getAttribute("aria-label") || e.placeholder || e.innerText || e.getAttribute("value") || e.getAttribute("title") || ""
|
|
21686
22159
|
).slice(0, 160);
|
|
21687
22160
|
};
|
|
21688
|
-
const textOf = (el2) =>
|
|
22161
|
+
const textOf = (el2) => normalize2(el2.innerText || el2.textContent || "").slice(0, 500);
|
|
21689
22162
|
const unionRects = (rects) => {
|
|
21690
22163
|
const visible = rects.filter(isVisibleRect);
|
|
21691
22164
|
if (!visible.length) return null;
|
|
@@ -21724,7 +22197,7 @@ async function locatePageTargets(cdpWsUrl, targets) {
|
|
|
21724
22197
|
return nodes.map((el2) => ({ el: el2, rect: el2.getBoundingClientRect() })).filter((item) => isVisibleRect(item.rect));
|
|
21725
22198
|
};
|
|
21726
22199
|
const textMatches = (needle, match) => {
|
|
21727
|
-
const wanted =
|
|
22200
|
+
const wanted = normalize2(needle);
|
|
21728
22201
|
const wantedLower = wanted.toLowerCase();
|
|
21729
22202
|
if (!wantedLower) return [];
|
|
21730
22203
|
const matches = [];
|
|
@@ -21732,7 +22205,7 @@ async function locatePageTargets(cdpWsUrl, targets) {
|
|
|
21732
22205
|
let node = walker.nextNode();
|
|
21733
22206
|
while (node) {
|
|
21734
22207
|
const raw = node.textContent || "";
|
|
21735
|
-
const normalizedRaw =
|
|
22208
|
+
const normalizedRaw = normalize2(raw);
|
|
21736
22209
|
const rawLower = raw.toLowerCase();
|
|
21737
22210
|
const normalizedLower = normalizedRaw.toLowerCase();
|
|
21738
22211
|
const ok = match === "exact" ? normalizedLower === wantedLower : normalizedLower.includes(wantedLower);
|
|
@@ -22914,7 +23387,7 @@ __export(server_exports, {
|
|
|
22914
23387
|
app: () => app
|
|
22915
23388
|
});
|
|
22916
23389
|
function configuredOrigins() {
|
|
22917
|
-
const origins = /* @__PURE__ */ new Set(["https://mcpscraper.dev"]);
|
|
23390
|
+
const origins = /* @__PURE__ */ new Set(["https://mcpscraper.dev", "https://www.mcpscraper.dev"]);
|
|
22918
23391
|
for (const raw of (process.env.ALLOWED_ORIGINS ?? process.env.APP_ORIGIN ?? "").split(",")) {
|
|
22919
23392
|
const trimmed = raw.trim();
|
|
22920
23393
|
if (trimmed) origins.add(trimmed);
|