From bed8a1b3b2561691332c5874137f7c76fda3c0f1 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 09:24:15 +0000 Subject: [PATCH 1/2] feat: add product-level root llms.txt Serve a product-level llms.txt at docs.page/llms.txt via an app-router route handler, mirroring the existing schema.json handler. Points agents at the docs and the per-repository agent surfaces (llms.txt, llms-full.txt, MCP, search.json, raw .md/.mdx). A route handler is used rather than a public/ file because the pages catch-all returns 404 for single-segment paths; the app-router handler reliably takes precedence. --- app/src/app/llms.txt/route.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 app/src/app/llms.txt/route.ts diff --git a/app/src/app/llms.txt/route.ts b/app/src/app/llms.txt/route.ts new file mode 100644 index 00000000..73a9fb3a --- /dev/null +++ b/app/src/app/llms.txt/route.ts @@ -0,0 +1,26 @@ +const LLMS_TXT = `# docs.page + +> docs.page hosts agent-ready documentation from any GitHub Markdown repository — with search, AI chat, an MCP server, and llms.txt — and no build step. + +## Documentation +- [Documentation](https://use.docs.page): full product documentation +- [Get started](https://use.docs.page/quickstart): quickstart guide +- [CLI](https://use.docs.page/reference/cli): local preview and project tooling +- [GitHub](https://github.com/invertase/docs.page): source repository (Apache-2.0) + +## Agent surfaces +Every site hosted on docs.page exposes machine-readable endpoints at a predictable per-repository pattern, where \`{owner}/{repo}\` is the backing GitHub repository: +- \`https://docs.page/{owner}/{repo}/llms.txt\`: LLM-oriented index of the site's pages +- \`https://docs.page/{owner}/{repo}/llms-full.txt\`: full concatenated Markdown of the site +- \`https://docs.page/{owner}/{repo}/mcp\`: Model Context Protocol server (tools: read_doc_page, list_doc_files) +- \`https://docs.page/{owner}/{repo}/search.json\`: search index +- Append \`.md\` or \`.mdx\` to any documentation page URL to fetch its raw Markdown source +`; + +export function GET() { + return new Response(LLMS_TXT, { + headers: { + "Content-Type": "text/plain; charset=utf-8", + }, + }); +} From 0f276d970c191f94823212dee5a73838131367fe Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 09:38:03 +0000 Subject: [PATCH 2/2] perf: add cache-control headers to root llms.txt --- app/src/app/llms.txt/route.ts | 6 +++++- app/src/proxy.ts | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/src/app/llms.txt/route.ts b/app/src/app/llms.txt/route.ts index 73a9fb3a..a07d6111 100644 --- a/app/src/app/llms.txt/route.ts +++ b/app/src/app/llms.txt/route.ts @@ -1,3 +1,5 @@ +import { ROOT_LLMS_TXT_CACHE_HEADERS, setDocsCacheHeaders } from "@/proxy"; + const LLMS_TXT = `# docs.page > docs.page hosts agent-ready documentation from any GitHub Markdown repository — with search, AI chat, an MCP server, and llms.txt — and no build step. @@ -18,9 +20,11 @@ Every site hosted on docs.page exposes machine-readable endpoints at a predictab `; export function GET() { - return new Response(LLMS_TXT, { + const response = new Response(LLMS_TXT, { headers: { "Content-Type": "text/plain; charset=utf-8", }, }); + setDocsCacheHeaders(response.headers, ROOT_LLMS_TXT_CACHE_HEADERS); + return response; } diff --git a/app/src/proxy.ts b/app/src/proxy.ts index 407c7bb5..38a81917 100644 --- a/app/src/proxy.ts +++ b/app/src/proxy.ts @@ -80,6 +80,16 @@ export const LLMS_FULL_TXT_CACHE_HEADERS = buildCdnCacheHeaders({ staleWhileRevalidate: CDN_STALE_SECONDS, staleIfError: CDN_STALE_SECONDS, }); +/** + * Root product-level `llms.txt`: a hand-written static file that changes rarely, + * so it tolerates a day-long edge TTL and hourly browser revalidation. + */ +export const ROOT_LLMS_TXT_CACHE_HEADERS = buildCdnCacheHeaders({ + edgeMaxAgeSeconds: SECONDS_PER_DAY, + staleWhileRevalidate: CDN_STALE_SECONDS, + staleIfError: CDN_STALE_SECONDS, + browserMaxAgeSeconds: 60 * 60, +}); export const SITEMAP_CACHE_HEADERS = buildCdnCacheHeaders({ edgeMaxAgeSeconds: 3600, staleWhileRevalidate: CDN_STALE_SECONDS,