@rivi-software/agentic-seo-check 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +210 -2
- package/dist/brand.d.ts +11 -0
- package/dist/brand.js +22 -0
- package/dist/brand.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +194 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/audit.d.ts +72 -0
- package/dist/core/audit.js +452 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/concurrency.d.ts +4 -0
- package/dist/core/concurrency.js +39 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/crawl.d.ts +40 -0
- package/dist/core/crawl.js +324 -0
- package/dist/core/crawl.js.map +1 -0
- package/dist/core/fetch.d.ts +27 -0
- package/dist/core/fetch.js +186 -0
- package/dist/core/fetch.js.map +1 -0
- package/dist/core/html/dom.d.ts +13 -0
- package/dist/core/html/dom.js +17 -0
- package/dist/core/html/dom.js.map +1 -0
- package/dist/core/html/extract.d.ts +10 -0
- package/dist/core/html/extract.js +207 -0
- package/dist/core/html/extract.js.map +1 -0
- package/dist/core/html/structured-data.d.ts +21 -0
- package/dist/core/html/structured-data.js +220 -0
- package/dist/core/html/structured-data.js.map +1 -0
- package/dist/core/html/text.d.ts +23 -0
- package/dist/core/html/text.js +152 -0
- package/dist/core/html/text.js.map +1 -0
- package/dist/core/lighthouse.d.ts +50 -0
- package/dist/core/lighthouse.js +216 -0
- package/dist/core/lighthouse.js.map +1 -0
- package/dist/core/performance-rules.d.ts +3 -0
- package/dist/core/performance-rules.js +125 -0
- package/dist/core/performance-rules.js.map +1 -0
- package/dist/core/render-rules.d.ts +8 -0
- package/dist/core/render-rules.js +144 -0
- package/dist/core/render-rules.js.map +1 -0
- package/dist/core/render.d.ts +22 -0
- package/dist/core/render.js +79 -0
- package/dist/core/render.js.map +1 -0
- package/dist/core/report/markdown.d.ts +2 -0
- package/dist/core/report/markdown.js +131 -0
- package/dist/core/report/markdown.js.map +1 -0
- package/dist/core/robots.d.ts +11 -0
- package/dist/core/robots.js +48 -0
- package/dist/core/robots.js.map +1 -0
- package/dist/core/rules/canonical.d.ts +2 -0
- package/dist/core/rules/canonical.js +141 -0
- package/dist/core/rules/canonical.js.map +1 -0
- package/dist/core/rules/content.d.ts +1 -0
- package/dist/core/rules/content.js +55 -0
- package/dist/core/rules/content.js.map +1 -0
- package/dist/core/rules/document.d.ts +1 -0
- package/dist/core/rules/document.js +83 -0
- package/dist/core/rules/document.js.map +1 -0
- package/dist/core/rules/headings.d.ts +1 -0
- package/dist/core/rules/headings.js +81 -0
- package/dist/core/rules/headings.js.map +1 -0
- package/dist/core/rules/hreflang.d.ts +1 -0
- package/dist/core/rules/hreflang.js +100 -0
- package/dist/core/rules/hreflang.js.map +1 -0
- package/dist/core/rules/images.d.ts +1 -0
- package/dist/core/rules/images.js +77 -0
- package/dist/core/rules/images.js.map +1 -0
- package/dist/core/rules/index.d.ts +14 -0
- package/dist/core/rules/index.js +77 -0
- package/dist/core/rules/index.js.map +1 -0
- package/dist/core/rules/indexing.d.ts +10 -0
- package/dist/core/rules/indexing.js +75 -0
- package/dist/core/rules/indexing.js.map +1 -0
- package/dist/core/rules/keyword.d.ts +1 -0
- package/dist/core/rules/keyword.js +111 -0
- package/dist/core/rules/keyword.js.map +1 -0
- package/dist/core/rules/links.d.ts +1 -0
- package/dist/core/rules/links.js +107 -0
- package/dist/core/rules/links.js.map +1 -0
- package/dist/core/rules/meta.d.ts +1 -0
- package/dist/core/rules/meta.js +81 -0
- package/dist/core/rules/meta.js.map +1 -0
- package/dist/core/rules/response.d.ts +1 -0
- package/dist/core/rules/response.js +93 -0
- package/dist/core/rules/response.js.map +1 -0
- package/dist/core/rules/rule.d.ts +42 -0
- package/dist/core/rules/rule.js +51 -0
- package/dist/core/rules/rule.js.map +1 -0
- package/dist/core/rules/social.d.ts +1 -0
- package/dist/core/rules/social.js +115 -0
- package/dist/core/rules/social.js.map +1 -0
- package/dist/core/rules/structured-data.d.ts +25 -0
- package/dist/core/rules/structured-data.js +381 -0
- package/dist/core/rules/structured-data.js.map +1 -0
- package/dist/core/rules/title.d.ts +1 -0
- package/dist/core/rules/title.js +74 -0
- package/dist/core/rules/title.js.map +1 -0
- package/dist/core/score.d.ts +3 -0
- package/dist/core/score.js +17 -0
- package/dist/core/score.js.map +1 -0
- package/dist/core/site-rules.d.ts +64 -0
- package/dist/core/site-rules.js +462 -0
- package/dist/core/site-rules.js.map +1 -0
- package/dist/core/sitemap.d.ts +13 -0
- package/dist/core/sitemap.js +140 -0
- package/dist/core/sitemap.js.map +1 -0
- package/dist/core/static.d.ts +9 -0
- package/dist/core/static.js +281 -0
- package/dist/core/static.js.map +1 -0
- package/dist/core/summary.d.ts +2 -0
- package/dist/core/summary.js +33 -0
- package/dist/core/summary.js.map +1 -0
- package/dist/core/types.d.ts +296 -0
- package/dist/core/types.js +33 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/url.d.ts +20 -0
- package/dist/core/url.js +191 -0
- package/dist/core/url.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-bin.d.ts +2 -0
- package/dist/mcp-bin.js +10 -0
- package/dist/mcp-bin.js.map +1 -0
- package/dist/mcp.d.ts +5 -0
- package/dist/mcp.js +380 -0
- package/dist/mcp.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +107 -3
- package/skills/seo-audit/SKILL.md +53 -0
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { MAKER, MCP_ICONS, REPOSITORY_URL } from "./brand.js";
|
|
5
|
+
import { auditDirectory, auditHtml, auditSite, auditUrl, compactAudit, } from "./core/audit.js";
|
|
6
|
+
import { runLighthouse } from "./core/lighthouse.js";
|
|
7
|
+
import { performanceFindings } from "./core/performance-rules.js";
|
|
8
|
+
import { renderMarkdown } from "./core/report/markdown.js";
|
|
9
|
+
import { ALL_RULES } from "./core/rules/index.js";
|
|
10
|
+
import { VERSION } from "./version.js";
|
|
11
|
+
export const MCP_SERVER_NAME = "agentic-seo-check";
|
|
12
|
+
export const MCP_SERVER_VERSION = VERSION;
|
|
13
|
+
const format = z
|
|
14
|
+
.enum(["json", "markdown"])
|
|
15
|
+
.default("json")
|
|
16
|
+
.describe("json (default) returns the structured audit; markdown returns a human-readable report.");
|
|
17
|
+
const detail = z
|
|
18
|
+
.enum(["full", "compact"])
|
|
19
|
+
.default("compact")
|
|
20
|
+
.describe("compact trims evidence lists and bulky fields to save tokens (default); full returns everything.");
|
|
21
|
+
const commonOptions = {
|
|
22
|
+
keyword: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Target keyword/phrase. Enables keyword.* checks (title, H1, body, description, URL, density)."),
|
|
26
|
+
includeInfo: z
|
|
27
|
+
.boolean()
|
|
28
|
+
.default(true)
|
|
29
|
+
.describe("Include info-level findings (best practices). Set false to see only errors and warnings."),
|
|
30
|
+
disableRules: z
|
|
31
|
+
.array(z.string())
|
|
32
|
+
.optional()
|
|
33
|
+
.describe('Rule ids to skip, e.g. ["twitter.card-missing"]. Use seo_list_rules to see ids.'),
|
|
34
|
+
timeoutMs: z
|
|
35
|
+
.number()
|
|
36
|
+
.int()
|
|
37
|
+
.min(1000)
|
|
38
|
+
.max(120_000)
|
|
39
|
+
.optional()
|
|
40
|
+
.describe("Per-request timeout in milliseconds (default 15000)."),
|
|
41
|
+
userAgent: z.string().optional().describe("Override the User-Agent header."),
|
|
42
|
+
headers: z
|
|
43
|
+
.record(z.string(), z.string())
|
|
44
|
+
.optional()
|
|
45
|
+
.describe('Extra request headers, e.g. {"Authorization": "Basic ..."} for staging sites.'),
|
|
46
|
+
format,
|
|
47
|
+
detail,
|
|
48
|
+
};
|
|
49
|
+
const toOptions = (input) => ({
|
|
50
|
+
keyword: input.keyword,
|
|
51
|
+
includeInfo: input.includeInfo,
|
|
52
|
+
disableRules: input.disableRules,
|
|
53
|
+
fetch: {
|
|
54
|
+
timeoutMs: input.timeoutMs,
|
|
55
|
+
userAgent: input.userAgent,
|
|
56
|
+
headers: input.headers,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
const respond = (audit, fmt, det) => {
|
|
60
|
+
const report = det === "compact" ? compactAudit(audit) : audit;
|
|
61
|
+
const text = fmt === "markdown"
|
|
62
|
+
? renderMarkdown(report)
|
|
63
|
+
: JSON.stringify(report, null, 2);
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: "text", text }],
|
|
66
|
+
structuredContent: { ...report },
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- a thrown value is unknown until this reads it
|
|
70
|
+
const fail = (error) => {
|
|
71
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
72
|
+
return {
|
|
73
|
+
isError: true,
|
|
74
|
+
content: [
|
|
75
|
+
{ type: "text", text: `agentic-seo-check error: ${message}` },
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
const metricCell = (name, value) => {
|
|
80
|
+
if (value === null) {
|
|
81
|
+
return "—";
|
|
82
|
+
}
|
|
83
|
+
return name.endsWith("Ms") ? `${Math.round(value)} ms` : String(value);
|
|
84
|
+
};
|
|
85
|
+
const lighthouseMarkdown = (payload) => {
|
|
86
|
+
const lines = [
|
|
87
|
+
`# Lighthouse (${payload.formFactor}): ${payload.finalUrl}`,
|
|
88
|
+
"",
|
|
89
|
+
"| Metric | Value |",
|
|
90
|
+
"| --- | --- |",
|
|
91
|
+
];
|
|
92
|
+
for (const [name, value] of Object.entries(payload.metrics)) {
|
|
93
|
+
lines.push(`| ${name} | ${metricCell(name, value)} |`);
|
|
94
|
+
}
|
|
95
|
+
lines.push("", "## Opportunities", "");
|
|
96
|
+
for (const o of payload.opportunities.slice(0, 10)) {
|
|
97
|
+
lines.push(`- ${o.title}${o.savingsMs ? ` (~${Math.round(o.savingsMs)} ms)` : ""}${o.savingsBytes ? ` (~${Math.round(o.savingsBytes / 1024)} KiB)` : ""}`);
|
|
98
|
+
}
|
|
99
|
+
lines.push("", "## Findings", "");
|
|
100
|
+
for (const f of payload.findings) {
|
|
101
|
+
lines.push(`- **\`${f.rule}\`** ${f.message}`, ` - Fix: ${f.recommendation}`);
|
|
102
|
+
}
|
|
103
|
+
return lines.join("\n");
|
|
104
|
+
};
|
|
105
|
+
export const createMcpServer = () => {
|
|
106
|
+
const server = new McpServer({
|
|
107
|
+
name: MCP_SERVER_NAME,
|
|
108
|
+
title: `agentic-seo-check by ${MAKER}`,
|
|
109
|
+
version: MCP_SERVER_VERSION,
|
|
110
|
+
description: "SEO audits for coding agents: pages, sites, build output and raw HTML, with findings an agent can act on.",
|
|
111
|
+
websiteUrl: REPOSITORY_URL,
|
|
112
|
+
icons: MCP_ICONS,
|
|
113
|
+
});
|
|
114
|
+
server.registerTool("seo_audit_page", {
|
|
115
|
+
title: "Audit one page for SEO",
|
|
116
|
+
description: "Audit a single page and return prioritised, actionable findings (rule id, severity, message, recommendation, CSS selector, evidence). " +
|
|
117
|
+
"Checks title, meta description, headings, canonical, robots/noindex, viewport/lang, Open Graph/Twitter cards, images, links, hreflang, structured data (JSON-LD/microdata), content quality and the HTTP response. " +
|
|
118
|
+
"Provide either `url` (live site, staging or http://localhost dev server) or `html` (raw HTML string, e.g. from a template or test render). " +
|
|
119
|
+
"Set `render: true` to also load the page in headless Chromium and report content that only exists after JavaScript runs (SPA/CSR problems). " +
|
|
120
|
+
"Set `lighthouse: true` for Core Web Vitals (LCP, CLS, TBT) and Lighthouse SEO audits; this takes 20-60 seconds. " +
|
|
121
|
+
"Typical workflow: audit, fix the errors and warnings in the source code, re-audit to confirm.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
url: z
|
|
124
|
+
.string()
|
|
125
|
+
.url()
|
|
126
|
+
.optional()
|
|
127
|
+
.describe("Absolute http(s) URL to fetch and audit."),
|
|
128
|
+
html: z
|
|
129
|
+
.string()
|
|
130
|
+
.optional()
|
|
131
|
+
.describe("Raw HTML to audit instead of fetching a URL."),
|
|
132
|
+
baseUrl: z
|
|
133
|
+
.string()
|
|
134
|
+
.url()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("With `html`: the URL the HTML would be served at (used to resolve links and classify them as internal/external)."),
|
|
137
|
+
render: z
|
|
138
|
+
.boolean()
|
|
139
|
+
.default(false)
|
|
140
|
+
.describe("Also render with headless Chromium (Playwright) and compare against the server HTML."),
|
|
141
|
+
lighthouse: z
|
|
142
|
+
.boolean()
|
|
143
|
+
.default(false)
|
|
144
|
+
.describe("Also run Lighthouse (performance, SEO, agentic-browsing categories). Slow."),
|
|
145
|
+
formFactor: z
|
|
146
|
+
.enum(["mobile", "desktop"])
|
|
147
|
+
.default("mobile")
|
|
148
|
+
.describe("Lighthouse device emulation."),
|
|
149
|
+
...commonOptions,
|
|
150
|
+
},
|
|
151
|
+
annotations: {
|
|
152
|
+
readOnlyHint: true,
|
|
153
|
+
openWorldHint: true,
|
|
154
|
+
idempotentHint: true,
|
|
155
|
+
},
|
|
156
|
+
}, async (input) => {
|
|
157
|
+
try {
|
|
158
|
+
const options = {
|
|
159
|
+
...toOptions(input),
|
|
160
|
+
render: input.render,
|
|
161
|
+
lighthouse: input.lighthouse,
|
|
162
|
+
formFactor: input.formFactor,
|
|
163
|
+
baseUrl: input.baseUrl,
|
|
164
|
+
};
|
|
165
|
+
const { html, url } = input;
|
|
166
|
+
let audit;
|
|
167
|
+
// `html` wins over `url`; an empty `html` counts only when a `url` came with it.
|
|
168
|
+
if (html !== undefined && (html || url)) {
|
|
169
|
+
audit = await auditHtml(html, { url: input.baseUrl, source: "html", target: "inline-html" }, options);
|
|
170
|
+
}
|
|
171
|
+
else if (url) {
|
|
172
|
+
audit = await auditUrl(url, options);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
throw new Error("Provide `url` or `html`.");
|
|
176
|
+
}
|
|
177
|
+
return respond(audit, input.format, input.detail);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
return fail(error);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
server.registerTool("seo_crawl_site", {
|
|
184
|
+
title: "Crawl a site and audit every page",
|
|
185
|
+
description: "Crawl internal links from a start URL (default 25 pages, depth 3), audit every HTML page, and run site-wide checks: robots.txt, XML sitemap coverage (missing/noindex/non-canonical/broken URLs), broken internal links with their sources, redirect chains, duplicate titles/descriptions/H1s/content, orphan and deep pages, soft-404 behaviour, http→https and www redirects, hreflang return links. " +
|
|
186
|
+
"Returns site findings plus `issues` (page findings rolled up by rule with example URLs) and per-page scores. Use seo_audit_page on a specific URL for full per-page detail. " +
|
|
187
|
+
"Works on http://localhost dev servers; for those set `respectRobots: false` if the dev server serves a blocking robots.txt.",
|
|
188
|
+
inputSchema: {
|
|
189
|
+
url: z.string().url().describe("Start URL (usually the homepage)."),
|
|
190
|
+
maxPages: z
|
|
191
|
+
.number()
|
|
192
|
+
.int()
|
|
193
|
+
.min(1)
|
|
194
|
+
.max(500)
|
|
195
|
+
.default(25)
|
|
196
|
+
.describe("Maximum pages to fetch."),
|
|
197
|
+
maxDepth: z
|
|
198
|
+
.number()
|
|
199
|
+
.int()
|
|
200
|
+
.min(0)
|
|
201
|
+
.max(10)
|
|
202
|
+
.default(3)
|
|
203
|
+
.describe("Maximum link depth from the start URL."),
|
|
204
|
+
concurrency: z
|
|
205
|
+
.number()
|
|
206
|
+
.int()
|
|
207
|
+
.min(1)
|
|
208
|
+
.max(10)
|
|
209
|
+
.default(4)
|
|
210
|
+
.describe("Parallel requests."),
|
|
211
|
+
delayMs: z
|
|
212
|
+
.number()
|
|
213
|
+
.int()
|
|
214
|
+
.min(0)
|
|
215
|
+
.max(5000)
|
|
216
|
+
.default(0)
|
|
217
|
+
.describe("Delay between requests per worker (politeness)."),
|
|
218
|
+
respectRobots: z
|
|
219
|
+
.boolean()
|
|
220
|
+
.default(true)
|
|
221
|
+
.describe("Obey robots.txt disallow rules while crawling."),
|
|
222
|
+
includeSubdomains: z
|
|
223
|
+
.boolean()
|
|
224
|
+
.default(false)
|
|
225
|
+
.describe("Treat subdomains of the start host as internal."),
|
|
226
|
+
checkExternalLinks: z
|
|
227
|
+
.boolean()
|
|
228
|
+
.default(false)
|
|
229
|
+
.describe("Also probe external link targets for 4xx/5xx (slower)."),
|
|
230
|
+
includePatterns: z
|
|
231
|
+
.array(z.string())
|
|
232
|
+
.optional()
|
|
233
|
+
.describe("Only crawl URLs matching one of these regular expressions."),
|
|
234
|
+
excludePatterns: z
|
|
235
|
+
.array(z.string())
|
|
236
|
+
.optional()
|
|
237
|
+
.describe("Skip URLs matching any of these regular expressions."),
|
|
238
|
+
pageDetail: z
|
|
239
|
+
.enum(["summary", "full"])
|
|
240
|
+
.default("summary")
|
|
241
|
+
.describe("summary: per-page scores/counts only (findings are rolled up in `issues`); full: every finding on every page."),
|
|
242
|
+
...commonOptions,
|
|
243
|
+
},
|
|
244
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
245
|
+
}, async (input) => {
|
|
246
|
+
try {
|
|
247
|
+
const audit = await auditSite(input.url, {
|
|
248
|
+
...toOptions(input),
|
|
249
|
+
maxPages: input.maxPages,
|
|
250
|
+
maxDepth: input.maxDepth,
|
|
251
|
+
concurrency: input.concurrency,
|
|
252
|
+
delayMs: input.delayMs,
|
|
253
|
+
respectRobots: input.respectRobots,
|
|
254
|
+
includeSubdomains: input.includeSubdomains,
|
|
255
|
+
checkExternalLinks: input.checkExternalLinks,
|
|
256
|
+
includePatterns: input.includePatterns,
|
|
257
|
+
excludePatterns: input.excludePatterns,
|
|
258
|
+
pageDetail: input.pageDetail,
|
|
259
|
+
});
|
|
260
|
+
return respond(audit, input.format, input.detail);
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
return fail(error);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
server.registerTool("seo_audit_directory", {
|
|
267
|
+
title: "Audit a static build output directory",
|
|
268
|
+
description: "Audit every .html file in a build output directory (dist/, out/, build/, .output/public) without running a server. Runs all page checks plus directory-level checks: internal links that resolve to no file, sitemap.xml/robots.txt in the output, duplicate titles/descriptions/content, unlinked files. " +
|
|
269
|
+
"Pass `baseUrl` as the production origin (e.g. https://www.example.com) so absolute links, canonicals and sitemap URLs are classified correctly.",
|
|
270
|
+
inputSchema: {
|
|
271
|
+
dir: z
|
|
272
|
+
.string()
|
|
273
|
+
.describe("Absolute or relative path to the directory containing built HTML files."),
|
|
274
|
+
baseUrl: z
|
|
275
|
+
.string()
|
|
276
|
+
.url()
|
|
277
|
+
.default("http://localhost/")
|
|
278
|
+
.describe("Origin the files will be served from."),
|
|
279
|
+
maxPages: z
|
|
280
|
+
.number()
|
|
281
|
+
.int()
|
|
282
|
+
.min(1)
|
|
283
|
+
.max(5000)
|
|
284
|
+
.default(500)
|
|
285
|
+
.describe("Maximum HTML files to audit."),
|
|
286
|
+
pageDetail: z
|
|
287
|
+
.enum(["summary", "full"])
|
|
288
|
+
.default("summary")
|
|
289
|
+
.describe("summary: per-page scores/counts only; full: every finding on every page."),
|
|
290
|
+
...commonOptions,
|
|
291
|
+
},
|
|
292
|
+
annotations: {
|
|
293
|
+
readOnlyHint: true,
|
|
294
|
+
openWorldHint: false,
|
|
295
|
+
idempotentHint: true,
|
|
296
|
+
},
|
|
297
|
+
}, async (input) => {
|
|
298
|
+
try {
|
|
299
|
+
const audit = await auditDirectory(input.dir, {
|
|
300
|
+
...toOptions(input),
|
|
301
|
+
baseUrl: input.baseUrl,
|
|
302
|
+
maxPages: input.maxPages,
|
|
303
|
+
pageDetail: input.pageDetail,
|
|
304
|
+
});
|
|
305
|
+
return respond(audit, input.format, input.detail);
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
return fail(error);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
server.registerTool("seo_lighthouse", {
|
|
312
|
+
title: "Run Lighthouse on a URL",
|
|
313
|
+
description: "Run Google Lighthouse (performance, SEO and agentic-browsing categories) against one URL using a local headless Chrome. Returns Core Web Vitals lab metrics (LCP, CLS, TBT, FCP, Speed Index, TTFB), the top opportunities with estimated savings, failed Lighthouse SEO audits, and findings with thresholds applied. Takes 20-60 seconds. Requires Chrome or Playwright Chromium on this machine.",
|
|
314
|
+
inputSchema: {
|
|
315
|
+
url: z.string().url().describe("Absolute http(s) URL."),
|
|
316
|
+
formFactor: z
|
|
317
|
+
.enum(["mobile", "desktop"])
|
|
318
|
+
.default("mobile")
|
|
319
|
+
.describe("Device emulation. Mobile is what Google uses for ranking."),
|
|
320
|
+
headers: z
|
|
321
|
+
.record(z.string(), z.string())
|
|
322
|
+
.optional()
|
|
323
|
+
.describe("Extra request headers (e.g. auth for staging)."),
|
|
324
|
+
format,
|
|
325
|
+
},
|
|
326
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
327
|
+
}, async (input) => {
|
|
328
|
+
try {
|
|
329
|
+
const result = await runLighthouse(input.url, {
|
|
330
|
+
formFactor: input.formFactor,
|
|
331
|
+
headers: input.headers,
|
|
332
|
+
});
|
|
333
|
+
const findings = performanceFindings(result);
|
|
334
|
+
const payload = { ...result, findings };
|
|
335
|
+
const text = input.format === "markdown"
|
|
336
|
+
? lighthouseMarkdown(payload)
|
|
337
|
+
: JSON.stringify(payload, null, 2);
|
|
338
|
+
return {
|
|
339
|
+
content: [{ type: "text", text }],
|
|
340
|
+
structuredContent: payload,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
return fail(error);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
server.registerTool("seo_list_rules", {
|
|
348
|
+
title: "List SEO rules",
|
|
349
|
+
description: "List every rule this tool can report, with id, category, typical severity, what it checks and the generic fix. Filter by category or look up one rule id to understand a finding.",
|
|
350
|
+
inputSchema: {
|
|
351
|
+
category: z
|
|
352
|
+
.string()
|
|
353
|
+
.optional()
|
|
354
|
+
.describe("Filter by category (title, meta, headings, canonical, indexing, document, social, images, links, international, structured-data, content, keyword, response, rendering, performance, site)."),
|
|
355
|
+
rule: z.string().optional().describe("Return only this rule id."),
|
|
356
|
+
},
|
|
357
|
+
annotations: {
|
|
358
|
+
readOnlyHint: true,
|
|
359
|
+
openWorldHint: false,
|
|
360
|
+
idempotentHint: true,
|
|
361
|
+
},
|
|
362
|
+
}, (input) => {
|
|
363
|
+
const rules = ALL_RULES.filter((r) => (!input.category || r.category === input.category) &&
|
|
364
|
+
(!input.rule || r.id === input.rule));
|
|
365
|
+
const payload = { count: rules.length, rules };
|
|
366
|
+
return {
|
|
367
|
+
content: [
|
|
368
|
+
{ type: "text", text: JSON.stringify(payload, null, 2) },
|
|
369
|
+
],
|
|
370
|
+
structuredContent: payload,
|
|
371
|
+
};
|
|
372
|
+
});
|
|
373
|
+
return server;
|
|
374
|
+
};
|
|
375
|
+
export const startMcpServer = async () => {
|
|
376
|
+
const server = createMcpServer();
|
|
377
|
+
const transport = new StdioServerTransport();
|
|
378
|
+
await server.connect(transport);
|
|
379
|
+
};
|
|
380
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION: string;
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
/** Package version, read from package.json (one level above both src/ and dist/) so a release bumps one file. */
|
|
3
|
+
// SAFETY: npm and pnpm refuse to pack or publish a package.json without a string version.
|
|
4
|
+
const manifest = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
5
|
+
export const VERSION = manifest.version;
|
|
6
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,iHAAiH;AACjH,0FAA0F;AAC1F,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CACzB,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAC5C,CAAC;AAEzB,MAAM,CAAC,MAAM,OAAO,GAAW,QAAQ,CAAC,OAAO,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,110 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivi-software/agentic-seo-check",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "SEO analysis for coding agents: a CLI and MCP server that audit URLs, static build output or raw HTML and return machine-actionable findings.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "RIVI Software (https://rivi.software)",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rivi-software/agentic-seo-check.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/rivi-software/agentic-seo-check#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/rivi-software/agentic-seo-check/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22.19"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"agentic-seo-check": "./dist/cli.js",
|
|
21
|
+
"agentic-seo-check-mcp": "./dist/mcp-bin.js"
|
|
22
|
+
},
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"skills",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"seo",
|
|
42
|
+
"audit",
|
|
43
|
+
"mcp",
|
|
44
|
+
"model-context-protocol",
|
|
45
|
+
"cli",
|
|
46
|
+
"coding-agents",
|
|
47
|
+
"lighthouse",
|
|
48
|
+
"crawler"
|
|
49
|
+
],
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
52
|
+
"commander": "^15.1.0",
|
|
53
|
+
"css-select": "^7.0.0",
|
|
54
|
+
"domhandler": "^6.0.1",
|
|
55
|
+
"domutils": "^4.0.2",
|
|
56
|
+
"fast-xml-parser": "^5.12.0",
|
|
57
|
+
"parse5": "^8.1.1",
|
|
58
|
+
"parse5-htmlparser2-tree-adapter": "^8.0.2",
|
|
59
|
+
"robots-parser": "^3.0.1",
|
|
60
|
+
"zod": "^4.6.7"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"chrome-launcher": "^1.2.3",
|
|
64
|
+
"lighthouse": "^13.6.0",
|
|
65
|
+
"playwright": "^1.65.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"chrome-launcher": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"lighthouse": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"playwright": {
|
|
75
|
+
"optional": true
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@arethetypeswrong/cli": "0.18.5",
|
|
80
|
+
"@changesets/cli": "3.0.3",
|
|
81
|
+
"@types/node": "22.20.5",
|
|
82
|
+
"chrome-launcher": "^1.2.3",
|
|
83
|
+
"lefthook": "2.2.2",
|
|
84
|
+
"lighthouse": "^13.6.0",
|
|
85
|
+
"oxfmt": "0.75.0",
|
|
86
|
+
"oxlint": "1.90.0",
|
|
87
|
+
"playwright": "^1.65.0",
|
|
88
|
+
"tsx": "^4.20.0",
|
|
89
|
+
"typescript": "^7.0.2",
|
|
90
|
+
"ultracite": "8.1.1",
|
|
91
|
+
"vitest": "^5.1.0"
|
|
92
|
+
},
|
|
93
|
+
"scripts": {
|
|
94
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
95
|
+
"dev": "tsx src/cli.ts",
|
|
96
|
+
"mcp": "tsx src/mcp-bin.ts",
|
|
97
|
+
"check": "tsc -p tsconfig.json",
|
|
98
|
+
"lint": "oxlint",
|
|
99
|
+
"format": "oxfmt",
|
|
100
|
+
"format:check": "oxfmt --check",
|
|
101
|
+
"fix": "ultracite fix -A unicorn/catch-error-name -A typescript/consistent-type-definitions -A unicorn/prefer-dom-node-append -A unicorn/prefer-dom-node-dataset -A unicorn/prefer-query-selector -A unicorn/prefer-at -A unicorn/prefer-set-has -A unicorn/prefer-spread -A unicorn/prefer-bigint-literals -A unicorn/prefer-import-meta-properties -A unicorn/prefer-number-coercion -A unicorn/prefer-type-error -A unicorn/prefer-regexp-test -A unicorn/text-encoding-identifier-case -A unicorn/no-useless-undefined -A vitest/prefer-describe-function-title",
|
|
102
|
+
"test": "vitest run",
|
|
103
|
+
"test:watch": "vitest",
|
|
104
|
+
"check:package": "node scripts/check-package.ts",
|
|
105
|
+
"verify": "pnpm run format:check && pnpm run lint && pnpm run check && pnpm run test && pnpm run build && pnpm run check:package",
|
|
106
|
+
"changeset": "changeset",
|
|
107
|
+
"version-packages": "changeset version && pnpm install --lockfile-only",
|
|
108
|
+
"release": "changeset publish"
|
|
109
|
+
}
|
|
6
110
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: seo-audit
|
|
3
|
+
description: Audit and fix SEO for a website or web project with the agentic-seo-check CLI/MCP tool. Use when asked to check, improve, or fix SEO, meta tags, titles, descriptions, canonicals, Open Graph, structured data, sitemaps, robots.txt, broken links, Core Web Vitals, or "why isn't this page indexed/ranking". Works on live URLs, localhost dev servers, static build output (dist/, out/) and raw HTML.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SEO audit workflow
|
|
7
|
+
|
|
8
|
+
You have `agentic-seo-check` available either as MCP tools (`seo_audit_page`, `seo_crawl_site`, `seo_audit_directory`, `seo_lighthouse`, `seo_list_rules`) or as a CLI (`agentic-seo-check`, or `npx @rivi-software/agentic-seo-check`). Both return the same JSON: findings with `rule`, `severity`, `message`, `recommendation`, `selector`, `evidence`, plus a 0-100 `score`.
|
|
9
|
+
|
|
10
|
+
## 1. Pick the target
|
|
11
|
+
|
|
12
|
+
| Situation | Use |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| One page, live or on `http://localhost:3000` | `seo_audit_page { url }` / `agentic-seo-check audit <url>` |
|
|
15
|
+
| Whole site, find site-wide problems | `seo_crawl_site { url, maxPages }` / `agentic-seo-check crawl <url> --max-pages 50` |
|
|
16
|
+
| Static export already built (Next `out/`, Astro `dist/`, Vite `dist/`) | `seo_audit_directory { dir, baseUrl }` / `agentic-seo-check audit ./dist --base-url https://www.example.com` |
|
|
17
|
+
| You rendered HTML yourself (template, test, snapshot) | `seo_audit_page { html, baseUrl }` / `cat page.html \| agentic-seo-check audit -` |
|
|
18
|
+
| Suspect client-side rendering hides content from crawlers | add `render: true` / `--render` |
|
|
19
|
+
| Performance / Core Web Vitals | `seo_lighthouse { url }` or `lighthouse: true` / `--lighthouse` (slow, 20-60 s) |
|
|
20
|
+
| Optimising for a specific search phrase | add `keyword` / `--keyword "blue widgets"` |
|
|
21
|
+
|
|
22
|
+
Prefer auditing the running dev server or the build output over guessing from source: the tool sees exactly what crawlers see.
|
|
23
|
+
|
|
24
|
+
## 2. Read the result
|
|
25
|
+
|
|
26
|
+
- Fix in this order: **error** → **warning** → **info**. Errors block indexing or are outright defects (missing title, conflicting canonicals, invalid JSON-LD, 4xx/5xx, broken links). Warnings hurt ranking or click-through. Info is best practice.
|
|
27
|
+
- For site audits start with `siteFindings` and `issues` (page findings rolled up per rule with example URLs). One template fix usually clears a rule on many pages.
|
|
28
|
+
- `selector` tells you where in the HTML the problem is; `evidence` contains the current values and the offending items (capped lists). `recommendation` says what to change.
|
|
29
|
+
- `seo_list_rules { rule }` explains any rule id.
|
|
30
|
+
|
|
31
|
+
## 3. Fix in the source, not in the output
|
|
32
|
+
|
|
33
|
+
Find where the page's `<head>` is produced and change it there, per route:
|
|
34
|
+
|
|
35
|
+
- Next.js app router: `export const metadata` / `generateMetadata` (title, description, alternates.canonical, openGraph, robots). Pages router: `next/head`. Sitemap: `app/sitemap.ts`; robots: `app/robots.ts`.
|
|
36
|
+
- Astro: `<head>` in the layout, props per page; `@astrojs/sitemap`.
|
|
37
|
+
- Nuxt: `useSeoMeta` / `useHead`; `@nuxtjs/sitemap`.
|
|
38
|
+
- SvelteKit: `<svelte:head>`; Remix/React Router: `meta` export; TanStack Start: `head()` on the route.
|
|
39
|
+
- Plain HTML/static generators: the template partial that renders `<head>`.
|
|
40
|
+
|
|
41
|
+
Typical mappings: `title.*`/`meta-description.*` → per-route metadata; `canonical.*` → absolute self-referencing canonical built from the site URL; `og.*`/`twitter.*` → social metadata with an absolute image URL; `structured-data.*` → JSON-LD emitted with `JSON.stringify`; `img.alt-missing` → alt props on image components; `site.broken-internal-links` → fix hrefs or add redirects; `site.sitemap-*` → generate the sitemap from the router so it stays in sync; `rendering.*` → move data fetching to the server (SSR/SSG) so the HTML carries the content.
|
|
42
|
+
|
|
43
|
+
## 4. Re-run and confirm
|
|
44
|
+
|
|
45
|
+
Run the same audit again. Report before/after score and the remaining findings. In CI, `agentic-seo-check audit <target> --fail-on error` exits 1 when errors remain.
|
|
46
|
+
|
|
47
|
+
## Caveats to tell the user about
|
|
48
|
+
|
|
49
|
+
- `robots.noindex` on a preview/staging host is often added by the hosting platform (X-Robots-Tag); check production before "fixing" it.
|
|
50
|
+
- A localhost audit cannot verify https, www redirects or the production robots.txt/sitemap; run a crawl against the deployed site for those.
|
|
51
|
+
- Scores are indicative (100 minus weighted deductions), not a ranking prediction.
|
|
52
|
+
- Lighthouse numbers are lab measurements from this machine; field data (CrUX) may differ.
|
|
53
|
+
- `render` needs Playwright or an installed Google Chrome; `lighthouse` needs Chrome. If unavailable the audit still completes and adds a `tool.*` info finding with install instructions.
|