# Make a blog agent-ready

This is the procedure a static blog was taken through, from one passing
check to a full score on every check that applies to a site whose job is
publishing text. It is ordered by value, not by the order a scanner reports
things in: the first three steps are most of the benefit, and the last two
exist mainly to be found.

Scan with a content-site profile if the tool offers one. Scored against
every check a scanner has, including the ones written for shops and APIs,
the same site comes out lower, and the gap is made of things a blog should
not publish. See "Where it stops" at the end.

Before you start, read what the site already publishes. Fetch
`/robots.txt`, `/llms.txt` and `/.well-known/api-catalog` and see which
return 404. Do not add a file that is already there under another name.

## 1. Give every page a Markdown twin

The single highest-value change. An agent parsing your HTML is reverse
engineering a rendering of text you already have.

Publish each page's source Markdown at the same path with `index.md`
appended, carrying the metadata as frontmatter: `title`, `description`,
`published`, `updated`, `language`, `tags`, `url`, `license`. Then serve it
from the canonical URL too, for a request whose `Accept` header asks for
`text/markdown`.

Generate these from the same source the HTML is rendered from. If the twin
is maintained separately it will drift, and a stale twin is worse than
none. It is a confident answer that is wrong.

Two details that are easy to get wrong:

- Serve `.md` with `Content-Type: text/markdown; charset=utf-8`. A
  `text/plain` twin is still useful; an `application/octet-stream` one gets
  downloaded instead of read.
- Content negotiation is usually a path rule, and a path rule cannot ask
  whether a file exists. A URL with no twin will answer `Accept:
  text/markdown` with a 404 rather than falling back to HTML. Either write
  a stub twin for every uncovered page, or scope the rule narrowly.

## 2. Publish an index an agent can fetch in one request

Three files, all generated:

- `/llms.txt`: what this site is and where the good copies are. Short. Its
  job is to route, not to contain.
- `/archive.json`: every post with metadata and no bodies. This is the one
  that gets used: it is small, and an agent can read the titles and decide
  what to fetch rather than guessing URLs.
- `/llms-full.txt`: every post in full, for one-shot ingestion.

Put a `language` field on each entry if you write in more than one. An agent
that assumes otherwise will translate a quote without saying it did.

## 3. State a crawling policy instead of a shrug

In `robots.txt`, name the AI crawlers explicitly: GPTBot, ClaudeBot,
Google-Extended, PerplexityBot and the rest. A bot covered only by
`User-agent: *` has been told nothing in particular.

Add a `Content-Signal` line per group declaring what the content may be used
for (`search`, `ai-input`, `ai-train`), and a `license` an agent can check
before quoting. Permission that is stated can be relied on; permission that
is merely implied by an `Allow` cannot.

## 4. Add an MCP server, if search would actually help

Worth it when the archive is too big to list whole. Streamable HTTP, no
authentication for public writing, three tools: list, get, search.

Be honest in the tool descriptions about what the search does. A
word-matching ranker is not an embedding model: it does not cross
languages and it does not know synonyms. Say so in the description, and
make a weak result say that it is weak. An agent has no other way to tell
the difference between "no match" and "here is the nearest noise".

Advertise it at `/.well-known/mcp/server-card.json`, and register the same
tools on `navigator.modelContext` so an agent driving a browser finds them
too.

## 5. Make the above discoverable

- `/.well-known/api-catalog`: an RFC 9727 linkset pointing at everything.
- `/.well-known/ai-catalog.json`, an ARD capability manifest: the same
  resources again, in the schema a registry wants, with two to five
  `representativeQueries` per entry so it can route a question here without
  fetching. Announce it with an `Agentmap:` line in `robots.txt` and a
  `<link rel="ai-catalog">` in the head; scanners look for all three.
  Each entry's id field is called `identifier`, not `id`, and the host
  object wants one too. Get that wrong and the manifest still returns 200,
  is still found by all three mechanisms, and has every entry rejected,
  which is a slow way to find out. Validate it before you believe it.
- A `Link` header on every response, with `rel="service-doc"` for
  `llms.txt` and `rel="alternate"` for the Markdown twin.
- DNS-AID records, if your zone is signed with DNSSEC, which the spec
  requires. A `TXT` at `_index._agents.<domain>` listing what you have,
  `agents=blog:mcp`, and an SVCB at `_mcp._agents.<domain>` of the form
  `1 <domain>. alpn="h2,h3" port=443`. That leading `1` is the SvcPriority
  and it carries the meaning: `0` is AliasMode and will not count, `1` is
  the ServiceMode record the resolvers are looking for. Publish the
  per-agent name your index names, too. An index entry `blog:mcp` sends a
  resolver to `blog._mcp._agents.<domain>`, and that should not be
  NXDOMAIN. It is an IETF draft whose index format is explicitly unfinished,
  so this buys you very little today beyond the check; include it because
  it is true, not because it is useful yet.
- `/auth.md`: say there is no authentication, and state the rate limits.
  An agent that cannot tell "open" from "I have not found the key yet" will
  back off from a site that was never closed.
- JSON-LD `BlogPosting` in each page head, from the same record as
  everything above.

## Where it stops

Do the above and you will land one level below the top, with two checks
outstanding. They are the two that ask you to lie.

One wants OAuth Protected Resource Metadata and an `agent_auth` block in
authorization-server metadata, so that agents can "register securely",
for a public archive with no authorization server, no registration and
nothing to protect. The other wants an A2A agent card at
`/.well-known/agent-card.json` describing your agent's name, version,
interfaces and skills, for a static site that is not an agent and has none
of those.

Both are files that assert a capability you do not have, to software that
will act on the assertion. Leave them out. The level below the top, honestly
earned, is the correct score.

One caveat, worth knowing before you trust your own result: the scanner I
used returns `"level": 4` in its API and renders the badge "LEVEL 5,
Agent-Native" on the page, which is the name of the level it has just
finished telling you that you have not reached. Read the JSON, not the dial.

## The rule to keep

Every file here is an assertion that something will act on without a human
in the loop. Do not write a claim into one that you have not checked: not
a summary the author did not write, not a capability the search does not
have, not a count you did not measure. The failure mode of an agent-readable
site is not being unreadable. It is being confidently wrong.

---

From [To blog seriously, first I have to move to 2026](https://zajdband.com/what-agent-ready-actually-means/) by Dan Zajdband, CC BY 4.0
(https://creativecommons.org/licenses/by/4.0/). This is the procedure behind that article, published
separately so you can act on it. https://zajdband.com is the site it was carried out
on, so you can check the result of every step against a live example before
you run it against yours.
