ax-audit 4.2.1 → 6.0.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 (188) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +39 -4
  3. package/dist/baseline.js +1 -1
  4. package/dist/baseline.js.map +1 -1
  5. package/dist/check-ids.js +1 -1
  6. package/dist/check-ids.js.map +1 -1
  7. package/dist/cli.js +1 -1
  8. package/dist/cli.js.map +1 -1
  9. package/dist/constants.d.ts +1 -85
  10. package/dist/constants.d.ts.map +1 -1
  11. package/dist/constants.js +0 -135
  12. package/dist/constants.js.map +1 -1
  13. package/dist/index.d.ts +3 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/license.d.ts +8 -0
  18. package/dist/license.d.ts.map +1 -0
  19. package/dist/license.js +18 -0
  20. package/dist/license.js.map +1 -0
  21. package/dist/metadata.d.ts +6 -0
  22. package/dist/metadata.d.ts.map +1 -0
  23. package/dist/metadata.js +205 -0
  24. package/dist/metadata.js.map +1 -0
  25. package/dist/orchestrator.d.ts +1 -0
  26. package/dist/orchestrator.d.ts.map +1 -1
  27. package/dist/orchestrator.js +134 -56
  28. package/dist/orchestrator.js.map +1 -1
  29. package/dist/scorer.js +2 -2
  30. package/dist/scorer.js.map +1 -1
  31. package/dist/types.d.ts +4 -0
  32. package/dist/types.d.ts.map +1 -1
  33. package/docs/api.md +14 -3
  34. package/docs/architecture.md +9 -98
  35. package/docs/ci.md +19 -0
  36. package/docs/cli.md +11 -0
  37. package/docs/faq.md +18 -0
  38. package/docs/getting-started.md +33 -1
  39. package/package.json +5 -5
  40. package/dist/checks/agent-access.d.ts +0 -33
  41. package/dist/checks/agent-access.d.ts.map +0 -1
  42. package/dist/checks/agent-access.js +0 -256
  43. package/dist/checks/agent-access.js.map +0 -1
  44. package/dist/checks/agent-card.d.ts +0 -37
  45. package/dist/checks/agent-card.d.ts.map +0 -1
  46. package/dist/checks/agent-card.js +0 -352
  47. package/dist/checks/agent-card.js.map +0 -1
  48. package/dist/checks/agent-operability.d.ts +0 -66
  49. package/dist/checks/agent-operability.d.ts.map +0 -1
  50. package/dist/checks/agent-operability.js +0 -383
  51. package/dist/checks/agent-operability.js.map +0 -1
  52. package/dist/checks/agent-skills.d.ts +0 -24
  53. package/dist/checks/agent-skills.d.ts.map +0 -1
  54. package/dist/checks/agent-skills.js +0 -316
  55. package/dist/checks/agent-skills.js.map +0 -1
  56. package/dist/checks/ai-catalog.d.ts +0 -28
  57. package/dist/checks/ai-catalog.d.ts.map +0 -1
  58. package/dist/checks/ai-catalog.js +0 -254
  59. package/dist/checks/ai-catalog.js.map +0 -1
  60. package/dist/checks/ai-directives.d.ts +0 -57
  61. package/dist/checks/ai-directives.d.ts.map +0 -1
  62. package/dist/checks/ai-directives.js +0 -263
  63. package/dist/checks/ai-directives.js.map +0 -1
  64. package/dist/checks/api-discovery.d.ts +0 -26
  65. package/dist/checks/api-discovery.d.ts.map +0 -1
  66. package/dist/checks/api-discovery.js +0 -432
  67. package/dist/checks/api-discovery.js.map +0 -1
  68. package/dist/checks/auth-discovery.d.ts +0 -28
  69. package/dist/checks/auth-discovery.d.ts.map +0 -1
  70. package/dist/checks/auth-discovery.js +0 -302
  71. package/dist/checks/auth-discovery.js.map +0 -1
  72. package/dist/checks/commerce-discovery.d.ts +0 -40
  73. package/dist/checks/commerce-discovery.d.ts.map +0 -1
  74. package/dist/checks/commerce-discovery.js +0 -295
  75. package/dist/checks/commerce-discovery.js.map +0 -1
  76. package/dist/checks/content-negotiation.d.ts +0 -4
  77. package/dist/checks/content-negotiation.d.ts.map +0 -1
  78. package/dist/checks/content-negotiation.js +0 -253
  79. package/dist/checks/content-negotiation.js.map +0 -1
  80. package/dist/checks/crawl-efficiency.d.ts +0 -16
  81. package/dist/checks/crawl-efficiency.d.ts.map +0 -1
  82. package/dist/checks/crawl-efficiency.js +0 -186
  83. package/dist/checks/crawl-efficiency.js.map +0 -1
  84. package/dist/checks/frontmatter.d.ts +0 -34
  85. package/dist/checks/frontmatter.d.ts.map +0 -1
  86. package/dist/checks/frontmatter.js +0 -100
  87. package/dist/checks/frontmatter.js.map +0 -1
  88. package/dist/checks/html-rendering.d.ts +0 -21
  89. package/dist/checks/html-rendering.d.ts.map +0 -1
  90. package/dist/checks/html-rendering.js +0 -221
  91. package/dist/checks/html-rendering.js.map +0 -1
  92. package/dist/checks/html-utils.d.ts +0 -66
  93. package/dist/checks/html-utils.d.ts.map +0 -1
  94. package/dist/checks/html-utils.js +0 -139
  95. package/dist/checks/html-utils.js.map +0 -1
  96. package/dist/checks/http-headers.d.ts +0 -11
  97. package/dist/checks/http-headers.d.ts.map +0 -1
  98. package/dist/checks/http-headers.js +0 -249
  99. package/dist/checks/http-headers.js.map +0 -1
  100. package/dist/checks/http-hygiene.d.ts +0 -26
  101. package/dist/checks/http-hygiene.d.ts.map +0 -1
  102. package/dist/checks/http-hygiene.js +0 -257
  103. package/dist/checks/http-hygiene.js.map +0 -1
  104. package/dist/checks/index.d.ts +0 -3
  105. package/dist/checks/index.d.ts.map +0 -1
  106. package/dist/checks/index.js +0 -55
  107. package/dist/checks/index.js.map +0 -1
  108. package/dist/checks/llms-txt.d.ts +0 -19
  109. package/dist/checks/llms-txt.d.ts.map +0 -1
  110. package/dist/checks/llms-txt.js +0 -281
  111. package/dist/checks/llms-txt.js.map +0 -1
  112. package/dist/checks/mcp-discovery.d.ts +0 -30
  113. package/dist/checks/mcp-discovery.d.ts.map +0 -1
  114. package/dist/checks/mcp-discovery.js +0 -523
  115. package/dist/checks/mcp-discovery.js.map +0 -1
  116. package/dist/checks/meta-tags.d.ts +0 -17
  117. package/dist/checks/meta-tags.d.ts.map +0 -1
  118. package/dist/checks/meta-tags.js +0 -188
  119. package/dist/checks/meta-tags.js.map +0 -1
  120. package/dist/checks/robots-parser.d.ts +0 -110
  121. package/dist/checks/robots-parser.d.ts.map +0 -1
  122. package/dist/checks/robots-parser.js +0 -277
  123. package/dist/checks/robots-parser.js.map +0 -1
  124. package/dist/checks/robots-txt.d.ts +0 -6
  125. package/dist/checks/robots-txt.d.ts.map +0 -1
  126. package/dist/checks/robots-txt.js +0 -367
  127. package/dist/checks/robots-txt.js.map +0 -1
  128. package/dist/checks/rsl.d.ts +0 -4
  129. package/dist/checks/rsl.d.ts.map +0 -1
  130. package/dist/checks/rsl.js +0 -242
  131. package/dist/checks/rsl.js.map +0 -1
  132. package/dist/checks/security-txt.d.ts +0 -4
  133. package/dist/checks/security-txt.d.ts.map +0 -1
  134. package/dist/checks/security-txt.js +0 -81
  135. package/dist/checks/security-txt.js.map +0 -1
  136. package/dist/checks/seo-basics.d.ts +0 -13
  137. package/dist/checks/seo-basics.d.ts.map +0 -1
  138. package/dist/checks/seo-basics.js +0 -221
  139. package/dist/checks/seo-basics.js.map +0 -1
  140. package/dist/checks/sitemap.d.ts +0 -12
  141. package/dist/checks/sitemap.d.ts.map +0 -1
  142. package/dist/checks/sitemap.js +0 -240
  143. package/dist/checks/sitemap.js.map +0 -1
  144. package/dist/checks/structured-data.d.ts +0 -4
  145. package/dist/checks/structured-data.d.ts.map +0 -1
  146. package/dist/checks/structured-data.js +0 -408
  147. package/dist/checks/structured-data.js.map +0 -1
  148. package/dist/checks/structured-fields.d.ts +0 -46
  149. package/dist/checks/structured-fields.d.ts.map +0 -1
  150. package/dist/checks/structured-fields.js +0 -112
  151. package/dist/checks/structured-fields.js.map +0 -1
  152. package/dist/checks/surface.d.ts +0 -59
  153. package/dist/checks/surface.d.ts.map +0 -1
  154. package/dist/checks/surface.js +0 -106
  155. package/dist/checks/surface.js.map +0 -1
  156. package/dist/checks/tls-https.d.ts +0 -13
  157. package/dist/checks/tls-https.d.ts.map +0 -1
  158. package/dist/checks/tls-https.js +0 -163
  159. package/dist/checks/tls-https.js.map +0 -1
  160. package/dist/checks/usage-policy.d.ts +0 -53
  161. package/dist/checks/usage-policy.d.ts.map +0 -1
  162. package/dist/checks/usage-policy.js +0 -339
  163. package/dist/checks/usage-policy.js.map +0 -1
  164. package/dist/checks/utils.d.ts +0 -40
  165. package/dist/checks/utils.d.ts.map +0 -1
  166. package/dist/checks/utils.js +0 -74
  167. package/dist/checks/utils.js.map +0 -1
  168. package/dist/checks/waf.d.ts +0 -75
  169. package/dist/checks/waf.d.ts.map +0 -1
  170. package/dist/checks/waf.js +0 -203
  171. package/dist/checks/waf.js.map +0 -1
  172. package/dist/checks/webmcp.d.ts +0 -55
  173. package/dist/checks/webmcp.d.ts.map +0 -1
  174. package/dist/checks/webmcp.js +0 -209
  175. package/dist/checks/webmcp.js.map +0 -1
  176. package/dist/checks/well-known.d.ts +0 -38
  177. package/dist/checks/well-known.d.ts.map +0 -1
  178. package/dist/checks/well-known.js +0 -202
  179. package/dist/checks/well-known.js.map +0 -1
  180. package/dist/fetcher.d.ts +0 -15
  181. package/dist/fetcher.d.ts.map +0 -1
  182. package/dist/fetcher.js +0 -127
  183. package/dist/fetcher.js.map +0 -1
  184. package/dist/guide-urls.d.ts +0 -2
  185. package/dist/guide-urls.d.ts.map +0 -1
  186. package/dist/guide-urls.js +0 -5
  187. package/dist/guide-urls.js.map +0 -1
  188. package/docs/roadmap.md +0 -367
