@automatelab/ai-seo-mcp 0.3.0 → 0.4.1
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 +37 -18
- package/dist/cli/audit-cli.d.ts +3 -0
- package/dist/cli/audit-cli.d.ts.map +1 -0
- package/dist/cli/audit-cli.js +136 -0
- package/dist/cli/audit-cli.js.map +1 -0
- package/dist/index.js +710 -227
- package/dist/index.js.map +1 -1
- package/dist/lib/cache.d.ts +12 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/cache.js +51 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/config.js +1 -1
- package/dist/lib/fetch.d.ts +5 -0
- package/dist/lib/fetch.d.ts.map +1 -1
- package/dist/lib/fetch.js +42 -1
- package/dist/lib/fetch.js.map +1 -1
- package/dist/lib/headless.d.ts +13 -0
- package/dist/lib/headless.d.ts.map +1 -0
- package/dist/lib/headless.js +73 -0
- package/dist/lib/headless.js.map +1 -0
- package/dist/lib/scorecard-html.js +94 -94
- package/dist/output-schemas.d.ts +658 -0
- package/dist/output-schemas.d.ts.map +1 -0
- package/dist/output-schemas.js +265 -0
- package/dist/output-schemas.js.map +1 -0
- package/dist/tools/audit-page.d.ts +11 -0
- package/dist/tools/audit-page.d.ts.map +1 -1
- package/dist/tools/audit-page.js +49 -2
- package/dist/tools/audit-page.js.map +1 -1
- package/dist/tools/audit-schema.d.ts +2 -1
- package/dist/tools/audit-schema.d.ts.map +1 -1
- package/dist/tools/audit-schema.js +2 -1
- package/dist/tools/audit-schema.js.map +1 -1
- package/dist/tools/audit-site.d.ts +41 -0
- package/dist/tools/audit-site.d.ts.map +1 -0
- package/dist/tools/audit-site.js +120 -0
- package/dist/tools/audit-site.js.map +1 -0
- package/dist/tools/audit-sitemap.d.ts +66 -0
- package/dist/tools/audit-sitemap.d.ts.map +1 -0
- package/dist/tools/audit-sitemap.js +264 -0
- package/dist/tools/audit-sitemap.js.map +1 -0
- package/dist/tools/check-technical.d.ts +2 -1
- package/dist/tools/check-technical.d.ts.map +1 -1
- package/dist/tools/check-technical.js +2 -1
- package/dist/tools/check-technical.js.map +1 -1
- package/dist/tools/diff-pages.js +2 -2
- package/dist/tools/diff-pages.js.map +1 -1
- package/dist/tools/extract-entities.d.ts +8 -1
- package/dist/tools/extract-entities.d.ts.map +1 -1
- package/dist/tools/extract-entities.js +110 -3
- package/dist/tools/extract-entities.js.map +1 -1
- package/dist/tools/save-audit-report.d.ts +22 -0
- package/dist/tools/save-audit-report.d.ts.map +1 -0
- package/dist/tools/save-audit-report.js +133 -0
- package/dist/tools/save-audit-report.js.map +1 -0
- package/dist/tools/test-citation.d.ts +51 -0
- package/dist/tools/test-citation.d.ts.map +1 -0
- package/dist/tools/test-citation.js +159 -0
- package/dist/tools/test-citation.js.map +1 -0
- package/package.json +8 -1
- package/smithery.yaml +13 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Tool: extract_entities
|
|
2
2
|
// Extracts named entities, linked concepts, and sameAs graph nodes from page content.
|
|
3
|
+
// Primary path: MCP sampling (host LLM does NER). Fallback: regex heuristic.
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { politeFetch } from "../lib/fetch.js";
|
|
5
6
|
import { parseBody } from "../lib/html.js";
|
|
@@ -10,30 +11,135 @@ export const extractEntitiesInputSchema = z
|
|
|
10
11
|
url: z.string().url().optional(),
|
|
11
12
|
text: z.string().optional(),
|
|
12
13
|
respect_robots: z.boolean().optional().default(true),
|
|
14
|
+
render: z.enum(["static", "headless"]).optional().default("static"),
|
|
13
15
|
})
|
|
14
16
|
.refine((d) => d.url !== undefined || d.text !== undefined, {
|
|
15
17
|
message: "One of url or text is required",
|
|
16
18
|
});
|
|
17
|
-
|
|
19
|
+
const SAMPLING_SYSTEM_PROMPT = `You are an entity extractor for AI-citation optimization. Identify named entities (organizations, people, products, technologies, locations) in the provided content.
|
|
20
|
+
|
|
21
|
+
For each entity:
|
|
22
|
+
- "name": the canonical entity name as it appears
|
|
23
|
+
- "type": one of "Organization", "Person", "Product", "Technology", "Location", "Event", "Other"
|
|
24
|
+
- "same_as": array of URLs found in the page that authoritatively identify this entity (Wikipedia, Wikidata, LinkedIn, official brand site, etc.). Empty array if none found in the provided URL list.
|
|
25
|
+
|
|
26
|
+
Skip generic terms, common words, and section headings. Cap at ~50 entities.
|
|
27
|
+
|
|
28
|
+
Return JSON only, no prose. Shape: { "entities": [{ "name": "...", "type": "...", "same_as": ["..."] }] }`;
|
|
29
|
+
async function trySampling(bodyText, externalLinks, server) {
|
|
30
|
+
const userMessage = `Page content (first 8000 chars):
|
|
31
|
+
---
|
|
32
|
+
${bodyText.substring(0, 8000)}
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
External links found on the page (use these to populate same_as where they authoritatively identify an entity):
|
|
36
|
+
${externalLinks.slice(0, 40).map((l) => `- ${l}`).join("\n") || "(none)"}
|
|
37
|
+
|
|
38
|
+
Extract entities and return JSON.`;
|
|
39
|
+
try {
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
41
|
+
// @ts-ignore - sampling API availability varies by client; not typed in all SDK versions
|
|
42
|
+
const samplingResult = await server.server.request({
|
|
43
|
+
method: "sampling/createMessage",
|
|
44
|
+
params: {
|
|
45
|
+
messages: [{ role: "user", content: { type: "text", text: userMessage } }],
|
|
46
|
+
systemPrompt: SAMPLING_SYSTEM_PROMPT,
|
|
47
|
+
maxTokens: 2048,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
+
{});
|
|
52
|
+
const text = samplingResult?.content?.text ?? samplingResult?.content?.[0]?.text ?? "";
|
|
53
|
+
if (!text)
|
|
54
|
+
return null;
|
|
55
|
+
const jsonMatch = text.match(/```json\n([\s\S]+?)\n```/) ?? text.match(/\{[\s\S]+\}/);
|
|
56
|
+
const jsonStr = jsonMatch ? (jsonMatch[1] ?? jsonMatch[0]) : text;
|
|
57
|
+
const parsed = JSON.parse(jsonStr);
|
|
58
|
+
if (!parsed.entities || !Array.isArray(parsed.entities))
|
|
59
|
+
return null;
|
|
60
|
+
return parsed.entities;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function countOccurrences(text, term) {
|
|
67
|
+
if (!term)
|
|
68
|
+
return 0;
|
|
69
|
+
const lower = text.toLowerCase();
|
|
70
|
+
const termLower = term.toLowerCase();
|
|
71
|
+
let count = 0;
|
|
72
|
+
let idx = 0;
|
|
73
|
+
while ((idx = lower.indexOf(termLower, idx)) !== -1) {
|
|
74
|
+
count++;
|
|
75
|
+
idx += termLower.length;
|
|
76
|
+
}
|
|
77
|
+
return count;
|
|
78
|
+
}
|
|
79
|
+
export async function extractEntities(input, hostDelays, robotsCache, server) {
|
|
18
80
|
const findings = [];
|
|
19
81
|
let bodyText = "";
|
|
20
82
|
let jsonLdBlocks = [];
|
|
83
|
+
let externalLinks = [];
|
|
21
84
|
if (input.url) {
|
|
22
85
|
const result = await politeFetch(input.url, {
|
|
23
86
|
respectRobots: input.respect_robots,
|
|
24
87
|
hostDelays,
|
|
25
88
|
robotsCache,
|
|
89
|
+
renderMode: input.render,
|
|
26
90
|
});
|
|
27
91
|
const pageData = parseBody(result.body, input.url);
|
|
28
92
|
bodyText = pageData.bodyText;
|
|
93
|
+
externalLinks = pageData.externalLinks ?? [];
|
|
29
94
|
jsonLdBlocks = parseJsonLd(result.body);
|
|
30
95
|
}
|
|
31
96
|
else {
|
|
32
97
|
bodyText = input.text;
|
|
33
98
|
}
|
|
34
99
|
const jsonLdEntities = extractJsonLdEntities(jsonLdBlocks);
|
|
35
|
-
|
|
36
|
-
|
|
100
|
+
// Primary path: MCP sampling.
|
|
101
|
+
let mode = "regex_fallback";
|
|
102
|
+
let entities = [];
|
|
103
|
+
if (server) {
|
|
104
|
+
const sampled = await trySampling(bodyText, externalLinks, server);
|
|
105
|
+
if (sampled && sampled.length > 0) {
|
|
106
|
+
mode = "sampling";
|
|
107
|
+
const sampledEntities = sampled.map((e) => ({
|
|
108
|
+
name: e.name,
|
|
109
|
+
type: e.type ?? null,
|
|
110
|
+
same_as: Array.isArray(e.same_as) ? e.same_as.filter((s) => typeof s === "string") : [],
|
|
111
|
+
mention_count: countOccurrences(bodyText, e.name),
|
|
112
|
+
is_defined: false,
|
|
113
|
+
}));
|
|
114
|
+
// Merge with JSON-LD entities (JSON-LD wins on dup name, lowercase keyed).
|
|
115
|
+
const byName = new Map();
|
|
116
|
+
for (const e of sampledEntities)
|
|
117
|
+
byName.set(e.name.toLowerCase(), e);
|
|
118
|
+
for (const e of jsonLdEntities) {
|
|
119
|
+
const key = e.name.toLowerCase();
|
|
120
|
+
const existing = byName.get(key);
|
|
121
|
+
const count = countOccurrences(bodyText, e.name);
|
|
122
|
+
if (existing) {
|
|
123
|
+
// JSON-LD's sameAs wins; merge with sampled name/type.
|
|
124
|
+
byName.set(key, {
|
|
125
|
+
...existing,
|
|
126
|
+
same_as: e.same_as.length > 0 ? e.same_as : existing.same_as,
|
|
127
|
+
type: e.type ?? existing.type,
|
|
128
|
+
mention_count: count || existing.mention_count,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
byName.set(key, { ...e, mention_count: count });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
entities = Array.from(byName.values()).sort((a, b) => b.mention_count - a.mention_count);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Fallback: regex extractor.
|
|
139
|
+
if (mode === "regex_fallback") {
|
|
140
|
+
const textEntities = extractTextEntities(bodyText);
|
|
141
|
+
entities = mergeEntities(jsonLdEntities, textEntities, bodyText);
|
|
142
|
+
}
|
|
37
143
|
const entity_count = entities.length;
|
|
38
144
|
const connected_entity_count = entities.filter((e) => e.same_as.length > 0).length;
|
|
39
145
|
// Threshold: 15 connected entities = score of 100
|
|
@@ -64,6 +170,7 @@ export async function extractEntities(input, hostDelays, robotsCache) {
|
|
|
64
170
|
connected_entity_count,
|
|
65
171
|
citation_density_score,
|
|
66
172
|
findings,
|
|
173
|
+
mode,
|
|
67
174
|
};
|
|
68
175
|
}
|
|
69
176
|
//# sourceMappingURL=extract-entities.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-entities.js","sourceRoot":"","sources":["../../src/tools/extract-entities.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,sFAAsF;
|
|
1
|
+
{"version":3,"file":"extract-entities.js","sourceRoot":"","sources":["../../src/tools/extract-entities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const saveAuditReportInputSchema: z.ZodObject<{
|
|
3
|
+
audit_result: z.ZodUnknown;
|
|
4
|
+
path: z.ZodString;
|
|
5
|
+
overwrite: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
path: string;
|
|
8
|
+
overwrite: boolean;
|
|
9
|
+
audit_result?: unknown;
|
|
10
|
+
}, {
|
|
11
|
+
path: string;
|
|
12
|
+
audit_result?: unknown;
|
|
13
|
+
overwrite?: boolean | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export type SaveAuditReportInput = z.infer<typeof saveAuditReportInputSchema>;
|
|
16
|
+
export interface SaveAuditReportResult {
|
|
17
|
+
saved_to: string;
|
|
18
|
+
bytes_written: number;
|
|
19
|
+
format: "audit_page" | "audit_site";
|
|
20
|
+
}
|
|
21
|
+
export declare function saveAuditReport(input: SaveAuditReportInput): Promise<SaveAuditReportResult>;
|
|
22
|
+
//# sourceMappingURL=save-audit-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save-audit-report.d.ts","sourceRoot":"","sources":["../../src/tools/save-audit-report.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,0BAA0B;;;;;;;;;;;;EAIrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,YAAY,GAAG,YAAY,CAAC;CACrC;AA8GD,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAyBjG"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Tool: save_audit_report
|
|
2
|
+
// Render an audit_page / audit_site result as Markdown and write to a workspace file.
|
|
3
|
+
// Restricted to the directory tree under MCP_WORKSPACE_ROOT (or cwd if unset).
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
6
|
+
import { dirname, isAbsolute, resolve, sep, relative } from "node:path";
|
|
7
|
+
export const saveAuditReportInputSchema = z.object({
|
|
8
|
+
audit_result: z.unknown().describe("The return value of `audit.page` or `audit.site`. Pass the structured result verbatim - the tool detects which shape it is and renders the matching Markdown report."),
|
|
9
|
+
path: z.string().min(1).describe("Target file path. May be relative to MCP_WORKSPACE_ROOT (or cwd if unset). Paths that escape the workspace root are rejected."),
|
|
10
|
+
overwrite: z.boolean().optional().default(true).describe("If true (default), overwrite an existing file. If false, the write fails when the target already exists."),
|
|
11
|
+
});
|
|
12
|
+
function severityEmoji(s) {
|
|
13
|
+
return s === "critical" ? "🔴" : s === "warning" ? "🟡" : "🔵";
|
|
14
|
+
}
|
|
15
|
+
function findingsToTable(findings) {
|
|
16
|
+
if (findings.length === 0)
|
|
17
|
+
return "_No findings._\n";
|
|
18
|
+
const rows = findings.map((f) => {
|
|
19
|
+
const impact = f.estimated_impact ? ` (impact: ${f.estimated_impact})` : "";
|
|
20
|
+
return `### ${severityEmoji(f.severity)} ${f.category} — ${f.message}\n\n` +
|
|
21
|
+
`**Where:** \`${f.where}\`${impact}\n\n` +
|
|
22
|
+
`**Fix:** ${f.fix}\n`;
|
|
23
|
+
});
|
|
24
|
+
return rows.join("\n");
|
|
25
|
+
}
|
|
26
|
+
function citationVerdict(grade) {
|
|
27
|
+
switch (grade) {
|
|
28
|
+
case "A": return "🟢 Highly citation-ready. AI assistants are likely to surface this page.";
|
|
29
|
+
case "B": return "🟢 Citation-ready with minor gaps. A few fixes will lift you into top-tier.";
|
|
30
|
+
case "C": return "🟡 Mixed signals. Several structural / authority issues are likely blocking citations.";
|
|
31
|
+
case "D": return "🟠 Low citation readiness. Multiple critical issues must be addressed.";
|
|
32
|
+
case "F": return "🔴 Not citation-ready. AI engines are likely skipping this page.";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function dimensionTable(scores) {
|
|
36
|
+
const rows = ["| Dimension | Score |", "|---|---|"];
|
|
37
|
+
for (const [k, v] of Object.entries(scores)) {
|
|
38
|
+
rows.push(`| ${k} | ${v}/100 |`);
|
|
39
|
+
}
|
|
40
|
+
return rows.join("\n");
|
|
41
|
+
}
|
|
42
|
+
function isAuditPage(x) {
|
|
43
|
+
return !!x && typeof x === "object" && "dimension_scores" in x && "grade" in x && "findings" in x;
|
|
44
|
+
}
|
|
45
|
+
function isAuditSite(x) {
|
|
46
|
+
return !!x && typeof x === "object" && "top_5_fixes" in x && "overall_grade" in x && "parts" in x;
|
|
47
|
+
}
|
|
48
|
+
function renderAuditPage(r) {
|
|
49
|
+
const sorted = [...r.findings].sort((a, b) => {
|
|
50
|
+
const rank = (s) => (s === "critical" ? 0 : s === "warning" ? 1 : 2);
|
|
51
|
+
return rank(a.severity) - rank(b.severity);
|
|
52
|
+
});
|
|
53
|
+
return [
|
|
54
|
+
`# AI-SEO audit: ${r.url}`,
|
|
55
|
+
``,
|
|
56
|
+
`> ${citationVerdict(r.grade)}`,
|
|
57
|
+
``,
|
|
58
|
+
`**Score:** ${r.score}/100 (grade ${r.grade})`,
|
|
59
|
+
`**Fetched:** ${r.fetched_at}`,
|
|
60
|
+
`**Content quality:** \`${r.content_quality}\``,
|
|
61
|
+
``,
|
|
62
|
+
`## Dimension scores`,
|
|
63
|
+
``,
|
|
64
|
+
dimensionTable(r.dimension_scores),
|
|
65
|
+
``,
|
|
66
|
+
`## Findings (${r.findings.length})`,
|
|
67
|
+
``,
|
|
68
|
+
findingsToTable(sorted),
|
|
69
|
+
``,
|
|
70
|
+
`---`,
|
|
71
|
+
``,
|
|
72
|
+
`_Generated by [@automatelab/ai-seo-mcp](https://github.com/AutomateLab-tech/ai-seo)._`,
|
|
73
|
+
``,
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
76
|
+
function renderAuditSite(r) {
|
|
77
|
+
return [
|
|
78
|
+
`# AI-SEO site audit: ${r.domain}`,
|
|
79
|
+
``,
|
|
80
|
+
`**Overall score:** ${r.overall_score}/100 (grade ${r.overall_grade})`,
|
|
81
|
+
`**Homepage:** ${r.homepage_url}`,
|
|
82
|
+
`**Fetched:** ${r.fetched_at}`,
|
|
83
|
+
``,
|
|
84
|
+
`## Top 5 fixes`,
|
|
85
|
+
``,
|
|
86
|
+
findingsToTable(r.top_5_fixes),
|
|
87
|
+
``,
|
|
88
|
+
`## Components`,
|
|
89
|
+
``,
|
|
90
|
+
`- \`audit_page\`: ${"error" in r.parts.audit_page ? `❌ ${r.parts.audit_page.error}` : `${r.parts.audit_page.score}/100`}`,
|
|
91
|
+
`- \`check_robots\`: ${"error" in r.parts.check_robots ? `❌ ${r.parts.check_robots.error}` : r.parts.check_robots.recommended_posture}`,
|
|
92
|
+
`- \`check_sitemap\`: ${"error" in r.parts.check_sitemap ? `❌ ${r.parts.check_sitemap.error}` : r.parts.check_sitemap.status}`,
|
|
93
|
+
`- \`audit_schema\`: ${"error" in r.parts.audit_schema ? `❌ ${r.parts.audit_schema.error}` : `${r.parts.audit_schema.ai_citation_readiness_score}/100`}`,
|
|
94
|
+
``,
|
|
95
|
+
`---`,
|
|
96
|
+
``,
|
|
97
|
+
`_Generated by [@automatelab/ai-seo-mcp](https://github.com/AutomateLab-tech/ai-seo)._`,
|
|
98
|
+
``,
|
|
99
|
+
].join("\n");
|
|
100
|
+
}
|
|
101
|
+
function resolveSafePath(target) {
|
|
102
|
+
const workspaceRoot = resolve(process.env["MCP_WORKSPACE_ROOT"] ?? process.cwd());
|
|
103
|
+
const absolute = isAbsolute(target) ? resolve(target) : resolve(workspaceRoot, target);
|
|
104
|
+
const rel = relative(workspaceRoot, absolute);
|
|
105
|
+
if (rel.startsWith("..") || rel.includes(`..${sep}`) || isAbsolute(rel)) {
|
|
106
|
+
throw new Error(`path "${target}" escapes MCP_WORKSPACE_ROOT (${workspaceRoot}). Set MCP_WORKSPACE_ROOT or pass a path inside it.`);
|
|
107
|
+
}
|
|
108
|
+
return absolute;
|
|
109
|
+
}
|
|
110
|
+
export async function saveAuditReport(input) {
|
|
111
|
+
let markdown;
|
|
112
|
+
let format;
|
|
113
|
+
if (isAuditSite(input.audit_result)) {
|
|
114
|
+
markdown = renderAuditSite(input.audit_result);
|
|
115
|
+
format = "audit_site";
|
|
116
|
+
}
|
|
117
|
+
else if (isAuditPage(input.audit_result)) {
|
|
118
|
+
markdown = renderAuditPage(input.audit_result);
|
|
119
|
+
format = "audit_page";
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
throw new Error("audit_result does not look like an audit_page or audit_site output (missing expected fields).");
|
|
123
|
+
}
|
|
124
|
+
const absolute = resolveSafePath(input.path);
|
|
125
|
+
await mkdir(dirname(absolute), { recursive: true });
|
|
126
|
+
await writeFile(absolute, markdown, { encoding: "utf8", flag: input.overwrite ? "w" : "wx" });
|
|
127
|
+
return {
|
|
128
|
+
saved_to: absolute,
|
|
129
|
+
bytes_written: Buffer.byteLength(markdown, "utf8"),
|
|
130
|
+
format,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=save-audit-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save-audit-report.js","sourceRoot":"","sources":["../../src/tools/save-audit-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type HostDelayMap } from "../lib/fetch.js";
|
|
3
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
export declare const testCitationInputSchema: z.ZodEffects<z.ZodObject<{
|
|
5
|
+
url: z.ZodOptional<z.ZodString>;
|
|
6
|
+
text: z.ZodOptional<z.ZodString>;
|
|
7
|
+
target_query: z.ZodString;
|
|
8
|
+
engine: z.ZodDefault<z.ZodOptional<z.ZodEnum<["chatgpt", "claude", "perplexity", "google_ai_overviews", "any"]>>>;
|
|
9
|
+
respect_robots: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
respect_robots: boolean;
|
|
12
|
+
target_query: string;
|
|
13
|
+
engine: "any" | "chatgpt" | "claude" | "perplexity" | "google_ai_overviews";
|
|
14
|
+
url?: string | undefined;
|
|
15
|
+
text?: string | undefined;
|
|
16
|
+
}, {
|
|
17
|
+
target_query: string;
|
|
18
|
+
url?: string | undefined;
|
|
19
|
+
respect_robots?: boolean | undefined;
|
|
20
|
+
text?: string | undefined;
|
|
21
|
+
engine?: "any" | "chatgpt" | "claude" | "perplexity" | "google_ai_overviews" | undefined;
|
|
22
|
+
}>, {
|
|
23
|
+
respect_robots: boolean;
|
|
24
|
+
target_query: string;
|
|
25
|
+
engine: "any" | "chatgpt" | "claude" | "perplexity" | "google_ai_overviews";
|
|
26
|
+
url?: string | undefined;
|
|
27
|
+
text?: string | undefined;
|
|
28
|
+
}, {
|
|
29
|
+
target_query: string;
|
|
30
|
+
url?: string | undefined;
|
|
31
|
+
respect_robots?: boolean | undefined;
|
|
32
|
+
text?: string | undefined;
|
|
33
|
+
engine?: "any" | "chatgpt" | "claude" | "perplexity" | "google_ai_overviews" | undefined;
|
|
34
|
+
}>;
|
|
35
|
+
export type TestCitationInput = z.infer<typeof testCitationInputSchema>;
|
|
36
|
+
export interface TestCitationResult {
|
|
37
|
+
target_query: string;
|
|
38
|
+
engine: "chatgpt" | "claude" | "perplexity" | "google_ai_overviews" | "any";
|
|
39
|
+
would_cite: boolean;
|
|
40
|
+
confidence: number;
|
|
41
|
+
citation_excerpt: string | null;
|
|
42
|
+
reasoning: string;
|
|
43
|
+
blocking_issues: string[];
|
|
44
|
+
improvements: Array<{
|
|
45
|
+
change: string;
|
|
46
|
+
estimated_impact: "high" | "medium" | "low";
|
|
47
|
+
}>;
|
|
48
|
+
mode: "sampling" | "static_heuristic";
|
|
49
|
+
}
|
|
50
|
+
export declare function testCitation(input: TestCitationInput, hostDelays?: HostDelayMap, robotsCache?: Map<string, string>, server?: McpServer): Promise<TestCitationResult>;
|
|
51
|
+
//# sourceMappingURL=test-citation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-citation.d.ts","sourceRoot":"","sources":["../../src/tools/test-citation.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAahC,CAAC;AAEL,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,YAAY,GAAG,qBAAqB,GAAG,KAAK,CAAC;IAC5E,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;KAAE,CAAC,CAAC;IACrF,IAAI,EAAE,UAAU,GAAG,kBAAkB,CAAC;CACvC;AA+FD,wBAAsB,YAAY,CAChC,KAAK,EAAE,iBAAiB,EACxB,UAAU,CAAC,EAAE,YAAY,EACzB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,MAAM,CAAC,EAAE,SAAS,GACjB,OAAO,CAAC,kBAAkB,CAAC,CA2E7B"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// Tool: test_citation
|
|
2
|
+
// Simulates "would an AI engine cite this page for this query?" via MCP sampling.
|
|
3
|
+
// Primary path: ask the host LLM to role-play an AI search engine, decide cite/no-cite,
|
|
4
|
+
// and return the excerpt it would surface. Fallback: deterministic heuristic from
|
|
5
|
+
// score_citation_worthiness + audit_page findings.
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { politeFetch } from "../lib/fetch.js";
|
|
8
|
+
import { parseBody } from "../lib/html.js";
|
|
9
|
+
import { scoreCitationWorthiness } from "./score-citation-worthiness.js";
|
|
10
|
+
export const testCitationInputSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
url: z.string().url().optional(),
|
|
13
|
+
text: z.string().optional(),
|
|
14
|
+
target_query: z.string().min(3),
|
|
15
|
+
engine: z
|
|
16
|
+
.enum(["chatgpt", "claude", "perplexity", "google_ai_overviews", "any"])
|
|
17
|
+
.optional()
|
|
18
|
+
.default("any"),
|
|
19
|
+
respect_robots: z.boolean().optional().default(true),
|
|
20
|
+
})
|
|
21
|
+
.refine((d) => d.url !== undefined || d.text !== undefined, {
|
|
22
|
+
message: "One of url or text is required",
|
|
23
|
+
});
|
|
24
|
+
const ENGINE_PERSONAS = {
|
|
25
|
+
chatgpt: "You are ChatGPT search. You prefer pages with BLUF openings, FAQPage / Article JSON-LD, named-entity density, and links to authoritative sources (Wikipedia, .gov, .edu).",
|
|
26
|
+
claude: "You are Claude Search. You favor pages with clear topical coherence, concise definitions, explicit authorship, and minimal marketing fluff. You penalize SEO boilerplate.",
|
|
27
|
+
perplexity: "You are Perplexity. You synthesize across sources and pick excerpts that are statistic-dense, citation-friendly, and clearly attributable. You favor numbered lists and comparison tables.",
|
|
28
|
+
google_ai_overviews: "You are Google's AI Overviews. You favor pages with Article / FAQ / HowTo schema, recent dateModified, E-E-A-T author signals, and direct answers to the query in the first 80 words.",
|
|
29
|
+
any: "You are a generic AI search assistant. You decide cite vs no-cite based on direct query answerability, structured data presence, entity density, and freshness.",
|
|
30
|
+
};
|
|
31
|
+
const SYSTEM_PROMPT_PREFIX = `You are simulating an AI search engine deciding whether to cite a web page in response to a user query.
|
|
32
|
+
|
|
33
|
+
Return JSON only, no prose. Shape:
|
|
34
|
+
{
|
|
35
|
+
"would_cite": boolean,
|
|
36
|
+
"confidence": 0-100 integer,
|
|
37
|
+
"citation_excerpt": string | null,
|
|
38
|
+
"reasoning": string,
|
|
39
|
+
"blocking_issues": [string, ...],
|
|
40
|
+
"improvements": [{ "change": string, "estimated_impact": "high" | "medium" | "low" }, ...]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Rules:
|
|
44
|
+
- would_cite=true ONLY if the page contains a clear, attributable answer to the query in the first ~500 words.
|
|
45
|
+
- citation_excerpt: if would_cite=true, the 20-60 word verbatim excerpt you would surface; otherwise null.
|
|
46
|
+
- confidence reflects how likely a real engine is to surface this page given the query (not your certainty about the decision).
|
|
47
|
+
- blocking_issues: 1-5 concrete reasons the page is/isn't citable. Empty array if would_cite=true and excerpt is clearly attributable.
|
|
48
|
+
- improvements: 1-5 ranked, specific edits the author should make to improve citation probability.`;
|
|
49
|
+
async function trySampling(bodyText, url, targetQuery, engine, server) {
|
|
50
|
+
const persona = ENGINE_PERSONAS[engine];
|
|
51
|
+
const userMessage = `Query: "${targetQuery}"
|
|
52
|
+
${url ? `Page URL: ${url}` : ""}
|
|
53
|
+
Page content (first 6000 chars):
|
|
54
|
+
---
|
|
55
|
+
${bodyText.substring(0, 6000)}
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
Decide cite vs no-cite. Return JSON only.`;
|
|
59
|
+
try {
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
61
|
+
// @ts-ignore - sampling API availability varies by client; not typed in all SDK versions
|
|
62
|
+
const samplingResult = await server.server.request({
|
|
63
|
+
method: "sampling/createMessage",
|
|
64
|
+
params: {
|
|
65
|
+
messages: [{ role: "user", content: { type: "text", text: userMessage } }],
|
|
66
|
+
systemPrompt: `${SYSTEM_PROMPT_PREFIX}\n\nPersona: ${persona}`,
|
|
67
|
+
maxTokens: 2048,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
+
{});
|
|
72
|
+
const text = samplingResult?.content?.text ?? samplingResult?.content?.[0]?.text ?? "";
|
|
73
|
+
if (!text)
|
|
74
|
+
return null;
|
|
75
|
+
const jsonMatch = text.match(/```json\n([\s\S]+?)\n```/) ?? text.match(/\{[\s\S]+\}/);
|
|
76
|
+
const jsonStr = jsonMatch ? (jsonMatch[1] ?? jsonMatch[0]) : text;
|
|
77
|
+
const parsed = JSON.parse(jsonStr);
|
|
78
|
+
if (typeof parsed.would_cite !== "boolean")
|
|
79
|
+
return null;
|
|
80
|
+
return {
|
|
81
|
+
would_cite: parsed.would_cite,
|
|
82
|
+
confidence: Number.isFinite(parsed.confidence) ? Math.max(0, Math.min(100, parsed.confidence)) : 50,
|
|
83
|
+
citation_excerpt: parsed.citation_excerpt ?? null,
|
|
84
|
+
reasoning: parsed.reasoning ?? "",
|
|
85
|
+
blocking_issues: Array.isArray(parsed.blocking_issues) ? parsed.blocking_issues : [],
|
|
86
|
+
improvements: Array.isArray(parsed.improvements) ? parsed.improvements : [],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function testCitation(input, hostDelays, robotsCache, server) {
|
|
94
|
+
let bodyText = input.text ?? "";
|
|
95
|
+
if (input.url) {
|
|
96
|
+
const result = await politeFetch(input.url, {
|
|
97
|
+
respectRobots: input.respect_robots,
|
|
98
|
+
hostDelays,
|
|
99
|
+
robotsCache,
|
|
100
|
+
});
|
|
101
|
+
const parsed = parseBody(result.body, input.url);
|
|
102
|
+
bodyText = parsed.bodyText;
|
|
103
|
+
}
|
|
104
|
+
// Primary: MCP sampling
|
|
105
|
+
if (server) {
|
|
106
|
+
const sampled = await trySampling(bodyText, input.url, input.target_query, input.engine, server);
|
|
107
|
+
if (sampled) {
|
|
108
|
+
return {
|
|
109
|
+
target_query: input.target_query,
|
|
110
|
+
engine: input.engine,
|
|
111
|
+
would_cite: sampled.would_cite,
|
|
112
|
+
confidence: sampled.confidence,
|
|
113
|
+
citation_excerpt: sampled.citation_excerpt,
|
|
114
|
+
reasoning: sampled.reasoning,
|
|
115
|
+
blocking_issues: sampled.blocking_issues,
|
|
116
|
+
improvements: sampled.improvements,
|
|
117
|
+
mode: "sampling",
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Fallback: heuristic from score_citation_worthiness.
|
|
122
|
+
const score = await scoreCitationWorthiness({ text: bodyText, target_query: input.target_query, respect_robots: false }, hostDelays, robotsCache);
|
|
123
|
+
// would_cite gate: overall >= 70 AND the relevant engine subscore >= 65.
|
|
124
|
+
const engineScoreKey = {
|
|
125
|
+
chatgpt: "chatgpt",
|
|
126
|
+
claude: "claude",
|
|
127
|
+
perplexity: "perplexity",
|
|
128
|
+
google_ai_overviews: "google_ai_overviews",
|
|
129
|
+
any: null,
|
|
130
|
+
};
|
|
131
|
+
const key = engineScoreKey[input.engine];
|
|
132
|
+
const engineScore = key ? score.engine_scores[key] : score.overall_score;
|
|
133
|
+
const wouldCite = score.overall_score >= 70 && engineScore >= 65;
|
|
134
|
+
const blocking = score.findings
|
|
135
|
+
.filter((f) => f.severity === "critical" || f.severity === "warning")
|
|
136
|
+
.slice(0, 5)
|
|
137
|
+
.map((f) => f.message);
|
|
138
|
+
const improvements = score.findings
|
|
139
|
+
.filter((f) => f.severity === "critical" || f.severity === "warning")
|
|
140
|
+
.slice(0, 5)
|
|
141
|
+
.map((f) => ({
|
|
142
|
+
change: f.fix,
|
|
143
|
+
estimated_impact: (f.estimated_impact ?? "medium"),
|
|
144
|
+
}));
|
|
145
|
+
return {
|
|
146
|
+
target_query: input.target_query,
|
|
147
|
+
engine: input.engine,
|
|
148
|
+
would_cite: wouldCite,
|
|
149
|
+
confidence: Math.round((score.overall_score + engineScore) / 2),
|
|
150
|
+
citation_excerpt: null,
|
|
151
|
+
reasoning: wouldCite
|
|
152
|
+
? `Heuristic verdict: overall citability ${score.overall_score}/100 and ${input.engine} subscore ${engineScore}/100 both clear the cite threshold.`
|
|
153
|
+
: `Heuristic verdict: overall citability ${score.overall_score}/100 and ${input.engine} subscore ${engineScore}/100 fall below cite threshold. Run with an MCP host that supports sampling for a model-based simulation.`,
|
|
154
|
+
blocking_issues: blocking,
|
|
155
|
+
improvements,
|
|
156
|
+
mode: "static_heuristic",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=test-citation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-citation.js","sourceRoot":"","sources":["../../src/tools/test-citation.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automatelab/ai-seo-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"mcpName": "io.github.AutomateLab-tech/ai-seo-mcp",
|
|
5
5
|
"description": "AI Citation Toolkit - audit, score, and rewrite web pages for AI-citation eligibility. AEO/GEO/LLM visibility for any MCP client.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
"undici": "7.25.0",
|
|
35
35
|
"zod": "3.25.76"
|
|
36
36
|
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"playwright-core": "^1.49.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"playwright-core": { "optional": true }
|
|
42
|
+
},
|
|
37
43
|
"devDependencies": {
|
|
38
44
|
"@types/node": "22.19.19",
|
|
39
45
|
"tsx": "4.22.0",
|
|
@@ -45,6 +51,7 @@
|
|
|
45
51
|
"src/lib/crawlers.json",
|
|
46
52
|
"src/lib/schema-types.json",
|
|
47
53
|
"action.yml",
|
|
54
|
+
"smithery.yaml",
|
|
48
55
|
"README.md",
|
|
49
56
|
"LICENSE"
|
|
50
57
|
],
|