@automatelab/ai-seo-mcp 0.1.0

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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +145 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/config.d.ts +8 -0
  8. package/dist/lib/config.d.ts.map +1 -0
  9. package/dist/lib/config.js +11 -0
  10. package/dist/lib/config.js.map +1 -0
  11. package/dist/lib/crawlers.json +6 -0
  12. package/dist/lib/entities.d.ts +17 -0
  13. package/dist/lib/entities.d.ts.map +1 -0
  14. package/dist/lib/entities.js +113 -0
  15. package/dist/lib/entities.js.map +1 -0
  16. package/dist/lib/fetch.d.ts +33 -0
  17. package/dist/lib/fetch.d.ts.map +1 -0
  18. package/dist/lib/fetch.js +190 -0
  19. package/dist/lib/fetch.js.map +1 -0
  20. package/dist/lib/html.d.ts +45 -0
  21. package/dist/lib/html.d.ts.map +1 -0
  22. package/dist/lib/html.js +166 -0
  23. package/dist/lib/html.js.map +1 -0
  24. package/dist/lib/llms-txt.d.ts +25 -0
  25. package/dist/lib/llms-txt.d.ts.map +1 -0
  26. package/dist/lib/llms-txt.js +149 -0
  27. package/dist/lib/llms-txt.js.map +1 -0
  28. package/dist/lib/robots.d.ts +26 -0
  29. package/dist/lib/robots.d.ts.map +1 -0
  30. package/dist/lib/robots.js +83 -0
  31. package/dist/lib/robots.js.map +1 -0
  32. package/dist/lib/schema-types.json +56 -0
  33. package/dist/lib/schema.d.ts +20 -0
  34. package/dist/lib/schema.d.ts.map +1 -0
  35. package/dist/lib/schema.js +311 -0
  36. package/dist/lib/schema.js.map +1 -0
  37. package/dist/lib/score.d.ts +32 -0
  38. package/dist/lib/score.d.ts.map +1 -0
  39. package/dist/lib/score.js +62 -0
  40. package/dist/lib/score.js.map +1 -0
  41. package/dist/tools/audit-canonical.d.ts +26 -0
  42. package/dist/tools/audit-canonical.d.ts.map +1 -0
  43. package/dist/tools/audit-canonical.js +122 -0
  44. package/dist/tools/audit-canonical.js.map +1 -0
  45. package/dist/tools/audit-page.d.ts +31 -0
  46. package/dist/tools/audit-page.d.ts.map +1 -0
  47. package/dist/tools/audit-page.js +221 -0
  48. package/dist/tools/audit-page.js.map +1 -0
  49. package/dist/tools/audit-schema.d.ts +34 -0
  50. package/dist/tools/audit-schema.d.ts.map +1 -0
  51. package/dist/tools/audit-schema.js +65 -0
  52. package/dist/tools/audit-schema.js.map +1 -0
  53. package/dist/tools/check-robots.d.ts +22 -0
  54. package/dist/tools/check-robots.d.ts.map +1 -0
  55. package/dist/tools/check-robots.js +142 -0
  56. package/dist/tools/check-robots.js.map +1 -0
  57. package/dist/tools/check-sitemap.d.ts +27 -0
  58. package/dist/tools/check-sitemap.d.ts.map +1 -0
  59. package/dist/tools/check-sitemap.js +214 -0
  60. package/dist/tools/check-sitemap.js.map +1 -0
  61. package/dist/tools/check-technical.d.ts +50 -0
  62. package/dist/tools/check-technical.d.ts.map +1 -0
  63. package/dist/tools/check-technical.js +209 -0
  64. package/dist/tools/check-technical.js.map +1 -0
  65. package/dist/tools/extract-entities.d.ts +35 -0
  66. package/dist/tools/extract-entities.d.ts.map +1 -0
  67. package/dist/tools/extract-entities.js +69 -0
  68. package/dist/tools/extract-entities.js.map +1 -0
  69. package/dist/tools/generate-llms-txt.d.ts +32 -0
  70. package/dist/tools/generate-llms-txt.d.ts.map +1 -0
  71. package/dist/tools/generate-llms-txt.js +157 -0
  72. package/dist/tools/generate-llms-txt.js.map +1 -0
  73. package/dist/tools/rewrite-for-aeo.d.ts +50 -0
  74. package/dist/tools/rewrite-for-aeo.d.ts.map +1 -0
  75. package/dist/tools/rewrite-for-aeo.js +119 -0
  76. package/dist/tools/rewrite-for-aeo.js.map +1 -0
  77. package/dist/tools/rewrite-for-geo.d.ts +51 -0
  78. package/dist/tools/rewrite-for-geo.d.ts.map +1 -0
  79. package/dist/tools/rewrite-for-geo.js +115 -0
  80. package/dist/tools/rewrite-for-geo.js.map +1 -0
  81. package/dist/tools/score-ai-overview-eligibility.d.ts +34 -0
  82. package/dist/tools/score-ai-overview-eligibility.d.ts.map +1 -0
  83. package/dist/tools/score-ai-overview-eligibility.js +186 -0
  84. package/dist/tools/score-ai-overview-eligibility.js.map +1 -0
  85. package/dist/tools/score-citation-worthiness.d.ts +51 -0
  86. package/dist/tools/score-citation-worthiness.d.ts.map +1 -0
  87. package/dist/tools/score-citation-worthiness.js +145 -0
  88. package/dist/tools/score-citation-worthiness.js.map +1 -0
  89. package/dist/tools/validate-llms-txt.d.ts +33 -0
  90. package/dist/tools/validate-llms-txt.d.ts.map +1 -0
  91. package/dist/tools/validate-llms-txt.js +62 -0
  92. package/dist/tools/validate-llms-txt.js.map +1 -0
  93. package/dist/types.d.ts +48 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/types.js +3 -0
  96. package/dist/types.js.map +1 -0
  97. package/package.json +60 -0
  98. package/src/lib/crawlers.json +6 -0
  99. package/src/lib/schema-types.json +56 -0
