mcp-scraper 0.3.16 → 0.3.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/dist/bin/api-server.cjs +133 -5
- 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 +121 -4
- 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 +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +121 -4
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/{chunk-7XARJHS6.js → chunk-N33YEY6W.js} +124 -5
- package/dist/chunk-N33YEY6W.js.map +1 -0
- package/dist/chunk-PVWWY5NV.js +7 -0
- package/dist/chunk-PVWWY5NV.js.map +1 -0
- package/dist/{chunk-ISZWGIWL.js → chunk-UFD7N36R.js} +2 -2
- package/dist/{server-LPVBSE2E.js → server-TKWY47JA.js} +11 -5
- package/dist/server-TKWY47JA.js.map +1 -0
- package/docs/mcp-tool-manifest.generated.json +1 -1
- package/package.json +1 -1
- package/dist/chunk-7XARJHS6.js.map +0 -1
- package/dist/chunk-SOMBZK3V.js +0 -7
- package/dist/chunk-SOMBZK3V.js.map +0 -1
- package/dist/server-LPVBSE2E.js.map +0 -1
- /package/dist/{chunk-ISZWGIWL.js.map → chunk-UFD7N36R.js.map} +0 -0
package/dist/bin/api-server.cjs
CHANGED
|
@@ -9054,6 +9054,104 @@ var init_seo_link_graph = __esm({
|
|
|
9054
9054
|
}
|
|
9055
9055
|
});
|
|
9056
9056
|
|
|
9057
|
+
// src/api/seo-link-report.ts
|
|
9058
|
+
function registrableDomain(host) {
|
|
9059
|
+
const h = host.replace(/^www\./, "").toLowerCase();
|
|
9060
|
+
const parts = h.split(".");
|
|
9061
|
+
return parts.length <= 2 ? h : parts.slice(-2).join(".");
|
|
9062
|
+
}
|
|
9063
|
+
function buildLinkReport(edges, metrics, siteUrl) {
|
|
9064
|
+
let siteReg = "";
|
|
9065
|
+
try {
|
|
9066
|
+
siteReg = registrableDomain(new URL(siteUrl).hostname);
|
|
9067
|
+
} catch {
|
|
9068
|
+
siteReg = "";
|
|
9069
|
+
}
|
|
9070
|
+
const internalEdges = edges.filter((e) => e.internal);
|
|
9071
|
+
const pages = metrics.length;
|
|
9072
|
+
const orphans = metrics.filter((m) => m.orphan).length;
|
|
9073
|
+
const brokenInternal = internalEdges.filter((e) => e.targetStatus != null && e.targetStatus >= 400).length;
|
|
9074
|
+
const sumInlinks = metrics.reduce((a, m) => a + m.inlinks, 0);
|
|
9075
|
+
const sumOutlinks = metrics.reduce((a, m) => a + m.outlinksInternal + m.outlinksExternal, 0);
|
|
9076
|
+
const distribution = { zero: 0, oneToTwo: 0, threeToTen: 0, elevenPlus: 0 };
|
|
9077
|
+
for (const m of metrics) {
|
|
9078
|
+
if (m.inlinks === 0) distribution.zero++;
|
|
9079
|
+
else if (m.inlinks <= 2) distribution.oneToTwo++;
|
|
9080
|
+
else if (m.inlinks <= 10) distribution.threeToTen++;
|
|
9081
|
+
else distribution.elevenPlus++;
|
|
9082
|
+
}
|
|
9083
|
+
const topByInlinks = [...metrics].sort((a, b) => b.inlinks - a.inlinks).slice(0, 20).map((m) => ({ url: m.url, inlinks: m.inlinks, outlinksInternal: m.outlinksInternal, outlinksExternal: m.outlinksExternal }));
|
|
9084
|
+
const domMap = /* @__PURE__ */ new Map();
|
|
9085
|
+
let externalTotal = 0;
|
|
9086
|
+
for (const e of edges) {
|
|
9087
|
+
let domain;
|
|
9088
|
+
try {
|
|
9089
|
+
domain = registrableDomain(new URL(e.to).hostname);
|
|
9090
|
+
} catch {
|
|
9091
|
+
continue;
|
|
9092
|
+
}
|
|
9093
|
+
if (!domain || domain === siteReg) continue;
|
|
9094
|
+
externalTotal++;
|
|
9095
|
+
const d = domMap.get(domain) ?? { links: 0, nofollow: 0, pages: /* @__PURE__ */ new Set() };
|
|
9096
|
+
d.links++;
|
|
9097
|
+
if (e.nofollow) d.nofollow++;
|
|
9098
|
+
d.pages.add(e.from);
|
|
9099
|
+
domMap.set(domain, d);
|
|
9100
|
+
}
|
|
9101
|
+
const externalDomains = [...domMap.entries()].map(([domain, d]) => ({ domain, links: d.links, nofollow: d.nofollow, pages: d.pages.size })).sort((a, b) => b.links - a.links);
|
|
9102
|
+
const round = (n) => Math.round(n * 10) / 10;
|
|
9103
|
+
return {
|
|
9104
|
+
summary: {
|
|
9105
|
+
internal: {
|
|
9106
|
+
totalLinks: internalEdges.length,
|
|
9107
|
+
pages,
|
|
9108
|
+
orphans,
|
|
9109
|
+
brokenInternal,
|
|
9110
|
+
avgInlinks: pages ? round(sumInlinks / pages) : 0,
|
|
9111
|
+
avgOutlinks: pages ? round(sumOutlinks / pages) : 0,
|
|
9112
|
+
distribution,
|
|
9113
|
+
topByInlinks
|
|
9114
|
+
},
|
|
9115
|
+
external: {
|
|
9116
|
+
totalLinks: externalTotal,
|
|
9117
|
+
uniqueDomains: externalDomains.length,
|
|
9118
|
+
topDomains: externalDomains.slice(0, 20)
|
|
9119
|
+
}
|
|
9120
|
+
},
|
|
9121
|
+
externalDomains
|
|
9122
|
+
};
|
|
9123
|
+
}
|
|
9124
|
+
function renderLinkReport(r) {
|
|
9125
|
+
const s = r.summary;
|
|
9126
|
+
const lines = [
|
|
9127
|
+
`## Link analysis`,
|
|
9128
|
+
`**Internal:** ${s.internal.totalLinks} links \xB7 ${s.internal.pages} pages \xB7 avg ${s.internal.avgInlinks} inlinks/page \xB7 ${s.internal.orphans} orphans \xB7 ${s.internal.brokenInternal} broken`,
|
|
9129
|
+
`**External:** ${s.external.totalLinks} links to ${s.external.uniqueDomains} domains`,
|
|
9130
|
+
``,
|
|
9131
|
+
`### Inlink distribution`,
|
|
9132
|
+
`- 0 inlinks (orphans): ${s.internal.distribution.zero}`,
|
|
9133
|
+
`- 1\u20132: ${s.internal.distribution.oneToTwo}`,
|
|
9134
|
+
`- 3\u201310: ${s.internal.distribution.threeToTen}`,
|
|
9135
|
+
`- 11+: ${s.internal.distribution.elevenPlus}`,
|
|
9136
|
+
``,
|
|
9137
|
+
`### Top 20 internal pages by inlinks`,
|
|
9138
|
+
`| inlinks | out (int/ext) | URL |`,
|
|
9139
|
+
`|---|---|---|`,
|
|
9140
|
+
...s.internal.topByInlinks.map((p) => `| ${p.inlinks} | ${p.outlinksInternal}/${p.outlinksExternal} | ${p.url} |`),
|
|
9141
|
+
``,
|
|
9142
|
+
`### Top 20 external domains by links`,
|
|
9143
|
+
`| links | nofollow | from pages | domain |`,
|
|
9144
|
+
`|---|---|---|---|`,
|
|
9145
|
+
...s.external.topDomains.map((d) => `| ${d.links} | ${d.nofollow} | ${d.pages} | ${d.domain} |`)
|
|
9146
|
+
];
|
|
9147
|
+
return lines.join("\n");
|
|
9148
|
+
}
|
|
9149
|
+
var init_seo_link_report = __esm({
|
|
9150
|
+
"src/api/seo-link-report.ts"() {
|
|
9151
|
+
"use strict";
|
|
9152
|
+
}
|
|
9153
|
+
});
|
|
9154
|
+
|
|
9057
9155
|
// src/api/seo-issues.ts
|
|
9058
9156
|
function dupes(pages, key) {
|
|
9059
9157
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -9722,6 +9820,7 @@ function chunk(items, size) {
|
|
|
9722
9820
|
function buildArtifacts(job, pages, branding, imageAudit) {
|
|
9723
9821
|
const { edges, metrics } = buildLinkGraph(pages, job.startUrl);
|
|
9724
9822
|
const issues = computeIssues(pages, metrics);
|
|
9823
|
+
const linkReport = buildLinkReport(edges, [...metrics.values()], job.startUrl);
|
|
9725
9824
|
let reportMd = renderIssueReport(job.startUrl, pages, issues, metrics);
|
|
9726
9825
|
if (imageAudit) reportMd += `
|
|
9727
9826
|
|
|
@@ -9737,7 +9836,10 @@ ${renderImageSection(imageAudit)}`;
|
|
|
9737
9836
|
{ name: "issues.json", body: JSON.stringify(issues, null, 2), type: "application/json" },
|
|
9738
9837
|
{ name: "pages.jsonl", body: jsonl(pageRows), type: "application/x-ndjson" },
|
|
9739
9838
|
{ name: "links.jsonl", body: jsonl(edges), type: "application/x-ndjson" },
|
|
9740
|
-
{ name: "link-metrics.jsonl", body: jsonl([...metrics.values()]), type: "application/x-ndjson" }
|
|
9839
|
+
{ name: "link-metrics.jsonl", body: jsonl([...metrics.values()]), type: "application/x-ndjson" },
|
|
9840
|
+
{ name: "link-report.md", body: renderLinkReport(linkReport), type: "text/markdown" },
|
|
9841
|
+
{ name: "links-summary.json", body: JSON.stringify(linkReport.summary, null, 2), type: "application/json" },
|
|
9842
|
+
{ name: "external-domains.json", body: JSON.stringify(linkReport.externalDomains, null, 2), type: "application/json" }
|
|
9741
9843
|
];
|
|
9742
9844
|
if (imageAudit) {
|
|
9743
9845
|
files.push({ name: "images.jsonl", body: jsonl(imageAudit.rows), type: "application/x-ndjson" });
|
|
@@ -9753,6 +9855,7 @@ var init_site_extract = __esm({
|
|
|
9753
9855
|
init_client();
|
|
9754
9856
|
init_site_extractor();
|
|
9755
9857
|
init_seo_link_graph();
|
|
9858
|
+
init_seo_link_report();
|
|
9756
9859
|
init_seo_issues();
|
|
9757
9860
|
init_image_audit();
|
|
9758
9861
|
init_blob_store();
|
|
@@ -16231,6 +16334,9 @@ function saveBulkSite(siteUrl, pages, seo, imageAudit) {
|
|
|
16231
16334
|
`- \`pages.jsonl\` \u2014 per-page SEO fields, one JSON object per page`,
|
|
16232
16335
|
`- \`links.jsonl\` \u2014 internal/external link edges (anchor, rel, target status)`,
|
|
16233
16336
|
`- \`link-metrics.jsonl\` \u2014 inlinks, crawl depth, orphan flags per page`,
|
|
16337
|
+
`- \`link-report.md\` \u2014 link analysis: top internal pages by inlinks, top external domains, distribution`,
|
|
16338
|
+
`- \`links-summary.json\` \u2014 link totals + top-20 internal pages + top-20 external domains`,
|
|
16339
|
+
`- \`external-domains.json\` \u2014 every external domain linked to, ordered by link count`,
|
|
16234
16340
|
...imageAudit ? [
|
|
16235
16341
|
`- \`images.jsonl\` \u2014 every image with byte size, format, over-100KB and legacy-format flags`,
|
|
16236
16342
|
`- \`images-summary.json\` \u2014 image totals (count, total weight, over-100KB, legacy)`
|
|
@@ -16266,7 +16372,13 @@ ${indexRows.join("\n")}`
|
|
|
16266
16372
|
(0, import_node_fs5.writeFileSync)(reportFile, seo.reportMd + (imageAudit ? `
|
|
16267
16373
|
|
|
16268
16374
|
${renderImageSection(imageAudit)}` : ""), "utf8");
|
|
16269
|
-
|
|
16375
|
+
const linkReportFile = (0, import_node_path7.join)(dir, "link-report.md");
|
|
16376
|
+
const linksSummaryFile = (0, import_node_path7.join)(dir, "links-summary.json");
|
|
16377
|
+
const externalDomainsFile = (0, import_node_path7.join)(dir, "external-domains.json");
|
|
16378
|
+
(0, import_node_fs5.writeFileSync)(linkReportFile, renderLinkReport(seo.linkReport), "utf8");
|
|
16379
|
+
(0, import_node_fs5.writeFileSync)(linksSummaryFile, JSON.stringify(seo.linkReport.summary, null, 2), "utf8");
|
|
16380
|
+
(0, import_node_fs5.writeFileSync)(externalDomainsFile, JSON.stringify(seo.linkReport.externalDomains, null, 2), "utf8");
|
|
16381
|
+
seoFiles = [pagesJsonl, linksJsonl, metricsJsonl, issuesFile, reportFile, linkReportFile, linksSummaryFile, externalDomainsFile];
|
|
16270
16382
|
if (imageAudit) {
|
|
16271
16383
|
const imagesJsonl = (0, import_node_path7.join)(dir, "images.jsonl");
|
|
16272
16384
|
const imagesSummary = (0, import_node_path7.join)(dir, "images-summary.json");
|
|
@@ -16718,6 +16830,7 @@ function buildSeoExport(siteUrl, pages, branding) {
|
|
|
16718
16830
|
metrics: [...metrics.values()],
|
|
16719
16831
|
issues,
|
|
16720
16832
|
reportMd: renderIssueReport(siteUrl, pages, issues, metrics),
|
|
16833
|
+
linkReport: buildLinkReport(edges, [...metrics.values()], siteUrl),
|
|
16721
16834
|
branding: branding ?? void 0
|
|
16722
16835
|
};
|
|
16723
16836
|
}
|
|
@@ -16826,6 +16939,9 @@ async function formatAuditSite(raw, input) {
|
|
|
16826
16939
|
const topIssues = Object.entries(seo.issues).sort((a, b) => b[1].count - a[1].count).slice(0, 8).map(([k, v]) => `- \`${k}\` \u2014 ${v.count}`).join("\n");
|
|
16827
16940
|
const img = imageAudit.summary;
|
|
16828
16941
|
const imgLine = `${img.unique} images \xB7 ${img.totalSize} total \xB7 ${img.over100kb} over 100 KB \xB7 ${img.legacyFormat} legacy format`;
|
|
16942
|
+
const lr = seo.linkReport.summary;
|
|
16943
|
+
const topExt = lr.external.topDomains.slice(0, 3).map((dm) => `${dm.domain} (${dm.links})`).join(", ") || "\u2014";
|
|
16944
|
+
const linkLine = `${lr.internal.totalLinks} internal / ${lr.external.totalLinks} external links \xB7 ${lr.internal.orphans} orphans \xB7 ${lr.internal.brokenInternal} broken \xB7 avg ${lr.internal.avgInlinks} inlinks/page \xB7 top external: ${topExt}`;
|
|
16829
16945
|
const durationLine = `**${pages.length} pages** \xB7 ${((d.durationMs ?? 0) / 1e3).toFixed(1)}s`;
|
|
16830
16946
|
const structuredContent = {
|
|
16831
16947
|
url: input.url,
|
|
@@ -16833,7 +16949,8 @@ async function formatAuditSite(raw, input) {
|
|
|
16833
16949
|
durationMs: d.durationMs ?? 0,
|
|
16834
16950
|
bulkFolder: bulk?.dir ?? null,
|
|
16835
16951
|
issues: Object.fromEntries(Object.entries(seo.issues).map(([k, v]) => [k, v.count])),
|
|
16836
|
-
images: { unique: img.unique, totalBytes: img.totalBytes, over100kb: img.over100kb, legacyFormat: img.legacyFormat }
|
|
16952
|
+
images: { unique: img.unique, totalBytes: img.totalBytes, over100kb: img.over100kb, legacyFormat: img.legacyFormat },
|
|
16953
|
+
links: { internal: lr.internal.totalLinks, external: lr.external.totalLinks, orphans: lr.internal.orphans, brokenInternal: lr.internal.brokenInternal, externalDomains: lr.external.uniqueDomains }
|
|
16837
16954
|
};
|
|
16838
16955
|
const location2 = bulk ? `
|
|
16839
16956
|
## \u{1F4C1} Technical audit saved
|
|
@@ -16852,6 +16969,9 @@ Report saving is disabled in this environment. Enable \`MCP_SCRAPER_SAVE_REPORTS
|
|
|
16852
16969
|
## Top issues
|
|
16853
16970
|
${topIssues || "_none found_"}`,
|
|
16854
16971
|
`
|
|
16972
|
+
## Links
|
|
16973
|
+
${linkLine}`,
|
|
16974
|
+
`
|
|
16855
16975
|
## Images
|
|
16856
16976
|
${imgLine}`
|
|
16857
16977
|
].join("\n");
|
|
@@ -18041,6 +18161,7 @@ var init_mcp_response_formatter = __esm({
|
|
|
18041
18161
|
init_errors();
|
|
18042
18162
|
init_workflow_catalog();
|
|
18043
18163
|
init_seo_link_graph();
|
|
18164
|
+
init_seo_link_report();
|
|
18044
18165
|
init_seo_issues();
|
|
18045
18166
|
init_image_audit();
|
|
18046
18167
|
reportSavingEnabled = true;
|
|
@@ -23577,7 +23698,7 @@ var PACKAGE_VERSION;
|
|
|
23577
23698
|
var init_version = __esm({
|
|
23578
23699
|
"src/version.ts"() {
|
|
23579
23700
|
"use strict";
|
|
23580
|
-
PACKAGE_VERSION = "0.3.
|
|
23701
|
+
PACKAGE_VERSION = "0.3.17";
|
|
23581
23702
|
}
|
|
23582
23703
|
});
|
|
23583
23704
|
|
|
@@ -23969,6 +24090,13 @@ var init_mcp_tool_schemas = __esm({
|
|
|
23969
24090
|
totalBytes: import_zod27.z.number().min(0),
|
|
23970
24091
|
over100kb: import_zod27.z.number().int().min(0),
|
|
23971
24092
|
legacyFormat: import_zod27.z.number().int().min(0)
|
|
24093
|
+
}),
|
|
24094
|
+
links: import_zod27.z.object({
|
|
24095
|
+
internal: import_zod27.z.number().int().min(0),
|
|
24096
|
+
external: import_zod27.z.number().int().min(0),
|
|
24097
|
+
orphans: import_zod27.z.number().int().min(0),
|
|
24098
|
+
brokenInternal: import_zod27.z.number().int().min(0),
|
|
24099
|
+
externalDomains: import_zod27.z.number().int().min(0)
|
|
23972
24100
|
})
|
|
23973
24101
|
};
|
|
23974
24102
|
MapsPlaceIntelOutputSchema = {
|
|
@@ -24828,7 +24956,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
24828
24956
|
}, async (input) => formatExtractSite(await executor.extractSite(input), input));
|
|
24829
24957
|
server.registerTool("audit_site", {
|
|
24830
24958
|
title: "Technical SEO Audit",
|
|
24831
|
-
description: withReportNote("Run a full technical SEO audit (Screaming-Frog-style) on a public website. Crawls up to maxPages and produces a complete on-page + crawl analysis: per-page title/meta lengths, heading breakdown, indexability and canonical status, schema, an internal link graph
|
|
24959
|
+
description: withReportNote("Run a full technical SEO audit (Screaming-Frog-style) on a public website. Crawls up to maxPages and produces a complete on-page + crawl analysis: per-page title/meta lengths, heading breakdown, indexability and canonical status, schema, an internal link graph (inlinks/orphans/crawl-depth), a link analysis (top internal pages by inlinks, top external domains by link count, broken/orphan counts), an issues report, and an image audit (byte size, format, over-100KB and legacy-format flags). Writes everything to a local folder (report.md, issues.json, pages.jsonl, links.jsonl, link-metrics.jsonl, link-report.md, links-summary.json, external-domains.json, images.jsonl) plus per-page content, and returns a headline summary plus the folder path. Use this when the user wants an audit or analysis; use extract_site when they just want page content."),
|
|
24832
24960
|
inputSchema: AuditSiteInputSchema,
|
|
24833
24961
|
outputSchema: AuditSiteOutputSchema,
|
|
24834
24962
|
annotations: liveWebToolAnnotations("Technical SEO Audit")
|