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
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HttpMcpToolExecutor,
|
|
4
4
|
buildPaaExtractorMcpServer
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-N33YEY6W.js";
|
|
6
6
|
import "../chunk-M2S27J6Z.js";
|
|
7
7
|
import "../chunk-WN7PBKMV.js";
|
|
8
8
|
import "../chunk-LFATOGDF.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-PVWWY5NV.js";
|
|
10
10
|
|
|
11
11
|
// bin/mcp-stdio-server.ts
|
|
12
12
|
import { readFileSync } from "fs";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "./chunk-LFATOGDF.js";
|
|
12
12
|
import {
|
|
13
13
|
PACKAGE_VERSION
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-PVWWY5NV.js";
|
|
15
15
|
|
|
16
16
|
// src/harvest-timeout.ts
|
|
17
17
|
var VERCEL_FUNCTION_MAX_MS = 3e5;
|
|
@@ -256,6 +256,99 @@ function buildLinkGraph(pages, startUrl) {
|
|
|
256
256
|
return { edges, metrics };
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
// src/api/seo-link-report.ts
|
|
260
|
+
function registrableDomain(host) {
|
|
261
|
+
const h = host.replace(/^www\./, "").toLowerCase();
|
|
262
|
+
const parts = h.split(".");
|
|
263
|
+
return parts.length <= 2 ? h : parts.slice(-2).join(".");
|
|
264
|
+
}
|
|
265
|
+
function buildLinkReport(edges, metrics, siteUrl) {
|
|
266
|
+
let siteReg = "";
|
|
267
|
+
try {
|
|
268
|
+
siteReg = registrableDomain(new URL(siteUrl).hostname);
|
|
269
|
+
} catch {
|
|
270
|
+
siteReg = "";
|
|
271
|
+
}
|
|
272
|
+
const internalEdges = edges.filter((e) => e.internal);
|
|
273
|
+
const pages = metrics.length;
|
|
274
|
+
const orphans = metrics.filter((m) => m.orphan).length;
|
|
275
|
+
const brokenInternal = internalEdges.filter((e) => e.targetStatus != null && e.targetStatus >= 400).length;
|
|
276
|
+
const sumInlinks = metrics.reduce((a, m) => a + m.inlinks, 0);
|
|
277
|
+
const sumOutlinks = metrics.reduce((a, m) => a + m.outlinksInternal + m.outlinksExternal, 0);
|
|
278
|
+
const distribution = { zero: 0, oneToTwo: 0, threeToTen: 0, elevenPlus: 0 };
|
|
279
|
+
for (const m of metrics) {
|
|
280
|
+
if (m.inlinks === 0) distribution.zero++;
|
|
281
|
+
else if (m.inlinks <= 2) distribution.oneToTwo++;
|
|
282
|
+
else if (m.inlinks <= 10) distribution.threeToTen++;
|
|
283
|
+
else distribution.elevenPlus++;
|
|
284
|
+
}
|
|
285
|
+
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 }));
|
|
286
|
+
const domMap = /* @__PURE__ */ new Map();
|
|
287
|
+
let externalTotal = 0;
|
|
288
|
+
for (const e of edges) {
|
|
289
|
+
let domain;
|
|
290
|
+
try {
|
|
291
|
+
domain = registrableDomain(new URL(e.to).hostname);
|
|
292
|
+
} catch {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (!domain || domain === siteReg) continue;
|
|
296
|
+
externalTotal++;
|
|
297
|
+
const d = domMap.get(domain) ?? { links: 0, nofollow: 0, pages: /* @__PURE__ */ new Set() };
|
|
298
|
+
d.links++;
|
|
299
|
+
if (e.nofollow) d.nofollow++;
|
|
300
|
+
d.pages.add(e.from);
|
|
301
|
+
domMap.set(domain, d);
|
|
302
|
+
}
|
|
303
|
+
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);
|
|
304
|
+
const round = (n) => Math.round(n * 10) / 10;
|
|
305
|
+
return {
|
|
306
|
+
summary: {
|
|
307
|
+
internal: {
|
|
308
|
+
totalLinks: internalEdges.length,
|
|
309
|
+
pages,
|
|
310
|
+
orphans,
|
|
311
|
+
brokenInternal,
|
|
312
|
+
avgInlinks: pages ? round(sumInlinks / pages) : 0,
|
|
313
|
+
avgOutlinks: pages ? round(sumOutlinks / pages) : 0,
|
|
314
|
+
distribution,
|
|
315
|
+
topByInlinks
|
|
316
|
+
},
|
|
317
|
+
external: {
|
|
318
|
+
totalLinks: externalTotal,
|
|
319
|
+
uniqueDomains: externalDomains.length,
|
|
320
|
+
topDomains: externalDomains.slice(0, 20)
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
externalDomains
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function renderLinkReport(r) {
|
|
327
|
+
const s = r.summary;
|
|
328
|
+
const lines = [
|
|
329
|
+
`## Link analysis`,
|
|
330
|
+
`**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`,
|
|
331
|
+
`**External:** ${s.external.totalLinks} links to ${s.external.uniqueDomains} domains`,
|
|
332
|
+
``,
|
|
333
|
+
`### Inlink distribution`,
|
|
334
|
+
`- 0 inlinks (orphans): ${s.internal.distribution.zero}`,
|
|
335
|
+
`- 1\u20132: ${s.internal.distribution.oneToTwo}`,
|
|
336
|
+
`- 3\u201310: ${s.internal.distribution.threeToTen}`,
|
|
337
|
+
`- 11+: ${s.internal.distribution.elevenPlus}`,
|
|
338
|
+
``,
|
|
339
|
+
`### Top 20 internal pages by inlinks`,
|
|
340
|
+
`| inlinks | out (int/ext) | URL |`,
|
|
341
|
+
`|---|---|---|`,
|
|
342
|
+
...s.internal.topByInlinks.map((p) => `| ${p.inlinks} | ${p.outlinksInternal}/${p.outlinksExternal} | ${p.url} |`),
|
|
343
|
+
``,
|
|
344
|
+
`### Top 20 external domains by links`,
|
|
345
|
+
`| links | nofollow | from pages | domain |`,
|
|
346
|
+
`|---|---|---|---|`,
|
|
347
|
+
...s.external.topDomains.map((d) => `| ${d.links} | ${d.nofollow} | ${d.pages} | ${d.domain} |`)
|
|
348
|
+
];
|
|
349
|
+
return lines.join("\n");
|
|
350
|
+
}
|
|
351
|
+
|
|
259
352
|
// src/api/seo-issues.ts
|
|
260
353
|
var THIN_WORDS = 200;
|
|
261
354
|
var TITLE_MAX = 60;
|
|
@@ -564,6 +657,9 @@ function saveBulkSite(siteUrl, pages, seo, imageAudit) {
|
|
|
564
657
|
`- \`pages.jsonl\` \u2014 per-page SEO fields, one JSON object per page`,
|
|
565
658
|
`- \`links.jsonl\` \u2014 internal/external link edges (anchor, rel, target status)`,
|
|
566
659
|
`- \`link-metrics.jsonl\` \u2014 inlinks, crawl depth, orphan flags per page`,
|
|
660
|
+
`- \`link-report.md\` \u2014 link analysis: top internal pages by inlinks, top external domains, distribution`,
|
|
661
|
+
`- \`links-summary.json\` \u2014 link totals + top-20 internal pages + top-20 external domains`,
|
|
662
|
+
`- \`external-domains.json\` \u2014 every external domain linked to, ordered by link count`,
|
|
567
663
|
...imageAudit ? [
|
|
568
664
|
`- \`images.jsonl\` \u2014 every image with byte size, format, over-100KB and legacy-format flags`,
|
|
569
665
|
`- \`images-summary.json\` \u2014 image totals (count, total weight, over-100KB, legacy)`
|
|
@@ -599,7 +695,13 @@ ${indexRows.join("\n")}`
|
|
|
599
695
|
writeFileSync(reportFile, seo.reportMd + (imageAudit ? `
|
|
600
696
|
|
|
601
697
|
${renderImageSection(imageAudit)}` : ""), "utf8");
|
|
602
|
-
|
|
698
|
+
const linkReportFile = join(dir, "link-report.md");
|
|
699
|
+
const linksSummaryFile = join(dir, "links-summary.json");
|
|
700
|
+
const externalDomainsFile = join(dir, "external-domains.json");
|
|
701
|
+
writeFileSync(linkReportFile, renderLinkReport(seo.linkReport), "utf8");
|
|
702
|
+
writeFileSync(linksSummaryFile, JSON.stringify(seo.linkReport.summary, null, 2), "utf8");
|
|
703
|
+
writeFileSync(externalDomainsFile, JSON.stringify(seo.linkReport.externalDomains, null, 2), "utf8");
|
|
704
|
+
seoFiles = [pagesJsonl, linksJsonl, metricsJsonl, issuesFile, reportFile, linkReportFile, linksSummaryFile, externalDomainsFile];
|
|
603
705
|
if (imageAudit) {
|
|
604
706
|
const imagesJsonl = join(dir, "images.jsonl");
|
|
605
707
|
const imagesSummary = join(dir, "images-summary.json");
|
|
@@ -1051,6 +1153,7 @@ function buildSeoExport(siteUrl, pages, branding) {
|
|
|
1051
1153
|
metrics: [...metrics.values()],
|
|
1052
1154
|
issues,
|
|
1053
1155
|
reportMd: renderIssueReport(siteUrl, pages, issues, metrics),
|
|
1156
|
+
linkReport: buildLinkReport(edges, [...metrics.values()], siteUrl),
|
|
1054
1157
|
branding: branding ?? void 0
|
|
1055
1158
|
};
|
|
1056
1159
|
}
|
|
@@ -1159,6 +1262,9 @@ async function formatAuditSite(raw, input) {
|
|
|
1159
1262
|
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");
|
|
1160
1263
|
const img = imageAudit.summary;
|
|
1161
1264
|
const imgLine = `${img.unique} images \xB7 ${img.totalSize} total \xB7 ${img.over100kb} over 100 KB \xB7 ${img.legacyFormat} legacy format`;
|
|
1265
|
+
const lr = seo.linkReport.summary;
|
|
1266
|
+
const topExt = lr.external.topDomains.slice(0, 3).map((dm) => `${dm.domain} (${dm.links})`).join(", ") || "\u2014";
|
|
1267
|
+
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}`;
|
|
1162
1268
|
const durationLine = `**${pages.length} pages** \xB7 ${((d.durationMs ?? 0) / 1e3).toFixed(1)}s`;
|
|
1163
1269
|
const structuredContent = {
|
|
1164
1270
|
url: input.url,
|
|
@@ -1166,7 +1272,8 @@ async function formatAuditSite(raw, input) {
|
|
|
1166
1272
|
durationMs: d.durationMs ?? 0,
|
|
1167
1273
|
bulkFolder: bulk?.dir ?? null,
|
|
1168
1274
|
issues: Object.fromEntries(Object.entries(seo.issues).map(([k, v]) => [k, v.count])),
|
|
1169
|
-
images: { unique: img.unique, totalBytes: img.totalBytes, over100kb: img.over100kb, legacyFormat: img.legacyFormat }
|
|
1275
|
+
images: { unique: img.unique, totalBytes: img.totalBytes, over100kb: img.over100kb, legacyFormat: img.legacyFormat },
|
|
1276
|
+
links: { internal: lr.internal.totalLinks, external: lr.external.totalLinks, orphans: lr.internal.orphans, brokenInternal: lr.internal.brokenInternal, externalDomains: lr.external.uniqueDomains }
|
|
1170
1277
|
};
|
|
1171
1278
|
const location = bulk ? `
|
|
1172
1279
|
## \u{1F4C1} Technical audit saved
|
|
@@ -1185,6 +1292,9 @@ Report saving is disabled in this environment. Enable \`MCP_SCRAPER_SAVE_REPORTS
|
|
|
1185
1292
|
## Top issues
|
|
1186
1293
|
${topIssues || "_none found_"}`,
|
|
1187
1294
|
`
|
|
1295
|
+
## Links
|
|
1296
|
+
${linkLine}`,
|
|
1297
|
+
`
|
|
1188
1298
|
## Images
|
|
1189
1299
|
${imgLine}`
|
|
1190
1300
|
].join("\n");
|
|
@@ -2715,6 +2825,13 @@ var AuditSiteOutputSchema = {
|
|
|
2715
2825
|
totalBytes: z.number().min(0),
|
|
2716
2826
|
over100kb: z.number().int().min(0),
|
|
2717
2827
|
legacyFormat: z.number().int().min(0)
|
|
2828
|
+
}),
|
|
2829
|
+
links: z.object({
|
|
2830
|
+
internal: z.number().int().min(0),
|
|
2831
|
+
external: z.number().int().min(0),
|
|
2832
|
+
orphans: z.number().int().min(0),
|
|
2833
|
+
brokenInternal: z.number().int().min(0),
|
|
2834
|
+
externalDomains: z.number().int().min(0)
|
|
2718
2835
|
})
|
|
2719
2836
|
};
|
|
2720
2837
|
var MapsPlaceIntelOutputSchema = {
|
|
@@ -3566,7 +3683,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
3566
3683
|
}, async (input) => formatExtractSite(await executor.extractSite(input), input));
|
|
3567
3684
|
server.registerTool("audit_site", {
|
|
3568
3685
|
title: "Technical SEO Audit",
|
|
3569
|
-
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
|
|
3686
|
+
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."),
|
|
3570
3687
|
inputSchema: AuditSiteInputSchema,
|
|
3571
3688
|
outputSchema: AuditSiteOutputSchema,
|
|
3572
3689
|
annotations: liveWebToolAnnotations("Technical SEO Audit")
|
|
@@ -3978,6 +4095,8 @@ var HttpMcpToolExecutor = class {
|
|
|
3978
4095
|
export {
|
|
3979
4096
|
harvestTimeoutBudget,
|
|
3980
4097
|
buildLinkGraph,
|
|
4098
|
+
buildLinkReport,
|
|
4099
|
+
renderLinkReport,
|
|
3981
4100
|
computeIssues,
|
|
3982
4101
|
renderIssueReport,
|
|
3983
4102
|
auditImages,
|
|
@@ -3996,4 +4115,4 @@ export {
|
|
|
3996
4115
|
registerPaaExtractorMcpTools,
|
|
3997
4116
|
HttpMcpToolExecutor
|
|
3998
4117
|
};
|
|
3999
|
-
//# sourceMappingURL=chunk-
|
|
4118
|
+
//# sourceMappingURL=chunk-N33YEY6W.js.map
|