@@ -1,19 +0,0 @@
1
- import type { CheckContext, CheckResult, CheckMeta } from '../types.js';
2
- /**
3
- * "llms-txt" — the curated index a site publishes for language models.
4
- *
5
- * Two things are worth being honest about in the report. Google has stated
6
- * plainly that Search ignores llms.txt, and adoption studies find the large
7
- * majority of published files are never fetched by any AI search crawler. The
8
- * consumers that *do* read it are coding agents — Claude Code, Cursor,
9
- * OpenCode — which makes it a developer-tooling signal rather than a search
10
- * visibility one. A report that implies otherwise is selling something.
11
- *
12
- * The specification was revised in 2026 ("v2"), adding three things this check
13
- * now looks for: files scoped to a subpath, a `describedby` link relation
14
- * pointing a page at the file that covers it, and per-page Markdown mirrors
15
- * reached by appending `.md`.
16
- */
17
- export declare const meta: CheckMeta;
18
- export default function check(ctx: CheckContext): Promise<CheckResult>;
19
- //# sourceMappingURL=llms-txt.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"llms-txt.d.ts","sourceRoot":"","sources":["../../src/checks/llms-txt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAW,MAAM,aAAa,CAAC;AAKjF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,IAAI,EAAE,SAKlB,CAAC;AAOF,wBAA8B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA6H3E"}
@@ -1,281 +0,0 @@
1
- import { guideUrl } from '../guide-urls.js';
2
- import { buildResult, checkContentType } from './utils.js';
3
- import { findLinkTags } from './html-utils.js';
4
- import { parseLinkHeader } from './http-headers.js';
5
- /**
6
- * "llms-txt" — the curated index a site publishes for language models.
7
- *
8
- * Two things are worth being honest about in the report. Google has stated
9
- * plainly that Search ignores llms.txt, and adoption studies find the large
10
- * majority of published files are never fetched by any AI search crawler. The
11
- * consumers that *do* read it are coding agents — Claude Code, Cursor,
12
- * OpenCode — which makes it a developer-tooling signal rather than a search
13
- * visibility one. A report that implies otherwise is selling something.
14
- *
15
- * The specification was revised in 2026 ("v2"), adding three things this check
16
- * now looks for: files scoped to a subpath, a `describedby` link relation
17
- * pointing a page at the file that covers it, and per-page Markdown mirrors
18
- * reached by appending `.md`.
19
- */
20
- export const meta = {
21
- id: 'llms-txt',
22
- name: 'LLMs.txt',
23
- description: 'Checks /llms.txt presence, spec compliance, and link health',
24
- category: 'discovery',
25
- };
26
- /** How many links to sample for liveness. Enough to catch rot without a crawl. */
27
- const LINK_SAMPLE_SIZE = 15;
28
- /** A file larger than this is unlikely to fit an agent's context budget usefully. */
29
- const LARGE_FILE_BYTES = 50_000;
30
- export default async function check(ctx) {
31
- const start = performance.now();
32
- const findings = [];
33
- let score = 100;
34
- const res = await ctx.fetch(`${ctx.url}/llms.txt`);
35
- if (!res.ok) {
36
- findings.push({
37
- status: 'fail',
38
- message: '/llms.txt not found',
39
- detail: `HTTP ${res.status || 'network error'}`,
40
- hint: 'Create a /llms.txt file at your site root following the llmstxt.org specification. It should be a Markdown file starting with "# Your Site Name" and include a description, sections, and links.',
41
- learnMoreUrl: guideUrl(meta.id, 'not-found'),
42
- });
43
- return buildResult(meta, 0, findings, start);
44
- }
45
- findings.push({ status: 'pass', message: '/llms.txt exists' });
46
- const ctFinding = checkContentType(res, ['text/plain', 'text/markdown'], {
47
- checkId: meta.id,
48
- resourceLabel: '/llms.txt',
49
- anchor: 'wrong-content-type',
50
- });
51
- if (ctFinding) {
52
- findings.push(ctFinding);
53
- score -= 5;
54
- }
55
- else {
56
- findings.push({
57
- status: 'pass',
58
- message: `/llms.txt Content-Type OK (${res.headers['content-type']?.split(';')[0]})`,
59
- });
60
- }
61
- const text = res.body;
62
- const lines = text
63
- .split('\n')
64
- .map((l) => l.trim())
65
- .filter(Boolean);
66
- if (!lines[0]?.startsWith('# ')) {
67
- findings.push({
68
- status: 'warn',
69
- message: 'Missing H1 heading (first line should start with "# ")',
70
- hint: 'Add an H1 heading as the first line of your llms.txt file, e.g.: # Your Site Name',
71
- learnMoreUrl: guideUrl(meta.id, 'missing-h1'),
72
- });
73
- score -= 15;
74
- }
75
- else {
76
- findings.push({ status: 'pass', message: `H1 heading: "${lines[0].slice(2)}"` });
77
- }
78
- const hasBlockquote = lines.some((l) => l.startsWith('> '));
79
- if (!hasBlockquote) {
80
- findings.push({
81
- status: 'warn',
82
- message: 'No blockquote description found ("> ...")',
83
- hint: 'Add a blockquote description after the H1 heading, e.g.: > A brief summary of your site for AI agents.',
84
- learnMoreUrl: guideUrl(meta.id, 'missing-blockquote'),
85
- });
86
- score -= 10;
87
- }
88
- else {
89
- findings.push({ status: 'pass', message: 'Blockquote description present' });
90
- }
91
- const sections = lines.filter((l) => l.startsWith('## '));
92
- if (sections.length === 0) {
93
- findings.push({
94
- status: 'warn',
95
- message: 'No section headings found (## ...)',
96
- hint: 'Organize your llms.txt content with ## section headings (e.g., ## About, ## API, ## Documentation).',
97
- learnMoreUrl: guideUrl(meta.id, 'missing-sections'),
98
- });
99
- score -= 10;
100
- }
101
- else {
102
- findings.push({ status: 'pass', message: `${sections.length} section heading(s) found` });
103
- }
104
- const linkPattern = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g;
105
- const links = [...text.matchAll(linkPattern)];
106
- if (links.length === 0) {
107
- findings.push({
108
- status: 'warn',
109
- message: 'No Markdown links found',
110
- hint: 'Add Markdown links to relevant pages: [Page Title](https://example.com/page). This helps AI agents navigate your site.',
111
- learnMoreUrl: guideUrl(meta.id, 'no-links'),
112
- });
113
- score -= 10;
114
- }
115
- else {
116
- findings.push({ status: 'pass', message: `${links.length} link(s) found` });
117
- }
118
- if (text.length < 100) {
119
- findings.push({
120
- status: 'warn',
121
- message: 'Content appears minimal (< 100 characters)',
122
- hint: 'Expand your llms.txt with more descriptive content about your site, its purpose, and available resources.',
123
- learnMoreUrl: guideUrl(meta.id, 'minimal-content'),
124
- });
125
- score -= 10;
126
- }
127
- const fullRes = await ctx.fetch(`${ctx.url}/llms-full.txt`);
128
- if (fullRes.ok) {
129
- findings.push({ status: 'pass', message: '/llms-full.txt also available (bonus)' });
130
- score = Math.min(100, score + 10);
131
- }
132
- else {
133
- findings.push({
134
- status: 'warn',
135
- message: '/llms-full.txt not found (optional but recommended)',
136
- hint: 'Create a /llms-full.txt with expanded content — full documentation, API details, and comprehensive site information for AI agents.',
137
- learnMoreUrl: guideUrl(meta.id, 'missing-full'),
138
- });
139
- }
140
- // Everything below is informational in 3.x: new findings inside a weighted
141
- // check must not move the score.
142
- reportSize(text, findings);
143
- reportDescribedBy(ctx, findings);
144
- await reportLinkHealth(ctx, links, findings);
145
- await reportMarkdownMirror(ctx, findings);
146
- reportConsumers(findings);
147
- return buildResult(meta, score, findings, start);
148
- }
149
- /* ── llms.txt v2 additions and link health (informational in 3.x) ───────── */
150
- /** A file too large to be read whole is a file an agent truncates. */
151
- function reportSize(text, findings) {
152
- const bytes = Buffer.byteLength(text, 'utf-8');
153
- if (bytes <= LARGE_FILE_BYTES)
154
- return;
155
- findings.push({
156
- status: 'warn',
157
- message: `/llms.txt is ${Math.round(bytes / 1024)} KB`,
158
- hint: 'An index this large competes with the content it points at for context budget. Keep llms.txt to a map of links ' +
159
- 'and move the prose into llms-full.txt or the pages themselves.',
160
- learnMoreUrl: guideUrl(meta.id, 'large-file'),
161
- });
162
- }
163
- /**
164
- * llms.txt v2 added a `describedby` relation so a page can point at the file
165
- * covering it. Without it, an agent that landed deep in the site has to guess
166
- * that a root llms.txt exists.
167
- */
168
- function reportDescribedBy(ctx, findings) {
169
- const inHeader = parseLinkHeader(ctx.headers?.['link'] ?? '').some((entry) => (entry.params['rel'] ?? '').split(/\s+/).includes('describedby'));
170
- const inHtml = findLinkTags(ctx.html ?? '', 'describedby').length > 0;
171
- if (inHeader || inHtml) {
172
- findings.push({
173
- status: 'pass',
174
- message: `Pages point at their llms.txt via rel="describedby" (${inHeader ? 'Link header' : 'HTML link'})`,
175
- });
176
- return;
177
- }
178
- findings.push({
179
- status: 'warn',
180
- message: 'No rel="describedby" link to llms.txt',
181
- hint: 'llms.txt v2 uses this relation so a page can name the file that covers it. Add ' +
182
- '<link rel="describedby" href="/llms.txt"> or the equivalent Link header, so an agent that landed on a deep ' +
183
- 'page does not have to guess that an index exists.',
184
- learnMoreUrl: guideUrl(meta.id, 'no-describedby'),
185
- });
186
- }
187
- /**
188
- * Sample the links and report the dead ones. An index of broken links is the
189
- * failure mode this file is most prone to: it is written once and never
190
- * revalidated, while the site moves underneath it.
191
- */
192
- async function reportLinkHealth(ctx, links, findings) {
193
- const urls = [...new Set(links.map((m) => m[2]))];
194
- if (urls.length === 0)
195
- return;
196
- const duplicates = links.length - urls.length;
197
- if (duplicates > 0) {
198
- findings.push({
199
- status: 'warn',
200
- message: `${duplicates} duplicate link(s) in llms.txt`,
201
- hint: 'Each URL should appear once. Duplicates spend an agent’s budget re-reading what it already has.',
202
- learnMoreUrl: guideUrl(meta.id, 'duplicate-links'),
203
- });
204
- }
205
- const sample = urls.slice(0, LINK_SAMPLE_SIZE);
206
- const dead = [];
207
- const redirected = [];
208
- for (const url of sample) {
209
- const head = await ctx.fetch(url, { method: 'HEAD', redirect: 'manual' });
210
- // Some origins refuse HEAD; a GET settles it rather than reporting a live
211
- // page as dead.
212
- const res = head.status === 405 || head.status === 501 ? await ctx.fetch(url, { redirect: 'manual' }) : head;
213
- if (res.status >= 300 && res.status < 400) {
214
- redirected.push(`${url} → ${res.redirectLocation ?? '(no Location)'}`);
215
- continue;
216
- }
217
- if (!res.ok)
218
- dead.push(`${url} (HTTP ${res.status || 'network error'})`);
219
- }
220
- if (dead.length > 0) {
221
- findings.push({
222
- status: 'fail',
223
- message: `${dead.length}/${sample.length} sampled llms.txt link(s) are broken`,
224
- detail: dead.join('\n'),
225
- hint: 'An index of dead links wastes exactly the budget it was meant to save, and an agent has no way to tell a ' +
226
- 'moved page from a removed one. Revalidate llms.txt when you move pages.',
227
- learnMoreUrl: guideUrl(meta.id, 'broken-links'),
228
- });
229
- }
230
- else {
231
- findings.push({ status: 'pass', message: `${sample.length} sampled link(s) resolve` });
232
- }
233
- if (redirected.length > 0) {
234
- findings.push({
235
- status: 'warn',
236
- message: `${redirected.length} sampled link(s) redirect`,
237
- detail: redirected.join('\n'),
238
- hint: 'Point llms.txt at the final URL. Each redirect is a round trip the agent pays for on every visit.',
239
- learnMoreUrl: guideUrl(meta.id, 'redirecting-links'),
240
- });
241
- }
242
- }
243
- /**
244
- * llms.txt v2 documents per-page Markdown mirrors reached by appending `.md`.
245
- * They matter more than the index itself: an agent that found a page still has
246
- * to read it, and the HTML costs several times the tokens.
247
- */
248
- async function reportMarkdownMirror(ctx, findings) {
249
- for (const path of ['/index.md', '/index.html.md']) {
250
- const res = await ctx.fetch(`${ctx.url}${path}`);
251
- if (!res.ok || res.body.trim().length === 0)
252
- continue;
253
- if (/^\s*(?:<!doctype\s+html|<html[\s>])/i.test(res.body))
254
- continue;
255
- findings.push({
256
- status: 'pass',
257
- message: `Per-page Markdown mirror available at ${path}`,
258
- detail: 'Agents that cannot negotiate on Accept can still read the cheap representation.',
259
- });
260
- return;
261
- }
262
- findings.push({
263
- status: 'warn',
264
- message: 'No per-page Markdown mirror found',
265
- detail: 'Tried /index.md and /index.html.md.',
266
- hint: 'llms.txt v2 documents appending .md to a URL for its Markdown version. The index tells an agent which pages ' +
267
- 'exist; the mirrors are what make reading them cheap.',
268
- learnMoreUrl: guideUrl(meta.id, 'no-markdown-mirror'),
269
- });
270
- }
271
- /** State plainly who reads this file, so nobody over-invests on the wrong premise. */
272
- function reportConsumers(findings) {
273
- findings.push({
274
- status: 'pass',
275
- message: 'Consumer note: llms.txt is read by coding agents, not by search',
276
- detail: 'Google has stated that Search ignores llms.txt, and adoption studies find most published files are never ' +
277
- 'fetched by an AI search crawler. Claude Code, Cursor and OpenCode do fetch it. Treat it as developer tooling, ' +
278
- 'not as a search-visibility signal.',
279
- });
280
- }
281
- //# sourceMappingURL=llms-txt.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"llms-txt.js","sourceRoot":"","sources":["../../src/checks/llms-txt.ts"],"names":[],"mappings":"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"}
@@ -1,30 +0,0 @@
1
- import type { CheckContext, CheckResult, CheckMeta } from '../types.js';
2
- /**
3
- * "mcp-discovery" — how an agent finds this site's MCP server.
4
- *
5
- * ax-audit used to look for `/.well-known/mcp.json` and validate a `tools[]`
6
- * array inside it. That path was never part of the Model Context Protocol
7
- * specification — it was a plausible-looking convention that several tools,
8
- * this one included, recommended before the ecosystem settled. Advertising
9
- * static tool lists was the wrong idea anyway: tools come from a live
10
- * `tools/list` call, and a copy in a static file starts drifting the day it is
11
- * written.
12
- *
13
- * What actually emerged is the **server card**: a small document identifying
14
- * the server and listing its remote endpoints, deliberately without tools.
15
- * SEP-2127 and the `experimental-ext-server-card` repository recommend serving
16
- * it at `<streamable-http-url>/server-card`, or referencing it from
17
- * `/.well-known/ai-catalog.json`. Cloudflare and Mintlify serve one at
18
- * `/.well-known/mcp/server-card.json`.
19
- *
20
- * All of that is still draft, and drafts get renamed — so this check probes the
21
- * whole chain, labels each hit with its standing, and never treats one draft
22
- * path as the single correct answer.
23
- *
24
- * Scoring note for 3.x: a site whose only MCP document is the legacy
25
- * `/.well-known/mcp.json` is validated with the pre-3.7 rules, so its score is
26
- * exactly what 3.6 produced. The path itself is reported, not penalised.
27
- */
28
- export declare const meta: CheckMeta;
29
- export default function check(ctx: CheckContext): Promise<CheckResult>;
30
- //# sourceMappingURL=mcp-discovery.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-discovery.d.ts","sourceRoot":"","sources":["../../src/checks/mcp-discovery.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAKhG;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,IAAI,EAAE,SAMlB,CAAC;AAqFF,wBAA8B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA2D3E"}