@automatelab/ai-seo-mcp 0.1.3 → 0.3.4

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 (62) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +14 -1
  3. package/action.yml +70 -0
  4. package/dist/cli/audit-cli.d.ts +3 -0
  5. package/dist/cli/audit-cli.d.ts.map +1 -0
  6. package/dist/cli/audit-cli.js +136 -0
  7. package/dist/cli/audit-cli.js.map +1 -0
  8. package/dist/index.js +246 -5
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/cache.d.ts +12 -0
  11. package/dist/lib/cache.d.ts.map +1 -0
  12. package/dist/lib/cache.js +51 -0
  13. package/dist/lib/cache.js.map +1 -0
  14. package/dist/lib/config.js +1 -1
  15. package/dist/lib/fetch.d.ts +5 -0
  16. package/dist/lib/fetch.d.ts.map +1 -1
  17. package/dist/lib/fetch.js +42 -1
  18. package/dist/lib/fetch.js.map +1 -1
  19. package/dist/lib/headless.d.ts +13 -0
  20. package/dist/lib/headless.d.ts.map +1 -0
  21. package/dist/lib/headless.js +73 -0
  22. package/dist/lib/headless.js.map +1 -0
  23. package/dist/lib/scorecard-html.js +94 -94
  24. package/dist/tools/audit-page.d.ts +11 -0
  25. package/dist/tools/audit-page.d.ts.map +1 -1
  26. package/dist/tools/audit-page.js +49 -2
  27. package/dist/tools/audit-page.js.map +1 -1
  28. package/dist/tools/audit-schema.d.ts +2 -1
  29. package/dist/tools/audit-schema.d.ts.map +1 -1
  30. package/dist/tools/audit-schema.js +2 -1
  31. package/dist/tools/audit-schema.js.map +1 -1
  32. package/dist/tools/audit-site.d.ts +41 -0
  33. package/dist/tools/audit-site.d.ts.map +1 -0
  34. package/dist/tools/audit-site.js +113 -0
  35. package/dist/tools/audit-site.js.map +1 -0
  36. package/dist/tools/audit-sitemap.d.ts +66 -0
  37. package/dist/tools/audit-sitemap.d.ts.map +1 -0
  38. package/dist/tools/audit-sitemap.js +264 -0
  39. package/dist/tools/audit-sitemap.js.map +1 -0
  40. package/dist/tools/check-technical.d.ts +2 -1
  41. package/dist/tools/check-technical.d.ts.map +1 -1
  42. package/dist/tools/check-technical.js +2 -1
  43. package/dist/tools/check-technical.js.map +1 -1
  44. package/dist/tools/diff-pages.js +2 -2
  45. package/dist/tools/diff-pages.js.map +1 -1
  46. package/dist/tools/extract-entities.d.ts +8 -1
  47. package/dist/tools/extract-entities.d.ts.map +1 -1
  48. package/dist/tools/extract-entities.js +110 -3
  49. package/dist/tools/extract-entities.js.map +1 -1
  50. package/dist/tools/rewrite-for-aeo.js +28 -28
  51. package/dist/tools/rewrite-for-geo.js +28 -28
  52. package/dist/tools/save-audit-report.d.ts +22 -0
  53. package/dist/tools/save-audit-report.d.ts.map +1 -0
  54. package/dist/tools/save-audit-report.js +133 -0
  55. package/dist/tools/save-audit-report.js.map +1 -0
  56. package/dist/tools/test-citation.d.ts +51 -0
  57. package/dist/tools/test-citation.d.ts.map +1 -0
  58. package/dist/tools/test-citation.js +159 -0
  59. package/dist/tools/test-citation.js.map +1 -0
  60. package/package.json +10 -2
  61. package/src/lib/crawlers.json +6 -6
  62. package/src/lib/schema-types.json +56 -56
@@ -17,20 +17,20 @@ export const rewriteForAeoInputSchema = z
17
17
  .refine((d) => d.url !== undefined || d.text !== undefined, {
18
18
  message: "One of url or text is required",
19
19
  });
