# Agent-readable pages

Public Markdown and discovery derive from the same content developers read.

## Public representations [#public-representations]

`/llms.txt` lists public content and connection endpoints. `/llms-full.txt` includes the landing, legal text, static demo content and every developer page. Each developer page has a `.md` representation, including `/docs.md`, deep pages and changelog entries.

```sh
curl http://localhost:3005/docs/features/todo-imports.md
curl http://localhost:3005/llms.txt
```

Developer content is English only. The landing and legal Markdown exist once per language at the page's path: `/index.md` and `/privacy.md` are English, `/de/privacy.md` and `/fr/privacy.md` the others, and a legacy `?lang=de` answers a 301 to the path form. Negotiating `text/markdown` on `/de` returns `/de/index.md`. Developer docs URLs enter the sitemap without hreflang alternates, with a `<lastmod>` from the frontmatter date or the git commit date. Robots permits docs and excludes authenticated routes.

## Finding the Markdown [#finding-the-markdown]

The Markdown copies are not in the sitemap; they are the same documents in another representation. Three paths lead to them:

- `/llms.txt` lists every public page as its `.md` URL.
- Every HTML page that has a copy carries `<link rel="alternate" type="text/markdown" href="…">`, and the Markdown answers with `Link: <…>; rel="canonical"; type="text/html"` back.
- Content negotiation: a `GET` with `text/markdown` as the best-weighted `Accept` type (`Accept: text/markdown`, or above `text/html`) is answered with the Markdown copy and a `Content-Location`. A browser's `text/html, …, */*;q=0.8` and a tie keep HTML. Every negotiable path sends `Vary: Accept`. The policy lives in `apps/web/src/platform/seo/request-policy.ts` and runs in `server.ts` around the framework handler.

```sh
curl -s -H 'Accept: text/markdown' http://localhost:3005/docs/features/agent-pages
curl -sI -H 'Accept: text/markdown' http://localhost:3005/ | grep -i 'content-type\|content-location\|vary'
```

## Discovery from the origin [#discovery-from-the-origin]

MCP clients that only know the origin probe `/.well-known/oauth-protected-resource`, `/.well-known/oauth-authorization-server` and `/.well-known/openid-configuration`. The Better Auth issuer is `<origin>/api/auth`, so each root form has a route under `apps/web/src/routes/[.]well-known/` that forwards into `auth.handler`: the protected-resource document unchanged (the plugin matches the root and `/api/mcp` path forms itself), the two server documents rewritten to their issuer-relative URL. The JSON is always the plugin's own.

Responses from an origin other than `VITE_SITE_URL` carry `X-Robots-Tag: noindex`, so a preview deployment's `llms.txt` and pages never enter an index ([SEO and discovery](/docs/features/seo)).

## Build and rendering [#build-and-rendering]

Fumadocs MDX compiles the repository content and processed Markdown at build time. The headless core supplies the page tree and search. Articles render on the server with shared UI tokens; a public GET route supports browser navigation. Runtime code reads no documentation files from disk and does not evaluate MDX source.

The existing ADR files and `docs/connect-your-agent.md` are imported into the collection, not copied. Relative references become docs links or repository links during compilation. Shiki highlighting happens during the build with github-light/github-dark tokens.

## Extend [#extend]

Add MDX with a title and description under `apps/web/content/docs`, then place it in its folder's `meta.json`. Use ordinary Markdown headings and links. Add a diagram as an accessible static SVG, with its light/dark variants. The source/link tests, sitemap and llms generation pick up the page automatically.

These routes contain only curated public content. Never add private Threads, account data or Organization exports to the public collection. Tool authorization remains the responsibility of the authenticated API.

## Key files [#key-files]

- [`apps/web/source.config.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/apps/web/source.config.ts)
- [`apps/web/src/features/docs/docs.source.server.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/apps/web/src/features/docs/docs.source.server.ts)
- [`apps/web/src/features/docs/docs.page.server.tsx`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/apps/web/src/features/docs/docs.page.server.tsx)
- [`apps/web/src/features/agent-pages/agent-pages.content.server.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/apps/web/src/features/agent-pages/agent-pages.content.server.ts)
- [`apps/web/src/routes/sitemap[.]xml.tsx`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/apps/web/src/routes/sitemap[.]xml.tsx)
- [`apps/web/src/platform/seo/request-policy.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/apps/web/src/platform/seo/request-policy.ts)
- [`apps/web/src/routes/[.]well-known/oauth-protected-resource.ts`](https://github.com/CarlosZiegler/template-cloudflare/blob/main/apps/web/src/routes/[.]well-known/oauth-protected-resource.ts)