@@ -0,0 +1,122 @@
1
+ // Tool: audit_canonical
2
+ // Canonical link integrity, trailing-slash hygiene, self-referencing, og:url mismatches.
3
+ import { z } from "zod";
4
+ import { politeFetch } from "../lib/fetch.js";
5
+ import { parseHead } from "../lib/html.js";
6
+ export const auditCanonicalInputSchema = z.object({
7
+ url: z.string().url(),
8
+ respect_robots: z.boolean().optional().default(true),
9
+ });
10
+ export async function auditCanonical(input, hostDelays, robotsCache) {
11
+ const result = await politeFetch(input.url, {
12
+ respectRobots: input.respect_robots,
13
+ hostDelays,
14
+ robotsCache,
15
+ });
16
+ const head = parseHead(result.body);
17
+ const findings = [];
18
+ const finalUrl = result.finalUrl;
19
+ let isSelfRef = false;
20
+ let isCrossDomain = false;
21
+ let trailingSlashConsistent = true;
22
+ let canonicalOgUrlMatch = true;
23
+ if (!head.canonical) {
24
+ findings.push({
25
+ severity: "warning",
26
+ category: "technical",
27
+ where: '<link rel="canonical">',
28
+ message: "No canonical link element found.",
29
+ fix: `Add <link rel="canonical" href="${finalUrl}"> to <head>.`,
30
+ estimated_impact: "medium",
31
+ });
32
+ }
33
+ else {
34
+ try {
35
+ const pageUrl = new URL(finalUrl);
36
+ const canonUrl = new URL(head.canonical, input.url);
37
+ isSelfRef =
38
+ canonUrl.hostname === pageUrl.hostname &&
39
+ canonUrl.pathname.replace(/\/$/, "") === pageUrl.pathname.replace(/\/$/, "");
40
+ isCrossDomain = canonUrl.hostname !== pageUrl.hostname;
41
+ // Trailing slash consistency
42
+ const pageHasSlash = pageUrl.pathname.endsWith("/");
43
+ const canonHasSlash = canonUrl.pathname.endsWith("/");
44
+ if (pageUrl.pathname !== "/" &&
45
+ canonUrl.pathname !== "/" &&
46
+ pageHasSlash !== canonHasSlash) {
47
+ trailingSlashConsistent = false;
48
+ findings.push({
49
+ severity: "warning",
50
+ category: "technical",
51
+ where: '<link rel="canonical">',
52
+ message: `Trailing slash inconsistency: page "${pageUrl.pathname}" vs canonical "${canonUrl.pathname}".`,
53
+ fix: "Ensure canonical href and page URL use the same trailing slash convention. Pick one and redirect all variants.",
54
+ estimated_impact: "low",
55
+ });
56
+ }
57
+ if (isCrossDomain) {
58
+ findings.push({
59
+ severity: "warning",
60
+ category: "technical",
61
+ where: '<link rel="canonical">',
62
+ message: `Canonical points to a different domain: ${canonUrl.hostname}.`,
63
+ fix: "Verify this is intentional (syndicated content). If not, update to a self-referencing canonical.",
64
+ estimated_impact: "medium",
65
+ });
66
+ }
67
+ else if (!isSelfRef) {
68
+ findings.push({
69
+ severity: "warning",
70
+ category: "technical",
71
+ where: '<link rel="canonical">',
72
+ message: "Canonical does not self-reference the current page URL.",
73
+ fix: `Update canonical to: <link rel="canonical" href="${finalUrl}">.`,
74
+ estimated_impact: "medium",
75
+ });
76
+ }
77
+ }
78
+ catch {
79
+ findings.push({
80
+ severity: "warning",
81
+ category: "technical",
82
+ where: '<link rel="canonical">',
83
+ message: `Canonical value "${head.canonical}" is not a valid URL.`,
84
+ fix: "Replace with a valid absolute URL.",
85
+ estimated_impact: "medium",
86
+ });
87
+ }
88
+ // og:url vs canonical mismatch
89
+ if (head.ogUrl && head.canonical) {
90
+ try {
91
+ const canonUrl = new URL(head.canonical, input.url);
92
+ const ogUrl = new URL(head.ogUrl, input.url);
93
+ canonicalOgUrlMatch =
94
+ canonUrl.hostname === ogUrl.hostname && canonUrl.pathname === ogUrl.pathname;
95
+ if (!canonicalOgUrlMatch) {
96
+ findings.push({
97
+ severity: "warning",
98
+ category: "technical",
99
+ where: "og:url vs canonical",
100
+ message: "og:url does not match canonical URL.",
101
+ fix: `Set og:url to match the canonical URL: <meta property="og:url" content="${head.canonical}">.`,
102
+ estimated_impact: "low",
103
+ });
104
+ }
105
+ }
106
+ catch {
107
+ // ignore URL parse errors
108
+ }
109
+ }
110
+ }
111
+ return {
112
+ url: input.url,
113
+ final_url: finalUrl,
114
+ canonical_value: head.canonical,
115
+ is_self_referential: isSelfRef,
116
+ is_cross_domain: isCrossDomain,
117
+ trailing_slash_consistent: trailingSlashConsistent,
118
+ canonical_og_url_match: canonicalOgUrlMatch,
119
+ findings,
120
+ };
121
+ }
122
+ //# sourceMappingURL=audit-canonical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-canonical.js","sourceRoot":"","sources":["../../src/tools/audit-canonical.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ import type { AuditResult } from "../types.js";
3
+ export declare const auditPageInputSchema: z.ZodObject<{
4
+ url: z.ZodString;
5
+ include_raw_html: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
6
+ respect_robots: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ url: string;
9
+ respect_robots: boolean;
10
+ include_raw_html: boolean;
11
+ }, {
12
+ url: string;
13
+ respect_robots?: boolean | undefined;
14
+ include_raw_html?: boolean | undefined;
15
+ }>;
16
+ export type AuditPageInput = z.infer<typeof auditPageInputSchema>;
17
+ export interface AuditPageResult extends AuditResult {
18
+ dimension_scores: {
19
+ schema: number;
20
+ robots: number;
21
+ technical: number;
22
+ freshness: number;
23
+ structure: number;
24
+ authority: number;
25
+ entity_density: number;
26
+ sitemap: number;
27
+ };
28
+ raw_html?: string;
29
+ }
30
+ export declare function auditPage(input: AuditPageInput): Promise<AuditPageResult>;
31
+ //# sourceMappingURL=audit-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-page.d.ts","sourceRoot":"","sources":["../../src/tools/audit-page.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,aAAa,CAAC;AAExD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAI/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,gBAAgB,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAsO/E"}
@@ -0,0 +1,221 @@
1
+ // Tool: audit_page
2
+ // Full AI-SEO audit of a single URL. Runs all sub-audits and returns a composite score.
3
+ import { z } from "zod";
4
+ import { politeFetch, ToolFetchError } from "../lib/fetch.js";
5
+ import { parseHead, parseBody } from "../lib/html.js";
6
+ import { parseJsonLd, getAllSchemaTypes } from "../lib/schema.js";
7
+ import { checkTechnical } from "./check-technical.js";
8
+ import { auditSchema } from "./audit-schema.js";
9
+ import { checkRobots } from "./check-robots.js";
10
+ import { checkSitemap } from "./check-sitemap.js";
11
+ import { freshnessScore, deriveGrade, computeWeightedScore } from "../lib/score.js";
12
+ export const auditPageInputSchema = z.object({
13
+ url: z.string().url(),
14
+ include_raw_html: z.boolean().optional().default(false),
15
+ respect_robots: z.boolean().optional().default(true),
16
+ });
17
+ export async function auditPage(input) {
18
+ const hostDelays = new Map();
19
+ const robotsCache = new Map();
20
+ // Fetch URL once
21
+ const result = await politeFetch(input.url, {
22
+ respectRobots: input.respect_robots,
23
+ hostDelays,
24
+ robotsCache,
25
+ });
26
+ const ct = result.headers["content-type"];
27
+ const ctStr = Array.isArray(ct) ? ct[0] : (ct ?? "");
28
+ if (ctStr && !ctStr.includes("html")) {
29
+ throw new ToolFetchError({
30
+ type: "non_html_response",
31
+ url: input.url,
32
+ content_type: ctStr,
33
+ });
34
+ }
35
+ const fetched_at = new Date().toISOString();
36
+ const allFindings = [];
37
+ // --- Schema dimension ---
38
+ let schemaScore = 50;
39
+ try {
40
+ const schemaResult = await auditSchema({ url: input.url, respect_robots: input.respect_robots }, hostDelays, robotsCache);
41
+ schemaScore = schemaResult.ai_citation_readiness_score;
42
+ allFindings.push(...schemaResult.findings);
43
+ }
44
+ catch {
45
+ // schema audit failed - use default score
46
+ }
47
+ // --- Technical dimension ---
48
+ let technicalScore = 50;
49
+ try {
50
+ const techResult = await checkTechnical({ url: input.url, respect_robots: input.respect_robots }, hostDelays, robotsCache);
51
+ // Derive technical score from findings
52
+ const techFindings = techResult.findings;
53
+ allFindings.push(...techFindings);
54
+ const criticals = techFindings.filter((f) => f.severity === "critical").length;
55
+ const warnings = techFindings.filter((f) => f.severity === "warning").length;
56
+ technicalScore = Math.max(0, 100 - criticals * 20 - warnings * 8);
57
+ // noindex is a killer
58
+ if (techResult.noindex)
59
+ technicalScore = Math.max(0, technicalScore - 30);
60
+ }
61
+ catch {
62
+ // technical audit failed
63
+ }
64
+ // --- Robots dimension ---
65
+ let robotsScore = 70;
66
+ try {
67
+ const hostname = new URL(input.url).hostname;
68
+ const robotsResult = await checkRobots({ domain: hostname });
69
+ const robotsFindings = robotsResult.findings.filter((f) => f.severity === "critical" || f.severity === "warning");
70
+ allFindings.push(...robotsResult.findings);
71
+ robotsScore = Math.max(0, 100 -
72
+ robotsFindings.filter((f) => f.severity === "critical").length * 15 -
73
+ robotsFindings.filter((f) => f.severity === "warning").length * 7);
74
+ }
75
+ catch {
76
+ // robots check failed
77
+ }
78
+ // --- Page content for structure/freshness/authority/entity_density ---
79
+ const head = parseHead(result.body);
80
+ const body = parseBody(result.body, input.url);
81
+ const jsonLdBlocks = parseJsonLd(result.body);
82
+ const foundTypes = getAllSchemaTypes(jsonLdBlocks);
83
+ // --- Freshness dimension ---
84
+ let dateModified = null;
85
+ for (const b of jsonLdBlocks) {
86
+ const dm = b.parsed["dateModified"] ?? b.parsed["datePublished"];
87
+ if (typeof dm === "string") {
88
+ dateModified = dm;
89
+ break;
90
+ }
91
+ }
92
+ const freshnessScoreVal = freshnessScore(dateModified);
93
+ if (freshnessScoreVal < 50) {
94
+ allFindings.push({
95
+ severity: "warning",
96
+ category: "freshness",
97
+ where: "Article.dateModified",
98
+ message: dateModified
99
+ ? "Content appears stale (dateModified > 90 days ago)."
100
+ : "No dateModified found in structured data.",
101
+ fix: "Update content and set dateModified to today in Article JSON-LD.",
102
+ estimated_impact: "medium",
103
+ });
104
+ }
105
+ // --- Structure dimension ---
106
+ const hasFaq = foundTypes.includes("FAQPage") || body.h3s.some((h) => h.endsWith("?"));
107
+ const hasHowTo = foundTypes.includes("HowTo");
108
+ const hasOrderedList = body.orderedLists > 0;
109
+ const hasTable = body.tables > 0;
110
+ const goodHeadings = body.h2s.length >= 2;
111
+ let structureScore = 20;
112
+ if (hasFaq)
113
+ structureScore += 30;
114
+ if (hasHowTo)
115
+ structureScore += 15;
116
+ if (hasOrderedList)
117
+ structureScore += 15;
118
+ if (hasTable)
119
+ structureScore += 10;
120
+ if (goodHeadings)
121
+ structureScore += 10;
122
+ structureScore = Math.min(100, structureScore);
123
+ if (!hasFaq) {
124
+ allFindings.push({
125
+ severity: "critical",
126
+ category: "structure",
127
+ where: "<body>",
128
+ message: "No FAQ structure found (no FAQPage schema or H3 question headings).",
129
+ fix: "Add FAQ H3 headings ending in '?' with answer paragraphs, and a FAQPage JSON-LD block.",
130
+ estimated_impact: "high",
131
+ });
132
+ }
133
+ // --- Authority dimension ---
134
+ const hasOrg = foundTypes.includes("Organization");
135
+ const hasPerson = jsonLdBlocks.some((b) => b.types.includes("Person"));
136
+ const hasArticleWithAuthorNode = jsonLdBlocks.some((b) => b.types.some((t) => ["Article", "BlogPosting", "NewsArticle"].includes(t)) &&
137
+ typeof b.parsed["author"] === "object" &&
138
+ b.parsed["author"] !== null);
139
+ let authorityScore = 10;
140
+ if (hasOrg)
141
+ authorityScore += 30;
142
+ if (hasPerson || hasArticleWithAuthorNode)
143
+ authorityScore += 30;
144
+ const sameAsCount = jsonLdBlocks.reduce((acc, b) => {
145
+ const sa = b.parsed["sameAs"];
146
+ return acc + (Array.isArray(sa) ? sa.length : sa ? 1 : 0);
147
+ }, 0);
148
+ if (sameAsCount > 0)
149
+ authorityScore += Math.min(30, sameAsCount * 5);
150
+ authorityScore = Math.min(100, authorityScore);
151
+ if (authorityScore < 40) {
152
+ allFindings.push({
153
+ severity: "warning",
154
+ category: "authority",
155
+ where: "page-level",
156
+ message: "Low authority signals - missing Organization or author Person schema.",
157
+ fix: "Add Organization JSON-LD and Article.author as a Person node with sameAs links.",
158
+ estimated_impact: "high",
159
+ });
160
+ }
161
+ // --- Entity density dimension ---
162
+ const authoritativeDomains = [
163
+ "wikipedia.org", "wikidata.org", ".gov", "linkedin.com", "twitter.com",
164
+ "x.com", "crunchbase.com", "bloomberg.com", "reuters.com",
165
+ ];
166
+ const authExternalLinks = body.externalLinks.filter((href) => authoritativeDomains.some((d) => href.includes(d))).length;
167
+ const entityDensityScore = Math.min(100, (sameAsCount + authExternalLinks) * 7);
168
+ // --- Sitemap dimension ---
169
+ let sitemapScore = 50;
170
+ try {
171
+ const hostname = new URL(input.url).hostname;
172
+ const sitemapResult = await checkSitemap({ domain: hostname, max_urls_to_check: 50 }, hostDelays, robotsCache);
173
+ if (sitemapResult.status === "found") {
174
+ sitemapScore = 80;
175
+ if (sitemapResult.urls_with_lastmod / Math.max(1, sitemapResult.total_urls) > 0.8) {
176
+ sitemapScore = 100;
177
+ }
178
+ }
179
+ else {
180
+ sitemapScore = 20;
181
+ }
182
+ }
183
+ catch {
184
+ // sitemap check failed
185
+ }
186
+ // --- Weighted composite score ---
187
+ const dimensionScores = {
188
+ schema: schemaScore,
189
+ robots: robotsScore,
190
+ technical: technicalScore,
191
+ freshness: freshnessScoreVal,
192
+ structure: structureScore,
193
+ authority: authorityScore,
194
+ entity_density: entityDensityScore,
195
+ sitemap: sitemapScore,
196
+ };
197
+ const score = computeWeightedScore(dimensionScores);
198
+ const grade = deriveGrade(score);
199
+ // Deduplicate findings (same message from multiple sub-tools)
200
+ const seen = new Set();
201
+ const deduped = allFindings.filter((f) => {
202
+ const key = `${f.severity}:${f.category}:${f.message}`;
203
+ if (seen.has(key))
204
+ return false;
205
+ seen.add(key);
206
+ return true;
207
+ });
208
+ const output = {
209
+ url: input.url,
210
+ fetched_at,
211
+ findings: deduped,
212
+ score,
213
+ grade,
214
+ dimension_scores: dimensionScores,
215
+ };
216
+ if (input.include_raw_html) {
217
+ output.raw_html = result.body;
218
+ }
219
+ return output;
220
+ }
221
+ //# sourceMappingURL=audit-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-page.js","sourceRoot":"","sources":["../../src/tools/audit-page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ import { type HostDelayMap } from "../lib/fetch.js";
3
+ import type { Finding } from "../types.js";
4
+ export declare const auditSchemaInputSchema: z.ZodEffects<z.ZodObject<{
5
+ url: z.ZodOptional<z.ZodString>;
6
+ schema_json: z.ZodOptional<z.ZodString>;
7
+ respect_robots: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ respect_robots: boolean;
10
+ url?: string | undefined;
11
+ schema_json?: string | undefined;
12
+ }, {
13
+ url?: string | undefined;
14
+ respect_robots?: boolean | undefined;
15
+ schema_json?: string | undefined;
16
+ }>, {
17
+ respect_robots: boolean;
18
+ url?: string | undefined;
19
+ schema_json?: string | undefined;
20
+ }, {
21
+ url?: string | undefined;
22
+ respect_robots?: boolean | undefined;
23
+ schema_json?: string | undefined;
24
+ }>;
25
+ export type AuditSchemaInput = z.infer<typeof auditSchemaInputSchema>;
26
+ export interface AuditSchemaResult {
27
+ source: "url" | "inline";
28
+ found_types: string[];
29
+ missing_priority_types: string[];
30
+ findings: Finding[];
31
+ ai_citation_readiness_score: number;
32
+ }
33
+ export declare function auditSchema(input: AuditSchemaInput, hostDelays?: HostDelayMap, robotsCache?: Map<string, string>): Promise<AuditSchemaResult>;
34
+ //# sourceMappingURL=audit-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-schema.d.ts","sourceRoot":"","sources":["../../src/tools/audit-schema.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEjF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;EAQ/B,CAAC;AAEL,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,KAAK,GAAG,QAAQ,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,2BAA2B,EAAE,MAAM,CAAC;CACrC;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,gBAAgB,EACvB,UAAU,CAAC,EAAE,YAAY,EACzB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,OAAO,CAAC,iBAAiB,CAAC,CAqD5B"}
@@ -0,0 +1,65 @@
1
+ // Tool: audit_schema
2
+ // Validates JSON-LD structured data against Schema.org rules and AI-citation best practices.
3
+ import { z } from "zod";
4
+ import { politeFetch, ToolFetchError } from "../lib/fetch.js";
5
+ import { parseJsonLd, getAllSchemaTypes, getMissingPriorityTypes, validateJsonLd, computeSchemaScore } from "../lib/schema.js";
6
+ export const auditSchemaInputSchema = z
7
+ .object({
8
+ url: z.string().url().optional(),
9
+ schema_json: z.string().optional(),
10
+ respect_robots: z.boolean().optional().default(true),
11
+ })
12
+ .refine((d) => d.url !== undefined || d.schema_json !== undefined, {
13
+ message: "One of url or schema_json is required",
14
+ });
15
+ export async function auditSchema(input, hostDelays, robotsCache) {
16
+ let html;
17
+ let source;
18
+ if (input.schema_json) {
19
+ source = "inline";
20
+ // Wrap inline JSON in minimal HTML
21
+ html = `<html><head><script type="application/ld+json">${input.schema_json}</script></head></html>`;
22
+ }
23
+ else {
24
+ source = "url";
25
+ const result = await politeFetch(input.url, {
26
+ respectRobots: input.respect_robots,
27
+ hostDelays,
28
+ robotsCache,
29
+ });
30
+ const ct = result.headers["content-type"];
31
+ const ctStr = Array.isArray(ct) ? ct[0] : (ct ?? "");
32
+ if (!ctStr.includes("html") && !ctStr.includes("xml") && ctStr !== "") {
33
+ throw new ToolFetchError({
34
+ type: "non_html_response",
35
+ url: input.url,
36
+ content_type: ctStr,
37
+ });
38
+ }
39
+ html = result.body;
40
+ }
41
+ const blocks = parseJsonLd(html);
42
+ const foundTypes = getAllSchemaTypes(blocks);
43
+ const missingPriorityTypes = getMissingPriorityTypes(foundTypes);
44
+ const findings = validateJsonLd(blocks);
45
+ // Add finding for no schema at all
46
+ if (foundTypes.length === 0 && source === "url") {
47
+ findings.push({
48
+ severity: "critical",
49
+ category: "schema",
50
+ where: "<head>",
51
+ message: "No JSON-LD structured data found on this page.",
52
+ fix: 'Add at least one <script type="application/ld+json"> block. Start with Article or FAQPage.',
53
+ estimated_impact: "high",
54
+ });
55
+ }
56
+ const ai_citation_readiness_score = computeSchemaScore(findings);
57
+ return {
58
+ source,
59
+ found_types: foundTypes,
60
+ missing_priority_types: missingPriorityTypes,
61
+ findings,
62
+ ai_citation_readiness_score,
63
+ };
64
+ }
65
+ //# sourceMappingURL=audit-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-schema.js","sourceRoot":"","sources":["../../src/tools/audit-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ import { type CrawlerStatus } from "../lib/robots.js";
3
+ import type { Finding } from "../types.js";
4
+ export declare const checkRobotsInputSchema: z.ZodObject<{
5
+ domain: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ domain: string;
8
+ }, {
9
+ domain: string;
10
+ }>;
11
+ export type CheckRobotsInput = z.infer<typeof checkRobotsInputSchema>;
12
+ export interface RobotsResult {
13
+ robots_url: string;
14
+ fetched_at: string;
15
+ training_crawlers: Record<string, CrawlerStatus>;
16
+ search_crawlers: Record<string, CrawlerStatus>;
17
+ user_triggered: Record<string, CrawlerStatus>;
18
+ findings: Finding[];
19
+ recommended_posture: "block_training_allow_search" | "allow_all" | "block_all" | "custom";
20
+ }
21
+ export declare function checkRobots(input: CheckRobotsInput): Promise<RobotsResult>;
22
+ //# sourceMappingURL=check-robots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-robots.d.ts","sourceRoot":"","sources":["../../src/tools/check-robots.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAgD,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,eAAO,MAAM,sBAAsB;;;;;;EAEjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACjD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC/C,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,mBAAmB,EAAE,6BAA6B,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;CAC3F;AAeD,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CA+IhF"}