@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,119 @@
1
+ // Tool: rewrite_for_aeo
2
+ // Rewrites content for Answer Engine Optimization via MCP sampling.
3
+ // Falls back to returning a structured prompt template if sampling is unavailable.
4
+ import { z } from "zod";
5
+ import { politeFetch } from "../lib/fetch.js";
6
+ import { parseBody } from "../lib/html.js";
7
+ import { scoreCitationWorthiness } from "./score-citation-worthiness.js";
8
+ export const rewriteForAeoInputSchema = z
9
+ .object({
10
+ url: z.string().url().optional(),
11
+ text: z.string().optional(),
12
+ target_query: z.string(),
13
+ format: z.enum(["article", "faq", "howto", "comparison"]).default("article"),
14
+ max_words: z.number().int().min(100).max(5000).optional().default(1500),
15
+ respect_robots: z.boolean().optional().default(true),
16
+ })
17
+ .refine((d) => d.url !== undefined || d.text !== undefined, {
18
+ message: "One of url or text is required",
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
+
34
+ Also generate a JSON-LD schema block appropriate for the content type and format.`;
35
+ export async function rewriteForAeo(input, hostDelays, robotsCache, server) {
36
+ // Fetch URL if provided
37
+ let originalText = input.text ?? "";
38
+ if (input.url) {
39
+ const result = await politeFetch(input.url, {
40
+ respectRobots: input.respect_robots,
41
+ hostDelays,
42
+ robotsCache,
43
+ });
44
+ const body = parseBody(result.body, input.url);
45
+ originalText = body.bodyText.substring(0, 8000);
46
+ }
47
+ // Compute before score
48
+ const beforeResult = await scoreCitationWorthiness({ text: originalText, target_query: input.target_query, respect_robots: false }, hostDelays, robotsCache);
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>
62
+ - changes_made: array of strings describing each change applied`;
63
+ // Attempt MCP sampling
64
+ if (server) {
65
+ try {
66
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
67
+ // @ts-ignore - sampling API availability varies by client; not typed in all SDK versions
68
+ const samplingResult = await server.server.request({
69
+ method: "sampling/createMessage",
70
+ params: {
71
+ messages: [{ role: "user", content: { type: "text", text: userMessage } }],
72
+ systemPrompt: AEO_SYSTEM_PROMPT,
73
+ maxTokens: 4096,
74
+ },
75
+ },
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ {});
78
+ const text = samplingResult?.content?.text ?? samplingResult?.content?.[0]?.text ?? "";
79
+ if (text) {
80
+ try {
81
+ // Extract JSON from response (may be wrapped in markdown code blocks)
82
+ const jsonMatch = text.match(/```json\n([\s\S]+?)\n```/) ?? text.match(/\{[\s\S]+\}/);
83
+ const jsonStr = jsonMatch ? jsonMatch[1] ?? jsonMatch[0] : text;
84
+ const parsed = JSON.parse(jsonStr);
85
+ const afterResult = await scoreCitationWorthiness({ text: parsed.rewritten_text, target_query: input.target_query, respect_robots: false }, hostDelays, robotsCache);
86
+ return {
87
+ rewritten_text: parsed.rewritten_text,
88
+ schema_additions: parsed.schema_additions,
89
+ changes_made: parsed.changes_made ?? [],
90
+ before_score,
91
+ after_score: afterResult.overall_score,
92
+ mode: "sampling",
93
+ };
94
+ }
95
+ catch {
96
+ // JSON parse failed - fall through to prompt template
97
+ }
98
+ }
99
+ }
100
+ catch {
101
+ // Sampling unavailable or failed - fall through to prompt template
102
+ }
103
+ }
104
+ // Prompt template fallback
105
+ const promptTemplate = `${AEO_SYSTEM_PROMPT}
106
+
107
+ ${userMessage}`;
108
+ return {
109
+ rewritten_text: promptTemplate,
110
+ schema_additions: "",
111
+ changes_made: [
112
+ "sampling/createMessage unavailable - returned prompt template for manual use",
113
+ ],
114
+ before_score,
115
+ after_score: before_score, // unchanged since we did not rewrite
116
+ mode: "prompt_template",
117
+ };
118
+ }
119
+ //# sourceMappingURL=rewrite-for-aeo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rewrite-for-aeo.js","sourceRoot":"","sources":["../../src/tools/rewrite-for-aeo.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 rewriteForGeoInputSchema: z.ZodEffects<z.ZodObject<{
5
+ url: z.ZodOptional<z.ZodString>;
6
+ text: z.ZodOptional<z.ZodString>;
7
+ target_query: z.ZodString;
8
+ add_comparison_table: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
9
+ max_words: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
10
+ respect_robots: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ respect_robots: boolean;
13
+ target_query: string;
14
+ max_words: number;
15
+ add_comparison_table: boolean;
16
+ url?: string | undefined;
17
+ text?: string | undefined;
18
+ }, {
19
+ target_query: string;
20
+ url?: string | undefined;
21
+ respect_robots?: boolean | undefined;
22
+ text?: string | undefined;
23
+ max_words?: number | undefined;
24
+ add_comparison_table?: boolean | undefined;
25
+ }>, {
26
+ respect_robots: boolean;
27
+ target_query: string;
28
+ max_words: number;
29
+ add_comparison_table: boolean;
30
+ url?: string | undefined;
31
+ text?: string | undefined;
32
+ }, {
33
+ target_query: string;
34
+ url?: string | undefined;
35
+ respect_robots?: boolean | undefined;
36
+ text?: string | undefined;
37
+ max_words?: number | undefined;
38
+ add_comparison_table?: boolean | undefined;
39
+ }>;
40
+ export type RewriteForGeoInput = z.infer<typeof rewriteForGeoInputSchema>;
41
+ export interface RewriteGeoResult {
42
+ rewritten_text: string;
43
+ schema_additions: string;
44
+ changes_made: string[];
45
+ before_score: number;
46
+ after_score: number;
47
+ entities_added: string[];
48
+ mode: "sampling" | "prompt_template";
49
+ }
50
+ export declare function rewriteForGeo(input: RewriteForGeoInput, hostDelays?: HostDelayMap, robotsCache?: Map<string, string>, server?: McpServer): Promise<RewriteGeoResult>;
51
+ //# sourceMappingURL=rewrite-for-geo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rewrite-for-geo.d.ts","sourceRoot":"","sources":["../../src/tools/rewrite-for-geo.ts"],"names":[],"mappings":"AAGA,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,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWjC,CAAC;AAEL,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAAC;CACtC;AAiBD,wBAAsB,aAAa,CACjC,KAAK,EAAE,kBAAkB,EACzB,UAAU,CAAC,EAAE,YAAY,EACzB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,MAAM,CAAC,EAAE,SAAS,GACjB,OAAO,CAAC,gBAAgB,CAAC,CAsG3B"}
@@ -0,0 +1,115 @@
1
+ // Tool: rewrite_for_geo
2
+ // Rewrites content for Generative Engine Optimization (entity-rich, synthesis-ready structure).
3
+ import { z } from "zod";
4
+ import { politeFetch } from "../lib/fetch.js";
5
+ import { parseBody } from "../lib/html.js";
6
+ import { scoreCitationWorthiness } from "./score-citation-worthiness.js";
7
+ export const rewriteForGeoInputSchema = z
8
+ .object({
9
+ url: z.string().url().optional(),
10
+ text: z.string().optional(),
11
+ target_query: z.string(),
12
+ add_comparison_table: z.boolean().optional().default(false),
13
+ max_words: z.number().int().min(100).max(5000).optional().default(1500),
14
+ respect_robots: z.boolean().optional().default(true),
15
+ })
16
+ .refine((d) => d.url !== undefined || d.text !== undefined, {
17
+ message: "One of url or text is required",
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
+
32
+ Generate JSON-LD schema for all key entities mentioned (Organization, Person, Product, SoftwareApplication as appropriate) with sameAs links.`;
33
+ export async function rewriteForGeo(input, hostDelays, robotsCache, server) {
34
+ let originalText = input.text ?? "";
35
+ if (input.url) {
36
+ const result = await politeFetch(input.url, {
37
+ respectRobots: input.respect_robots,
38
+ hostDelays,
39
+ robotsCache,
40
+ });
41
+ const body = parseBody(result.body, input.url);
42
+ originalText = body.bodyText.substring(0, 8000);
43
+ }
44
+ const beforeResult = await scoreCitationWorthiness({ text: originalText, target_query: input.target_query, respect_robots: false }, hostDelays, robotsCache);
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
59
+ - entities_added: array of entity names you defined or linked`;
60
+ if (server) {
61
+ try {
62
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
63
+ // @ts-ignore - sampling API availability varies by client
64
+ const samplingResult = await server.server.request({
65
+ method: "sampling/createMessage",
66
+ params: {
67
+ messages: [{ role: "user", content: { type: "text", text: userMessage } }],
68
+ systemPrompt: GEO_SYSTEM_PROMPT,
69
+ maxTokens: 4096,
70
+ },
71
+ },
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ {});
74
+ const text = samplingResult?.content?.text ?? samplingResult?.content?.[0]?.text ?? "";
75
+ if (text) {
76
+ try {
77
+ const jsonMatch = text.match(/```json\n([\s\S]+?)\n```/) ?? text.match(/\{[\s\S]+\}/);
78
+ const jsonStr = jsonMatch ? jsonMatch[1] ?? jsonMatch[0] : text;
79
+ const parsed = JSON.parse(jsonStr);
80
+ const afterResult = await scoreCitationWorthiness({ text: parsed.rewritten_text, target_query: input.target_query, respect_robots: false }, hostDelays, robotsCache);
81
+ return {
82
+ rewritten_text: parsed.rewritten_text,
83
+ schema_additions: parsed.schema_additions,
84
+ changes_made: parsed.changes_made ?? [],
85
+ before_score,
86
+ after_score: afterResult.overall_score,
87
+ entities_added: parsed.entities_added ?? [],
88
+ mode: "sampling",
89
+ };
90
+ }
91
+ catch {
92
+ // fall through to prompt template
93
+ }
94
+ }
95
+ }
96
+ catch {
97
+ // sampling unavailable
98
+ }
99
+ }
100
+ const promptTemplate = `${GEO_SYSTEM_PROMPT}
101
+
102
+ ${userMessage}`;
103
+ return {
104
+ rewritten_text: promptTemplate,
105
+ schema_additions: "",
106
+ changes_made: [
107
+ "sampling/createMessage unavailable - returned prompt template for manual use",
108
+ ],
109
+ before_score,
110
+ after_score: before_score,
111
+ entities_added: [],
112
+ mode: "prompt_template",
113
+ };
114
+ }
115
+ //# sourceMappingURL=rewrite-for-geo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rewrite-for-geo.js","sourceRoot":"","sources":["../../src/tools/rewrite-for-geo.ts"],"names":[],"mappings":"AAAA,wBAAwB;AACxB,gGAAgG;AAEhG,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,wBAAwB,GAAG,CAAC;KACtC,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;IACxB,oBAAoB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC3D,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACvE,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;AAcL,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;8IAaoH,CAAC;AAE/I,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAyB,EACzB,UAAyB,EACzB,WAAiC,EACjC,MAAkB;IAElB,IAAI,YAAY,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;IACpC,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,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/C,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,uBAAuB,CAChD,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,EAC/E,UAAU,EACV,WAAW,CACZ,CAAC;IACF,MAAM,YAAY,GAAG,YAAY,CAAC,aAAa,CAAC;IAEhD,MAAM,WAAW,GAAG,kBAAkB,KAAK,CAAC,YAAY;wBAClC,KAAK,CAAC,oBAAoB;aACrC,KAAK,CAAC,SAAS;;;;EAI1B,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;;;;;;;8DAO6B,CAAC;IAE7D,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,6DAA6D;YAC7D,0DAA0D;YAC1D,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAChD;gBACE,MAAM,EAAE,wBAAwB;gBAChC,MAAM,EAAE;oBACN,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;oBAC1E,YAAY,EAAE,iBAAiB;oBAC/B,SAAS,EAAE,IAAI;iBAChB;aACF;YACD,8DAA8D;YAC9D,EAAS,CACV,CAAC;YACF,MAAM,IAAI,GACR,cAAc,EAAE,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;YAC5E,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;oBACtF,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;oBAChE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAKhC,CAAC;oBAEF,MAAM,WAAW,GAAG,MAAM,uBAAuB,CAC/C,EAAE,IAAI,EAAE,MAAM,CAAC,cAAc,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,EACxF,UAAU,EACV,WAAW,CACZ,CAAC;oBAEF,OAAO;wBACL,cAAc,EAAE,MAAM,CAAC,cAAc;wBACrC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;wBACzC,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE;wBACvC,YAAY;wBACZ,WAAW,EAAE,WAAW,CAAC,aAAa;wBACtC,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,EAAE;wBAC3C,IAAI,EAAE,UAAU;qBACjB,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBACP,kCAAkC;gBACpC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;IACH,CAAC;IAED,MAAM,cAAc,GAAG,GAAG,iBAAiB;;EAE3C,WAAW,EAAE,CAAC;IAEd,OAAO;QACL,cAAc,EAAE,cAAc;QAC9B,gBAAgB,EAAE,EAAE;QACpB,YAAY,EAAE;YACZ,8EAA8E;SAC/E;QACD,YAAY;QACZ,WAAW,EAAE,YAAY;QACzB,cAAc,EAAE,EAAE;QAClB,IAAI,EAAE,iBAAiB;KACxB,CAAC;AACJ,CAAC"}
@@ -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 scoreAiOverviewEligibilityInputSchema: z.ZodObject<{
5
+ url: z.ZodString;
6
+ respect_robots: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ url: string;
9
+ respect_robots: boolean;
10
+ }, {
11
+ url: string;
12
+ respect_robots?: boolean | undefined;
13
+ }>;
14
+ export type ScoreAiOverviewEligibilityInput = z.infer<typeof scoreAiOverviewEligibilityInputSchema>;
15
+ export interface AiOverviewEligibilityResult {
16
+ url: string;
17
+ overall_eligibility_score: number;
18
+ factors: {
19
+ semantic_completeness: number;
20
+ structured_data: number;
21
+ eeat_signals: number;
22
+ entity_density: number;
23
+ content_freshness: number;
24
+ passage_structure: number;
25
+ multimodal_content: number;
26
+ };
27
+ top_improvements: Array<{
28
+ action: string;
29
+ estimated_score_gain: number;
30
+ }>;
31
+ findings: Finding[];
32
+ }
33
+ export declare function scoreAiOverviewEligibility(input: ScoreAiOverviewEligibilityInput, hostDelays?: HostDelayMap, robotsCache?: Map<string, string>): Promise<AiOverviewEligibilityResult>;
34
+ //# sourceMappingURL=score-ai-overview-eligibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score-ai-overview-eligibility.d.ts","sourceRoot":"","sources":["../../src/tools/score-ai-overview-eligibility.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,eAAO,MAAM,qCAAqC;;;;;;;;;EAGhD,CAAC;AAEH,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qCAAqC,CAAC,CAAC;AAEpG,MAAM,WAAW,2BAA2B;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,yBAAyB,EAAE,MAAM,CAAC;IAClC,OAAO,EAAE;QACP,qBAAqB,EAAE,MAAM,CAAC;QAC9B,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,gBAAgB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,+BAA+B,EACtC,UAAU,CAAC,EAAE,YAAY,EACzB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,OAAO,CAAC,2BAA2B,CAAC,CAqLtC"}
@@ -0,0 +1,186 @@
1
+ // Tool: score_ai_overview_eligibility
2
+ // Scores a page's probability of appearing in Google AI Overviews using correlation factors.
3
+ import { z } from "zod";
4
+ import { politeFetch } from "../lib/fetch.js";
5
+ import { parseHead, parseBody } from "../lib/html.js";
6
+ import { parseJsonLd, getAllSchemaTypes } from "../lib/schema.js";
7
+ import { freshnessScore } from "../lib/score.js";
8
+ export const scoreAiOverviewEligibilityInputSchema = z.object({
9
+ url: z.string().url(),
10
+ respect_robots: z.boolean().optional().default(true),
11
+ });
12
+ export async function scoreAiOverviewEligibility(input, hostDelays, robotsCache) {
13
+ const result = await politeFetch(input.url, {
14
+ respectRobots: input.respect_robots,
15
+ hostDelays,
16
+ robotsCache,
17
+ });
18
+ const head = parseHead(result.body);
19
+ const body = parseBody(result.body, input.url);
20
+ const jsonLdBlocks = parseJsonLd(result.body);
21
+ const foundTypes = getAllSchemaTypes(jsonLdBlocks);
22
+ const findings = [];
23
+ // 1. Semantic completeness - count paragraphs with 100-200 words
24
+ const selfContainedBlocks = body.paragraphs.filter((p) => {
25
+ const words = p.split(/\s+/).filter((w) => w.length > 0);
26
+ return words.length >= 100 && words.length <= 200;
27
+ });
28
+ const semantic_completeness = Math.min(100, selfContainedBlocks.length * 25);
29
+ // 2. Structured data presence - tier 1 types
30
+ const tier1Types = ["FAQPage", "HowTo", "Article", "BlogPosting", "NewsArticle", "Organization"];
31
+ const tier1Found = tier1Types.filter((t) => foundTypes.includes(t));
32
+ const structured_data = Math.min(100, tier1Found.length * 25);
33
+ // 3. E-E-A-T signals: author Person, Organization, sameAs
34
+ let eeatScore = 30; // base
35
+ const hasOrg = foundTypes.includes("Organization");
36
+ const hasPerson = jsonLdBlocks.some((b) => b.types.includes("Person"));
37
+ const hasSameAs = jsonLdBlocks.some((b) => {
38
+ const sa = b.parsed["sameAs"];
39
+ return Array.isArray(sa) ? sa.length > 0 : !!sa;
40
+ });
41
+ const hasArticleWithAuthorNode = jsonLdBlocks.some((b) => b.types.some((t) => ["Article", "BlogPosting", "NewsArticle"].includes(t)) &&
42
+ typeof b.parsed["author"] === "object" &&
43
+ b.parsed["author"] !== null);
44
+ if (hasOrg)
45
+ eeatScore += 20;
46
+ if (hasPerson)
47
+ eeatScore += 20;
48
+ if (hasSameAs)
49
+ eeatScore += 15;
50
+ if (hasArticleWithAuthorNode)
51
+ eeatScore += 15;
52
+ const eeat_signals = Math.min(100, eeatScore);
53
+ // 4. Entity density - sameAs links + authoritative external hrefs
54
+ const authoritativeDomains = [
55
+ "wikipedia.org", "wikidata.org", ".gov", "linkedin.com", "twitter.com",
56
+ "x.com", "crunchbase.com", "bloomberg.com", "reuters.com",
57
+ ];
58
+ const sameAsCount = jsonLdBlocks.reduce((acc, b) => {
59
+ const sa = b.parsed["sameAs"];
60
+ return acc + (Array.isArray(sa) ? sa.length : sa ? 1 : 0);
61
+ }, 0);
62
+ const authExternalLinks = body.externalLinks.filter((href) => authoritativeDomains.some((d) => href.includes(d))).length;
63
+ const entity_density = Math.min(100, (sameAsCount + authExternalLinks) * 7);
64
+ // 5. Content freshness - from JSON-LD dateModified
65
+ let dateModified = null;
66
+ for (const b of jsonLdBlocks) {
67
+ const dm = b.parsed["dateModified"] ?? b.parsed["datePublished"];
68
+ if (typeof dm === "string") {
69
+ dateModified = dm;
70
+ break;
71
+ }
72
+ }
73
+ const content_freshness = freshnessScore(dateModified);
74
+ // 6. Passage structure - FAQ, comparison table, ordered list
75
+ const hasH3Questions = body.h3s.some((h) => h.endsWith("?"));
76
+ const hasFaqSchema = foundTypes.includes("FAQPage");
77
+ const hasFaq = hasH3Questions || hasFaqSchema;
78
+ const hasComparisonTable = body.tables > 0;
79
+ const hasOrderedList = body.orderedLists > 0;
80
+ let passage_structure = 0;
81
+ if (hasFaq)
82
+ passage_structure += 25;
83
+ if (hasComparisonTable)
84
+ passage_structure += 25;
85
+ if (hasOrderedList)
86
+ passage_structure += 25;
87
+ if (body.h2s.length >= 3)
88
+ passage_structure += 25; // good heading structure
89
+ passage_structure = Math.min(100, passage_structure);
90
+ // 7. Multimodal content
91
+ let multimodal = 0;
92
+ if (body.images > 0)
93
+ multimodal += 30;
94
+ if (body.videos > 0)
95
+ multimodal += 30;
96
+ if (foundTypes.includes("ImageObject"))
97
+ multimodal += 20;
98
+ if (foundTypes.includes("VideoObject"))
99
+ multimodal += 20;
100
+ const multimodal_content = Math.min(100, multimodal);
101
+ // Weighted score per spec:
102
+ // semantic_completeness 25%, structured_data 25%, passage_structure 20%,
103
+ // eeat_signals 15%, entity_density 10%, content_freshness 3%, multimodal_content 2%
104
+ const overall_eligibility_score = Math.round(semantic_completeness * 0.25 +
105
+ structured_data * 0.25 +
106
+ passage_structure * 0.20 +
107
+ eeat_signals * 0.15 +
108
+ entity_density * 0.10 +
109
+ content_freshness * 0.03 +
110
+ multimodal_content * 0.02);
111
+ // Top improvements
112
+ const improvements = [];
113
+ if (!hasFaqSchema) {
114
+ improvements.push({
115
+ action: "Add FAQPage JSON-LD schema with 3-5 Q&A pairs targeting sub-queries",
116
+ estimated_score_gain: Math.round(25 * 0.25 + 25 * 0.20), // structured_data + passage_structure
117
+ });
118
+ }
119
+ if (!hasArticleWithAuthorNode) {
120
+ improvements.push({
121
+ action: "Add Article JSON-LD with author as a Person node including sameAs links",
122
+ estimated_score_gain: Math.round(15 * 0.15),
123
+ });
124
+ }
125
+ if (!hasComparisonTable) {
126
+ improvements.push({
127
+ action: "Add a comparison table or step-by-step list to improve passage structure",
128
+ estimated_score_gain: Math.round(25 * 0.20),
129
+ });
130
+ }
131
+ if (semantic_completeness < 50) {
132
+ improvements.push({
133
+ action: "Expand body paragraphs to 100-200 words each for self-contained answer blocks",
134
+ estimated_score_gain: Math.round(50 * 0.25),
135
+ });
136
+ }
137
+ improvements.sort((a, b) => b.estimated_score_gain - a.estimated_score_gain);
138
+ const top_improvements = improvements.slice(0, 3);
139
+ // Key findings
140
+ if (!hasFaqSchema && !hasH3Questions) {
141
+ findings.push({
142
+ severity: "warning",
143
+ category: "structure",
144
+ where: "page-level",
145
+ message: "No FAQ structure found (no FAQPage schema and no H3 questions).",
146
+ fix: "Add FAQ H3 headings ending in '?' with answer paragraphs, plus FAQPage JSON-LD.",
147
+ estimated_impact: "high",
148
+ });
149
+ }
150
+ if (eeat_signals < 50) {
151
+ findings.push({
152
+ severity: "warning",
153
+ category: "authority",
154
+ where: "page-level",
155
+ message: "Weak E-E-A-T signals - no Organization or author Person schema found.",
156
+ fix: "Add Organization JSON-LD to site-wide head, and Article.author as a Person node.",
157
+ estimated_impact: "high",
158
+ });
159
+ }
160
+ if (content_freshness < 50 && dateModified) {
161
+ findings.push({
162
+ severity: "warning",
163
+ category: "freshness",
164
+ where: "Article.dateModified",
165
+ message: "Content appears stale (dateModified > 90 days ago).",
166
+ fix: "Update the page content and set dateModified to today in the Article JSON-LD.",
167
+ estimated_impact: "medium",
168
+ });
169
+ }
170
+ return {
171
+ url: input.url,
172
+ overall_eligibility_score,
173
+ factors: {
174
+ semantic_completeness,
175
+ structured_data,
176
+ eeat_signals,
177
+ entity_density,
178
+ content_freshness,
179
+ passage_structure,
180
+ multimodal_content,
181
+ },
182
+ top_improvements,
183
+ findings,
184
+ };
185
+ }
186
+ //# sourceMappingURL=score-ai-overview-eligibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score-ai-overview-eligibility.js","sourceRoot":"","sources":["../../src/tools/score-ai-overview-eligibility.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,6FAA6F;AAE7F,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAqB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5D,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACrD,CAAC,CAAC;AAoBH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAAsC,EACtC,UAAyB,EACzB,WAAiC;IAEjC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE;QAC1C,aAAa,EAAE,KAAK,CAAC,cAAc;QACnC,UAAU;QACV,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,iEAAiE;IACjE,MAAM,mBAAmB,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACvD,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACzD,OAAO,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,MAAM,qBAAqB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,mBAAmB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAE7E,6CAA6C;IAC7C,MAAM,UAAU,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC;IACjG,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAE9D,0DAA0D;IAC1D,IAAI,SAAS,GAAG,EAAE,CAAC,CAAC,OAAO;IAC3B,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvE,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QACxC,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IACH,MAAM,wBAAwB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACvD,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,QAAQ;QACtC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,CAC5B,CAAC;IACF,IAAI,MAAM;QAAE,SAAS,IAAI,EAAE,CAAC;IAC5B,IAAI,SAAS;QAAE,SAAS,IAAI,EAAE,CAAC;IAC/B,IAAI,SAAS;QAAE,SAAS,IAAI,EAAE,CAAC;IAC/B,IAAI,wBAAwB;QAAE,SAAS,IAAI,EAAE,CAAC;IAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAE9C,kEAAkE;IAClE,MAAM,oBAAoB,GAAG;QAC3B,eAAe,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa;QACtE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa;KAC1D,CAAC;IACF,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QACjD,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC9B,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC,EAAE,CAAC,CAAC,CAAC;IACN,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAC3D,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CACnD,CAAC,MAAM,CAAC;IACT,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;IAE5E,mDAAmD;IACnD,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACjE,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC3B,YAAY,GAAG,EAAE,CAAC;YAClB,MAAM;QACR,CAAC;IACH,CAAC;IACD,MAAM,iBAAiB,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IAEvD,6DAA6D;IAC7D,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,cAAc,IAAI,YAAY,CAAC;IAC9C,MAAM,kBAAkB,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3C,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IAC7C,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,MAAM;QAAE,iBAAiB,IAAI,EAAE,CAAC;IACpC,IAAI,kBAAkB;QAAE,iBAAiB,IAAI,EAAE,CAAC;IAChD,IAAI,cAAc;QAAE,iBAAiB,IAAI,EAAE,CAAC;IAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC;QAAE,iBAAiB,IAAI,EAAE,CAAC,CAAC,yBAAyB;IAC5E,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAErD,wBAAwB;IACxB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,UAAU,IAAI,EAAE,CAAC;IACtC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,UAAU,IAAI,EAAE,CAAC;IACtC,IAAI,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,UAAU,IAAI,EAAE,CAAC;IACzD,IAAI,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,UAAU,IAAI,EAAE,CAAC;IACzD,MAAM,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAErD,2BAA2B;IAC3B,yEAAyE;IACzE,oFAAoF;IACpF,MAAM,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAC1C,qBAAqB,GAAG,IAAI;QAC5B,eAAe,GAAG,IAAI;QACtB,iBAAiB,GAAG,IAAI;QACxB,YAAY,GAAG,IAAI;QACnB,cAAc,GAAG,IAAI;QACrB,iBAAiB,GAAG,IAAI;QACxB,kBAAkB,GAAG,IAAI,CAC1B,CAAC;IAEF,mBAAmB;IACnB,MAAM,YAAY,GAA4D,EAAE,CAAC;IAEjF,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,qEAAqE;YAC7E,oBAAoB,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,sCAAsC;SAChG,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAC9B,YAAY,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,yEAAyE;YACjF,oBAAoB,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC;SAC5C,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,YAAY,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,0EAA0E;YAClF,oBAAoB,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC;SAC5C,CAAC,CAAC;IACL,CAAC;IACD,IAAI,qBAAqB,GAAG,EAAE,EAAE,CAAC;QAC/B,YAAY,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,+EAA+E;YACvF,oBAAoB,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC;SAC5C,CAAC,CAAC;IACL,CAAC;IAED,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,oBAAoB,GAAG,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAC7E,MAAM,gBAAgB,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAElD,eAAe;IACf,IAAI,CAAC,YAAY,IAAI,CAAC,cAAc,EAAE,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,WAAW;YACrB,KAAK,EAAE,YAAY;YACnB,OAAO,EAAE,iEAAiE;YAC1E,GAAG,EAAE,iFAAiF;YACtF,gBAAgB,EAAE,MAAM;SACzB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,YAAY,GAAG,EAAE,EAAE,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,WAAW;YACrB,KAAK,EAAE,YAAY;YACnB,OAAO,EAAE,uEAAuE;YAChF,GAAG,EAAE,kFAAkF;YACvF,gBAAgB,EAAE,MAAM;SACzB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,iBAAiB,GAAG,EAAE,IAAI,YAAY,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,WAAW;YACrB,KAAK,EAAE,sBAAsB;YAC7B,OAAO,EAAE,qDAAqD;YAC9D,GAAG,EAAE,+EAA+E;YACpF,gBAAgB,EAAE,QAAQ;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,yBAAyB;QACzB,OAAO,EAAE;YACP,qBAAqB;YACrB,eAAe;YACf,YAAY;YACZ,cAAc;YACd,iBAAiB;YACjB,iBAAiB;YACjB,kBAAkB;SACnB;QACD,gBAAgB;QAChB,QAAQ;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import { type HostDelayMap } from "../lib/fetch.js";
3
+ import type { Finding } from "../types.js";
4
+ export declare const scoreCitationWorthinessInputSchema: z.ZodEffects<z.ZodObject<{
5
+ url: z.ZodOptional<z.ZodString>;
6
+ text: z.ZodOptional<z.ZodString>;
7
+ target_query: z.ZodOptional<z.ZodString>;
8
+ respect_robots: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ respect_robots: boolean;
11
+ url?: string | undefined;
12
+ text?: string | undefined;
13
+ target_query?: string | undefined;
14
+ }, {
15
+ url?: string | undefined;
16
+ respect_robots?: boolean | undefined;
17
+ text?: string | undefined;
18
+ target_query?: string | undefined;
19
+ }>, {
20
+ respect_robots: boolean;
21
+ url?: string | undefined;
22
+ text?: string | undefined;
23
+ target_query?: string | undefined;
24
+ }, {
25
+ url?: string | undefined;
26
+ respect_robots?: boolean | undefined;
27
+ text?: string | undefined;
28
+ target_query?: string | undefined;
29
+ }>;
30
+ export type ScoreCitationWorthinessInput = z.infer<typeof scoreCitationWorthinessInputSchema>;
31
+ export interface CitationWorthinessResult {
32
+ overall_score: number;
33
+ engine_scores: {
34
+ perplexity: number;
35
+ chatgpt: number;
36
+ google_ai_overviews: number;
37
+ claude: number;
38
+ };
39
+ sub_scores: {
40
+ bluf_present: boolean;
41
+ faq_blocks: number;
42
+ statistic_density: number;
43
+ entity_clarity: number;
44
+ heading_question_ratio: number;
45
+ answer_completeness: number;
46
+ };
47
+ improvements: string[];
48
+ findings: Finding[];
49
+ }
50
+ export declare function scoreCitationWorthiness(input: ScoreCitationWorthinessInput, hostDelays?: HostDelayMap, robotsCache?: Map<string, string>): Promise<CitationWorthinessResult>;
51
+ //# sourceMappingURL=score-citation-worthiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score-citation-worthiness.d.ts","sourceRoot":"","sources":["../../src/tools/score-citation-worthiness.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;EAS3C,CAAC;AAEL,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,UAAU,EAAE;QACV,YAAY,EAAE,OAAO,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,EAAE,MAAM,CAAC;QACvB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AA+BD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,EACnC,UAAU,CAAC,EAAE,YAAY,EACzB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,OAAO,CAAC,wBAAwB,CAAC,CA2HnC"}