<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andriy Pyvovarchuk</title>
    <description>The latest articles on DEV Community by Andriy Pyvovarchuk (@andriy_pyvovarchuk).</description>
    <link>https://dev.to/andriy_pyvovarchuk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047540%2F6ba0b6e9-d68c-4c26-8775-023575f37013.jpg</url>
      <title>DEV Community: Andriy Pyvovarchuk</title>
      <link>https://dev.to/andriy_pyvovarchuk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andriy_pyvovarchuk"/>
    <language>en</language>
    <item>
      <title>llms.txt v2 is out — here's what actually changed</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Fri, 18 Sep 2026 20:30:56 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/llmstxt-v2-is-out-heres-what-actually-changed-1hkp</link>
      <guid>https://dev.to/andriy_pyvovarchuk/llmstxt-v2-is-out-heres-what-actually-changed-1hkp</guid>
      <description>&lt;p&gt;The &lt;a href="https://llmstxt.org/" rel="noopener noreferrer"&gt;llms.txt proposal&lt;/a&gt; was quietly revised in August 2026,&lt;br&gt;
nearly two years after the original. If you already publish an &lt;code&gt;llms.txt&lt;/code&gt;, the&lt;br&gt;
good news is short: &lt;strong&gt;the file format didn't change.&lt;/strong&gt; A v1 file is still a valid&lt;br&gt;
v2 file. Nothing to migrate.&lt;/p&gt;

&lt;p&gt;What changed is everything &lt;em&gt;around&lt;/em&gt; the file — how agents find it, what a&lt;br&gt;
subpath file means, and which tooling the spec expects. I maintain an llms.txt&lt;br&gt;
validator, so I had to go through the diff line by line and change real code.&lt;br&gt;
Here's the list, and what each item costs you.&lt;/p&gt;
&lt;h2&gt;
  
  
  The format is untouched
&lt;/h2&gt;

&lt;p&gt;Still exactly this, in this order: an optional BOM, an &lt;code&gt;#&lt;/code&gt; H1 with the project&lt;br&gt;
name (still the &lt;em&gt;only&lt;/em&gt; required element), a &lt;code&gt;&amp;gt;&lt;/code&gt; blockquote summary, optional&lt;br&gt;
heading-free prose, then zero or more &lt;code&gt;##&lt;/code&gt; sections holding Markdown link lists.&lt;/p&gt;

&lt;p&gt;If your file scored well against v1, it scores the same against v2. You can stop&lt;br&gt;
reading here if all you wanted was "do I need to rewrite my file". You don't.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Link relations — the actual new thing
&lt;/h2&gt;

&lt;p&gt;This is the substantial addition, and it answers the most common complaint from&lt;br&gt;
two years of adoption: &lt;em&gt;given a page, how does an agent find its Markdown&lt;br&gt;
version, or the llms.txt that covers it, without guessing at URLs?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;v2 answers with two standard link relations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rel="describedby"&lt;/code&gt; → the llms.txt that covers this page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rel="alternate" type="text/markdown"&lt;/code&gt; → this page's Markdown version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either can be an HTML element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"describedby"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/llms.txt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/markdown"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/docs/page.html.md"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or an HTTP response header — which is the only option for non-HTML resources&lt;br&gt;
like the Markdown files themselves, and which you can set in your CDN config&lt;br&gt;
without touching a single page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Link: &amp;lt;/docs/page.html.md&amp;gt;; rel="alternate"; type="text/markdown", &amp;lt;/docs/llms.txt&amp;gt;; rel="describedby"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;If you followed the older advice&lt;/strong&gt; — including advice I published myself — to&lt;br&gt;
advertise the file with &lt;code&gt;rel="alternate" type="text/plain"&lt;/code&gt;, swap it for&lt;br&gt;
&lt;code&gt;rel="describedby"&lt;/code&gt;. That was a reasonable convention while the spec had none.&lt;br&gt;
Now it has one.&lt;/p&gt;

&lt;p&gt;Cost to you: one line in your layout, or one CDN header rule.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Both &lt;code&gt;.md&lt;/code&gt; URL forms are now legal
&lt;/h2&gt;

&lt;p&gt;v1 specified one shape for a page's Markdown twin: &lt;code&gt;.md&lt;/code&gt; appended to the full&lt;br&gt;
page URL, so &lt;code&gt;page.html&lt;/code&gt; → &lt;code&gt;page.html.md&lt;/code&gt;. In practice several publishing tools&lt;br&gt;
replaced the extension instead, producing &lt;code&gt;page.md&lt;/code&gt;. v2 blesses both. For URLs&lt;br&gt;
without a filename, append &lt;code&gt;index.html.md&lt;/code&gt; or &lt;code&gt;index.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cost to you: zero. Whichever form you already emit is now correct.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Subpath files are finally defined
&lt;/h2&gt;

&lt;p&gt;v1 permitted an &lt;code&gt;llms.txt&lt;/code&gt; outside the root but never said what it &lt;em&gt;meant&lt;/em&gt;. v2&lt;br&gt;
defines it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A file covers the URLs under its path, and where more than one file applies,&lt;br&gt;
agents should use the most specific one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So &lt;code&gt;/docs/llms.txt&lt;/code&gt; covers everything under &lt;code&gt;/docs/&lt;/code&gt;, and an agent reading a docs&lt;br&gt;
page prefers it over the root file.&lt;/p&gt;

&lt;p&gt;This matters most if you control a path but not a host — a GitHub Pages project&lt;br&gt;
site, a docs section on a shared domain. It's also the spec's stated reason for&lt;br&gt;
sticking with a conventional filename instead of &lt;code&gt;/.well-known/&lt;/code&gt;, which only ever&lt;br&gt;
exists at the origin root.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the one that bit me.&lt;/strong&gt; My validator normalized any input to&lt;br&gt;
&lt;code&gt;https://host/llms.txt&lt;/code&gt; — it threw the path away. So &lt;code&gt;fastht.ml/docs&lt;/code&gt; came back&lt;br&gt;
as "no llms.txt found" even though &lt;code&gt;fastht.ml/docs/llms.txt&lt;/code&gt; has existed for two&lt;br&gt;
years. That's the spec's own reference example, and I was reporting it as&lt;br&gt;
missing. The fix is to target the path's file and walk up to the root only if&lt;br&gt;
there's nothing there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com/docs/tutorials  →  /docs/tutorials/llms.txt
                            →  /docs/llms.txt          ← found, stop
                            →  /llms.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One detail worth getting right: only walk up on a definitive &lt;strong&gt;404/410&lt;/strong&gt;. A 403&lt;br&gt;
or 429 is a WAF blocking you, and a 5xx is a broken server — neither tells you&lt;br&gt;
anything about the parent path, so treating them as "not here" invents a wrong&lt;br&gt;
answer.&lt;/p&gt;