20
- const AEO_SYSTEM_PROMPT = `You are an Answer Engine Optimization (AEO) specialist. Rewrite the provided content to maximize AI engine citation probability.
21
-
22
- Rules:
23
- 1. Open with a direct 40-60 word answer to the target query (BLUF - Bottom Line Up Front).
24
- 2. Structure body content into FAQ format: H3 questions ending in "?" followed by 40-60 word answers.
25
- 3. Include at least one ordered list for procedural content.
26
- 4. Define key technical terms inline (e.g., "X is a type of Y that...").
27
- 5. Cite statistics with year and source where present.
28
- 6. End with a "Key Takeaways" or "Summary" section.
29
- 7. No em-dashes. No filler phrases ("In conclusion", "It is important to note").
30
- 8. Keep within the max_words limit.
31
- 9. For howto format: use numbered HowToStep structure.
32
- 10. For comparison format: include a comparison table.
33
-
20
+ const AEO_SYSTEM_PROMPT = `You are an Answer Engine Optimization (AEO) specialist. Rewrite the provided content to maximize AI engine citation probability.
21
+
22
+ Rules:
23
+ 1. Open with a direct 40-60 word answer to the target query (BLUF - Bottom Line Up Front).
24
+ 2. Structure body content into FAQ format: H3 questions ending in "?" followed by 40-60 word answers.
25
+ 3. Include at least one ordered list for procedural content.
26
+ 4. Define key technical terms inline (e.g., "X is a type of Y that...").
27
+ 5. Cite statistics with year and source where present.
28
+ 6. End with a "Key Takeaways" or "Summary" section.
29
+ 7. No em-dashes. No filler phrases ("In conclusion", "It is important to note").
30
+ 8. Keep within the max_words limit.
31
+ 9. For howto format: use numbered HowToStep structure.
32
+ 10. For comparison format: include a comparison table.
33
+
34
34
  Also generate a JSON-LD schema block appropriate for the content type and format.`;
35
35
  export async function rewriteForAeo(input, hostDelays, robotsCache, server) {
36
36
  // Fetch URL if provided
@@ -47,18 +47,18 @@ export async function rewriteForAeo(input, hostDelays, robotsCache, server) {
47
47
  // Compute before score
48
48
  const beforeResult = await scoreCitationWorthiness({ text: originalText, target_query: input.target_query, respect_robots: false }, hostDelays, robotsCache);
49
49
  const before_score = beforeResult.overall_score;
50
- const userMessage = `Target query: "${input.target_query}"
51
- Format: ${input.format}
52
- Max words: ${input.max_words}
53
-
54
- Original content:
55
- ---
56
- ${originalText.substring(0, 4000)}
57
- ---
58
-
59
- Rewrite this content for AEO. Return your response as JSON with these fields:
60
- - rewritten_text: the rewritten content (Markdown)
61
- - schema_additions: JSON-LD string to add to the page <head>
50
+ const userMessage = `Target query: "${input.target_query}"
51
+ Format: ${input.format}
52
+ Max words: ${input.max_words}
53
+
54
+ Original content:
55
+ ---
56
+ ${originalText.substring(0, 4000)}
57
+ ---
58
+
59
+ Rewrite this content for AEO. Return your response as JSON with these fields:
60
+ - rewritten_text: the rewritten content (Markdown)
61
+ - schema_additions: JSON-LD string to add to the page <head>
62
62
  - changes_made: array of strings describing each change applied`;
63
63
  // Attempt MCP sampling
64
64
  if (server) {
@@ -102,8 +102,8 @@ Rewrite this content for AEO. Return your response as JSON with these fields:
102
102
  }
103
103
  }
104
104
  // Prompt template fallback
105
- const promptTemplate = `${AEO_SYSTEM_PROMPT}
106
-
105
+ const promptTemplate = `${AEO_SYSTEM_PROMPT}
106
+
107
107
  ${userMessage}`;
108
108
  return {
109
109
  rewritten_text: promptTemplate,
@@ -16,19 +16,19 @@ export const rewriteForGeoInputSchema = z
16
16
  .refine((d) => d.url !== undefined || d.text !== undefined, {
17
17
  message: "One of url or text is required",
18
18
  });
19
- const GEO_SYSTEM_PROMPT = `You are a Generative Engine Optimization (GEO) specialist focused on Google AI Overviews and multi-source synthesis engines.
20
-
21
- Rules:
22
- 1. Define every key entity inline on first mention: "X (also known as Y) is a type of Z that...".
23
- 2. Include authoritative external citations with hyperlinks (Wikipedia, official docs, research papers).
24
- 3. Add a comparison table if requested or if the query implies comparison.
25
- 4. Use numbered feature lists for technical capabilities.
26
- 5. Structure for multi-source synthesis: each section should be independently citable without reading the full article.
27
- 6. Cite statistics with source, year, and context.
28
- 7. Add a "Related entities" section at the end listing key entities with sameAs links.
29
- 8. No em-dashes. No filler phrases.
30
- 9. Keep within the max_words limit.
31
-
19
+ const GEO_SYSTEM_PROMPT = `You are a Generative Engine Optimization (GEO) specialist focused on Google AI Overviews and multi-source synthesis engines.
20
+
21
+ Rules:
22
+ 1. Define every key entity inline on first mention: "X (also known as Y) is a type of Z that...".
23
+ 2. Include authoritative external citations with hyperlinks (Wikipedia, official docs, research papers).
24
+ 3. Add a comparison table if requested or if the query implies comparison.
25
+ 4. Use numbered feature lists for technical capabilities.
26
+ 5. Structure for multi-source synthesis: each section should be independently citable without reading the full article.
27
+ 6. Cite statistics with source, year, and context.
28
+ 7. Add a "Related entities" section at the end listing key entities with sameAs links.
29
+ 8. No em-dashes. No filler phrases.
30
+ 9. Keep within the max_words limit.
31
+
32
32
  Generate JSON-LD schema for all key entities mentioned (Organization, Person, Product, SoftwareApplication as appropriate) with sameAs links.`;
