llms.txt and .md pages
Every site publishes a machine-readable index at /llms.txt, the whole corpus at /llms-full.txt, and a clean-Markdown twin of every page at <path>.md — plus a Copy page button so readers can hand a page to their own AI tool.
Every site publishes a machine-readable index at /llms.txt, the whole corpus at /llms-full.txt, and a clean-Markdown twin of every page at <path>.md — plus a Copy page button so readers can hand a page to their own AI tool.
AI clients don’t want your HTML. They want the prose, they want to know what pages exist, and
they want both without running a browser. Every Papervine site publishes three surfaces for
exactly that, generated from the same content the site renders — nothing to configure, nothing
to keep in sync. This works the same whether your site is hosted here or served by the CLI
(papervine dev / papervine serve).
An index of every page: title, link, and description, under headings that mirror your navigation.
The index plus the full Markdown body of every page — the whole site in one fetch.
Any page’s Markdown source. /guides/auth also serves at /guides/auth.md.
Readers hold AI tools too, and their next move is often “paste this page into a chat”. Every page carries a Copy page button at the top of the article, with two more actions behind its chevron:
| Action | What it does |
|---|---|
| Copy page | Puts the page’s Markdown on the clipboard — the same bytes an agent gets from <path>.md. |
| Ask Assistant | Opens the AI assistant already scoped to this page. |
| Download PDF | Opens the print dialog, where Save as PDF produces the file. |
Because Copy page fetches the page’s own .md twin, there’s exactly one definition of “this
page as Markdown” — what a reader copies and what an agent reads can’t drift apart. On a site
with reader authentication, the request carries the reader’s session, so a
signed-in reader copies the gated page they’re already reading.
Two notes on the PDF. It’s the page you’re on, printed with the docs chrome removed — the navigation, sidebar, and table of contents don’t reach the paper, and code blocks, tables, and callouts aren’t split across sheets. For the whole site as one document, use Export all content instead.
Ask Assistant is hidden when the assistant is off for a site, and generated API reference
pages have no Copy page button — they’re built from your OpenAPI spec rather than an MDX file,
so there’s no Markdown source to hand over. The spec itself is linked from /llms.txt.
/llms.txt follows the llmstxt.org convention: an H1 with your site
name, a blockquote summary, then the pages as a linked list.
# Acme Docs
> Everything about the Acme API.
## Get started
- [Introduction](https://docs.acme.com/index.md): What Acme does and why.
- [Quickstart](https://docs.acme.com/quickstart.md): Ship your first request in five minutes.
## Guides
### Authentication
- [
Three things worth noticing:
.md, not the page. A client that follows a link gets Markdown, not
a page it has to strip.description becomes its one-line summary
in the index, truncated to 300 characters./llms-full.txt is the same index followed by every page’s body, each under its title and the
URL it came from. Use it when you want the whole corpus in one request; use /llms.txt when you
want an agent to fetch only the pages it actually needs.
Both are also served at their /.well-known/ paths — /.well-known/llms.txt and
/.well-known/llms-full.txt — for clients that probe there first.
Append .md to any page’s URL to get its Markdown source:
The response is the page’s authored MDX with its frontmatter replaced by an H1 title and the
description. Component tags are left as written: an <Accordion> in the source tells a reader
(human or not) that the content is grouped, and flattening it would throw that away.
The index page’s twin is /index.md, since / alone has nowhere to put the extension.
The feed is built from the same navigation and the same access rules as the site itself, so it can’t drift from what a reader sees:
| A page that is… | Appears in the feed? |
|---|---|
| In your navigation | Yes |
noindex: true | No — the same opt-out that withholds a page from search and SEO |
hidden: true (reachable by URL, not in the sidebar) | Only with seo.indexing: "all" |
| Gated behind reader authentication | Not in /llms.txt; see below |
An external link (frontmatter url) | Yes, in an Optional section, pointing off-site |
/llms.txt and /llms-full.txt are anonymous surfaces — there’s no reader session on a
crawler’s request — so on a site with reader authentication they list and inline only the
public subset. A gated page never appears in the index and its body never appears in the
corpus dump.
A page’s .md twin is different, because it’s a single page rather than a bulk dump: it honors
the reader’s real session. A signed-in reader gets the Markdown for a page they can already read
in the browser; an anonymous client gets the same 404 the HTML page would give, so a gated page
never confirms it exists.
Nothing is required. Four docs.json fields tune the output:
A one-line summary of the site, rendered as the blockquote under the H1. This is the first thing an AI client reads to decide what your docs are about — worth writing deliberately.
Free-form guidance for AI clients, emitted verbatim after the summary. Use it to say what a model should know before quoting you — which version supersedes which, how to cite you, what’s out of scope.
"all" adds pages that exist in your repo but appear nowhere in the navigation, under an
Additional pages heading. noindex still wins.
{
"name": "Acme Docs",
"description": "Everything about the Acme API.",
"markdown": {
"instructions": "Cite the version banner on each page. The v1 pages are archived."
},
"seo": { "indexing": "all" }
}
If the generated index isn’t what you want an agent to read, commit an llms.txt (or
llms-full.txt) at the root of your docs repo, next to docs.json. It’s served verbatim and
nothing is generated. This is an all-or-nothing override — there’s no merging — so reach for it
only when you want to hand-curate the whole file.
Every docs page’s response carries headers pointing at the index, so a client that landed on any page can find it without guessing:
X-Llms-Txt: /llms.txt
Link: </llms.txt>; rel="alternate"; type="text/plain"; title="llms.txt"
Link: </llms-full.txt>; rel="alternate"; type="text/plain"; title="llms-full.txt"
A .md response additionally carries a Link: …; rel="canonical" to the HTML page, so a client
that quotes the Markdown can cite the URL a human should open.
Fetches of all three surfaces are recorded as agent traffic, attributed by client — the same accounting the MCP server uses — and show up in the Agents tab in Analytics. This is how you find out which AI tools are reading your docs, and which pages they keep coming back for.
A very large index is currently served whole rather than split across paginated
/_llms/ sections. Nothing truncates, so no page goes missing; a site with thousands of pages
will just have a large /llms.txt.