&lt;p&gt;Cost to you: nothing, unless you build tooling. If you do, check your URL&lt;br&gt;
normalization today.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The context-expansion tooling is gone (and &lt;code&gt;## Optional&lt;/code&gt; lost its teeth)
&lt;/h2&gt;

&lt;p&gt;v1 described &lt;code&gt;llms_txt2ctx&lt;/code&gt;, a tool that expanded an llms.txt into a single LLM&lt;br&gt;
context. v2 drops it and states the expectation directly instead: agents view or&lt;br&gt;
search the llms.txt, then follow the links they need. Which means &lt;strong&gt;the links&lt;br&gt;
should point at LLM-friendly content&lt;/strong&gt;, and the file itself stays small enough to&lt;br&gt;
sit in context.&lt;/p&gt;

&lt;p&gt;With that tooling goes the mechanical meaning of the &lt;code&gt;## Optional&lt;/code&gt; section, which&lt;br&gt;
existed to tell those tools what to omit. Optional sections are still allowed and&lt;br&gt;
still a useful convention for secondary links an agent can skip — they just don't&lt;br&gt;
drive anything automatic any more.&lt;/p&gt;

&lt;p&gt;Worth saying out loud since it comes up constantly: &lt;strong&gt;&lt;code&gt;llms-full.txt&lt;/code&gt; was never&lt;br&gt;
in the spec&lt;/strong&gt;, in either version. It's a widely adopted industry practice, and a&lt;br&gt;
good one for large documentation sets. Just don't call it a requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually do this week
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;rel="describedby"&lt;/code&gt; pointing at your &lt;code&gt;llms.txt&lt;/code&gt;. One line.&lt;/li&gt;
&lt;li&gt;If you publish Markdown twins, advertise them with
&lt;code&gt;rel="alternate" type="text/markdown"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Replace any &lt;code&gt;rel="alternate" type="text/plain"&lt;/code&gt; discovery hint.&lt;/li&gt;
&lt;li&gt;If your docs are the part agents care about, consider scoping a
&lt;code&gt;/docs/llms.txt&lt;/code&gt; to them — that's now a first-class choice, not a grey area.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why there was a revision at all
&lt;/h2&gt;

&lt;p&gt;Because the premise stopped being speculative. When llms.txt was proposed in&lt;br&gt;
September 2024, "agents will read your website" was a prediction. Today&lt;br&gt;
documentation platforms generate the file automatically, Chrome's Lighthouse&lt;br&gt;
audits sites for one as part of its agentic-browsing checks, and the AI labs&lt;br&gt;
publish llms.txt files for their own developer docs. v2 is what a proposal looks&lt;br&gt;
like after real adoption tests its assumptions. The&lt;br&gt;
&lt;a href="https://llmstxt.org/changes.html" rel="noopener noreferrer"&gt;changes page&lt;/a&gt; is short and worth reading in&lt;br&gt;
full.&lt;/p&gt;




&lt;p&gt;If you want to check where your own site stands against v2, my&lt;br&gt;
&lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; covers both halves of this&lt;br&gt;
post. It follows subpath files the way v2 says agents should — paste&lt;br&gt;
&lt;code&gt;example.com/docs&lt;/code&gt; and it targets &lt;code&gt;/docs/llms.txt&lt;/code&gt;, walking up to the root only&lt;br&gt;
on a real 404 — and it reports which of the two link relations your pages&lt;br&gt;
actually advertise, reading both the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags and the &lt;code&gt;Link:&lt;/code&gt; header, so a&lt;br&gt;
CDN-level header counts the same as markup.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>news</category>
      <category>seo</category>
    </item>
    <item>
      <title>How to add llms.txt to WordPress (3 ways, pick the one that fits)</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Thu, 10 Sep 2026 09:02:14 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/how-to-add-llmstxt-to-wordpress-3-ways-pick-the-one-that-fits-32kb</link>
      <guid>https://dev.to/andriy_pyvovarchuk/how-to-add-llmstxt-to-wordpress-3-ways-pick-the-one-that-fits-32kb</guid>
      <description>&lt;p&gt;WordPress powers something like 40% of the web, so "how do I add an &lt;code&gt;llms.txt&lt;/code&gt; to WordPress?" is a question a lot of people are about to ask. Good news: the file just has to be served at &lt;code&gt;https://yoursite.com/llms.txt&lt;/code&gt; as plain text — and there are three ways to get there depending on how much access you have.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(New to the format? It's a small Markdown file that tells AI models what your site is and which pages matter — start with a quick primer, then come back.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1 — Upload the file (simplest, no plugin)
&lt;/h2&gt;

&lt;p&gt;If you can reach your files (host file manager or SFTP), this is the cleanest path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write your &lt;code&gt;llms.txt&lt;/code&gt; (one &lt;code&gt;#&lt;/code&gt; title, a &lt;code&gt;&amp;gt;&lt;/code&gt; summary, &lt;code&gt;##&lt;/code&gt; sections of links).&lt;/li&gt;
&lt;li&gt;Upload it to the &lt;strong&gt;web root&lt;/strong&gt; — usually &lt;code&gt;public_html/&lt;/code&gt; — so it lands at &lt;code&gt;yoursite.com/llms.txt&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Done. No plugin, nothing to maintain. This is the right answer for most self- or managed-hosted WordPress sites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2 — Serve it with a snippet (no file access)
&lt;/h2&gt;

&lt;p&gt;Can't upload files? Add a tiny route. Drop this in your theme's &lt;code&gt;functions.php&lt;/code&gt; (or a code-snippets plugin):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'llms.txt'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: text/plain; charset=utf-8'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Example&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt; Example is a WordPress site about X. These links cover the essentials.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"## Guides&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- [Start here](https://example.com/start): The 5-minute intro&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It intercepts the request for &lt;code&gt;/llms.txt&lt;/code&gt; and returns plain text directly — no file on disk needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 3 — Use a plugin
&lt;/h2&gt;

&lt;p&gt;Several SEO/AI plugins now generate an &lt;code&gt;llms.txt&lt;/code&gt; from your pages automatically. Handy if you don't want to touch code — but &lt;strong&gt;review the output&lt;/strong&gt;. Auto-generators love to dump every page and wrap in marketing copy; &lt;code&gt;llms.txt&lt;/code&gt; is a &lt;em&gt;curated&lt;/em&gt; index, so trim it to the pages that actually matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  The WordPress-specific gotchas
&lt;/h2&gt;

&lt;p&gt;These trip people up more than the file itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purge your cache/CDN&lt;/strong&gt; after publishing — WordPress caching plugins and CDNs will happily serve a stale 404 for &lt;code&gt;/llms.txt&lt;/code&gt; until you clear them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root, not a subfolder&lt;/strong&gt; — it must be &lt;code&gt;yoursite.com/llms.txt&lt;/code&gt;, not &lt;code&gt;yoursite.com/blog/llms.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain text, not HTML&lt;/strong&gt; — make sure it's served as &lt;code&gt;text/plain&lt;/code&gt;, not rendered through a page template as &lt;code&gt;text/html&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Verify it actually works
&lt;/h2&gt;

&lt;p&gt;The failure you won't see on your own: it loads for you but returns a cached 404 to everyone else, or the linked pages 404. Run your domain through an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; — it confirms the file is served correctly as plain text and that every linked page actually loads. Aim for 100/100, then you're done.&lt;/p&gt;

&lt;p&gt;Pick the option that matches your access, mind the cache, validate, ship. Ten minutes on the world's most common CMS.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>seo</category>
      <category>tutorial</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>Shopify already made you an llms.txt — and it might not be working for you</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Thu, 10 Sep 2026 08:58:28 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/shopify-already-made-you-an-llmstxt-and-it-might-not-be-working-for-you-5b8d</link>
      <guid>https://dev.to/andriy_pyvovarchuk/shopify-already-made-you-an-llmstxt-and-it-might-not-be-working-for-you-5b8d</guid>
      <description>&lt;p&gt;If you run a Shopify store, open &lt;code&gt;https://yourstore.com/llms.txt&lt;/code&gt; right now. There's almost certainly a file there. You didn't write it — Shopify generates one for every store as part of its Agentic Commerce push.&lt;/p&gt;

&lt;p&gt;Here's the part worth knowing: &lt;strong&gt;the default file often steers AI shopping agents toward Shopify's own Shop app rather than your store and your pages.&lt;/strong&gt; It's a sensible default for Shopify. It's not necessarily the one you want.&lt;/p&gt;

&lt;p&gt;So on Shopify the job isn't "add an llms.txt" — it's &lt;em&gt;take control of the one you already have&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, look at what you're serving
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;/llms.txt&lt;/code&gt; on your own domain and read it as an agent would. Ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does it point at &lt;strong&gt;my&lt;/strong&gt; collections, products and policies?&lt;/li&gt;
&lt;li&gt;Or does it mostly route to Shopify infrastructure?&lt;/li&gt;
&lt;li&gt;Is it a curated shortlist, or a dump?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That tells you whether you need to do anything at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1 — override it with a theme template (recommended)
&lt;/h2&gt;

&lt;p&gt;The cleanest way to take control. In &lt;strong&gt;Online Store → Themes → ⋯ → Edit code&lt;/strong&gt;, open the &lt;code&gt;Templates&lt;/code&gt; folder and create a new template named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;llms.txt.liquid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shopify serves that template at &lt;code&gt;/llms.txt&lt;/code&gt;, so its content replaces the default. Keep it short and curated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Example Store&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Example sells handmade ceramics. This file points AI shopping agents at the&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; pages that matter.&lt;/span&gt;

&lt;span class="gu"&gt;## Catalog&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Best sellers&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/collections/best-sellers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Top products
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;New arrivals&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/collections/new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Latest additions

&lt;span class="gu"&gt;## Help&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Shipping &amp;amp; returns&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/policies/shipping-policy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Policies
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Contact&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/pages/contact&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Support
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because it's a Liquid template you &lt;em&gt;can&lt;/em&gt; generate parts of it dynamically — but resist the urge to loop over every product (see below).&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2 — use an app
&lt;/h2&gt;

&lt;p&gt;The Shopify App Store has several llms.txt apps that generate the file from your products, collections and pages, and refresh it as the catalog changes. Good if you don't want to touch theme code — but &lt;strong&gt;review what it outputs&lt;/strong&gt;, because the default behaviour of most generators is "include everything."&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistake almost everyone makes: dumping the catalog
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is a &lt;em&gt;curated index&lt;/em&gt;, not a sitemap. A 60-line file beats a 5,000-line one, because an agent reading it has a limited context budget and every junk entry spends it.&lt;/p&gt;

&lt;p&gt;List &lt;strong&gt;collections&lt;/strong&gt;, best sellers and key policies — not all 2,000 SKUs. The agent can follow a collection link if it needs depth; it can't recover from drowning in your entire product feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  One thing you can stop doing
&lt;/h2&gt;

&lt;p&gt;If you set this up in 2024–2025, you may have uploaded a file to Shopify's CDN and added a URL redirect to reach it. &lt;strong&gt;That workaround is obsolete&lt;/strong&gt; — Shopify serves &lt;code&gt;/llms.txt&lt;/code&gt; natively now, so use a theme template or an app instead of maintaining a redirect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then verify it
&lt;/h2&gt;

&lt;p&gt;The failure you won't notice: a file that looks right but links to collections you renamed or products you retired. Paste your domain into an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; — it confirms the file is reachable and well-formed, and fetches every linked URL so dead links surface before an agent hits them. Re-check after big catalog changes.&lt;/p&gt;

&lt;p&gt;Shopify handed you a head start on this one. Ten minutes of curation turns it from &lt;em&gt;a file that exists&lt;/em&gt; into &lt;em&gt;a file that sells for you&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>seo</category>
    </item>
    <item>
      <title>llms-full.txt explained: the companion file to llms.txt</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Fri, 04 Sep 2026 13:04:39 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/llms-fulltxt-explained-the-companion-file-to-llmstxt-mgd</link>
      <guid>https://dev.to/andriy_pyvovarchuk/llms-fulltxt-explained-the-companion-file-to-llmstxt-mgd</guid>
      <description>&lt;p&gt;Once you've published an &lt;code&gt;llms.txt&lt;/code&gt;, you'll run into its lesser-known sibling: &lt;code&gt;llms-full.txt&lt;/code&gt;. Same idea, different job — and knowing when to publish which saves an AI a lot of round-trips. Here's the short version.&lt;/p&gt;

&lt;h2&gt;
  
  
  llms.txt is the table of contents. llms-full.txt is the whole book.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/strong&gt; is a compact, linked overview. A model reads it, then has to &lt;strong&gt;fetch each link&lt;/strong&gt; to actually read the content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;llms-full.txt&lt;/code&gt;&lt;/strong&gt; embeds the &lt;em&gt;complete content&lt;/em&gt; of those pages directly in one file. A model ingests everything in a &lt;strong&gt;single request&lt;/strong&gt; — no follow-up fetches.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the entire distinction. One points; the other contains.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to publish llms-full.txt
&lt;/h2&gt;

&lt;p&gt;It's optional — only &lt;code&gt;llms.txt&lt;/code&gt; (really, just its H1) is required. Reach for &lt;code&gt;llms-full.txt&lt;/code&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have &lt;strong&gt;documentation&lt;/strong&gt; that's genuinely useful read in full — API refs, SDK guides, tutorials.&lt;/li&gt;
&lt;li&gt;You want agents to work &lt;strong&gt;without crawling dozens of pages&lt;/strong&gt; one by one.&lt;/li&gt;
&lt;li&gt;You already keep &lt;strong&gt;Markdown sources&lt;/strong&gt; you can concatenate, so generating it is cheap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your site is a handful of marketing pages, skip it — the index is enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't forget per-page &lt;code&gt;.md&lt;/code&gt; versions
&lt;/h2&gt;

&lt;p&gt;The spec also suggests serving a clean Markdown version of each page at the same URL with &lt;code&gt;.md&lt;/code&gt; appended (and &lt;code&gt;index.html.md&lt;/code&gt; for directory URLs). That gives a model a parse-friendly copy of any &lt;em&gt;single&lt;/em&gt; page without the nav, cookie banner, and ads wrapped around your HTML. Three levels, increasing granularity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;llms.txt&lt;/code&gt; — the curated index&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;llms-full.txt&lt;/code&gt; — everything, in one file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page.md&lt;/code&gt; — one clean page on demand&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Who actually does this
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;llms.txt&lt;/code&gt; + &lt;code&gt;llms-full.txt&lt;/code&gt; pairing shows up on documentation-heavy sites — Anthropic and Vercel among them: the index for orientation, the full dump for deep ingestion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you ship either
&lt;/h2&gt;

&lt;p&gt;Both files are plain text served at your root, and both fail the same quiet way: they look fine but point at (or contain links to) dead pages. Run your domain through an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; — it fetches every linked URL and, when you check a domain, reports whether a sibling &lt;code&gt;llms-full.txt&lt;/code&gt; exists. Start with &lt;code&gt;llms.txt&lt;/code&gt;; add &lt;code&gt;llms-full.txt&lt;/code&gt; when your content is worth reading end to end.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>documentation</category>
      <category>llm</category>
    </item>
    <item>
      <title>How to create your first llms.txt (a 10-minute, copy-paste guide)</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Fri, 04 Sep 2026 12:53:06 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/how-to-create-your-first-llmstxt-a-10-minute-copy-paste-guide-2lh6</link>
      <guid>https://dev.to/andriy_pyvovarchuk/how-to-create-your-first-llmstxt-a-10-minute-copy-paste-guide-2lh6</guid>
      <description>&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; sounds like a spec you need to study. It isn't — it's a small Markdown file, and you can write a good one in about ten minutes. Here's every part in order, then a complete file you can paste and adapt.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. One H1 title
&lt;/h2&gt;

&lt;p&gt;The first line is a single &lt;code&gt;#&lt;/code&gt; heading — your project or site name. This is the &lt;em&gt;only&lt;/em&gt; strictly required part of the whole format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Example Docs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. A blockquote summary
&lt;/h2&gt;

&lt;p&gt;Right under the title, add a one- or two-sentence &lt;code&gt;&amp;gt;&lt;/code&gt; blockquote. It's the first context a model reads, so be specific: what the thing is and who it's for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gt"&gt;&amp;gt; Example is a payments API. This file links to the docs an LLM needs to&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; integrate and debug Example.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. (Optional) a line or two of prose
&lt;/h2&gt;

&lt;p&gt;You may add plain paragraphs before any section — a bit more orientation. No headings here; keep it to a sentence or two.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Group links under &lt;code&gt;##&lt;/code&gt; sections
&lt;/h2&gt;

&lt;p&gt;Each section is an &lt;code&gt;##&lt;/code&gt; heading followed by a Markdown list. Every item is a link with an optional description after a colon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Docs&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Quickstart&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/docs/quickstart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Integrate in five minutes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;API reference&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/docs/api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Every endpoint and parameter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two rules that matter: use &lt;strong&gt;absolute &lt;code&gt;https://&lt;/code&gt; URLs&lt;/strong&gt;, and write descriptions for a developer or model — not marketing copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Push nice-to-haves into &lt;code&gt;## Optional&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A section literally named &lt;code&gt;## Optional&lt;/code&gt; is the convention for secondary stuff an agent can skip when it needs a shorter context — changelogs, blog posts, extra references.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Host it at the root (or the path it covers)
&lt;/h2&gt;

&lt;p&gt;Serve it at &lt;code&gt;https://yourdomain.com/llms.txt&lt;/code&gt; as &lt;code&gt;Content-Type: text/plain&lt;/code&gt;. Spec v2 also lets it live at a subpath that covers everything beneath it — &lt;code&gt;/docs/llms.txt&lt;/code&gt; describes &lt;code&gt;/docs/&lt;/code&gt;, and agents prefer the most specific file that applies. Big docs sites often also publish an &lt;code&gt;llms-full.txt&lt;/code&gt; with the full expanded content.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Let your pages point at it
&lt;/h2&gt;

&lt;p&gt;So agents don't have to guess the URL, advertise it from your pages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"describedby"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/llms.txt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…or set the equivalent &lt;code&gt;Link:&lt;/code&gt; response header once in your CDN config.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete file (paste &amp;amp; adapt)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Example — Payments API&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Example is a payments API. This file links to the docs an LLM needs to&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; integrate, test, and debug Example.&lt;/span&gt;

&lt;span class="gu"&gt;## Docs&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Quickstart&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/docs/quickstart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Integrate in five minutes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Authentication&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/docs/auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: API keys and OAuth
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;API reference&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/docs/api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Every endpoint and parameter

&lt;span class="gu"&gt;## Examples&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Sample app&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/examples/app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: A working end-to-end integration

&lt;span class="gu"&gt;## Optional&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Changelog&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/changelog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Release notes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/blog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Background and deep dives
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap in your own name, summary, and links. Keep it curated — the best few pages, not every page.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Validate before you ship
&lt;/h2&gt;

&lt;p&gt;The one step people skip: actually checking it resolves. A file can be perfect Markdown and still point at 404s, which wastes a crawler's budget. Paste your domain into an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; — it checks the structure, fetches every linked URL, and lists exactly what to fix. Aim for 100/100, then ship it.&lt;/p&gt;

&lt;p&gt;That's the whole thing. Ten minutes, one small file, and your site finally tells AI models what it is instead of making them guess.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to test your llms.txt before you ship it</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Thu, 06 Aug 2026 13:29:41 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/how-to-test-your-llmstxt-before-you-ship-it-1579</link>
      <guid>https://dev.to/andriy_pyvovarchuk/how-to-test-your-llmstxt-before-you-ship-it-1579</guid>
      <description>&lt;p&gt;An &lt;code&gt;llms.txt&lt;/code&gt; is easy to write and easy to get subtly wrong. The file can look perfectly fine in your editor and still be malformed, or point at pages that 404 — and unlike a broken web page, nothing visibly breaks, so you never notice. Here's exactly what to check before (and after) you publish.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Structure
&lt;/h2&gt;

&lt;p&gt;Confirm the spec basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;exactly one &lt;code&gt;#&lt;/code&gt; H1 title,&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a &lt;code&gt;&amp;gt;&lt;/code&gt; blockquote summary&lt;/strong&gt; right under it,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;##&lt;/code&gt; sections&lt;/strong&gt; that each contain a Markdown list of links.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiple H1s and empty sections are the two most common structural mistakes. If your file has two &lt;code&gt;#&lt;/code&gt; lines, demote one to &lt;code&gt;##&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Link reachability (the check almost everyone skips)
&lt;/h2&gt;

&lt;p&gt;This is the big one. Every URL you link should actually resolve. A flawless-looking file that points at 404s wastes a crawler's budget and quietly erodes trust — and most "llms.txt checkers" only lint the &lt;em&gt;syntax&lt;/em&gt;, so dead links sail straight through them.&lt;/p&gt;

&lt;p&gt;You have to actually &lt;strong&gt;request each linked URL&lt;/strong&gt; and look for anything that's broken or redirecting. This is where doing it by hand falls apart: eyeballing structure is fine, but clicking twenty links to check status codes is tedious and easy to get wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Best practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Absolute &lt;code&gt;https://&lt;/code&gt; URLs — not relative paths, not &lt;code&gt;http://&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A short &lt;code&gt;: description&lt;/code&gt; after each link so the model knows what it is.&lt;/li&gt;
&lt;li&gt;Served as &lt;code&gt;Content-Type: text/plain; charset=utf-8&lt;/code&gt; (a lot of servers wrongly return &lt;code&gt;text/html&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Manual vs automated
&lt;/h2&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; verify all of the above by hand. Structure, sure. But link reachability across dozens of URLs, checking content type, re-checking every time you edit the file — that's exactly the kind of tedious, error-prone work worth automating.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fast way
&lt;/h2&gt;

&lt;p&gt;Paste your domain (or the raw text) into an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt;. It checks the structure, fetches every linked URL concurrently, applies the best-practice rules above, and returns a 0-100 score with a prioritized, fixable list of findings. Re-run it whenever you change the file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm7jsp7e52n6djifk8l1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm7jsp7e52n6djifk8l1s.png" alt="A 100/100 llms.txt report scored by structure, links and best practices" width="800" height="688"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;What a clean result looks like: 100/100, every link fetched, with the HTTP status, size, content type and link count broken out. A single broken link drags the score down even when the Markdown is perfect.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30-second checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] One &lt;code&gt;#&lt;/code&gt; H1, one &lt;code&gt;&amp;gt;&lt;/code&gt; summary, &lt;code&gt;##&lt;/code&gt; sections with links&lt;/li&gt;
&lt;li&gt;[ ] Every linked URL returns 200 (no 404s, no redirect chains)&lt;/li&gt;
&lt;li&gt;[ ] All links absolute &lt;code&gt;https://&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] A &lt;code&gt;: description&lt;/code&gt; on each link&lt;/li&gt;
&lt;li&gt;[ ] Served as &lt;code&gt;text/plain; charset=utf-8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Re-checked after your latest edit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get those six right and your &lt;code&gt;llms.txt&lt;/code&gt; is in better shape than most on the web today. Test it once, fix what's flagged, ship it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Which AI crawlers actually read your llms.txt? (GPTBot, ClaudeBot &amp; friends)</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Thu, 06 Aug 2026 05:55:44 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/which-ai-crawlers-actually-read-your-llmstxt-gptbot-claudebot-friends-3677</link>
      <guid>https://dev.to/andriy_pyvovarchuk/which-ai-crawlers-actually-read-your-llmstxt-gptbot-claudebot-friends-3677</guid>
      <description>&lt;p&gt;You added an &lt;code&gt;llms.txt&lt;/code&gt;. Reasonable next question: &lt;em&gt;who's actually reading it?&lt;/em&gt; The honest answer surprises people — so let's clear it up, and more importantly sort out which bots you should care about and how you actually control them (spoiler: not with &lt;code&gt;llms.txt&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  First, split the bots into two piles
&lt;/h2&gt;

&lt;p&gt;Lumping all "AI crawlers" together is where people get confused. There are two kinds, and you treat them very differently:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer / search crawlers&lt;/strong&gt; — they fetch a page to answer a user's question &lt;em&gt;right now&lt;/em&gt;, and they're the ones that get you cited in AI answers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI: &lt;code&gt;OAI-SearchBot&lt;/code&gt;, &lt;code&gt;ChatGPT-User&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Anthropic: &lt;code&gt;Claude-SearchBot&lt;/code&gt;, &lt;code&gt;Claude-User&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Perplexity: &lt;code&gt;PerplexityBot&lt;/code&gt;, &lt;code&gt;Perplexity-User&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You generally &lt;strong&gt;want&lt;/strong&gt; these.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Training crawlers&lt;/strong&gt; — they collect data to train future models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt;, &lt;code&gt;Google-Extended&lt;/code&gt;, &lt;code&gt;Applebot-Extended&lt;/code&gt;, &lt;code&gt;CCBot&lt;/code&gt;, &lt;code&gt;Bytespider&lt;/code&gt;, &lt;code&gt;Meta-ExternalAgent&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether to allow these is a &lt;strong&gt;separate, opt-out decision&lt;/strong&gt; — and blocking them does &lt;em&gt;not&lt;/em&gt; hurt your AI-search visibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  So which of them read llms.txt today?
&lt;/h2&gt;

&lt;p&gt;Straight answer: &lt;strong&gt;adoption is still early.&lt;/strong&gt; &lt;code&gt;llms.txt&lt;/code&gt; is a young, voluntary convention. It's picking up support in AI developer tools and agent frameworks, but most of the big crawlers above do &lt;strong&gt;not&lt;/strong&gt; yet fetch it automatically. No bot is &lt;em&gt;required&lt;/em&gt; to read it.&lt;/p&gt;

&lt;p&gt;That sounds like a reason to skip it. It isn't — for two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The clean pages it points to help models right now.&lt;/strong&gt; Even a crawler that ignores the file itself benefits from the well-structured, Markdown-friendly pages you curated for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's cheap readiness.&lt;/strong&gt; A correct &lt;code&gt;llms.txt&lt;/code&gt; costs ten minutes and positions you for the moment adoption tips — which, given how fast this space moves, won't be long.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The part people get wrong: llms.txt doesn't control access
&lt;/h2&gt;

&lt;p&gt;This is the big misconception. &lt;strong&gt;&lt;code&gt;llms.txt&lt;/code&gt; never blocks or grants anything.&lt;/strong&gt; It only &lt;em&gt;curates&lt;/em&gt; — a suggested reading list. If you want to control which AI crawlers may fetch your pages, that's &lt;code&gt;robots.txt&lt;/code&gt;'s job.&lt;/p&gt;

&lt;p&gt;A visibility-friendly setup allows the answer crawlers and makes a deliberate call on the training ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;OAI&lt;/span&gt;-&lt;span class="n"&gt;SearchBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;PerplexityBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;GPTBot&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That example says "let AI search engines cite me, but keep my content out of model training." Your call on that last line — just know it's a training opt-out, not a visibility switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Allow&lt;/strong&gt; the answer/search crawlers (&lt;code&gt;OAI-SearchBot&lt;/code&gt;, &lt;code&gt;Claude-SearchBot&lt;/code&gt;, &lt;code&gt;PerplexityBot&lt;/code&gt;, …) so AI tools can cite you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide training opt-outs&lt;/strong&gt; (&lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;Google-Extended&lt;/code&gt;) deliberately — blocking them doesn't touch AI-search visibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish a curated &lt;code&gt;llms.txt&lt;/code&gt;&lt;/strong&gt; and then actually check it — the most common failure is a valid-looking file pointing at dead links. Run it through an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; that fetches every URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is the map; &lt;code&gt;robots.txt&lt;/code&gt; is the gate. Keep the map honest and the gate deliberate, and you're ahead of nearly everyone.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>seo</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Does llms.txt help SEO? An honest answer</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Tue, 04 Aug 2026 06:22:19 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/does-llmstxt-help-seo-an-honest-answer-ln3</link>
      <guid>https://dev.to/andriy_pyvovarchuk/does-llmstxt-help-seo-an-honest-answer-ln3</guid>
      <description>&lt;p&gt;If you searched this, you've probably also seen the other kind of post — "add llms.txt and watch your rankings climb." Let me save you the disappointment and then tell you why the file is still worth ten minutes of your time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer: no, not for classic SEO
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is &lt;strong&gt;not a Google ranking signal.&lt;/strong&gt; Google has said Googlebot doesn't use it for search ranking. It's not a meta tag, not a sitemap replacement, not a backlink, and no, it won't move you up the results page. Anyone selling "llms.txt → #1 on Google" is selling snake oil.&lt;/p&gt;

&lt;p&gt;So if your only goal is blue-link rankings, &lt;code&gt;llms.txt&lt;/code&gt; isn't your lever. Close the tab, go fix your Core Web Vitals.&lt;/p&gt;

&lt;h2&gt;
  
  
  The longer answer: it helps a &lt;em&gt;different&lt;/em&gt; kind of visibility
&lt;/h2&gt;

&lt;p&gt;Still here? Good — because the question is slightly out of date. More and more, people don't get an answer &lt;em&gt;about&lt;/em&gt; your product from a ranked list of links. They ask ChatGPT, Claude, Perplexity, or Google's AI overview, and read whatever those models say about you.&lt;/p&gt;

&lt;p&gt;Being cleanly readable by those models is its own discipline now — people call it &lt;strong&gt;GEO (generative engine optimization)&lt;/strong&gt;, the AI-answer cousin of SEO. And that's exactly what &lt;code&gt;llms.txt&lt;/code&gt; is for. A tidy one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;points models at your best, most current pages instead of letting them guess,&lt;/li&gt;
&lt;li&gt;gives them clean context so answers &lt;em&gt;about you&lt;/em&gt; are accurate rather than hallucinated,&lt;/li&gt;
&lt;li&gt;pairs with Markdown versions of your pages so the model reads content, not your nav bar, cookie banner, and ads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It won't rank you. It changes what the AI &lt;em&gt;says&lt;/em&gt; when someone asks about you — which, increasingly, is the thing that actually drives the click.&lt;/p&gt;

&lt;h2&gt;
  
  
  So is it worth it?
&lt;/h2&gt;

&lt;p&gt;Simple test: &lt;strong&gt;does your audience ask AI tools about your domain&lt;/strong&gt; — your docs, your API, your product? If yes, &lt;code&gt;llms.txt&lt;/code&gt; is a small file with real upside and basically no downside. Write it once, keep it curated, done.&lt;/p&gt;

&lt;p&gt;If you're a purely local business with no documentation and nobody's asking an LLM about you, the payoff is smaller — spend the time elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do instead of chasing rankings with it
&lt;/h2&gt;

&lt;p&gt;Treat &lt;code&gt;llms.txt&lt;/code&gt; as one piece of AI-readiness, alongside the things that genuinely help discovery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a clean &lt;code&gt;robots.txt&lt;/code&gt; that actually lets the AI crawlers you want in,&lt;/li&gt;
&lt;li&gt;schema.org structured data,&lt;/li&gt;
&lt;li&gt;a real sitemap,&lt;/li&gt;
&lt;li&gt;and, yes, a curated &lt;code&gt;llms.txt&lt;/code&gt; on top.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then make sure the file is actually correct — the most common failure is a perfectly-formatted &lt;code&gt;llms.txt&lt;/code&gt; that points at 404s, which does the opposite of building trust. Run it through an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; that fetches every linked URL, and fix whatever it flags.&lt;/p&gt;

&lt;p&gt;The honest pitch: &lt;code&gt;llms.txt&lt;/code&gt; won't help your SEO. It'll help the thing SEO is slowly turning into. That's a better bet anyway.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>seo</category>
    </item>
    <item>
      <title>I made my website callable by another AI agent — here's the actual JSON-RPC</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Tue, 04 Aug 2026 06:01:22 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/i-made-my-website-callable-by-another-ai-agent-heres-the-actual-json-rpc-42m</link>
      <guid>https://dev.to/andriy_pyvovarchuk/i-made-my-website-callable-by-another-ai-agent-heres-the-actual-json-rpc-42m</guid>
      <description>&lt;p&gt;Most "AI integration" still means a human reads your docs and writes a client. A2A flips that: another &lt;em&gt;agent&lt;/em&gt; reads a small machine description of what your service does and calls it directly — no browser, no scraping, no human in the loop. I wired it up on a small tool I run (a free llms.txt validator) and it's simpler than the acronyms suggest. Here's the whole thing, with the real requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  What A2A actually is
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A2A (Agent2Agent)&lt;/strong&gt; is an open protocol — now under the Linux Foundation — that lets AI agents &lt;em&gt;discover and call each other&lt;/em&gt; over plain HTTP. Think of it as the agent-to-agent counterpart of a public API: instead of publishing OpenAPI docs for humans, you publish a machine description another agent can consume and invoke.&lt;/p&gt;

&lt;p&gt;It has exactly two moving parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The agent card&lt;/strong&gt; — a discovery document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The skill&lt;/strong&gt; — a callable JSON-RPC endpoint.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. You can ship a useful A2A surface with one skill and a single method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1 — the agent card (discovery)
&lt;/h2&gt;

&lt;p&gt;You serve a JSON file at &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt; describing who you are and what you can do. The required fields are &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt; (your endpoint) and at least one &lt;code&gt;skill&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"protocolVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llms.txt Validator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validate a website's llms.txt and return a score with findings."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://llms-txt-validator.dev/a2a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validate_llms_txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validate llms.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Given a domain or URL, fetch and validate its llms.txt."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The card is a &lt;strong&gt;contract&lt;/strong&gt;, not a meta tag. An agent fetches it, sees a &lt;code&gt;validate_llms_txt&lt;/code&gt; skill, and knows both what you offer and where to call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2 — the endpoint (a callable skill)
&lt;/h2&gt;

&lt;p&gt;The card's &lt;code&gt;url&lt;/code&gt; points at a &lt;strong&gt;JSON-RPC 2.0&lt;/strong&gt; endpoint. An agent invokes the &lt;code&gt;message/send&lt;/code&gt; method with a message; you do the work and return a &lt;code&gt;Task&lt;/code&gt;. Here's a real call to my agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/a&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"message/send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validate llmstxt.org"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and the reply — a completed task carrying a human-readable summary &lt;em&gt;and&lt;/em&gt; structured data the calling agent can use directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"artifacts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validated llmstxt.org: score 100/100..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"report"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"scores"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"overall"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No HTML, no parsing, no guessing. The agent asked a question in natural language and got back exactly the data it needed. You can &lt;code&gt;curl&lt;/code&gt; this yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The minimum viable A2A
&lt;/h2&gt;

&lt;p&gt;You don't need the whole spec to start. Pick &lt;em&gt;one&lt;/em&gt; real thing your service does and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serve a valid agent card at &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt; with one skill.&lt;/li&gt;
&lt;li&gt;Implement the synchronous &lt;code&gt;message/send&lt;/code&gt; method wrapping that capability.&lt;/li&gt;
&lt;li&gt;Return a completed &lt;code&gt;Task&lt;/code&gt; with the result as an artifact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Streaming, task history, and push notifications are all optional — set &lt;code&gt;capabilities.streaming&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; and add them only when you actually need them.&lt;/p&gt;

&lt;h2&gt;
  
  
  A2A vs WebMCP vs MCP (they're not competitors)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A2A&lt;/strong&gt; — networked agents calling your service over HTTP (this article).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebMCP&lt;/strong&gt; — an agent running &lt;em&gt;inside the browser&lt;/em&gt; calling tools on your open page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP&lt;/strong&gt; — connecting tools to a single model or app, usually locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They stack rather than compete: A2A reaches networked agents, WebMCP reaches in-browser ones, MCP wires tools into one model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one rule: don't publish a card you can't back
&lt;/h2&gt;

&lt;p&gt;It's tempting to drop an agent card to look modern and leave the endpoint returning &lt;code&gt;501&lt;/code&gt;. Don't. An agent that fetches your card and calls a dead &lt;code&gt;url&lt;/code&gt; trusts you &lt;em&gt;less&lt;/em&gt; afterward — you've spent its call for nothing. Every skill you advertise should resolve to real, working behavior. The &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;validator&lt;/a&gt; I built reports whether your agent card is there and parses as valid JSON — it deliberately stops short of calling the &lt;code&gt;url&lt;/code&gt; inside it, because firing &lt;code&gt;message/send&lt;/code&gt; at someone's agent just because they were scanned is not a thing a checker should do. That last mile is yours: call your own endpoint and confirm it answers.&lt;/p&gt;

&lt;p&gt;If you ship one, drop your agent card in the comments — I'd like to call it.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>api</category>
      <category>json</category>
    </item>
    <item>
      <title>7 llms.txt mistakes I see constantly (and the 30-second fix for each)</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Sun, 26 Jul 2026 06:57:50 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/7-llmstxt-mistakes-i-see-constantly-and-the-30-second-fix-for-each-10m1</link>
      <guid>https://dev.to/andriy_pyvovarchuk/7-llmstxt-mistakes-i-see-constantly-and-the-30-second-fix-for-each-10m1</guid>
      <description>&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is deceptively simple — it's just Markdown — so most people write one in five minutes and move on. The problem: a file can look perfectly valid and still be broken in ways that quietly waste an AI crawler's time or get it ignored. Here are the seven mistakes I run into most, and the quick fix for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. More than one H1
&lt;/h2&gt;

&lt;p&gt;The spec wants exactly &lt;strong&gt;one&lt;/strong&gt; top-level &lt;code&gt;# Title&lt;/code&gt;. Extra &lt;code&gt;#&lt;/code&gt; headings scramble the structure a model relies on. &lt;strong&gt;Fix:&lt;/strong&gt; keep a single &lt;code&gt;#&lt;/code&gt;, demote every other heading to &lt;code&gt;##&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. No blockquote summary
&lt;/h2&gt;

&lt;p&gt;The one- or two-sentence &lt;code&gt;&amp;gt;&lt;/code&gt; blockquote right under the title is the &lt;em&gt;first&lt;/em&gt; context a model reads. Skipping it is the single most common miss. &lt;strong&gt;Fix:&lt;/strong&gt; add one line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Acme&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Acme turns CSV files into charts. These links cover setup, the API, and pricing.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Sections with no links (or no sections at all)
&lt;/h2&gt;

&lt;p&gt;Empty &lt;code&gt;##&lt;/code&gt; sections add nothing; a wall of links with no structure is just as bad. &lt;strong&gt;Fix:&lt;/strong&gt; group your key pages under real headings (&lt;code&gt;## Docs&lt;/code&gt;, &lt;code&gt;## Guides&lt;/code&gt;) with a Markdown bullet list of links under each.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Broken links
&lt;/h2&gt;

&lt;p&gt;This is the big one. A file can be flawless Markdown and still point at 404s — which burns crawl budget and erodes trust. Most linters check &lt;em&gt;syntax&lt;/em&gt; and never actually fetch the URLs, so broken links sail straight through. &lt;strong&gt;Fix:&lt;/strong&gt; verify every link resolves. (This is the check I built my validator around — it fetches each one.)&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Relative or &lt;code&gt;http://&lt;/code&gt; links
&lt;/h2&gt;

&lt;p&gt;Models may not resolve relative paths like &lt;code&gt;/docs/start&lt;/code&gt;, and plain &lt;code&gt;http://&lt;/code&gt; links look untrustworthy and can break under HTTPS-only crawling. &lt;strong&gt;Fix:&lt;/strong&gt; use absolute &lt;code&gt;https://&lt;/code&gt; URLs everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The wrong content type
&lt;/h2&gt;

&lt;p&gt;Serve the file as &lt;code&gt;text/plain; charset=utf-8&lt;/code&gt;. A surprising number of servers return &lt;code&gt;text/html&lt;/code&gt; for it (a default misconfiguration), and some consumers then won't treat it as &lt;code&gt;llms.txt&lt;/code&gt; at all. &lt;strong&gt;Fix:&lt;/strong&gt; set the content type explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. A bloated, uncurated dump
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is a &lt;em&gt;curated index&lt;/em&gt;, not a sitemap. Hundreds of undescribed links bury the signal you're trying to send. &lt;strong&gt;Fix:&lt;/strong&gt; keep the best pages, add a short &lt;code&gt;: description&lt;/code&gt; after each, and push secondary links to an &lt;code&gt;## Optional&lt;/code&gt; section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catch all seven at once
&lt;/h2&gt;

&lt;p&gt;You don't have to eyeball these by hand. Paste your domain into an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; — it scores structure, fetches every linked URL, and tells you exactly which of the above to fix, in plain language.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm7jsp7e52n6djifk8l1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm7jsp7e52n6djifk8l1s.png" alt="A 100/100 llms.txt report, scored by structure, links and best practices" width="800" height="688"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A clean report: 100/100 with every link fetched. Broken links drag the score down even when the Markdown is perfect — which is exactly the failure mode syntax-only checkers miss.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fix these seven and you're ahead of most &lt;code&gt;llms.txt&lt;/code&gt; files on the web today. The bar is genuinely low right now — which is the opportunity.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>llms.txt vs robots.txt vs sitemap.xml: what actually does what</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Sun, 26 Jul 2026 06:26:18 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/llmstxt-vs-robotstxt-vs-sitemapxml-what-actually-does-what-506m</link>
      <guid>https://dev.to/andriy_pyvovarchuk/llmstxt-vs-robotstxt-vs-sitemapxml-what-actually-does-what-506m</guid>
      <description>&lt;p&gt;If you run a website in 2026 you now have &lt;em&gt;three&lt;/em&gt; plain-text files that can live at your root and all look suspiciously similar: &lt;code&gt;robots.txt&lt;/code&gt;, &lt;code&gt;sitemap.xml&lt;/code&gt;, and the newcomer, &lt;code&gt;llms.txt&lt;/code&gt;. They get confused constantly — I've seen people add &lt;code&gt;llms.txt&lt;/code&gt; thinking it blocks AI crawlers (it doesn't) or skip it thinking &lt;code&gt;robots.txt&lt;/code&gt; already covers it (it doesn't). Here's the clean mental model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;robots.txt&lt;/code&gt;&lt;/strong&gt; → controls crawler &lt;strong&gt;access&lt;/strong&gt;: which paths bots may or may not fetch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sitemap.xml&lt;/code&gt;&lt;/strong&gt; → lists &lt;strong&gt;every indexable page&lt;/strong&gt; so search engines miss nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/strong&gt; → curates a &lt;strong&gt;shortlist of high-signal pages&lt;/strong&gt; for AI models at answer time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Access, coverage, curation. Three different jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  robots.txt is about permission
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;robots.txt&lt;/code&gt; is an access-control convention: "Googlebot may crawl &lt;code&gt;/blog&lt;/code&gt; but not &lt;code&gt;/admin&lt;/code&gt;." It's a bouncer at the door. It doesn't describe your content, doesn't rank it, and isn't written for language models in particular. Blocking or allowing a path there says nothing about &lt;em&gt;what the page is&lt;/em&gt; — only whether a bot may request it.&lt;/p&gt;

&lt;p&gt;Crucially: &lt;strong&gt;&lt;code&gt;llms.txt&lt;/code&gt; is not an access-control file.&lt;/strong&gt; It can't stop an AI crawler from reading anything. If that's your goal, &lt;code&gt;robots.txt&lt;/code&gt; (and server-side rules) is still the tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  sitemap.xml is about coverage
&lt;/h2&gt;

&lt;p&gt;A sitemap tries to be &lt;em&gt;exhaustive&lt;/em&gt;: every URL you want indexed, so search engines don't overlook a page. It's machine-oriented XML for crawlers, not a human-readable summary, and it makes no attempt to say which pages matter most — they're all just listed.&lt;/p&gt;

&lt;h2&gt;
  
  
  llms.txt is about curation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is the opposite of exhaustive. It's a hand-picked, Markdown list of the &lt;em&gt;few&lt;/em&gt; pages that best help a model understand and use your site, each with a short description. It exists for &lt;strong&gt;inference&lt;/strong&gt; — the moment a user asks ChatGPT or Claude "how do I do X with your product" — not for training datasets and not for access control.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Acme&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Acme turns CSV files into charts. These links cover setup, the API, and pricing.&lt;/span&gt;

&lt;span class="gu"&gt;## Docs&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Quickstart&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/quickstart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: first chart in 5 minutes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;API reference&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: endpoints and auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where a sitemap says "here is everything," &lt;code&gt;llms.txt&lt;/code&gt; says "here is what's worth your limited context window."&lt;/p&gt;

&lt;h2&gt;
  
  
  You don't choose — you use all three
&lt;/h2&gt;

&lt;p&gt;This is the part people miss. A strong 2026 setup has all three, each doing its job:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;th&gt;Audience&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;robots.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;May you fetch this?&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;all crawlers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sitemap.xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;What should you index?&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;search engines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;What matters most here?&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;AI models&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;robots.txt&lt;/code&gt; governs access, &lt;code&gt;sitemap.xml&lt;/code&gt; ensures coverage, and &lt;code&gt;llms.txt&lt;/code&gt; curates signal for AI. They complement each other; none replaces another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting llms.txt right
&lt;/h2&gt;

&lt;p&gt;The catch with &lt;code&gt;llms.txt&lt;/code&gt; is that it's easy to write one that &lt;em&gt;looks&lt;/em&gt; valid but points at dead links or ships as the wrong content type — and unlike the other two, almost nobody checks it. After you write yours, run it through an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt; that fetches every linked URL and confirms it resolves, then fix whatever it flags. Aim for no broken links first; structure second.&lt;/p&gt;

&lt;p&gt;Three files, three jobs. Once that clicks, &lt;code&gt;llms.txt&lt;/code&gt; stops being mysterious — it's just the curation layer the web didn't have until AI needed one.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>seo</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I made my website callable by AI agents — the three layers (with the code I shipped)</title>
      <dc:creator>Andriy Pyvovarchuk</dc:creator>
      <pubDate>Sun, 26 Jul 2026 05:26:06 +0000</pubDate>
      <link>https://dev.to/andriy_pyvovarchuk/i-made-my-website-callable-by-ai-agents-the-three-layers-with-the-code-i-shipped-140i</link>
      <guid>https://dev.to/andriy_pyvovarchuk/i-made-my-website-callable-by-ai-agents-the-three-layers-with-the-code-i-shipped-140i</guid>
      <description>&lt;p&gt;"Make your site agent-ready" gets thrown around a lot, but it actually means three &lt;em&gt;different&lt;/em&gt; things to three &lt;em&gt;different&lt;/em&gt; kinds of AI agent — and they stack. I built a small tool (a free llms.txt validator) and wired up all three layers on it, so this is the copy-the-code version, not the hand-wavy one.&lt;/p&gt;

&lt;p&gt;But before the three layers, let's start at the bottom — because the whole stack rests on one small file most sites still don't have.&lt;/p&gt;

&lt;h2&gt;
  
  
  First: what is llms.txt, and why should you care?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;llms.txt is a plain-text file you put at &lt;code&gt;yoursite.com/llms.txt&lt;/code&gt; that tells AI models what your site is about and which pages actually matter.&lt;/strong&gt; That's it. It's written in Markdown, and it's read by AI assistants and crawlers — ChatGPT, Claude, Perplexity, and the growing crowd of AI agents — when they try to understand your site.&lt;/p&gt;

&lt;p&gt;Here's the problem it solves. When an AI tries to "read" your website today, it gets a mess: HTML wrappers, navigation menus, cookie banners, ads, JavaScript that may not even run. It has to guess what's important and burns a big chunk of its limited context window doing it. Often it guesses wrong, or gives up.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; hands the AI a clean, curated map instead — &lt;em&gt;you&lt;/em&gt; decide what your site is and which pages are the good ones, in priority order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Example — Project Management for Teams&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Example is a project-management app. This file points AI assistants&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; at our most useful docs and product pages.&lt;/span&gt;

&lt;span class="gu"&gt;## Docs&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Quickstart&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/quickstart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: get set up in 5 minutes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;API reference&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: every endpoint, with examples

&lt;span class="gu"&gt;## Product&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Pricing&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/pricing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: plans and limits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two useful mental models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's the AI counterpart to &lt;code&gt;sitemap.xml&lt;/code&gt;.&lt;/strong&gt; A sitemap helps &lt;em&gt;search engines&lt;/em&gt; find every page; &lt;code&gt;llms.txt&lt;/code&gt; helps &lt;em&gt;AI models&lt;/em&gt; understand the few pages that matter, in the format they read most cheaply.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's the flip side of &lt;code&gt;robots.txt&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;robots.txt&lt;/code&gt; says what crawlers may &lt;em&gt;not&lt;/em&gt; touch; &lt;code&gt;llms.txt&lt;/code&gt; says what your site &lt;em&gt;is&lt;/em&gt; and where the value is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters now:&lt;/strong&gt; people increasingly get answers &lt;em&gt;about&lt;/em&gt; your product from an AI assistant instead of clicking to your site. If the AI can't cheaply and correctly understand you, it either skips you or describes you wrong. &lt;code&gt;llms.txt&lt;/code&gt; is your chance to feed it an accurate, curated summary — so you're represented the way you intend. It's a context layer, not a ranking trick: you're not gaming anything, you're just making your site legible to machines.&lt;/p&gt;

&lt;p&gt;(There's an optional companion, &lt;code&gt;llms-full.txt&lt;/code&gt;, that inlines the full text of those pages so an AI can ingest everything in one fetch. Start with &lt;code&gt;llms.txt&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;That's the foundation. Now the three layers that build on it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fro21hjax8277r813bk8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fro21hjax8277r813bk8m.png" alt="The llms.txt validator homepage — paste a domain, get a score in seconds" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The tool this article is about: paste a domain, it fetches &lt;code&gt;/llms.txt&lt;/code&gt;, checks it against the spec, and tests every link.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here are the three independent layers. You can adopt any one on its own:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;llms.txt&lt;/strong&gt; — context for crawlers reading your site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebMCP&lt;/strong&gt; — tools for an agent driving your UI &lt;em&gt;inside the browser&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A&lt;/strong&gt; — a callable endpoint for another agent hitting you &lt;em&gt;over the network&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Crawler → browser agent → networked agent. Different agents, different mechanisms, zero overlap.&lt;/p&gt;


&lt;h2&gt;
  
  
  Layer 1 — llms.txt: context for crawlers
&lt;/h2&gt;

&lt;p&gt;The baseline, and the highest-value one for most sites. A plain-text &lt;code&gt;llms.txt&lt;/code&gt; at your root tells an AI crawler what your site is and which pages matter, in priority order. It's read at fetch time — no JavaScript, no protocol, no SDK. It's literally Markdown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Example Docs&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Developer documentation for the Example API.&lt;/span&gt;

&lt;span class="gu"&gt;## Core&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Quickstart&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/quickstart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: 5-minute setup
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Authentication&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: API keys and OAuth

&lt;span class="gu"&gt;## Reference&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;API reference&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: every endpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules that actually matter in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One &lt;code&gt;#&lt;/code&gt; H1 title, one &lt;code&gt;&amp;gt;&lt;/code&gt; blockquote summary.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;##&lt;/code&gt; sections with real Markdown links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Absolute HTTPS URLs&lt;/strong&gt; — relative links are the single most common failure.&lt;/li&gt;
&lt;li&gt;Serve it as &lt;code&gt;text/plain&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole layer. If you do nothing else, do this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2 — WebMCP: tools for in-browser agents
&lt;/h2&gt;

&lt;p&gt;WebMCP lets a page expose callable &lt;strong&gt;tools&lt;/strong&gt; to an AI agent running inside the browser — so instead of the agent guessing at your form fields and clicking around, it invokes a typed action you defined. There are two ways to expose a tool, and I shipped both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declarative&lt;/strong&gt; — annotate a real form and the browser synthesizes a tool from it. Zero JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;toolname=&lt;/span&gt;&lt;span class="s"&gt;"open_llms_txt_report"&lt;/span&gt;
      &lt;span class="na"&gt;tooldescription=&lt;/span&gt;&lt;span class="s"&gt;"Validate a site's llms.txt and open its report."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt; &lt;span class="na"&gt;toolparamdescription=&lt;/span&gt;&lt;span class="s"&gt;"Domain or URL to validate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Imperative&lt;/strong&gt; — register a tool in JavaScript that returns structured data directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modelContext&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;validate_llms_txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Validate a website's llms.txt; returns a 0-100 score and findings.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/validate?url=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Guard the imperative call behind &lt;code&gt;if (navigator.modelContext)&lt;/code&gt; so it's a no-op where WebMCP isn't present. Reality check: it's a Chrome origin trial today, so actual reach is tiny. Treat it as a &lt;strong&gt;forward signal and a clean reference&lt;/strong&gt;, not a traffic source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3 — A2A: a callable agent for other agents
&lt;/h2&gt;

&lt;p&gt;The Agent2Agent (A2A) protocol — now under the Linux Foundation — lets other agents &lt;strong&gt;discover and call&lt;/strong&gt; your service over plain HTTP. No browser, no scraping. It's the agent-to-agent counterpart of a public API.&lt;/p&gt;

&lt;p&gt;Two parts: a discovery document (the &lt;strong&gt;agent card&lt;/strong&gt;) and a callable endpoint (the &lt;strong&gt;skill&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agent card&lt;/strong&gt; — a JSON file at &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"protocolVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llms.txt Validator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validate a website's llms.txt and return a score with findings."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://llms-txt-validator.dev/a2a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validate_llms_txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validate llms.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Given a domain or URL, fetch and validate its llms.txt."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The endpoint&lt;/strong&gt; — the card's &lt;code&gt;url&lt;/code&gt; points at a JSON-RPC 2.0 endpoint. An agent calls &lt;code&gt;message/send&lt;/code&gt;; you do the work and return a &lt;code&gt;Task&lt;/code&gt;. A real call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/a&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"message/send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validate llmstxt.org"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and the reply — a completed task with a human-readable summary &lt;em&gt;and&lt;/em&gt; structured data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"artifacts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Validated llmstxt.org: score 100/100..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"report"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"scores"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"overall"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent gets exactly the data it asked for. You don't need the whole protocol to start — pick one real capability, serve a card with one skill, implement the synchronous &lt;code&gt;message/send&lt;/code&gt;, return a completed &lt;code&gt;Task&lt;/code&gt;. Streaming, task history and push notifications are optional (&lt;code&gt;capabilities.streaming: false&lt;/code&gt;) until you need them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one rule: don't publish a signal you can't back
&lt;/h2&gt;

&lt;p&gt;It's tempting to drop an empty agent card or fake form annotations to make some audit go green. &lt;strong&gt;Don't.&lt;/strong&gt; An agent that fetches your card and calls a dead endpoint — or invokes a tool that does nothing — trusts you &lt;em&gt;less&lt;/em&gt; afterward. Every signal should resolve to something real — and be honest about what a checker can actually see. The validator I built reports an A2A card as present when it's there and parses; it doesn't call the endpoint behind it. For WebMCP it can only see declarative &lt;code&gt;&amp;lt;form toolname=…&amp;gt;&lt;/code&gt; markup, because an imperative &lt;code&gt;navigator.modelContext.registerTool&lt;/code&gt; runs in the browser and is invisible to a server-side fetch — so it says that in the result instead of reporting a blank as "absent". Whether the thing behind the signal works is still on you to test.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to verify each layer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;llms.txt&lt;/strong&gt; — run your domain through an &lt;a href="https://llms-txt-validator.dev/" rel="noopener noreferrer"&gt;llms.txt validator&lt;/a&gt;; aim for 100/100 with no broken links.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm7jsp7e52n6djifk8l1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm7jsp7e52n6djifk8l1s.png" alt="A 100/100 llms.txt report broken down by Structure, Links and Best practices" width="800" height="688"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A clean report: 100/100, with Structure / Links / Best practices scored separately. The tool fetches every linked URL, so a broken link drags the score down even when the Markdown is perfect.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebMCP&lt;/strong&gt; — in Chrome 149+, enable &lt;code&gt;chrome://flags/#enable-webmcp-testing&lt;/code&gt; and run Lighthouse's Agentic Browsing audit; your tools should be listed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A&lt;/strong&gt; — fetch your &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt;, then POST a &lt;code&gt;message/send&lt;/code&gt; call to its &lt;code&gt;url&lt;/code&gt; and confirm you get a completed task back.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That's the whole stack: llms.txt for crawlers, WebMCP for in-browser agents, A2A for networked ones. Start with llms.txt (real value today), add WebMCP and A2A as forward signals when you have a genuine action or service to expose.&lt;/p&gt;

&lt;p&gt;If you build one, I'd love to see it — drop your agent card in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