33
33
  export async function rewriteForGeo(input, hostDelays, robotsCache, server) {
34
34
  let originalText = input.text ?? "";
@@ -43,19 +43,19 @@ export async function rewriteForGeo(input, hostDelays, robotsCache, server) {
43
43
  }
44
44
  const beforeResult = await scoreCitationWorthiness({ text: originalText, target_query: input.target_query, respect_robots: false }, hostDelays, robotsCache);
45
45
  const before_score = beforeResult.overall_score;
46
- const userMessage = `Target query: "${input.target_query}"
47
- Add comparison table: ${input.add_comparison_table}
48
- Max words: ${input.max_words}
49
-
50
- Original content:
51
- ---
52
- ${originalText.substring(0, 4000)}
53
- ---
54
-
55
- Rewrite this content for GEO. Return JSON with:
56
- - rewritten_text: Markdown content
57
- - schema_additions: JSON-LD for key entities
58
- - changes_made: array of changes applied
46
+ const userMessage = `Target query: "${input.target_query}"
47
+ Add comparison table: ${input.add_comparison_table}
48
+ Max words: ${input.max_words}
49
+
50
+ Original content:
51
+ ---
52
+ ${originalText.substring(0, 4000)}
53
+ ---
54
+
55
+ Rewrite this content for GEO. Return JSON with:
56
+ - rewritten_text: Markdown content
57
+ - schema_additions: JSON-LD for key entities
58
+ - changes_made: array of changes applied
59
59
  - entities_added: array of entity names you defined or linked`;
60
60
  if (server) {
61
61
  try {
@@ -97,8 +97,8 @@ Rewrite this content for GEO. Return JSON with:
97
97
  // sampling unavailable
98
98
  }
99
99
  }
100
- const promptTemplate = `${GEO_SYSTEM_PROMPT}
101
-
100
+ const promptTemplate = `${GEO_SYSTEM_PROMPT}
101
+
102
102
  ${userMessage}`;
103
103
  return {
104
104
  rewritten_text: promptTemplate,
@@ -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"),
9
+ path: z.string().min(1).describe("Target file path. May be relative to MCP_WORKSPACE_ROOT (or cwd)."),
10
+ overwrite: z.boolean().optional().default(true),
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":"AAAA,sBAAsB;AACtB,kFAAkF;AAClF,wFAAwF;AACxF,kFAAkF;AAClF,mDAAmD;AAEnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAqB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC;KACrC,MAAM,CAAC;IACN,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAChC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,qBAAqB,EAAE,KAAK,CAAC,CAAC;SACvE,QAAQ,EAAE;SACV,OAAO,CAAC,KAAK,CAAC;IACjB,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACrD,CAAC;KACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE;IAC1D,OAAO,EAAE,gCAAgC;CAC1C,CAAC,CAAC;AAgBL,MAAM,eAAe,GAAgD;IACnE,OAAO,EACL,2KAA2K;IAC7K,MAAM,EACJ,2KAA2K;IAC7K,UAAU,EACR,4LAA4L;IAC9L,mBAAmB,EACjB,uLAAuL;IACzL,GAAG,EACD,iKAAiK;CACpK,CAAC;AAEF,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;mGAiBsE,CAAC;AAWpG,KAAK,UAAU,WAAW,CACxB,QAAgB,EAChB,GAAuB,EACvB,WAAmB,EACnB,MAAmC,EACnC,MAAiB;IAEjB,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,WAAW,WAAW;EAC1C,GAAG,CAAC,CAAC,CAAC,aAAa,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE;;;EAG7B,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;;;0CAGa,CAAC;IAEzC,IAAI,CAAC;QACH,6DAA6D;QAC7D,yFAAyF;QACzF,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAChD;YACE,MAAM,EAAE,wBAAwB;YAChC,MAAM,EAAE;gBACN,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;gBAC1E,YAAY,EAAE,GAAG,oBAAoB,gBAAgB,OAAO,EAAE;gBAC9D,SAAS,EAAE,IAAI;aAChB;SACF;QACD,8DAA8D;QAC9D,EAAS,CACV,CAAC;QACF,MAAM,IAAI,GACR,cAAc,EAAE,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;QAC5E,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACtF,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA4B,CAAC;QAC9D,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACxD,OAAO;YACL,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;YACpG,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,IAAI;YACjD,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;YACjC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE;YACpF,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;SAC5E,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAwB,EACxB,UAAyB,EACzB,WAAiC,EACjC,MAAkB;IAElB,IAAI,QAAQ,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;IAChC,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE;YAC1C,aAAa,EAAE,KAAK,CAAC,cAAc;YACnC,UAAU;YACV,WAAW;SACZ,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACjD,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED,wBAAwB;IACxB,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjG,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO;gBACL,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;gBAC1C,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,IAAI,EAAE,UAAU;aACjB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,sDAAsD;IACtD,MAAM,KAAK,GAAG,MAAM,uBAAuB,CACzC,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,EAC3E,UAAU,EACV,WAAW,CACZ,CAAC;IAEF,yEAAyE;IACzE,MAAM,cAAc,GAAiF;QACnG,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE,YAAY;QACxB,mBAAmB,EAAE,qBAAqB;QAC1C,GAAG,EAAE,IAAI;KACV,CAAC;IACF,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC;IACzE,MAAM,SAAS,GAAG,KAAK,CAAC,aAAa,IAAI,EAAE,IAAI,WAAW,IAAI,EAAE,CAAC;IAEjE,MAAM,QAAQ,GAAa,KAAK,CAAC,QAAQ;SACtC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC;SACpE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAEzB,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC;SACpE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,MAAM,EAAE,CAAC,CAAC,GAAG;QACb,gBAAgB,EAAE,CAAC,CAAC,CAAC,gBAAgB,IAAI,QAAQ,CAA8B;KAChF,CAAC,CAAC,CAAC;IAEN,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC/D,gBAAgB,EAAE,IAAI;QACtB,SAAS,EAAE,SAAS;YAClB,CAAC,CAAC,yCAAyC,KAAK,CAAC,aAAa,YAAY,KAAK,CAAC,MAAM,aAAa,WAAW,qCAAqC;YACnJ,CAAC,CAAC,yCAAyC,KAAK,CAAC,aAAa,YAAY,KAAK,CAAC,MAAM,aAAa,WAAW,2GAA2G;QAC3N,eAAe,EAAE,QAAQ;QACzB,YAAY;QACZ,IAAI,EAAE,kBAAkB;KACzB,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automatelab/ai-seo-mcp",
3
- "version": "0.1.3",
3
+ "version": "0.3.4",
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",
@@ -13,7 +13,8 @@
13
13
  "engines": { "node": ">=20" },
14
14
  "type": "module",
15
15
  "bin": {
16
- "ai-seo-mcp": "dist/index.js"
16
+ "ai-seo-mcp": "dist/index.js",
17
+ "ai-seo-audit": "dist/cli/audit-cli.js"
17
18
  },
18
19
  "main": "dist/index.js",
19
20
  "scripts": {
@@ -33,6 +34,12 @@
33
34
  "undici": "7.25.0",
34
35
  "zod": "3.25.76"
35
36
  },
37
+ "peerDependencies": {
38
+ "playwright-core": "^1.49.0"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "playwright-core": { "optional": true }
42
+ },
36
43
  "devDependencies": {
37
44
  "@types/node": "22.19.19",
38
45
  "tsx": "4.22.0",
@@ -43,6 +50,7 @@
43
50
  "dist",
44
51
  "src/lib/crawlers.json",
45
52
  "src/lib/schema-types.json",
53
+ "action.yml",
46
54
  "README.md",
47
55
  "LICENSE"
48
56
  ],
@@ -1,6 +1,6 @@
1
- {
2
- "training": ["GPTBot", "ClaudeBot", "CCBot", "Meta-ExternalAgent", "Amazonbot"],
3
- "search": ["OAI-SearchBot", "Claude-SearchBot", "PerplexityBot", "Bingbot", "DuckAssistBot"],
4
- "user_triggered": ["ChatGPT-User", "Claude-User", "Google-Agent"],
5
- "robots_token_only": ["Google-Extended", "Applebot-Extended"]
6
- }
1
+ {
2
+ "training": ["GPTBot", "ClaudeBot", "CCBot", "Meta-ExternalAgent", "Amazonbot"],
3
+ "search": ["OAI-SearchBot", "Claude-SearchBot", "PerplexityBot", "Bingbot", "DuckAssistBot"],
4
+ "user_triggered": ["ChatGPT-User", "Claude-User", "Google-Agent"],
5
+ "robots_token_only": ["Google-Extended", "Applebot-Extended"]
6
+ }