Documentation is read by models as much as by people, and all four answers below
are generated from the same collections the pages render from — so none of them
can drift from what is published.

| Path             | What it is                                                    |
| :--------------- | :------------------------------------------------------------- |
| `/llms.txt`      | An index: site name, summary, then every page's `.md` link      |
| `/llms-full.txt` | A duxt extension: the same pages in one Markdown file           |
| `<page>.md`      | Any page's own Markdown source                                  |
| `/mcp`           | An MCP server — four tools, from `list_versions` to `read_page` |

## Markdown, not HTML

Everything here hands over the Markdown as it was written, which is why the
collection schema asks Content to store `rawbody`. A model given rendered HTML
has to undo a layout to find a heading; one given the source reads what the
author wrote, with the code fences still fences and an MDC block still a
component call.

`<page>.md` is served by a **middleware** rather than a route, because `.md` is a
suffix on the last path segment and not a segment of its own — there is no Nitro
route pattern meaning "any path ending in .md".

## How a page announces itself

None of this has to be guessed at. Every documentation page carries two link
relations in its head — the first names its own Markdown, the second the index
that covers it:

```html
<link rel="alternate" type="text/markdown" href="/duxt/getting-started.md" />
<link rel="describedby" href="/llms.txt" />
```

The alternate points at the page being rendered rather than at its canonical, so
an old version hands over its own source instead of the current one's. Both are
`<link>` elements and not HTTP `Link` headers deliberately: a prerendered page
keeps them when an edge serves it as a static file, with no header configuration
anywhere in the deployment.

## Why both llms files

The [llmstxt.org](https://llmstxt.org) convention defines the index. duxt adds
`/llms-full.txt` as an extension for a model that cannot fetch the `.md` links
page by page and needs the same default-version documentation at once.

## Why a real MCP server

`/mcp` speaks the protocol through the official SDK rather than being a JSON
endpoint of duxt's own design. A client connects to it the way it connects to
anything else, instead of someone writing an adapter around a shape only this
theme has.

The server exposes four bounded tools: `list_versions`, `list_pages`,
`search_docs` and `read_page`. Search covers titles, descriptions and the
authored Markdown body, with source, version and path prefixes available to
narrow it. It deliberately does not reuse the browser's client-side index: the
tool has the deployed Content database and returns protocol-sized pages of
results instead. [MCP endpoint](/reference/endpoints/mcp) is the lookup reference
for arguments, pagination and result shapes.

### The one Nitro flag duxt turns on for you

`nitro.experimental.asyncContext` is set by the layer, and `/mcp` is why. An MCP
tool is called by the SDK, not by Nitro, so it is handed the protocol's own
request object and nothing of the HTTP request behind it — `useEvent()` is the
only way back to that, and it needs Nitro's async context to exist.

Nothing is asked of you and nothing else changes. A consumer who sets
`nitro: { experimental: { asyncContext: false } }` turns the MCP server off and
leaves the rest of the site exactly as it was.