# Documentation > An AI-native documentation framework for Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. ## Introduction URL: https://docs.farming-labs.dev/docs An AI-native documentation framework that just works @farming-labs/docs An AI-native documentation framework built on top of Fumadocs and other preset providers. One config file, zero boilerplate, with docs that are ready for humans, IDEs, and agents out of the box. Why @farming-labs/docs? - Zero boilerplate — No layout files, no [[...slug]] wrappers except for fully fledged functionality like AI, Search and metadata to work. Just write Markdown or MDX. - One config — Everything in docs.config.ts: theme, colors, typography, icons, components, metadata. - Token efficient — Your entire docs framework surface is a single config file (~15 lines). AI tools like Cursor, Copilot, and Claude spend less time reading framework plumbing and more time working with your actual content. Built-in JSON-LD structured data, llms.txt, sitemaps, generated robots.txt, and docs agent compact keep the machine-readable layer lean too. Learn more → - Multiple frameworks — First-class support for Next.js, TanStack Start, SvelteKit, Astro, and Nuxt that just works. - 11 themes — Default, Darksharp, Pixel Border, Colorful, Shiny, Ledger, DarkBold, GreenTree, Concrete, Command Grid, and Hardline — or build your own with createTheme(). - Built-in search — Zero-config simple search by default, or upgrade to Typesense, Algolia, or a custom adapter. - CLI scaffolding — npx @farming-labs/docs init first asks whether you're on an existing project or starting fresh; then it detects your framework, lets you pick a theme (or create your own), can scaffold locale folders for i18n, and generates config, routes, and sample pages. Use --template with --name to bootstrap a new project without prompts. Looking Ahead: Cloud Cloud is the infrastructure layer we are building around @farming-labs/docs. You ship the code and we write the docs for you. The open runtime stays yours. Cloud adds the managed layer around it for teams that want stronger search, retrieval, analytics, review workflows, and agent-ready docs operations without giving up Git as the source of truth. Join the waitlist to be an early user → other docs frameworks? We are not trying to replace Fumadocs — we aim to be complementary. @farming-labs/docs builds on Fumadocs (and other providers) to offer a single config, multi-framework support, and optional themes and tooling, so you can use Fumadocs with less boilerplate and across Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. If you're choosing between documentation tools, here’s when @farming-labs/docs is a good fit: - vs. Fumadocs alone — You get the same UI and MDX experience, but with a single docs.config and first-class support for TanStack Start, SvelteKit, Astro, and Nuxt — not just Next.js. One config model and CLI so you don’t wire layouts, themes, and search by hand. - vs. Docusaurus / VitePress — You keep full control inside your existing app (Next, TanStack Start, SvelteKit, Astro, Nuxt) instead of a separate docs site. No separate framework or build; your docs live in the same repo and stack, with one config file and optional built-in AI chat and search. - vs. Mintlify / ReadMe / hosted docs — You own the code and hosting. No vendor lock-in or per-seat pricing; you can deploy to Vercel, Cloudflare Pages, or anywhere. Built-in llms.txt, sitemaps, generated robots.txt, and AI chat give you AI-friendly docs without a third-party docs platform. - vs. hand-rolled MDX — No need to build layouts, catch-all routes, search, or theming yourself. The CLI scaffolds everything; you spend time on content and optional customization (themes, colors, sidebar, page actions) instead of framework plumbing. In short: use @farming-labs/docs when you want an AI-native docs runtime, one config, multiple frameworks, and minimal boilerplate — without leaving your current stack or adopting a separate docs-only framework. Customize themes and share them You can create your own theme and distribute it to others — as an npm package, a shared preset, or a CSS file. Themes control layout, colors, typography, and components; once built, anyone can plug them in via config. See Themes for built-in presets and Creating themes for the full API. Built-in docs UI — enable and customize from config Modern docs features are built in; you turn them on in configuration instead of wiring them yourself. Each element can be customized — layout, behavior, and appearance — via the same config and theme options. See Customization for the full overview. - Ask AI — RAG-powered chat, multiple models and providers, floating or sidebar mode. Customize labels, suggested questions, model list, and UI mode. - Search — Built-in simple search works out of the box, and you can switch to Typesense, Algolia, or a custom adapter from configuration. - Page actions — Copy Markdown, open in ChatGPT/other tools. Customize which actions appear and their options. - Sidebar — Collapsible, flat or nested, banner, footer. Customize structure, style, and content (banner, footer, nav title). - Components — Built-in MDX components like Callout, Tabs, and HoverLink, plus your own custom React components. - llms.txt — LLM-friendly index whose page links point directly to markdown routes. Options described in the llms.txt docs. - Sitemaps — XML and Markdown maps of canonical docs URLs, descriptions, related pages, and freshness metadata. Runtime routes are enabled by default. - Robots.txt — Runtime agent-friendly crawl policy at /robots.txt when no static file already exists; use docs robots generate for static export or committed policies. Theme-level customization (colors, typography, components) is covered in Configuration and Customization (including Colors and Typography). No custom components or routes required — enable and tweak what you need in docs.config. Packages | Package | Description | | ---------------------------- | ------------------------------------------------------------------------------------ | | @farming-labs/docs | Core types, defineDocs(), createTheme(), extendTheme() | | @farming-labs/theme | Next.js theme presets, layout components, CSS presets | | @farming-labs/next | Next.js adapter — withDocs(), MDX processing, search API | | @farming-labs/tanstack-start | TanStack Start adapter — docs page renderer, MDX processing, search API, server loader | | @farming-labs/svelte | SvelteKit adapter — server-side docs loader, markdown processing | | @farming-labs/svelte-theme | SvelteKit theme presets, DocsLayout, DocsContent components | | @farming-labs/astro | Astro adapter — server-side docs loader, markdown processing | | @farming-labs/astro-theme | Astro theme presets, DocsLayout, DocsContent components | | @farming-labs/nuxt | Nuxt 3 adapter — defineDocsHandler(), server-side docs loader, markdown processing | | @farming-labs/nuxt-theme | Nuxt theme presets, DocsLayout, DocsContent components | Quick Start The fastest way to get started is with the CLI: The CLI first asks existing project or fresh? — then (for existing) auto-detects your framework, lets you pick a theme or create your own, can scaffold locale folders and i18n config, and generates config, routes, CSS, and sample pages. Prompts with a default (e.g. entry path docs) accept Enter to skip typing. See the CLI reference for the full init flow, or set up manually with the Installation guide. Next Steps - Guides — Long-form playbooks, starting with writing agent-friendly docs - Token Efficiency — Why this is the most AI-friendly docs framework - Cloud — Where the managed infrastructure layer for humans and agents is heading - Configuration — Customize your theme, colors, typography, sidebar, and more - Themes — Browse available themes and learn how to create your own - Customization — Colors, typography, sidebar, AI chat, page actions - Ask AI — Add RAG-powered AI chat to your docs - API Reference — Complete reference for every config option - Contributing — How to report issues, suggest features, and submit pull requests --- ## CLI URL: https://docs.farming-labs.dev/docs/cli Scaffold, upgrade, compact agent docs, generate sitemaps and robots.txt, sync search, and run MCP CLI The @farming-labs/docs CLI has a few main jobs: - init — create a new docs app or add docs to an existing app - upgrade — bump the docs packages in an existing project - agent compact — generate sibling agent.md files from resolved docs pages - agents generate — write root and static AGENTS.md instructions for coding agents - doctor — audit local docs readiness and optional hosted agent routes - mcp — run the built-in docs MCP server over stdio for local clients and IDE agents - search sync — push docs content into an external search index like Typesense or Algolia - sitemap generate — write sitemap metadata plus static sitemap.xml and sitemap.md files - robots generate — write or append an agent-friendly robots.txt policy If you only need the common commands, start here. The framework-specific output and generated file lists are further down this page. Quick Start From scratch Use init --template when you want a brand new project directory with docs already set up. This creates a new folder, clones the example template, installs dependencies, and leaves you with a ready-to-run docs app. Existing project Use plain init inside an app you already have. Run that from your app root. The CLI will: 1. detect your framework from package.json or ask you to choose 2. ask for a theme 3. ask where docs should live, usually docs 4. ask about aliases, global CSS, and optional i18n when supported 5. generate the docs files and install the correct @farming-labs/* packages Upgrade an existing docs project Use upgrade when docs are already installed and you just want newer package versions. By default, the CLI detects your framework and package manager from the current project. If detection is ambiguous, pass --framework. Run the built-in MCP server locally Use mcp when you want to expose the current docs project to a local MCP client over stdio. If your config lives outside the project root default: To test the HTTP MCP route in this repo instead of stdio, start the Next example: Then point your MCP client or inspector at: Sync an external search index Use search sync when you want to pre-index docs into a hosted search backend instead of waiting for the first request to trigger sync. The command reads .env and .env.local from the current project, scans the docs content based on entry / contentDir, and uploads search documents to the selected provider. Typesense example: Required env: Optional: Algolia example: Required env: Generate sitemaps Use sitemap generate when you want static sitemap.xml and sitemap.md files, or when a server-rendered deployment needs a generated manifest with reliable lastmod dates. By default the command writes: - .farming-labs/sitemap-manifest.json - public/sitemap.xml - public/sitemap.md - public/.well-known/sitemap.md SvelteKit writes the public files under static/ instead of public/. Use --config when your config lives outside the project root: Useful flags: | Flag | Description | | ---- | ----------- | | --config | Use a custom docs config path. | | --manifest-only | Write only .farming-labs/sitemap-manifest.json. Useful for server-rendered apps that serve sitemap routes through the runtime. | | --public | Explicitly write public sitemap files. This is already the default unless --manifest-only is set. | | --check | Fail if the generated manifest or public sitemap files are stale. | The generator resolves lastmod from each page source file's last git commit date, then falls back to filesystem modified time. See Sitemaps for route config, static Generate robots.txt Use robots generate when you want a static robots.txt file that explicitly allows docs, markdown, llms.txt, sitemap, AGENTS.md, skill, MCP, and agent discovery routes. By default the command resolves the framework public directory and writes: - public/robots.txt for Next.js, TanStack Start, Astro, and Nuxt projects - static/robots.txt for SvelteKit projects If the target file already exists, the command keeps it untouched. Use --append to add or update the generated @farming-labs/docs block inside an existing policy, or --force to replace the file: Useful flags: | Flag | Description | | ---- | ----------- | | --path | Write to a specific robots.txt path. Positional paths work too. | | --append | Add or update the generated block inside an existing robots.txt. | | --force | Replace the target robots.txt with the generated policy. | | --check | Fail if the generated output would change. | | --config | Use a custom docs config path. | Configure defaults with robots in docs.config.ts, then run the command during release prep or static export builds when you want the policy committed. Compact page-level agent docs For commands, config, and how generated agent.md files affect .md routes, docs API markdown output, and MCP, see Agent compact below. Agent compact Use agent compact when you want shorter page-level machine docs without hand-writing every agent.md. What it does: - resolves the same machine-readable page the runtime already uses: agent.md, then Agent blocks, then normal page markdown - compresses that document with the Token Company API - creates missing sibling agent.md files and overwrites existing ones - makes the written agent.md the new source for .md routes, docs API markdown output, and MCP read_page() Common forms: Selection notes: - positional page arguments are the preferred UX; --page is an optional repeatable alias - page identifiers can be a slug, docs path, .md path, full docs URL, or . for the root docs page - --all compacts every folder-based docs page under the configured contentDir - --changed compacts only docs pages changed in the current git working tree, including staged, unstaged, and untracked docs changes plus handwritten agent.md sources - --stale only refreshes generated agent.md files whose source content or compaction settings drifted - --include-missing only works with --stale, and creates missing agent.md files for explicit pages or pages that define agent.tokenBudget - only folder-based pages can be written automatically, because the command needs a sibling agent.md target Recommended config defaults: If you prefer to reference the env var directly in docs.config.tsx, that works too: The command loads .env and .env.local from the current project before it resolves the key, so the default TOKENCOMPANYAPI_KEY env var works without exporting it manually. Per-page budget overrides live in frontmatter: That agent.tokenBudget value becomes the compact output target for just that page. It overrides global agent.compact.maxOutputTokens defaults and CLI --max-output-tokens values for the same page. If the page already has a sibling agent.md, the command compacts that file. If it does not, the command compacts the generated machine-readable page output first and then writes the resulting agent.md. If a global minOutputTokens would be higher than the page budget, the CLI clamps it down to the same page budget before calling the compression API. Generated agent.md files include hidden provenance metadata so the CLI can tell the difference between: - fresh generated files - stale generated files whose page content or compact settings changed - modified generated files that were edited by hand after generation - unknown handwritten agent.md files that do not carry generated provenance - missing files for pages that still use the fallback machine-readable page output docs doctor --agent reports those states, and docs agent compact --stale only refreshes the generated files that are actually stale. Handwritten agent.md files still count as explicit machine context; the doctor reports them as unknown freshness instead of treating them as stale. Useful checks: See Configuration for agent.compact, and Token Efficiency for when you should use compaction instead of writing agent.md by hand. Agents Generate Generate committed AGENTS.md files for coding agents and static exports: By default the command writes a managed root AGENTS.md plus public copies at /AGENTS.md, /.well-known/AGENTS.md, /AGENT.md, and /.well-known/AGENT.md (static/ instead of public/ for SvelteKit). If a hand-written root AGENTS.md or AGENT.md already exists, the command keeps it and uses that content for missing static aliases unless --force is passed. Doctor Use docs doctor --agent when you want a quick health check for the machine-facing side of the docs site. Use docs doctor --site when you want a reader-facing audit of navigation, descriptions, structure, trust signals, feedback, and reading-time cues. This command does not generate files. It inspects the current project and scores how ready the docs are for agents, IDEs, and GEO-style crawlers. What it checks: - docs config resolution - docs content discovery - framework docs API route wiring - public agent route wiring - agent discovery spec - llms.txt - sitemap.xml and sitemap.md - robots.txt - AGENTS.md - skill.md - MCP - search - agent feedback, which is enabled by default and can be explicitly opted out - page metadata that feeds markdown output and JSON-LD structured data - explicit agent-friendly pages - generated agent.md freshness and agent.compact defaults Common forms: Expected output: _Sample abbreviated for brevity. Full runs can also include a Next steps section with the most useful follow-up improvements._ Reader-facing runs use the same overall shape, but print doctor — site and score: - docs config resolution - docs content discovery - navigation coverage - page descriptions - page structure - search - trust signals - reader feedback - reading-time cues When you need structured output instead of terminal text, add --json: Sample shape: The JSON report itself is written to stdout. Separate loader notices, such as config fallback warnings, are outside the JSON payload. Hosted agent checks Add --url when you want the doctor to verify a deployed docs site in addition to the local project files: Without --url, the command only inspects the repository: config, content, route files, metadata, generated sitemap state, and local agent-readiness signals. With --url, it also sends real requests to the hosted site and adds hosted checks to the report. The hosted pass probes: - /.well-known/agent.json - /llms.txt - /llms-full.txt - sitemap routes from the discovery spec, usually /sitemap.xml, /sitemap.md, and /.well-known/sitemap.md - the robots route from the discovery spec, usually /robots.txt - /AGENTS.md - /.well-known/AGENTS.md - /skill.md - /.well-known/skill.md - one representative page markdown route, such as /docs.md - the representative markdown route's canonical Link response header - sampled docs page HTML for application/ld+json structured data - sampled docs page HTML for ` - /mcp - /.well-known/mcp - https://mcp./mcp - https://mcp./ For MCP, the doctor performs a Streamable HTTP initialize request, reuses mcp-session-id when the server returns one, sends tools/list, and expects the built-in docs tools: - list_pages - get_navigation - search_docs - read_page Hosted checks add extra weighted checks, but the top-level score stays normalized to 100%. The JSON report also includes the normalized url field and hosted check IDs such as hosted-agent-discovery, hosted-llms, hosted-sitemap, hosted-robots, hosted-skill, hosted-markdown, hosted-markdown-canonical, hosted-structured-data, hosted-markdown-alternate, and hosted-mcp. Use this form after deploying, in preview-deployment CI, or any time a site looks healthy locally but an agent cannot discover the public routes. A hosted failure usually means the deployment is missing a rewrite, middleware forwarder, or public route, not that the MDX page content itself is wrong. How to read it: - the score is a quick summary, not the only thing that matters - PASS / WARN / FAIL lines tell you where the machine-facing surface is strong or thin - Explicit agent-friendly pages means pages that define extra machine-only context through agent.md or Agent blocks - the Next steps section points at the most useful improvements to make next When to use it: - before saying a docs site is agent-ready or agent-optimized - before calling the human docs experience polished or reader-ready - after changing docs.config, route wiring, .well-known endpoints, or MCP - before opening a PR for docs infrastructure work - in CI if you want a quick quality gate for the agent or human-facing surface - when another agent, script, or dashboard needs structured results instead of terminal formatting How essential it is: - not required to run the framework - very useful when you care about agent discovery, LLM consumption, MCP, or GEO - best thought of as a readiness audit for the machine-facing docs layer Init init without --template When you run init without --template, the CLI asks whether you're working with an existing project or starting from scratch. | Choice | Best for | What happens | | ------ | -------- | ------------ | | Existing project | You already have a Next.js, TanStack Start, Nuxt, SvelteKit, or Astro app | The CLI adds docs to the current app | | Fresh project | You want a new docs starter | The CLI creates a new folder from the selected example template | From scratch If you select Fresh project, the CLI asks for: 1. Framework — Next.js, TanStack Start, Nuxt, SvelteKit, or Astro 2. Project name — default is my-docs 3. Package manager — used for install and the suggested dev command You can skip those prompts entirely with --template and --name. Replace next with tanstack-start, nuxt, sveltekit, or astro. Existing project If you select Existing project, the CLI stays in the current folder and wires docs into the app you already have. 1. Framework — Auto-detected from package.json, or you pick one if detection fails. 2. Theme — One of the built-in themes or Create your own theme (you’ll be asked for a theme name; it creates themes/.ts and themes/.css). 3. Path aliases — Whether to use @/, $lib/, or ~/ for imports (framework-dependent). 4. Entry path — Where docs live (e.g. docs). Press Enter to use the default. 5. Optional API reference — Whether to scaffold API reference support. If you enable it, the CLI pre-fills the detected API route root (usually api) and lets you edit it before writing apiReference.routeRoot. 6. Optional i18n — Whether to scaffold internationalized docs, which locales to generate, any extra locale codes to add, and which locale should be the default. 7. Global CSS — Which file to use (auto-detected if there’s only one, otherwise you pick or type the path; Enter uses the default). 8. The CLI generates config, layout, and sample pages, then detects the package manager from a lockfile when possible. If no lockfile is found, it asks which package manager you want to use before installing dependencies, and can then start the dev server. Typical flow: Prompts that show a placeholder (e.g. entry path docs, project name my-docs, theme name my-theme) use that value as the default — you can press Enter to accept it instead of typing. Existing-project flags Use these to skip the matching prompts during init: | Flag | Description | | ---- | ----------- | | --theme | Skip the theme prompt | | --entry | Skip the docs entry prompt | | --api-reference | Enable API reference scaffold during init | | --no-api-reference | Explicitly skip API reference scaffold | | --api-route-root | Set the detected API route root written to apiReference.routeRoot | Example: --api-route-root is optional. If you omit it, the CLI tries to detect a sensible default from the framework’s route conventions and shows that value as the default in the prompt. When API reference is enabled through init: - Next.js writes the apiReference block and app/api-reference/[[...slug]]/route.ts (or src/app/...). - TanStack Start, SvelteKit, Astro, and Nuxt write the apiReference block and also scaffold the minimal /{path} handler files so the reference works immediately. Optional i18n scaffold If you enable i18n during init, the CLI will: 1. Let you multi-select common locales such as en, fr, es, de, pt, ja, ko, zh, ar, hi, and ru 2. Let you add custom locale codes such as pt-BR 3. Ask which locale should be the default The generated project gets: - An i18n block in docs.config.ts / src/lib/docs.config.ts - Locale content folders like docs/en, docs/fr, and docs/zh - Framework-specific starter files so routes like /docs?lang=en and /docs?lang=fr work out of the box Upgrade Upgrade all @farming-labs/ docs packages to the latest version. Run from your project root; the CLI auto-detects* your framework from package.json and upgrades the right packages for Next.js, TanStack Start, Nuxt, SvelteKit, and Astro. The CLI also detects your package manager from the lockfile in the current directory (pnpm-lock.yaml, yarn.lock, bun.lock, bun.lockb, or package-lock.json). If no lockfile is found, it asks which package manager you want to use instead of defaulting to npm. | Framework | Packages upgraded to @latest | | --------- | ------------------------------ | | Next.js | @farming-labs/docs, @farming-labs/theme, @farming-labs/next | | TanStack Start | @farming-labs/docs, @farming-labs/theme, @farming-labs/tanstack-start | | Nuxt | @farming-labs/docs, @farming-labs/nuxt, @farming-labs/nuxt-theme | | SvelteKit | @farming-labs/docs, @farming-labs/svelte, @farming-labs/svelte-theme | | Astro | @farming-labs/docs, @farming-labs/astro, @farming-labs/astro-theme | To specify the framework explicitly (e.g. in a monorepo or when auto-detect fails), use --framework: Valid values: next, tanstack-start, nuxt, sveltekit, astro. Use --latest (default) to install the latest stable release, or --beta to install beta versions: If you prefer the shorter command form, the CLI also accepts upgrade@beta and upgrade@latest. For example, pnpx @farming-labs/docs upgrade@beta behaves like pnpx @farming-labs/docs upgrade --beta. MCP Run the built-in docs MCP server over stdio from the current project. The command reads docs.config.ts[x] from the current project root by default and reuses the docs graph from your configured entry and contentDir. Use --config when the config lives somewhere else: The current built-in MCP surface includes: - list_pages - get_navigation - search_docs - read_page For the HTTP endpoint version, see MCP Server. Supported Frameworks | Framework | Detection | init | upgrade | | --------- | --------- | ------ | --------- | | Next.js | next in dependencies | Supported | Supported | | TanStack Start | @tanstack/react-start in dependencies | Supported | Supported | | SvelteKit | @sveltejs/kit in dependencies | Supported | Supported | | Astro | astro in dependencies | Supported | Supported | | Nuxt | nuxt in dependencies | Supported | Supported | Available Themes The init CLI offers several built-in themes plus an option to create your own: | Theme | Description | | -------------- | ---------------------------------------------------------- | | fumadocs | Clean, neutral docs theme (default) | | darksharp | All-black, sharp corners, no rounded edges | | pixel-border | Refined dark UI inspired by better-auth.com | | colorful | Fumadocs-style neutral theme with description support | | greentree | Emerald green accent, Inter font, Mintlify-inspired | | ledger | Stripe Docs-inspired product docs shell with navy code panels | | command-grid | Mono-first paper-grid preset inspired by the better-cmdk landing page | | concrete | Brutalist poster-style theme with offset shadows | | hardline | Hard-edge theme with square corners and bold borders | | darkbold | Pure monochrome, Geist typography, clean minimalism | | shiny | Glossy, modern look with subtle shimmer effects | | Create your own theme | Scaffolds themes/.ts and themes/.css; you’re prompted for the theme name (default my-theme). See Creating your own theme. | What it does (Existing project) When you choose Existing project, the CLI does the following (framework-specific files vary): 1. Detects Next.js via next in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default my-theme) 3. Asks for the entry path — Default is docs (press Enter to accept) 4. Asks about path aliases — Use @/ alias or relative paths for imports 5. Optionally scaffolds i18n — Generates locale folders like app/docs/en and app/docs/fr, plus locale-aware wrapper pages 6. Generates files: - docs.config.ts — Full config with your selected theme - next.config.ts — Wraps your existing config with withDocs() - app/global.css — Imports Tailwind and theme CSS - app/layout.tsx — Root layout with RootProvider - app/docs/layout.tsx — Docs layout - app/docs/page.mdx — Sample documentation page when i18n is disabled - app/docs/installation/page.mdx — Sample installation guide when i18n is disabled - app/docs/quickstart/page.mdx — Sample quickstart guide when i18n is disabled 7. Installs dependencies — @farming-labs/docs, @farming-labs/theme, @farming-labs/next 8. Starts the dev server — Opens your docs at http://localhost:3000/docs 1. Detects TanStack Start via @tanstack/react-start in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default my-theme) 3. Asks about path aliases — Use @/ alias or relative paths for imports 4. Asks for the entry path — Default is docs (press Enter to accept) 5. Skips the built-in i18n scaffold so the generated TanStack routes stay minimal and working 6. Generates files: - docs.config.ts — Full config with your selected theme - src/lib/docs.server.ts — TanStack docs server - src/lib/docs.functions.ts — Server function wrapper for page loading - src/routes/{entry}/index.tsx — Docs index route - src/routes/{entry}/$.tsx — Catch-all docs route - src/routes/api/docs.ts — Search and AI API route - src/routes/$.ts — Public llms.txt, sitemap, .well-known, .md, and MCP aliases - src/styles/app.css — Imports Tailwind and theme CSS - docs/page.mdx — Sample documentation page - docs/installation/page.mdx — Sample installation guide - docs/quickstart/page.mdx — Sample quickstart guide 7. Updates existing app files when needed — Injects RootProvider into src/routes/root.tsx and adds docsMdx() / Tailwind plugins to vite.config.ts 8. Installs dependencies — @farming-labs/docs, @farming-labs/theme, @farming-labs/tanstack-start 9. Starts the dev server — Opens your docs at http://localhost:5173/docs > In this repo's examples and in other monorepos, keep those @farming-labs/ dependencies linked locally while testing unpublished changes. CI and Vercel on Node 22 should consume the built adapter entrypoints, not raw src/.ts files from node_modules. 1. Detects SvelteKit via @sveltejs/kit in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default) 3. Asks for the entry path — Default is docs (press Enter to accept) 4. Asks about path aliases — Use $lib/ alias or relative paths for imports 5. Optionally scaffolds i18n — Generates locale folders like docs/en and docs/fr plus a docs root page for /docs?lang=... 6. Generates files (does not touch svelte.config.js): - src/lib/docs.config.ts — Full config with your selected theme - src/lib/docs.server.ts — Server-side docs loader with import.meta.glob for production builds - src/routes/docs/+layout.svelte — Docs layout component - src/routes/docs/+layout.server.js — Layout data loader - src/routes/docs/[...slug]/+page.svelte — Dynamic page component - src/routes/docs/+page.svelte — Generated when i18n is enabled - src/routes/api/docs/+server.ts — Search and AI API route - src/hooks.server.ts — Public llms.txt, sitemap, .well-known, .md, and MCP aliases - src/app.css — Theme CSS import - docs/page.md — Sample documentation page - docs/installation/page.md — Sample installation guide - docs/quickstart/page.md — Sample quickstart guide 7. Installs dependencies — @farming-labs/docs, @farming-labs/svelte, @farming-labs/svelte-theme 8. Starts the dev server — Opens your docs at http://localhost:5173/docs > Production note: The generated docs.server.ts uses import.meta.glob to bundle your markdown files at build time. This is required for serverless deployments (Vercel, Netlify, etc.) where the filesystem is not available at runtime. 1. Detects Astro via astro in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default) 3. Asks for the entry path — Default is docs (press Enter to accept) 4. Optionally scaffolds i18n — Generates locale folders like docs/en and docs/fr 5. Generates files: - src/lib/docs.config.ts — Full config with your selected theme - src/lib/docs.server.ts — Server-side docs loader with import.meta.glob for production builds - src/pages/{entry}/index.astro — Docs index page - src/pages/{entry}/[...slug].astro — Dynamic page component - src/pages/api/{entry}.ts — API route for search and AI - src/middleware.ts — Public llms.txt, sitemap, .well-known, .md, and MCP aliases - astro.config.mjs — Astro config with SSR enabled - docs/page.md — Sample documentation page - docs/installation/page.md — Sample installation guide - docs/quickstart/page.md — Sample quickstart guide 6. Installs dependencies — @farming-labs/docs, @farming-labs/astro, @farming-labs/astro-theme 7. Starts the dev server — Opens your docs at http://localhost:4321/docs > Production note: The generated docs.server.ts uses import.meta.glob to bundle your markdown files at build time. This is required for serverless deployments (Vercel, Netlify, etc.) where the filesystem is not available at runtime. 1. Detects Nuxt via nuxt in your package.json (or asks you to pick a framework) 2. Asks for a theme — built-in themes or Create your own theme (then prompts for theme name; press Enter for default) 3. Asks for the entry path — Default is docs (press Enter to accept) 4. Optionally scaffolds i18n — Generates locale folders like docs/en and docs/fr 5. Generates files: - docs.config.ts — Full config with your selected theme - nuxt.config.ts — Nuxt config with theme CSS and server assets - server/api/docs.ts — docs data, search, AI, and page loading - server/middleware/docs-public.ts — public llms.txt, sitemap, .well-known, .md, and MCP aliases - pages/docs/[[...slug]].vue — Catch-all page component that handles the docs index and nested docs routes - docs/page.md — Sample documentation page - docs/installation/page.md — Sample installation guide - docs/quickstart/page.md — Sample quickstart guide 6. Installs dependencies — @farming-labs/docs, @farming-labs/nuxt, @farming-labs/nuxt-theme 7. Starts the dev server — Opens your docs at http://localhost:3000/docs Example Output Flags | Flag | Description | | ----------------------- | --------------------------------------------------------------------------- | | --template | Bootstrap a project: next, tanstack-start, nuxt, sveltekit, or astro. Use with --name. | | --name | Project folder name when using --template; prompt if omitted (e.g. my-docs) | | --theme | Skip theme prompt: fumadocs, darksharp, pixel-border, colorful, darkbold, shiny, ledger, greentree, concrete, command-grid, hardline | | --entry | Skip entry path prompt (e.g. --entry docs) | Starting from scratch? Use --template with --name . The CLI bootstraps a project with the given name, installs dependencies, and then you run cd && pnpm run dev to start. Manual Code Here is every file the CLI generates, ready to copy-paste if you prefer manual setup. For each framework, add your theme's CSS to the global stylesheet (or config) shown below. The import must match the theme you use in docs.config (e.g. default, greentree, darksharp). Without it, docs pages will lack the correct styling. If your config includes JSX or React nodes, rename docs.config.ts to docs.config.ts. This applies when you add things like a custom nav.title, React icons, or React-based components directly in the config file. docs.config.ts next.config.ts app/global.css — add your theme's CSS here so docs styling applies. app/layout.tsx app/docs/layout.tsx app/docs/page.mdx (sample page) > If you chose darksharp or pixel-border, the CSS import line will differ: > - Darksharp: @import "@farming-labs/theme/darksharp/css"; > - Pixel Border: @import "@farming-labs/theme/pixel-border/css"; docs.config.ts vite.config.ts src/styles/app.css — add your theme's CSS here so docs styling applies. src/routes/__root.tsx src/lib/docs.server.ts src/routes/docs/index.tsx > TanStack Start uses the same @farming-labs/theme theme package as Next.js. Keep the theme name in docs.config.ts and src/styles/app.css aligned. src/lib/docs.config.ts src/lib/docs.server.ts src/routes/docs/+layout.svelte src/routes/docs/+layout.server.js src/routes/docs/[...slug]/+page.svelte src/routes/api/docs/+server.js (search & AI) src/app.css — add your theme's CSS here so docs styling applies. docs/page.md (sample page) > If you chose darksharp or pixel-border, swap the theme factory and CSS: > - Darksharp: import { darksharp } from "@farming-labs/svelte-theme" + @import "@farming-labs/svelte-theme/darksharp/css"; > - Pixel Border: import { pixelBorder } from "@farming-labs/svelte-theme" + @import "@farming-labs/svelte-theme/pixel-border/css"; src/lib/docs.config.ts src/lib/docs.server.ts src/pages/docs/index.astro and src/pages/docs/[...slug].astro — import your theme's CSS in each (e.g. @farming-labs/astro-theme/css for default; use greentree/css etc. for other themes). src/pages/api/docs.ts (search & AI) astro.config.mjs docs/page.md (sample page) > If you chose darksharp or pixel-border, swap the theme factory and CSS: > - Darksharp: import { darksharp } from "@farming-labs/astro-theme" + @farming-labs/astro-theme/darksharp/css > - Pixel Border: import { pixelBorder } from "@farming-labs/astro-theme" + @farming-labs/astro-theme/pixel-border/css docs.config.ts nuxt.config.ts — add your theme's CSS in the css array so docs styling applies. server/api/docs.ts (search, AI, and page loading) and server/middleware/docs-public.ts (public agent/GEO aliases) pages/docs/[...slug].vue docs/page.md (sample page) > If you chose darksharp or pixel-border, swap the theme factory and CSS: > - Darksharp: import { darksharp } from "@farming-labs/nuxt-theme" + @farming-labs/nuxt-theme/darksharp/css > - Pixel Border: import { pixelBorder } from "@farming-labs/nuxt-theme" + @farming-labs/nuxt-theme/pixel-border/css Project Structure After running init`, your project will look like this: --- ## Configuration URL: https://docs.farming-labs.dev/docs/configuration Everything lives in docs.config.ts Configuration All configuration lives in a single docs.config.ts file. If your config includes JSX or React nodes, rename docs.config.ts to docs.config.tsx. This is common when setting things like nav.title, custom icons, or React-based components in the config. The config file lives at the project root as docs.config.tsx when it contains JSX: The config file lives at the project root as docs.config.tsx when it contains JSX: > TanStack Start, SvelteKit, Astro, and Nuxt require contentDir (path to your markdown files) and nav (sidebar title/URL) since routing is handled differently from Next.js. The config file lives at src/lib/docs.config.ts: > TanStack Start, SvelteKit, Astro, and Nuxt require contentDir (path to your markdown files) and nav (sidebar title/URL) since routing is handled differently from Next.js. The config file lives at src/lib/docs.config.ts: The config file lives at the project root as docs.config.ts: Config Options | Option | Type | Default | Description | | -------------- | ------------------------------ | --------------- | -------------------------------------------------- | | entry | string | "docs" | The docs URL path prefix (e.g. "docs" → /docs) | | contentDir | string | same as entry | Path to content files (TanStack Start, SvelteKit, Astro, Nuxt) | | staticExport | boolean | false | Set true for full static builds (see Static export) | | theme | DocsTheme | — | Theme preset from a theme factory | | nav | { title, url } | — | Sidebar title and base URL | | github | string \| GithubConfig | — | GitHub repo for "Edit on GitHub" links | | themeToggle | boolean \| ThemeToggleConfig | true | Light/dark mode toggle | | breadcrumb | boolean \| BreadcrumbConfig | true | Breadcrumb navigation | | sidebar | boolean \| SidebarConfig | true | Sidebar visibility and style | | icons | Record | — | Shared icon registry for frontmatter icon fields and built-ins like Prompt | | components | Record | — | Custom MDX components and built-in overrides like HoverLink and Prompt | | onCopyClick | (data: CodeBlockCopyData) => void | — | Callback when the user clicks the copy button on a code block | | feedback | boolean \| FeedbackConfig | false for UI | Human page feedback UI; agent feedback endpoints are default-on unless opted out | | agent | DocsAgentConfig | — | Defaults for docs agent compact | | readingTime | boolean \| ReadingTimeConfig | false | Opt-in estimated read-time label with optional per-page overrides | | pageActions | PageActionsConfig | — | Copy Markdown, Open in LLM buttons | | ai | AIConfig | — | RAG-powered AI chat | | search | boolean \| DocsSearchConfig | true | Built-in simple search, Typesense, Algolia, or a custom adapter | | llmsTxt | boolean \| LlmsTxtConfig | true | Generated root and optional section-level llms.txt files with markdown page links | | changelog | boolean \| ChangelogConfig | false | Generated changelog feed and entry pages from dated MDX entries (Next.js) | | mcp | boolean \| DocsMcpConfig | enabled | Built-in MCP server over stdio, /mcp, and /.well-known/mcp | | apiReference | boolean \| ApiReferenceConfig | false | Generated API reference from framework route conventions or a hosted OpenAPI JSON | | sitemap | boolean \| DocsSitemapConfig | true | Generated sitemap.xml, sitemap.md, and /.well-known/sitemap.md | | robots | boolean \| DocsRobotsConfig | true | Runtime/generated robots.txt policy for docs and agent-readable routes | | i18n | DocsI18nConfig | — | Query-param locale support and locale switcher | | metadata | DocsMetadata | — | SEO metadata template and JSON-LD page inputs | | og | OGConfig | — | Dynamic Open Graph images (see API Reference) | For sidebar folder parents that have their own landing page, use sidebar.folderIndexBehavior: "toggle" if you want parent rows to only open and close their children instead of navigating. Use sidebar.folderIndexBehaviorOverrides when only selected sections should behave that way. A folder landing page can also override both with frontmatter: If you want to remove the parent page from the sidebar entirely while keeping its child pages, set sidebar.folderIndexBehavior: "hidden" on that folder landing page. In a flat sidebar this renders as a plain section label with child links underneath, without a parent navigation target. Manual visits to the hidden parent route redirect to its first visible child. Machine-readable markdown routes No separate config flag is required for machine-readable page markdown. - The shared docs API supports GET /api/docs?format=markdown&path= - Next.js withDocs() and the generated TanStack Start, SvelteKit, Astro, and Nuxt forwarding layer serve /docs.md and /docs/.md - Successful markdown responses include a Link: ; rel="canonical" response header so agents can read the markdown mirror but cite the normal docs page - In Next.js, sending Accept: text/markdown to /docs/ returns the same markdown response - In Next.js, sending a non-empty Signature-Agent header to /docs/ also returns the same markdown response - These negotiated Next.js reads are handled by the existing /api/docs route; withDocs() does not create a separate /api/docs/markdown wrapper - Embedded ... blocks stay hidden in the normal UI but are included in the .md fallback and MCP read_page - When a page folder has agent.md, that file is returned instead of the normal page markdown - Page frontmatter related is rendered as a comma-separated markdown metadata line beside Description when the page uses normal markdown or embedded ` fallback, so agents can discover adjacent pages without scraping the UI. A sibling agent.md remains a full override; include any Related: line manually inside agent.md when needed. You can keep the canonical docs URL and ask for markdown with an HTTP header: In Next.js, browsers still receive HTML from /docs/installation; agents, scripts, and crawlers that send Accept: text/markdown or Signature-Agent receive the same machine-readable output as /docs/installation.md. The request is resolved by the shared /api/docs handler, so custom apps should keep one docs API route instead of adding a second markdown-only wrapper. Other adapters should use the .md URL or the API format route. Successful markdown responses also include a canonical Link response header. The body is still the full markdown document; the header simply tells agents that /docs/installation is the source-of-truth URL for /docs/installation.md or a negotiated markdown read. Negotiated markdown responses include cache-safe Vary headers: Accept for Accept: text/markdown, and Accept, Signature-Agent for Signature-Agent. That means a CDN can cache the HTML and markdown versions separately even though the URL is the same: If a markdown request misses, the 404 response is still markdown. It points agents at the agent discovery spec, search endpoint, and sitemap routes so they can recover instead of stopping at a plain Not Found. See Agent Primitive for the page-level authoring model. Structured data Every docs page automatically includes Schema.org JSON-LD using TechArticle, BreadcrumbList, canonical URL, and dateModified. This is a hidden application/ld+json script, not a visible component and not a separate route. The page title and description come from frontmatter. The canonical URL uses sitemap.baseUrl, llmsTxt.baseUrl, robots.baseUrl, or ai.docsUrl when one is configured. The agent discovery JSON advertises this as capabilities.structuredData so agents know the HTML page contains structured metadata. The JSON-LD script is escaped before insertion so page titles and descriptions cannot break out of the application/ld+json script tag. For filesystem-backed pages, dateModified comes from the page source file. For preloaded Astro, SvelteKit, Nuxt, and TanStack Start builds, run docs sitemap generate and include /.farming-labs/sitemap-manifest.json in _preloadedContent so dateModified can reuse stable sitemap lastmod values. If no stable freshness date is available, the runtime omits dateModified instead of using the current request time. Sitemaps Sitemaps are enabled by default. Add sitemap only when you want to customize the base URL, route prefix, manifest path, or output format details: Default routes: - /sitemap.xml - /sitemap.md - /.well-known/sitemap.md - /api/docs?format=sitemap-xml - /api/docs?format=sitemap-md For static export, run the generator before your framework build: The command writes .farming-labs/sitemap-manifest.json plus public sitemap files. It uses each page source file's last git commit date for lastmod, with filesystem modified time as a fallback. Preloaded adapters can also read the generated manifest for stable JSON-LD dateModified values. Use routePrefix to move all sitemap routes together: That generates /docs-map/sitemap.xml, /docs-map/sitemap.md, and /docs-map/.well-known/sitemap.md. See Sitemaps for static export, manifest, lastmod, and output examples. Robots.txt Use robots when you want docs robots generate to produce an agent-friendly crawl policy: The generated policy explicitly allows the docs entry, .md routes, llms.txt, sitemap routes, AGENTS.md, skill.md, MCP aliases, and agent discovery routes. It also includes common AI crawler user agents such as GPTBot, ChatGPT-User, ClaudeBot, CCBot, and Google-Extended. Generate the file with: Server-rendered apps also serve /robots.txt through the shared docs handler by default when no static file already owns that route. The generator is still the right tool for static export or when you want to commit a policy file. If a project already owns public/robots.txt, the command leaves it alone. Use --append to add or update the generated block inside the existing file, or --force when you intentionally want to replace the file: Use path when the file lives somewhere custom: docs doctor --agent checks the resolved robots.txt path or runtime route and flags policies that block agent-readable docs routes or common AI crawlers. Static export For fully static builds (e.g. Cloudflare Pages, static hosting with no server), set staticExport: true in your config. This: - Next.js: With output: "export" in next.config, the /api/docs route is not generated. The layout hides Cmd+K search and AI chat. - TanStack Start / SvelteKit / Astro / Nuxt: The layout hides the search trigger and floating AI when staticExport is true. Omit or don’t deploy the docs API route so no server is required. Use this when you deploy to a static host and don’t run a server. Search and AI require a server; with staticExport: true they are hidden so the site works without one. Search Search is enabled by default. If you do nothing, the docs API uses the built-in simple adapter with section-based chunking so heading matches feel more precise than page-level search. Use an object when you want to switch providers or tune indexing behavior: Simple search Typesense Use mode: "hybrid" together with embeddings when you want keyword + semantic retrieval. The initial built-in embeddings provider is Ollama, so you can keep that stack local or self-hosted. If you want to pre-sync the index instead of letting the first request do it lazily: Algolia You can also pre-sync Algolia outside request handling: MCP-backed search Use MCP search when you want the docs search UI to query an MCP search_docs tool instead of a native search backend. This is useful when another docs service or agent-facing MCP layer already owns retrieval and ranking. Custom adapters Notes: - search: false disables search entirely - chunking.strategy defaults to "section" and can be changed to "page" for one-document-per-page indexing - Typesense and Algolia can sync documents automatically on the first search request when adminApiKey is present - Use pnpm dlx @farming-labs/docs search sync --typesense or --algolia when you want manual indexing as a CLI step - provider: "mcp" can target a relative route like /mcp or /.well-known/mcp or an absolute remote MCP endpoint - when MCP search points at the same site's relative MCP route, the MCP search_docs tool falls back to built-in simple search to avoid recursive loops - If you use a custom Next.js docs API route, import createDocsAPI from @farming-labs/next/api and pass the whole config: createDocsAPI(docsConfig) - Search requires the docs API route; with staticExport: true the UI is hidden because there is no server route The older @farming-labs/theme/api import path still works, but new Next.js apps should use @farming-labs/next/api. The theme-level path is being kept for compatibility and will be deprecated. MCP Server MCP is enabled by default. Use mcp to customize the built-in MCP server for AI tools and IDE agents, or set enabled: false to opt out. Opt out explicitly: Default behavior: - Public HTTP route: /mcp - Well-known HTTP route: /.well-known/mcp - Canonical HTTP route: /api/docs/mcp - stdio command: pnpx @farming-labs/docs mcp - Built-in tools: listpages, getnavigation, searchdocs, readpage Framework notes: - Next.js: withDocs() auto-generates the default /api/docs/mcp route and public /mcp plus /.well-known/mcp rewrites - TanStack Start: the current init scaffold adds one src/routes/$.ts public forwarder for /api/docs/mcp, /mcp, /.well-known/mcp, llms.txt, agent discovery, and .md routes - SvelteKit: the current init scaffold adds one src/hooks.server.ts public forwarder for /api/docs/mcp, /mcp, /.well-known/mcp, llms.txt, agent discovery, and .md routes - Astro: the current init scaffold adds one src/middleware.ts public forwarder for /api/docs/mcp, /mcp, /.well-known/mcp, llms.txt, agent discovery, and .md routes - Nuxt: the current init scaffold adds one server/middleware/docs-public.ts public forwarder for /api/docs/mcp, /mcp, /.well-known/mcp, llms.txt, agent discovery, and .md routes - Custom routes: set mcp.route in docs.config and update the framework public forwarder so the configured path and the actual endpoint stay aligned See the full MCP Server guide for the route snippets and stdio usage. API Reference Use apiReference to generate an API reference from either your framework route handlers or a hosted OpenAPI JSON document. Use route scanning when your API lives in the same app. Use specUrl when your backend is hosted elsewhere and already exposes an openapi.json. apiReference is supported in Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. In Next.js, enabling apiReference in docs.config is enough when you use withDocs(). The API reference route is generated automatically. In TanStack Start, SvelteKit, Astro, and Nuxt, docs.config controls scanning, remote spec rendering, theming, routeRoot, and exclude, but you still need to add the framework route handler that serves /{path}. If your docs app and backend are deployed separately, set apiReference.specUrl to a hosted openapi.json. This keeps the API reference in your docs UI without requiring local route files for the actual backend. If your backend lives somewhere else, point the API reference at a hosted openapi.json instead of scanning local route files: | Property | Type | Default | Description | | ----------- | ---------- | ----------------- | ----------- | | enabled | boolean | true inside the object | Enables generated API reference pages | | path | string | "api-reference" | URL path where the generated reference lives | | specUrl | string | — | Absolute URL to a hosted OpenAPI JSON document. When set, local route scanning is skipped | | routeRoot | string | "api" | Filesystem route root to scan. Bare values like "api" resolve inside app/ or src/app/; full values like "app/internal-api" are supported too | | exclude | string[] | [] | Routes to omit from the generated reference. Accepts URL-style paths like "/api/hello" or route-root-relative entries like "hello" / "hello/route.ts" | When specUrl is set, routeRoot and exclude are ignored because the reference is rendered from the remote spec. That does not change the framework routing requirements: - Next.js still generates the API reference route automatically with withDocs() - TanStack Start, SvelteKit, Astro, and Nuxt still need the /{path} route files because those routes serve the generated API reference page If you use output: "export" in Next.js, the generated API reference route is skipped automatically because it needs a server route handler. Route conventions by framework: - Next.js: app/api//route.ts or src/app/api//route.ts - TanStack Start: src/routes/api.*.ts and nested route files under the configured route root - SvelteKit: src/routes/api//+server.ts or +server.js - Astro: src/pages/api//*.ts or .js - Nuxt: server/api//*.ts or .js Opt-in route wiring Next.js - Nothing else is required beyond apiReference in docs.config and withDocs() in next.config.ts. TanStack Start Create src/routes/api-reference.index.ts: Create src/routes/api-reference.$.ts with the same handler and createFileRoute("/api-reference/$"). SvelteKit Create src/routes/api-reference/+server.ts: Create src/routes/api-reference/[...slug]/+server.ts with the same GET export. Astro Create src/pages/api-reference/index.ts: Create src/pages/api-reference/[...slug].ts with the same GET export. Nuxt Create server/routes/api-reference/index.ts: Create server/routes/api-reference/[...slug].ts with the same default export. Changelog Use changelog to render a release feed and entry pages from dated MDX files inside your docs content tree. The turn-key generated changelog pages are currently wired in Next.js when you use withDocs(). With the default Next.js docs tree, source entries live under app/docs/changelog/YYYY-MM-DD/page.mdx. That publishes: - /docs/changelogs - /docs/changelogs/YYYY-MM-DD | Property | Type | Default | Description | | -------- | ---- | ------- | ----------- | | enabled | boolean | true inside the object | Enables generated changelog pages | | path | string | "changelog" | URL path where the changelog listing lives inside the docs layout | | contentDir | string | "changelog" | Source directory for dated entry folders like app/docs/changelog/2026-03-04/page.mdx | | title | string | "Changelog" | Listing page title | | description | string | — | Listing page description and metadata | | search | boolean | true | Show the built-in changelog search field | | actionsComponent | ReactNode \| Component | — | Custom action content rendered in the changelog rail | Useful entry frontmatter: - title - description - image - authors - version - tags - pinned - draft If you pass actionsComponent, use docs.config.tsx so JSX is valid. When you use withDocs(), the changelog route files are generated automatically. There is no separate changelog.generated.tsx file to maintain. Internationalization Use i18n to enable locale-aware docs with a language selector and query-param routing like /docs?lang=en and /docs?lang=fr. When i18n is enabled: - The docs UI renders a locale selector in the sidebar footer - Links and search navigation preserve the active lang query parameter - Each locale can have its own content tree and sidebar structure - The CLI can scaffold locale folders like docs/en and docs/fr for existing projects For content structure, place localized docs inside locale folders. For example: The generated examples and CLI scaffold use this folder structure across Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. Theme Toggle Controls the light/dark mode switcher. Works on Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. | Property | Type | Default | Description | | --------- | ------------------------------------- | -------------- | ---------------------------------- | | enabled | boolean | true | Show/hide the theme toggle | | default | "light" \| "dark" \| "system" | "system" | Forced theme when toggle is hidden | | mode | "light-dark" \| "light-dark-system" | "light-dark" | Toggle mode | GitHub / Edit on GitHub Enables "Edit on GitHub" links on each docs page footer. The link appears automatically when both url and directory are set. | Property | Type | Default | Description | | ----------- | -------- | -------- | ---------------------------------- | | url | string | — | Repository URL | | branch | string | "main" | Branch name | | directory | string | — | Subdirectory for the content files | Breadcrumb Controls the breadcrumb navigation above page content. Theme Configuration Pass options to your theme preset to customize colors, typography, layout, and more: Navigation The nav option controls the sidebar header: In Next.js and TanStack Start, title can also be a React element: Icons Register icons in the config and reference them in frontmatter: SvelteKit uses a built-in icon map. Add icon to your page frontmatter: Built-in icons: book, terminal, rocket, settings, shield, puzzle, zap, database, key, mail, file, folder, link, lightbulb, code, users, globe, lock. Astro uses a built-in icon map. Add icon to your page frontmatter: Built-in icons: book, terminal, rocket, settings, shield, puzzle, zap, database, key, mail, file, folder, link, lightbulb, code, users, globe, lock. Nuxt uses a built-in icon map. Add icon to your page frontmatter: Built-in icons: book, terminal, rocket, settings, shield, puzzle, zap, database, key, mail, file, folder, link, lightbulb, code, users, globe, lock. Code block copy callback Run a callback when the user clicks the copy button on a code block (in addition to the default copy-to-clipboard). Useful for analytics or logging. Next.js / TanStack Start: Set onCopyClick in defineDocs(); it is passed through to the MDX components. SvelteKit, Astro, Nuxt: Config is serialized at build time so you cannot pass a function. Use one of these: 1. Global callback — In a client-side script (e.g. in your layout), set window.fdOnCopyClick to your function. It will be called with CodeBlockCopyData after each copy. 2. Custom event — Listen for the fd:code-block-copy event on document or window; event.detail is the same { title?, content, url, language? } object. The callback receives title (if the code block has a title), content (raw code), url (current page URL), and language (syntax hint) when available. Page Feedback Show a built-in feedback prompt at the end of each docs page. The callback receives whether the user clicked the positive or negative button, plus the current page path, slug, title, URL, and locale when available. If you just want the UI without a callback, use feedback: true. Next.js / TanStack Start / SvelteKit / Nuxt: feedback.onFeedback is called from the built-in page footer UI with no extra client bridge file. Astro: feedback: true still enables the built-in UI with no extra setup. For optional client-side analytics hooks, the same payload is also emitted through window.fdOnFeedback and the fd:feedback custom event. Page feedback is emitted as untrusted user data. The framework does not add comments from this endpoint to Ask AI prompts, docs search context, or llms.txt. If your onFeedback callback forwards feedback to an LLM, issue triager, or support workflow, label it as user-provided text and keep your own validation, length limits, and rate limiting in place. This website enables feedback in website/docs.config.tsx and posts the callback payload to website/app/api/feedback/route.ts, where it is inserted into Prisma with the model defined in website/prisma/schema.prisma. Agent Compaction Use agent.compact to set defaults for docs agent compact, which generates sibling agent.md files from resolved docs pages. If you want to reference the key directly from docs.config.tsx, this is supported too: Supported fields: - apiKey - apiKeyEnv - baseUrl - model - aggressiveness - maxOutputTokens - minOutputTokens - protectJson docs agent compact creates missing agent.md files and overwrites existing ones. The written agent.md becomes the machine-readable source for .md routes, GET /api/docs?format=markdown&path=, and MCP read_page(). The human docs UI still renders the normal page. Handwritten agent.md files are also valid. They count as explicit machine context in docs doctor --agent, but their freshness is reported as unknown because there is no generated provenance block to compare against the page source. The CLI loads .env and .env.local before resolving the key, so TOKENCOMPANYAPI_KEY works out of the box. Use positional page args by default: Per-page compaction budgets live in frontmatter: Notes: - agent.tokenBudget overrides global agent.compact.maxOutputTokens defaults and CLI --max-output-tokens for that one page - if a sibling agent.md already exists, docs agent compact compacts that file - if no agent.md exists, the command compacts the generated machine-readable page output and then writes a new sibling agent.md - docs agent compact --changed only processes docs pages changed in the current git working tree, including staged, unstaged, and untracked docs changes - docs agent compact --stale --include-missing will also create missing agent.md files for pages that define agent.tokenBudget - if inherited minOutputTokens would be greater than the page budget, the CLI clamps it down to that page budget before calling the compression API See CLI for the command surface and Token Efficiency for when to use compaction instead of writing agent.md by hand. Agent Feedback Endpoints Machine-readable feedback routes are enabled by default for coding agents and docs-aware automation. The default submit route is a no-op 202 response until you add a callback. This is separate from the built-in page footer UI, which remains opt-in. Use feedback.agent only when you want to customize the callback, routes, schema, or explicitly opt out. Agents should discover the configured routes first with GET /.well-known/agent.json. GET /.well-known/agent is the fallback public alias, and GET /api/docs/agent/spec is the canonical framework route. Generated projects wire all three to the same JSON. The spec includes site identity, locale config, capability flags, the search endpoint, markdown route pattern, Accept: text/markdown contract, Signature-Agent support, API, public, and section-level llms.txt routes, sitemap routes, the robots.txt route, generated AGENTS.md metadata, root skill.md metadata, Skills CLI install metadata, MCP public/well-known endpoints and enabled tools, and the active agent feedback schema and submit endpoints. Use docs robots generate when a static export or committed policy file should own the advertised route. Default behavior: - GET /.well-known/agent.json is the preferred public agent discovery URL - GET /.well-known/agent is the public fallback discovery URL - GET /api/docs/agent/spec returns the canonical framework discovery document - GET /AGENTS.md serves a root AGENTS.md or AGENT.md file when present, and otherwise returns a generated fallback - GET /.well-known/AGENTS.md and GET /api/docs?format=agents return the same agent instructions - GET /skill.md serves a root skill.md file when present, and otherwise returns a generated fallback - GET /.well-known/skill.md and GET /api/docs?format=skill return the same skill document - the discovery JSON advertises agentSpecDefault, agentSpecFallback, agents.file, agents.route, skills.file, skills.route, the root and section llms.txt routes, robots.route, and sitemap routes when enabled - GET /api/docs/agent/feedback/schema returns the feedback schema - POST /api/docs/agent/feedback accepts { context?, payload }; without onFeedback it returns { ok: true, handled: false } - non-Next server adapters also expose the same contract through GET /api/docs?feedback=agent&schema=1 and POST /api/docs?feedback=agent - the shared /api/docs handler is still the source of truth - in Next.js, withDocs() adds the public rewrites automatically - feedback.agent alone does not enable the human footer UI - set feedback: false or feedback: { agent: false } to opt out of the agent feedback routes Agent feedback submissions are validated data passed to feedback.agent.onFeedback; they are not inserted into the Ask AI system prompt or retrieved as documentation context. Treat the payload as untrusted if your callback stores it, opens issues, posts to chat, or sends it to another model. Default request shape: Customize the public route or the payload schema when needed: Quick test: Reading Time Use readingTime when you want an estimated label like 5 min read on docs pages. Default behavior: - the feature is opt-in - the estimate uses 220 words per minute unless you override it - fenced code blocks, inline code, links, images, and raw HTML are stripped before counting words - changelog pages do not render the reading-time label - page frontmatter wins when readingTime is present, even if the global config is disabled Per-page overrides live in frontmatter: You can also opt a page in explicitly when the global config is off: And you can pin an exact number: That gives you a simple override model: - global readingTime.enabled: true -> pages get a computed label by default - page readingTime: false -> hide the label for that page - page readingTime: true -> force a computed label for that page - page readingTime: 12 -> force an exact 12 min read label for that page Reading time follows the same title-area slot as page actions. With pageActions.position: "above-title", it renders directly under the action row. With pageActions.position: "below-title", it stays grouped in the below-title metadata area, including when page actions are disabled but the slot is still configured. Page Actions Enable "Copy as Markdown" and "Open in LLM" buttons: Use openDocs: true for the built-in ChatGPT + Claude provider list, or pass providers to fully customize it. If you use the built-in Prompt component, you can also add promptUrlTemplate to a provider so the same provider name and icon can open prompt text directly: See the full Page Actions reference for all options. Ask AI Add a RAG-powered AI chat that lets users ask questions about your docs: The API key is read from process.env.OPENAIAPIKEY automatically. Pass the API key through src/lib/docs.server.ts: Pass the API key through docs.server.ts (SvelteKit requires server-only env access): Pass the API key through docs.server.ts` (Astro requires server-only env access): Pass the API key through the server handler (Nuxt requires server-only env access): See the full Ask AI reference for all options including custom providers, labels, system prompts, and more. Full Example --- ## Contributing URL: https://docs.farming-labs.dev/docs/contributing How to contribute to @farming-labs/docs Contributing Thanks for your interest in contributing to @farming-labs/docs. This guide explains how to report issues, propose changes, and submit pull requests. Code of conduct Please be respectful and constructive. We aim to keep the community welcoming and focused on building a great documentation framework. How to contribute Reporting bugs - GitHub Issues — Open an issue at github.com/farming-labs/docs/issues. - Include: - Clear title and description - Steps to reproduce - Expected vs actual behavior - Your environment (Node version, package manager, framework: Next.js / TanStack Start / SvelteKit / Astro / Nuxt) - Relevant config or code snippets Suggesting features - Open a discussion or issue describing the use case and proposed behavior. - Check existing issues first to avoid duplicates. Pull requests 1. Fork the farming-labs/docs repo and clone it locally. 2. Branch from main (e.g. fix/typo-install-docs or feat/contributing-guide). 3. Install dependencies from the repo root: 4. Build the monorepo (optional, for core/theme changes): 5. Run the docs site (from repo root): Then open http://localhost:3000 to preview. 6. Make your changes — Keep edits focused; doc-only and small fixes are welcome. 7. Commit with a clear message (e.g. docs: add contributing guide, fix: correct CLI flag in installation). 8. Push your branch and open a Pull Request against main. 9. Describe what you changed and why; link any related issues. Project structure - packages/ — Core packages: docs, fumadocs (theme), next, svelte, svelte-theme, astro, astro-theme, nuxt, nuxt-theme. - website/ — This docs site (Next.js); content under app/docs/. - examples/ — Example apps (Next.js, TanStack Start, and others) for testing. When you work on this monorepo, keep local @farming-labs/ example dependencies linked with workspace: so CI and Vercel on Node 22 resolve the built workspace adapters instead of an older published package from node_modules. Doc content lives in website/app/docs/ as MDX. Config and sidebar are driven by docs.config.tsx in website/. Documentation changes - Edit or add .mdx files under website/app/docs/. - Use existing frontmatter (title, description, icon, order) so the sidebar stays consistent. - Use the shared `, , and ` components where they fit. Questions - GitHub Discussions — For general questions and ideas, use github.com/farming-labs/docs/discussions. - GitHub Issues — You can still use Issues for bugs and actionable feature requests. - Issues — For bugs and concrete feature requests. Thank you for contributing. --- ## Customization URL: https://docs.farming-labs.dev/docs/customization Colors, typography, sidebar, components, analytics, observability, agent primitives, robots.txt, OG images, AI chat, and page actions Customization Everything is customizable from docs.config.ts. No CSS files to edit, no layout files to create. What You Can Customize - Colors — Primary, accent, background, border, and more - Typography — Font families, heading sizes, weights, spacing - Sidebar — Title, icons, collapsible groups, style - Components — Override built-in or add custom MDX components, including live HoverLink previews - Agent Primitive — Built-in ` blocks, sibling agent.md page overrides, generated AGENTS.md, and discovery routes for coding agents - OG Images — Dynamic or static Open Graph images; what context your image generator receives; how the docs website does it - Ask AI — RAG-powered AI chat with configurable LLM, floating/search modes, and more - Page Actions — "Copy Markdown" and "Open in LLM" buttons on doc pages - Analytics — Track page views, search, AI, feedback, page actions, agent reads, and MCP usage - Observability — Trace Ask AI and MCP runs with span IDs, timing, status, previews, and errors - MCP Server — Built-in MCP tools/resources over stdio, /mcp, and /.well-known/mcp - llms.txt — Auto-generate llms.txt and llms-full.txt for LLM-friendly documentation - Sitemaps — Runtime sitemap.xml and sitemap.md for crawlers and agents, plus static export generation - Robots.txt — Runtime agent-friendly crawl policy at /robots.txt; use docs robots generate for static export or committed policies All customization happens through the theme and top-level config options in docs.config.ts`. --- ## Agent Primitive URL: https://docs.farming-labs.dev/docs/customization/agent-primitive Author page-level agent context, generated AGENTS.md instructions, and machine-readable route discovery. Agent Primitive @farming-labs/docs gives you two page-level primitives for agent-facing documentation: - ... inside page.mdx for additive machine-only context - agent.md beside page.mdx for a full machine-readable override The same page model also powers the built-in machine-readable routes: - the shared docs API supports GET /api/docs?format=markdown&path= - Next.js withDocs() and the generated TanStack Start, SvelteKit, Astro, and Nuxt forwarding layer serve /docs.md and /docs/.md - in Next.js, Accept: text/markdown on /docs/ returns the same markdown - in Next.js, Signature-Agent on /docs/ returns the same markdown for agents that fetch canonical URLs - coding agents can fetch site-level instructions with GET /AGENTS.md, fall back to GET /.well-known/AGENTS.md, or use GET /api/docs?format=agents - agents can fetch the site skill with GET /skill.md, fall back to GET /.well-known/skill.md, or use GET /api/docs?format=skill - agents can discover the configured contract with GET /.well-known/agent.json, fall back to GET /.well-known/agent, or use the canonical framework route at GET /api/docs/agent/spec /docs/installation stays the human HTML page by default. In Next.js, send Accept: text/markdown or Signature-Agent to that same URL when an agent or script wants the machine-readable version without appending .md. In the other adapters, use /docs/installation.md or the format=markdown API route. These reads go through the existing shared /api/docs handler. You do not need another markdown-specific API wrapper, and adding one usually makes discovery, caching, and 404 recovery harder to keep aligned. The markdown response varies by the relevant request headers, so CDNs do not mix the HTML and markdown representations for the same URL. A browser can keep receiving cached HTML while an agent receives cached markdown for /docs/installation. Successful markdown responses include a canonical Link response header pointing to the normal docs page, so agents can read /docs/installation.md while still citing /docs/installation. Missing markdown pages return a markdown 404 with recovery links to the agent discovery spec, search endpoint, and sitemap routes. Use Agent when the human page is already mostly correct and just needs a little extra implementation context. Use agent.md when the entire machine-readable page should be more operational, more structured, or fully different from the visible UI page. Agent is already part of the default MDX component map. You can write ` directly in any docs page without adding it to docs.config.ts, mdx-components.tsx, or a custom components map. If you intentionally build your own MDX map from scratch, import it from @farming-labs/theme or @farming-labs/theme/mdx. Use Agent For Additive Context Agent is the lightweight option. It keeps the normal page clean while adding agent-only guidance to the .md route and MCP fallback for that page. That gives you: - /docs/installation for the normal UI page - /docs/installation.md for machine-readable markdown that includes the Agent block - /docs/installation with Accept: text/markdown for the same machine-readable markdown - /docs/installation with Signature-Agent for agents that read the canonical URL - MCP read_page("/docs/installation") with the same page-level machine context Bootstrap Agents With The Spec Use the docs landing page or another high-traffic route to tell implementation agents how to discover the machine-readable surface before they start reading individual pages. No setup is required first; the default runtime already understands the Agent primitive. This works well on /docs because many agents start there first. The instruction stays hidden from the human UI, but it appears when an agent reads /docs.md, requests /docs with Accept: text/markdown or Signature-Agent, or reads the page through MCP. Use agent.md For A Full Override When the machine-readable page should be completely different, add a sibling agent.md. In that setup: - /docs/customization/agent-primitive renders page.mdx - /docs/customization/agent-primitive.md returns agent.md - MCP read_page("/docs/customization/agent-primitive") also prefers agent.md This is the stronger option. Use it when agents need: - exact file paths and command ordering - implementation-only constraints - testing or validation steps - a shorter, more targeted page than the human docs should show Generate agent.md From The CLI You do not have to hand-write every override. docs agent compact can take the resolved machine-readable page for one or more routes and write a sibling agent.md automatically. It uses the same resolution order as the runtime: - existing agent.md - hidden Agent blocks - normal page markdown That makes it a good fit when you want to start with additive context and later freeze a shorter, more operational machine page. Once the file is written: - /docs/customization/agent-primitive.md returns the generated agent.md - GET /api/docs?format=markdown&path=customization/agent-primitive returns the same file - MCP read_page("/docs/customization/agent-primitive") prefers that agent.md The visible UI page still renders page.mdx. Only the machine-readable surface changes. Audit The Agent Surface After you add blocks, agent.md, AGENTS.md, skill.md, or discovery endpoints, use the Doctor section of the CLI docs to audit whether the machine-facing layer is actually wired and visible the way you expect. That is the quickest follow-up after authoring page-level primitives. If you add an Agent block or sibling agent.md, the doctor output should reflect stronger explicit agent-friendly page coverage instead of only looking correct in the UI. Point Agents At Feedback Endpoints Page primitives are also a good place to tell agents how to report whether the docs were enough to complete a task. Agent feedback is enabled by default unless the site opts out. Agents can read the schema first and then post structured feedback back into the shared docs API: - schema: /api/docs/agent/feedback/schema - submit: /api/docs/agent/feedback Example additive block: That works especially well when: - the feedback instruction belongs to one page or workflow - you want the agent-facing .md route to carry the reporting contract - the human page should stay uncluttered Agent Discovery GET /.well-known/agent.json is the preferred public discovery URL. GET /.well-known/agent is the public fallback, and GET /api/docs/agent/spec is the canonical framework route. Next.js wires these with withDocs(). TanStack Start, SvelteKit, Astro, and Nuxt scaffold one public forwarder each, so the well-known aliases share the same docs server handler. Agents should fetch it before choosing a transport. It tells them: - which docs site and entry they are reading - which locales are configured and which query parameter selects one - which agent-facing capabilities are enabled - where the shared docs API lives - which agent discovery URL is the default and which public route to use as fallback - where to search the docs with a query - which markdown URL pattern to use for page reads - whether the same docs URLs support Accept: text/markdown or Signature-Agent - whether HTML pages include Schema.org TechArticle JSON-LD structured data - where to fetch llms.txt and llms-full.txt content, including the default public URLs and the public /.well-known aliases when available - where to fetch sitemap.xml, sitemap.md, and the /.well-known/sitemap.md alias when sitemap is enabled - where to fetch robots.txt, AGENTS.md, and the skill.md document for this site - how to install the published Skills pack and which skill is recommended first - whether MCP is enabled, which public/well-known endpoint to call, and which tools are available - whether agent feedback is enabled, plus the schema and submit endpoints to use The generated spec includes agentSpecDefault: "/.well-known/agent.json" and agentSpecFallback: "/.well-known/agent" so downstream integrations do not need to hard-code the preference themselves. It also includes robots.route so agents can find the static crawl policy without guessing. The skill document is available at GET /skill.md, with GET /.well-known/skill.md as the well-known alias and GET /api/docs?format=skill as the shared API format. Put a skill.md file at the project root, beside docs.config.ts, when you want custom site-specific instructions. If that file is missing, the framework generates a short fallback from config that points agents to markdown, search, MCP, llms.txt, sitemap routes, robots.txt, feedback, and the reusable Skills CLI install command. AGENTS.md is available at GET /AGENTS.md, with GET /.well-known/AGENTS.md as the well-known alias and GET /api/docs?format=agents as the shared API format. It is generated by default as a coding-agent operating guide that points to markdown routes, llms.txt, sitemap routes, robots.txt, OpenAPI, MCP, feedback, and upgrade guidance. Put AGENTS.md at the project root when you want fully custom instructions; that root file overrides both dynamic endpoints. AGENT.md is also accepted as a compatibility alias. Choosing Between Them - Use Agent when the normal page should stay the source of truth and you only need extra machine context - Use agent.md when the machine-readable page should become its own focused document - Use both patterns page-by-page; you do not need a global config flag to turn them on Page Primitives Vs Skills Agent and agent.md are page-scoped primitives. They are the right fit when the machine context belongs to a single docs route. Use /AGENTS.md when a coding agent needs site-level operating instructions for this repo or docs deployment. Use /skill.md when an agent needs a concise installable-skill style guide for this hosted docs site. Use an installable skill when the guidance should be reusable across many pages or workflows, such as setup, CLI usage, theme creation, page actions, or deeper configuration tasks. In practice, the two features work well together: - use Agent or agent.md to make one page more useful to agents - use skills when the knowledge should be bundled as a reusable workflow outside a single page This repo already publishes skills under skills/farming-labs/. Install them with: Common choices include: - getting-started for setup and installation - cli for init, upgrade, and CLI flags - configuration for docs.config options - page-actions` for Copy Markdown and Open in LLM behavior Use page primitives for route-level context. Use skills for repeatable product knowledge that should travel across tasks. --- ## Ask AI URL: https://docs.farming-labs.dev/docs/customization/ai-chat Add a RAG-powered AI chat to your documentation Ask AI Add a built-in AI chat that lets users ask questions about your documentation. The AI searches relevant pages, builds context, and streams a response from any OpenAI-compatible LLM. Quick Start That's it. The AI reads your OPENAIAPIKEY environment variable and uses gpt-4o-mini by default. Add OPENAIAPIKEY to your .env file: The key is automatically read from process.env.OPENAIAPIKEY. Add OPENAIAPIKEY to your .env file: Pass it through src/lib/docs.server.ts: Add OPENAIAPIKEY to your .env file: Pass it through docs.server.ts (SvelteKit requires server-only env access): Add OPENAIAPIKEY to your .env file: Pass it through docs.server.ts: Add OPENAIAPIKEY to your .env file: Nuxt automatically reads environment variables via Nitro's runtime config. The defineDocsHandler reads process.env.OPENAIAPIKEY on the server. Configuration Reference All options go inside the ai object in docs.config.ts: enabled Whether to enable AI chat functionality. | Type | Default | | --------- | ------- | | boolean | false | mode How the AI chat UI is presented. | Type | Default | | ------------------------ | ---------- | | "search" \| "floating" | "search" | - "search" — AI tab integrated into the Cmd+K search dialog. Users switch between "Search" and "AI" tabs. - "floating" — A floating chat widget with a button on screen. Opens as a panel, modal, or full-screen overlay. position Position of the floating chat button on screen. Only used when mode is "floating". | Type | Default | | ---------------------------------------------------- | ---------------- | | "bottom-right" \| "bottom-left" \| "bottom-center" | "bottom-right" | floatingStyle Visual style of the floating chat when opened. Only used when mode is "floating". | Type | Default | | ------------------------------------------------- | --------- | | "panel" \| "modal" \| "popover" \| "full-modal" | "panel" | - "panel" — A tall panel that slides up from the button position. No backdrop overlay. - "modal" — A centered modal dialog with a backdrop overlay, similar to the Cmd+K search dialog. - "popover" — A compact popover near the button. Suitable for quick questions. - "full-modal" — A full-screen immersive overlay. Messages scroll in the center, input is pinned at the bottom, suggested questions appear as horizontal pills. model The LLM model configuration. Can be a simple string (single model) or an object with multiple selectable models. Simple — single model: | Type | Default | | -------- | --------------- | | string | "gpt-4o-mini" | Advanced — multiple models with UI dropdown: | Type | Default | | -------- | ------- | | object | — | Each model entry has: - id — The model identifier sent to the LLM API (e.g. "gpt-4o-mini") - label — Display name shown in the UI dropdown (e.g. "GPT-4o mini (fast)") - provider — (optional) Key matching a named provider in the providers config. If omitted, uses the default baseUrl and apiKey. When model is an object with a models array, a model selector dropdown appears in the AI chat interface so users can pick which model to use. providers Named provider configurations. Each provider has its own baseUrl and apiKey, allowing models from different providers to coexist in a single config. | Type | Default | | -------- | ------- | | object | — | When a user selects a model in the dropdown, the backend automatically uses that model's provider to resolve the correct baseUrl and apiKey. All providers must be OpenAI Chat Completions API compatible (OpenAI, Groq, Together, Fireworks, OpenRouter, Ollama, any vLLM deployment). baseUrl Default base URL for an OpenAI-compatible API endpoint. Used when no per-model provider is configured. | Type | Default | | -------- | ----------------------------- | | string | "https://api.openai.com/v1" | apiKey Default API key for the LLM provider. Used when no per-model provider is configured. Falls back to process.env.OPENAIAPIKEY if not set. | Type | Default | | -------- | ---------------------------- | | string | process.env.OPENAIAPIKEY | Warning: Never hardcode API keys. Always use environment variables. systemPrompt Custom system prompt prepended to the AI conversation. Documentation context is automatically appended after this prompt. | Type | Default | | -------- | ------------------------------------------------ | | string | "You are a helpful documentation assistant..." | maxResults Maximum number of search results to include as context for the AI. More results = more context but higher token usage. | Type | Default | | -------- | ------- | | number | 5 | useMcp Route Ask AI retrieval through the MCP server your docs site already exposes, without changing the normal docs search API. | Type | Default | | ---- | ------- | | boolean \| DocsAskAIMcpConfig | false | useMcp: true uses the built-in docs MCP server and calls its search_docs tool to retrieve Ask AI context. By default that is the canonical mcp.route at /api/docs/mcp; if you configure mcp.route, Ask AI uses that route automatically: Pass an object only when Ask AI should use a hosted or external MCP endpoint instead of this site's own MCP route. MCP handles retrieval only; the model, apiKey, baseUrl, or providers config still controls the LLM generation request. If your top-level search config already uses provider: "mcp", Ask AI follows that automatically. Use ai.useMcp when only Ask AI should route retrieval through MCP. DocsAskAIMcpConfig accepts the MCP search options (endpoint, headers, toolName, protocolVersion, maxResults, and enabled). If MCP is disabled or search_docs is turned off, Ask AI falls back to the normal top-level search config. End-to-end, Ask AI does two server-side calls: 1. Retrieve docs context from search_docs on the configured MCP endpoint. 2. Send the prompt plus retrieved context to the configured OpenAI-compatible model endpoint. So a custom MCP endpoint must support Streamable HTTP MCP, expose a search tool (default search_docs), and return results with url, content, optional description, and optional section. Stateful endpoints may return an mcp-session-id; stateless endpoints can omit it. Retrieval Quality Ask AI uses the same configured docs search pipeline as the search API. If you configure simple search, Typesense, Algolia, MCP search, or a custom search adapter, Ask AI uses those results before building the model context. The context passed to the model is hydrated from the local docs page or matching section, preserving fenced code blocks so install commands, config snippets, and examples can be quoted accurately. feedback Completed Ask AI responses show copy, like, and dislike actions by default. Set feedback: false to hide the action row. | Type | Default | | ---- | ------- | | boolean \| { enabled?: boolean; onFeedback?: (data) => void \| Promise } | true | onActions Single callback for Ask AI response actions. Use data.type to handle "copy", "like", and "dislike" from one place. The callback receives type, question, answer, model, surface, url, path, and the visible chat messages up to that answer. Copy actions also include copied. Like/dislike actions also include value for compatibility with feedback.onFeedback. Like/dislike still dispatch the legacy fd:ai-feedback browser event and emit an ai_feedback analytics event when analytics is enabled. All three actions dispatch fd:ai-action. suggestedQuestions Pre-filled suggested questions shown in the AI chat when the conversation is empty. Clicking one fills the input and submits automatically. | Type | Default | | ---------- | ------- | | string[] | [] | aiLabel Display name for the AI assistant in the chat UI. Shown as the message label and header title. | Type | Default | | -------- | ------- | | string | "AI" | packageName Optional package-name override for unusual docs where install/import examples do not mention the main package clearly. Most projects should leave this unset: Ask AI infers package names, install commands, and exact import lines from the retrieved docs context. | Type | Default | | -------- | ------- | | string | inferred from docs context | docsUrl The public URL of your documentation site. The AI will use this for absolute links instead of relative paths. | Type | Default | | -------- | ------- | | string | — | loader Loading indicator variant shown while the AI generates a response. | Type | Default | | -------- | ---------------- | | string | "shimmer-dots" | Available variants: "shimmer-dots", "circular", "dots", "typing", "wave", "bars", "pulse", "pulse-dot", "terminal", "text-blink", "text-shimmer", "loading-dots". loadingComponent Custom React component that completely overrides the built-in loader variant. Receives { name } (the aiLabel value). Only works in Next.js — for other frameworks, use the loader option. | Type | Default | | ------------------------------------------ | ------- | | (props: { name: string }) => ReactNode | — | triggerComponent Custom trigger button for the floating chat. Replaces the default sparkles button. Only used when mode is "floating". Each framework accepts its native component format — pass it as a prop on DocsLayout (or a slot in Astro). | Type | Default | | ----------- | ------------------------ | | Component | Built-in sparkles button | Pass a React component via docs.config.tsx: Import a Svelte component and pass it as a prop on DocsLayout: Use the trigger-component slot on DocsLayout: Import a Vue component and pass it as a prop on DocsLayout: Full Example — Single Provider Full Example — Multiple Providers Users see a model dropdown in the AI chat interface. When they pick a model, the backend automatically routes the request to the correct provider's API with the right credentials. Using a Different LLM Provider Single provider (simple) Use any OpenAI-compatible API by setting baseUrl and model: Multiple providers Use the providers map to configure multiple APIs, then reference them from each model entry: Compatible providers: OpenAI, Groq, Together AI, Fireworks, OpenRouter, Azure OpenAI, Ollama (local), any vLLM deployment — anything that speaks the OpenAI Chat Completions API format. --- ## Analytics URL: https://docs.farming-labs.dev/docs/customization/analytics Track product and usage events from docs, search, AI, feedback, agent, and MCP routes Analytics Use analytics when you want to understand how people and agents use your docs. It emits product-level usage events: page views, search activity, Ask AI requests, feedback, page actions, machine-readable markdown reads, agent feedback routes, and MCP requests. Analytics is intentionally separate from Observability. Analytics answers "what did users or agents do?" Observability answers "what happened inside this agent run?" Quick Start analytics: true logs usage events to the console with the [@farming-labs/docs:analytics] prefix. Send Events To Your Own Sink The callback receives a normalized DocsAnalyticsEvent: Event Types Analytics events describe product usage: what a person or agent did, which surface they used, and whether the action completed. Client Events | Event | Meaning | | -------------------------------- | ----------------------------------------------------------------------- | | page_view | A docs page was viewed in the browser. | | search_open | The search UI was opened. | | search_close | The search UI was closed. | | search_query | A search query was submitted or changed enough to run search. | | searchresultclick | A search result was selected. | | search_error | Client-side search failed. | | ai_open | The Ask AI UI was opened. | | ai_close | The Ask AI UI was closed. | | ai_question | A user submitted a question to Ask AI from the client UI. | | ai_response | The Ask AI UI received a successful response. | | ai_feedback | A user rated a completed Ask AI answer with the response feedback buttons. | | ai_error | The Ask AI UI encountered an error. | | ai_clear | The Ask AI conversation was cleared. | | pageactioncopy_markdown | The Copy Markdown page action was used. | | pageactionopendocsmenu | The Open in LLM/Open docs page-action menu was opened. | | pageactionopen_docs | A destination in the Open in LLM/Open docs menu was selected. | | codeblockcopy | A code block copy button was clicked. | | feedback_select | A feedback rating or option was selected. | | feedback_submit | Feedback was submitted successfully. | | feedback_error | Feedback submission failed. | Server And Agent-Surface Events | Event | Meaning | | ------------------------------- | ----------------------------------------------------------------------- | | api_search | The docs API handled a search request. | | apiairequest | The docs API accepted an Ask AI request. | | apiairesponse | The docs API returned a successful Ask AI response. | | apiaierror | The docs API rejected or failed an Ask AI request. | | agent_read | A page was read through a machine-readable docs surface. | | markdown_request | A markdown route or API markdown request was served. | | llms_request | /llms.txt or /llms-full.txt was requested. | | skill_request | An Agent Skills file or skill index was requested. | | agentspecrequest | The agent-facing docs spec endpoint was requested. | | agentfeedbackschema | The agent feedback schema endpoint was requested. | | agentfeedbacksubmit | Agent feedback was submitted successfully. | | agentfeedbackerror | Agent feedback submission failed. | | mcp_request | The MCP endpoint received a request. | | mcptool | An MCP tool call was handled, such as searchdocs or read_page. | agentread includes properties.delivery so you can see how the page was read: mdroute, acceptheader, apiformat, or mcp_tool. Input Privacy By default, analytics events include safe metadata such as paths, counts, durations, status, result counts, model IDs, content lengths, and whether a comment exists. Search queries, AI questions, feedback comments, and copied content are not included unless you explicitly set includeInputs: true. Enable raw input capture only when you have user consent and a retention policy: Options | Option | Type | Default | | --------------- | ----------------------------------------- | ---------------------------- | | enabled | boolean | true when object is passed | | console | boolean \| "log" \| "info" \| "debug" | true for analytics: true | | includeInputs | boolean | false | | onEvent | (event) => void \| Promise | undefined | Configure both streams when you want usage analytics and runtime traces: --- ## Colors URL: https://docs.farming-labs.dev/docs/customization/colors Override any color token from config Colors Override any color token directly from your config. Colors are mapped to --color-fd-* CSS variables at runtime. Setting Colors Available Tokens | Token | CSS Variable | Description | | ------------------- | ------------------------------- | --------------------------- | | primary | --color-fd-primary | Primary brand color | | primaryForeground | --color-fd-primary-foreground | Text on primary backgrounds | | background | --color-fd-background | Page background | | foreground | --color-fd-foreground | Default text color | | muted | --color-fd-muted | Muted background | | mutedForeground | --color-fd-muted-foreground | Muted text | | border | --color-fd-border | Border color | | card | --color-fd-card | Card background | | accent | --color-fd-accent | Accent background | | accentForeground | --color-fd-accent-foreground | Text on accent | | secondary | --color-fd-secondary | Secondary background | | ring | --color-fd-ring | Focus ring color | How It Works Only colors you explicitly set are emitted as inline CSS variables. Theme preset defaults stay in the CSS file — your overrides don't break light/dark mode handling. Color Formats Any valid CSS color value works: --- ## Components URL: https://docs.farming-labs.dev/docs/customization/components Override built-in or add custom MDX components Components Pass custom React components that become available in all MDX files — no imports needed. How It Works When you register components in docs.config.ts, they're merged into the MDX component map via getMDXComponents(). The merge order is: 1. Built-in components — headings, code blocks, callouts, Tab, Tabs, HoverLink, etc. 2. Theme defaults — from theme.ui.components for built-ins like HoverLink and Prompt 3. Your components — from docs.config.ts (overrides built-ins if names match) This means you can both add new components and override existing ones. Creating a Custom Component 1. Create the component 2. Register in config 3. Use in any MDX file No import needed — just use it directly: Overriding Built-in Components You can replace any built-in MDX component by registering one with the same name. Your component will take precedence. Available built-in components The following components are available by default (from fumadocs-ui): - h1, h2, h3, h4, h5, h6 — heading elements - p, a, ul, ol, li — text and list elements - table, thead, tbody, tr, th, td — table elements - pre, code — code blocks and inline code - img — images - blockquote — blockquotes - hr — horizontal rules - HoverLink — hover-triggered popover link card with title, description, and CTA - Prompt — reusable AI prompt card with copy/open actions - Tab, Tabs — tabbed content - Callout — callout/admonition boxes Example: Use HoverLink HoverLink gives you an inline trigger that opens a richer popover card on hover or focus. It is useful when you want to reference another page without breaking the reader's flow immediately. Live example: installation guide Theme defaults vs config overrides Use theme.ui.components when you want to keep a built-in like HoverLink or Prompt but change its default props across the site. Use components.HoverLink or components.Prompt when you want to replace the component entirely. Example: Use Prompt Prompt turns a reusable AI prompt into a first-class docs component. By default, the prompt body is treated as payload for copy/open actions rather than visible page content, so the card stays compact like Mintlify-style prompt blocks. Useful visibility props: - showPrompt — render the prompt body inside the card - showTitle — hide the title even if title is set - showDescription — hide the description even if description is set Live example: Write a concise agent-friendly documentation page for this feature. Include: - the user problem it solves - the exact setup steps - one verification checklist - one troubleshooting section If you want the prompt text visible on the page too, opt in: Live example: Write a concise agent-friendly documentation page for this feature. Include: - the user problem it solves - the exact setup steps - one verification checklist - one troubleshooting section Example: Override the a tag Now every link in your MDX files will use CustomLink — external links open in a new tab with an arrow indicator. Example: Override blockquote Example: Override pre (code blocks) Multiple Components Register as many components as you need: Then use them all across your MDX without imports: Component Props Components receive their props as defined in your implementation. The only requirement is that they must be valid React components (function or class). For built-in overrides, your component should accept the same props as the original element (e.g., React.AnchorHTMLAttributes for a, React.HTMLAttributes for pre). For custom components, define whatever props interface you need. --- ## llms.txt URL: https://docs.farming-labs.dev/docs/customization/llms-txt Auto-generate llms.txt and llms-full.txt for LLM-friendly documentation llms.txt Serve llms.txt content through your existing docs API and the default public aliases. It is enabled by default. What is llms.txt? llms.txt is a standard for making website content accessible to LLMs. Next.js withDocs() and the generated TanStack Start, SvelteKit, Astro, and Nuxt forwarding layer serve conventional public aliases that forward to the same docs handler: - /llms.txt — The default concise markdown listing of all pages with titles, URLs, and descriptions - /llms-full.txt — The default full stripped content of every page, ready for LLM consumption - /.well-known/llms.txt — Alias for /llms.txt - /.well-known/llms-full.txt — Alias for /llms-full.txt The shared API handler remains the source of truth: - /api/docs?format=llms — A concise markdown listing of all pages with titles, URLs, and descriptions - /api/docs?format=llms-full — The full stripped content of every page, ready for LLM consumption Quick Start No config is required for the default routes. Add llmsTxt only when you want to customize the public base URL, title, description, or explicitly opt out. That's it. The existing API handler serves the content automatically. Next.js adds rewrites with withDocs(); TanStack Start, SvelteKit, Astro, and Nuxt add one public forwarder each so the public aliases do not need one route file per URL. Custom Static Files You do not need a config flag to replace the generated files. If your app already ships a native static file at the same public route, that file wins and the generated route is only used as the fallback. | Framework | Static file location | | --------- | -------------------- | | Next.js, Astro, Nuxt, TanStack Start | public/llms.txt, public/llms-full.txt | | SvelteKit | static/llms.txt, static/llms-full.txt | The same convention applies to /.well-known/llms.txt and /.well-known/llms-full.txt when you publish those files in the framework's static directory. Keep llmsTxt configured only when you want generated fallback metadata such as baseUrl, siteTitle, sections, or a size budget. Configuration Reference All options go inside the optional llmsTxt object in docs.config.ts: llmsTxt.enabled Enable or disable llms.txt generation. | Type | Default | | --------- | ------- | | boolean | true | llmsTxt.baseUrl Base URL prepended to all page links in the generated files. | Type | Default | | -------- | ------- | | string | "" | llmsTxt.siteTitle Title shown at the top of the generated files. Falls back to nav.title if not set. | Type | Default | | -------- | --------------- | | string | nav.title | llmsTxt.siteDescription Description shown below the title. | Type | Default | | -------- | ----------- | | string | undefined | llmsTxt.maxChars Character budget for generated compact llms.txt files. The default warns when a root or section file grows past 50,000 characters. llms-full.txt is intentionally not budgeted because it is the full-content fallback. | Type | Default | | ------------------------------------------ | ------------------------------- | | { mode?: "warn" \| "error" \| "off"; chars?: number } | { mode: "warn", chars: 50000 } | llmsTxt.sections Optional section-level llms.txt files for larger docs. Each matcher is a public URL pattern. The framework derives both routes from match. Derived routes: - /docs/api/llms.txt - /docs/api/llms-full.txt - /docs/guides/llms.txt - /docs/guides/llms-full.txt This docs site dogfoods the section feature with Guides, Customization, and Themes sections. For example, the generated compact Guides section file is /docs/guides/llms.txt, and the full-content sibling is /docs/guides/llms-full.txt. Those routes are not UI pages; they are machine-readable text responses served by the same docs API. How It Works The existing API handler serves the llms.txt content when you pass the format query parameter: - GET /api/docs?format=llms — concise page listing - GET /api/docs?format=llms-full — full page content Next.js withDocs() and the generated TanStack Start, SvelteKit, Astro, and Nuxt forwarding layer also expose the crawler-friendly public aliases: - GET /llms.txt — default concise route - GET /llms-full.txt — default full-content route - GET /.well-known/llms.txt - GET /.well-known/llms-full.txt - GET /.well-known/agent.json for the preferred agent discovery spec that references those llms.txt routes, the AGENTS.md routes, the robots.txt route, and the sitemap routes when enabled - GET /.well-known/agent as the fallback agent discovery alias Those aliases rewrite to the same shared API output, so there is still only one content pipeline. The agent discovery spec also advertises defaultTxt: "/llms.txt" and defaultFull: "/llms-full.txt" so integrations can use the intended defaults without adding their own framework-specific routes. TanStack Start, SvelteKit, Astro, and Nuxt can use the shared API query routes directly when they do not want to add the public aliases. The compact llms.txt listing links to each page's markdown route by default, for example /docs/installation.md instead of /docs/installation. That gives agents a direct path to the machine-readable page and keeps the root index aligned with AFDocs-style checks. llms-full.txt still includes the canonical page URL in its URL: line because the content is already embedded in that file. If apiReference is enabled, root llms.txt also includes an API Schemas section that links to /api/docs?format=openapi. Agents can fetch the schema before reading rendered endpoint docs. When llmsTxt.sections is configured, root /llms.txt lists those section files first and only keeps unmatched pages in the root page list. Each section route serves the matching pages, and its llms-full.txt sibling serves the full matching content. No UI is added; this only changes the machine-readable route layer. When you also want crawlers and AI agents to see an explicit access policy, publish /robots.txt with docs robots generate. The generated policy allows the public llms.txt aliases along with docs, markdown, sitemap, skill, MCP, and agent discovery routes. Output Example /api/docs?format=llms /api/docs?format=llms-full Footer Links Because llms.txt is enabled by default, llms.txt and llms-full.txt links automatically appear in the page footer next to "Edit on GitHub", pointing to the public /llms.txt and /llms-full.txt routes. Full Example --- ## MCP Server URL: https://docs.farming-labs.dev/docs/customization/mcp Expose your docs as MCP tools and resources over stdio, /mcp, or /.well-known/mcp MCP Server @farming-labs/docs can expose your docs as a built-in MCP server, so AI clients can search the docs, read pages, and inspect the nav tree without scraping HTML. The current built-in surface includes: - list_pages - get_navigation - search_docs - read_page It also exposes resources for: - docs://navigation - one docs://… resource per page Default behavior MCP is enabled by default. That gives you the built-in MCP surface with the default public Streamable HTTP routes: The hosted docs site has MCP enabled at https://docs.farming-labs.dev/mcp and https://docs.farming-labs.dev/.well-known/mcp. Opt out explicitly: Default HTTP Routes /mcp is the short public MCP endpoint in Next.js, and /.well-known/mcp is the discovery-friendly public MCP endpoint. Both rewrite to the canonical framework route at /api/docs/mcp, so the MCP handler still lives in one place. Next.js auto-generates the default /api/docs/mcp route when you use withDocs(), and also adds public /mcp and /.well-known/mcp rewrites unless you explicitly disable MCP. TanStack Start, SvelteKit, Astro, and Nuxt use one public forwarder each so /api/docs/mcp, /mcp, and /.well-known/mcp share the same built-in handler without creating one route file per alias. Next.js With withDocs(), no extra route file is needed for the default path. TanStack Start SvelteKit Your src/lib/docs.server.ts can keep using the normal helper: Astro Nuxt Custom route If you want a custom MCP route, set it in docs.config and add the route file yourself. The package only auto-generates the default Next.js route at /api/docs/mcp. If you choose a custom route, keep that path in docs.config and update the framework public forwarder so the app and the MCP client point at the same endpoint. Example custom Next.js route: @farming-labs/theme/api still works if you already rely on it, but prefer @farming-labs/next/api for Next.js routes going forward. The theme-level path will be deprecated. When search is configured, the MCP search_docs tool uses that same adapter pipeline too. That means your docs UI search and MCP search can share the same provider, chunking strategy, and custom ranking logic instead of drifting apart. That also means you can flip docs search itself over to MCP: For Ask AI only, keep the top-level search provider as-is and enable MCP retrieval in the AI config. This uses the MCP server the docs site already exposes: If you changed the canonical MCP route, useMcp: true follows it automatically: For a hosted or external MCP endpoint, configure the endpoint on ai.useMcp instead. This does not replace your LLM provider config; the MCP endpoint retrieves docs context, then Ask AI still sends the final prompt to your configured model provider. The custom MCP endpoint must support Streamable HTTP MCP and expose a search tool that returns docs results. The default tool name is search_docs; override toolName only if your server uses a different name. Stateful MCP servers may return an mcp-session-id on initialize; stateless servers can omit it, and the docs client will only send the session header when one is provided. For local self-hosted setups, relative MCP endpoints like /mcp and /.well-known/mcp are supported. The canonical /api/docs/mcp route remains available too. The built-in search_docs tool automatically falls back to simple search internally when it detects that same-route loop, so the route stays usable for testing and local examples. Stdio transport The same docs graph is available locally over stdio: Or with pnpm in an installed project: By default the CLI reads docs.config.ts[x] from the project root. If your config lives somewhere else: Use the hosted MCP endpoint If you just want to try the live docs server, you can point your MCP client at: More setup docs - Cursor MCP docs - VS Code MCP docs - Claude Code MCP docs Manual setup Cursor project or global config: VS Code workspace config: Claude Code: If you are connecting to a local Next dev server, use http://127.0.0.1:3000/mcp or http://127.0.0.1:3000/.well-known/mcp. Using https://localhost:3000/... against a non-TLS dev server will fail with SSL errors. Test the Next example This repo's Next example exposes the default MCP endpoint directly. Start the example: Then connect your MCP client or inspector to: The built-in HTTP route exposes the full MCP surface: - list_pages - get_navigation - search_docs - read_page What to ask it Once your MCP client is connected, these are good first checks: - search_docs for feedback, page actions, or mcp - read_page for /docs/configuration or installation - get_navigation to inspect the docs tree - list_pages to confirm the server is loading the expected docs set In Cursor or VS Code, natural prompts like these work well too: - Search the Farming Labs docs for page feedback setup - Read the configuration page and summarize MCP config - Find the page actions docs and tell me how openDocs works What the tools return - list_pages returns page titles, slugs, and URLs - get_navigation returns the docs tree in a readable text outline - search_docs ranks pages by title, description, and content matches - read_page accepts either a slug like installation or a full docs path like /docs/installation The built-in resources mirror that same data: - docs://navigation - docs://docs - docs://docs/installation - docs://docs/guides/quickstart When to use this vs markdown routes and llms.txt - Use markdown routes when you want a plain HTTP URL for one page; in Next.js, Signature-Agent can return markdown from the canonical page URL too - Use llms.txt when you want a static, crawler-friendly docs summary - Use robots.txt when crawlers and AI agents need an explicit policy that allows docs and machine-readable routes - Use MCP when you want a structured, queryable docs interface for agents and IDE tools They complement each other well. Markdown routes are the simplest page-level HTTP surface, llms.txt is lightweight and public, robots.txt makes access policy explicit, and MCP is interactive and tool-based. --- ## Observability URL: https://docs.farming-labs.dev/docs/customization/observability Trace Ask AI and MCP runs with span IDs, timing, status, previews, errors, and callbacks Observability Use observability when you need step-level visibility into agent runtime behavior. It emits span-like trace events for Ask AI and MCP tools: run start/end, prompt building, retrieval, model calls, model responses, streaming handoff, tool calls, tool results, errors, retries, and timeouts. Observability is separate from Analytics. It does not receive pageview, searchquery, apiairequest, or mcp_tool; those usage events belong to analytics.onEvent. Quick Start observability: true logs trace events to the console with the [@farming-labs/docs:observability] prefix. Live Local Logs Use console observability when you want to watch Ask AI and MCP work as it happens: Every trace event prints to the dev-server terminal with its traceId, spanId, name, status, durationMs, previews, and metadata. Callback Use onEvent when you want to forward traces to a log drain, APM, OpenTelemetry bridge, database, or your own debugging dashboard: Set console: false if you only want the callback and do not want terminal logs. For metrics, aggregate by event.type, event.name, event.status, and event.durationMs. For traces, group by event.traceId and order by startedAt or timestamp. Event Shape onEvent receives a normalized DocsObservabilityEvent: Example tool event: Trace Events Observability events describe runtime steps inside an agent run. They are span-like: use traceId to group a run, spanId to identify a step, and parentSpanId to connect child steps to their parent. Ask AI Events | Event | Meaning | | ------------------ | --------------------------------------------------------------------- | | run.start | A new Ask AI run started. This is the root span for the request. | | user.input | The latest user message was accepted and summarized into safe counts. | | retrieval.query | Docs retrieval started for the user question. | | retrieval.result | Docs retrieval completed and produced measurable result counts. | | retrieval.error | Docs retrieval failed before the model call could use the context. | | prompt.build | The system prompt and retrieved docs context were assembled. | | model.call | The outbound model request started. | | model.response | The model provider returned successful response headers. | | model.stream | A streaming model response was handed back to the client. | | model.error | The model request failed, threw, or returned an error status. | | agent.final | The final response handoff was reached after a successful model call. | | run.error | The run ended unsuccessfully. | | run.end | The run ended, either successfully or after an error. | MCP Tool Events | Event | Meaning | | ------------- | ----------------------------------------------------------------- | | tool.call | An MCP tool call started, such as searchdocs or readpage. | | tool.result | An MCP tool completed successfully and returned a result. | | tool.error | An MCP tool returned an error result or threw during execution. | Generic Runtime Events | Event | Meaning | | --------- | --------------------------------------------------------------------- | | retry | A retry attempt was recorded for a failed or unstable step. | | timeout | A step timed out before it could complete. | | error | A generic run-level error was recorded outside a more specific event. | Built-in trace events avoid raw user-authored text. They use lengths, counts, route names, model IDs, status codes, and content sizes so they are safer to aggregate. Using Analytics Together Configure both hooks when you want product analytics and runtime traces: In that setup: - analytics.onEvent receives events like pageview, searchquery, apiairequest, and mcp_tool - observability.onEvent receives events like run.start, prompt.build, model.call, and tool.result Options | Option | Type | Default | | --------------- | ----------------------------------------- | ---------------------------- | | enabled | boolean | true when object is passed | | console | boolean \| "log" \| "info" \| "debug" | true for observability: true | | includeInputs | boolean | false | | onEvent | (event) => void \| Promise | undefined | --- ## OG Images URL: https://docs.farming-labs.dev/docs/customization/og-images Dynamic and static Open Graph images for docs — how they work, what context is passed, and how the docs website generates them OG Images Open Graph (OG) images are the preview images shown when your docs pages are shared on social platforms, Slack, or messaging apps. This guide explains how the framework supports dynamic (generated per page) and static (per-page image file) OG images, what data your image generator receives, and how the docs website implements dynamic OG. Overview: static vs dynamic One design for all pages; title and description change per page. You implement an API route. The framework calls it with the page’s title and description (from frontmatter) as query params and uses the returned image URL for og:image and twitter:image. Custom image per page (e.g. diagrams, screenshots). Set openGraph and/or twitter (or the shorthand ogImage) in that page’s frontmatter. The framework uses those values instead of calling the dynamic endpoint. You can mix both: use a dynamic endpoint by default and override with static images on specific pages via frontmatter. How dynamic OG works 1. You configure an OG endpoint in docs.config.ts: - og: { enabled: true, type: "dynamic", endpoint: "/api/og" } 2. For each docs page, the framework builds a single URL that includes the page’s context: - Next.js (MDX): A remark plugin injects that URL into the page’s exported metadata (from frontmatter). The URL is your endpoint plus query params. - Query params the framework adds: - title — the page’s title from frontmatter (required). - description — the page’s description from frontmatter (optional). 3. Your API route receives a GET request with those query params and returns a 1200×630 image (e.g. PNG). 4. The framework sets that URL as og:image and twitter:image in the page metadata, so crawlers and social platforms fetch the image when the page is shared. So your image generator gets full context: the same title and description that appear in the doc and in meta tags. You can render them (and any other logic) inside your image. What context your image generator receives When the framework calls your dynamic OG endpoint, it passes: | Query param | Source | Example value | | ------------- | ----------------- | -------------------- | | title | Page frontmatter title | Introduction or OG Images | | description | Page frontmatter description | Dynamic and static Open Graph images for docs. | The URL looks like: Your route reads searchParams.get("title") and searchParams.get("description") and uses them when rendering the image (e.g. in ImageResponse). That’s the full context the framework provides; you can add more query params in your own links if needed. Example: how the docs website uses dynamic OG The @farming-labs/docs website which uses Nextjs uses a dynamic OG endpoint so every docs page gets a unique preview image with that page’s title and description. 1. Config (docs.config.ts) No other OG config is required; the framework builds the URL for each page and injects it into metadata. 2. API route (app/api/og/route.ts) The route receives title and description from the query string and returns a 1200×630 image using Next.js ImageResponse: So the image generator has full context: the same title and description that come from the MDX frontmatter. The live site loads Inter and JetBrains Mono for typography and uses the same layout with diagonal pattern and borders; the only variables are title and description. 3. What each page gets - Introduction (/docs) → /api/og?title=Introduction&description=... - OG Images (/docs/customization/og-images) → /api/og?title=OG+Images&description=Dynamic+and+static+... - API Reference (/docs/reference) → /api/og?title=API+Reference&description=... Each URL is set as that page’s og:image and twitter:image, so shares show the correct title and description on the card. Using static OG for specific pages To use a static image for a single page (e.g. a pre-made graphic or screenshot), set openGraph and twitter in that page’s frontmatter. The framework will use those instead of calling the dynamic endpoint for that page. Shorthand: you can set only ogImage: "/og/some-page.png" and the framework will use it for both Open Graph and Twitter unless you override with full openGraph / twitter. See Page Frontmatter for all options. Configuration reference | Option | Type | Description | | ------------- | -------- | ----------- | | og.enabled | boolean| Turn OG/twitter images on or off. | | og.type | "static" \| "dynamic" | Use a fixed image or a dynamic endpoint. | | og.endpoint | string| Path to your OG API route (e.g. "/api/og"). Required for dynamic. | | og.defaultImage | string | Fallback OG image URL when no page-specific OG is set. | Full details: OGConfig in the API reference. Testing your OG image 1. Start your dev server (e.g. pnpm dev). 2. Open the endpoint with sample params in the browser: - http://localhost:3000/api/og?title=Test+Page&description=My+description 3. Confirm the image shows the right title and description. 4. After code changes, do a hard refresh (e.g. Cmd+Shift+R) or use a cache-busting query (e.g. &_=1) so you see the latest image. Summary - Dynamic OG gives every docs page a generated preview: the framework calls your endpoint with title and description from frontmatter, and you return a 1200×630 image. The docs website uses this with a single /api/og route. - Static OG lets you set a specific image per page via frontmatter (openGraph, twitter, or ogImage), which overrides the dynamic endpoint for that page. - Your image generator receives the full context it needs: the page’s title and description as query parameters, so you can render them (and anything else you derive from them) in your image. --- ## Page Actions URL: https://docs.farming-labs.dev/docs/customization/page-actions Add "Open in LLM" and "Copy Markdown" buttons to your doc pages Page Actions Page actions are buttons rendered above or below the page title. They let users interact with page content — copy it as Markdown, or open it in an LLM provider like ChatGPT or Claude. Quick Start This adds two buttons: Copy Markdown and an Open in... dropdown. Configuration Reference All options go inside the pageActions object in docs.config.ts: pageActions.position Where to render the page action buttons relative to the page title. | Type | Default | | -------------------------------- | --------------- | | "above-title" \| "below-title" | "below-title" | If reading time is active for the current page, the read-time label stays attached to the same title-area slot. above-title places it directly under the action row. below-title keeps it grouped in the below-title metadata area. This also applies when a page opts in with frontmatter like readingTime: true or readingTime: 8. pageActions.alignment Control whether the action row is left- or right-aligned. | Type | Default | | --------------------- | -------- | | "left" \| "right" | "left" | Copy Markdown pageActions.copyMarkdown Whether to show the "Copy Markdown" button. Copies the current page's content as Markdown to the clipboard. | Type | Default | | --------------------------------- | ------- | | boolean \| { enabled: boolean } | false | Open in LLM The Open in... dropdown lets users send the current page to an LLM or tool. Each provider gets a link that includes the page URL as context. pageActions.openDocs Enable the "Open in..." dropdown. Can be a boolean or an object with additional configuration. | Type | Default | | --------------------------- | ------- | | boolean \| OpenDocsConfig | false | When set to true, the built-in provider list is used: ChatGPT and Claude. pageActions.openDocs.enabled Whether to show the "Open in..." dropdown. | Type | Default | | --------- | ------- | | boolean | false | pageActions.openDocs.providers Custom list of LLM / tool providers to show in the dropdown. Overrides the default list. | Type | Default | | -------------------- | ----------------- | | OpenDocsProvider[] | Built-in defaults | Each provider has the following shape: {githubUrl} only resolves when the top-level github config is set in defineDocs(). Provider URL Templates The urlTemplate string supports these placeholders: | Placeholder | Replaced with | | ------------- | ----------------------------------------------------------------------------- | | {url} | The current page URL (e.g. https://docs.example.com/docs/installation) | | {mdxUrl} | The raw .mdx source URL for the page (useful when you want the source file) | | {githubUrl} | GitHub edit URL for the current page (same as "Edit on GitHub"). Requires github in config. Use urlTemplate: "{githubUrl}" so "Open in GitHub" opens the file on GitHub. | {mdxUrl} points at the raw source file. If you want the built-in machine-readable docs page instead, use {url}.md. That route can return a sibling agent.md when the page has one. In Next.js, HTTP clients can also request the normal page URL with Accept: text/markdown for the same response, or send Signature-Agent when they identify themselves as an agent. See Agent Primitive. Custom Providers Example Example using the public markdown route instead of raw source: Cursor deeplink: The example uses the web format (https://cursor.com/link/prompt?text=...). To open the Cursor app directly instead, use the app scheme: cursor://anysphere.cursor-deeplink/prompt?text=Read+this+documentation:+{url}. With Icons (Next.js) In Next.js, you can pass React elements for icons: Full Example --- ## Sidebar URL: https://docs.farming-labs.dev/docs/customization/sidebar Title, icons, collapsible groups, and styling Sidebar The sidebar is auto-generated from your file structure. Customize its appearance through config. Custom Title The sidebar title supports strings or React components: Icons Add icons to sidebar items via frontmatter: Register icons in your config: Collapsible Groups Nested directories automatically become collapsible groups. A directory with its own page.mdx becomes a folder with an index page: Use sidebar.folderIndexBehavior when you want to control whether the parent row navigates, only expands/collapses, or stays as a plain label: With folderIndexBehavior: "toggle", the folder landing page is rendered as the first child item inside the group, and clicking the parent row no longer changes the URL. Set folderIndexBehavior: "link" if you want the parent row itself to navigate. If you want to remove the folder landing-page link entirely and only show the child pages, use folderIndexBehavior: "hidden": With folderIndexBehavior: "hidden", the framework omits the landing-page link from the sidebar tree and treats the folder like a label-only parent. In the default flat sidebar, that gives you a plain list of child links underneath. If someone manually opens the parent route, the framework redirects it to the first visible child. Hidden folder landing pages are also left out of the machine-readable markdown/search surfaces so they do not keep behaving like standalone docs pages. If you only want that behavior in selected sections, use sidebar.folderIndexBehaviorOverrides and key each override by the folder landing-page URL: That lets one folder like Components stay toggle-only while other folders still navigate on parent click. You can also override the behavior from the folder landing page itself. This wins over the global config and over folderIndexBehaviorOverrides: Flat Mode Render all sidebar items without collapsible sections (Mintlify-style): Pair flat: true with folderIndexBehavior: "hidden" on a folder landing page when you want a Mintlify-style section label with child links, but no parent navigation target. Sidebar Header & Footer Add custom content above and below the navigation items using banner and footer: The banner renders above the navigation items and footer renders below them, both inheriting the theme's sidebar styling automatically. Next.js and TanStack Start can pass React nodes directly in docs.config.tsx. For non-React frameworks, use named slots: Pass sidebarHeader and sidebarFooter snippets: Use sidebar-header and sidebar-footer named slots: Use #sidebar-header and #sidebar-footer slots: Custom Sidebar Component Replace the entire sidebar navigation with your own component. The component receives the full page tree with all parent-child relationships. Pass a render function via sidebar.component in your config: Your component receives the full tree structure: Pass a render function via sidebar.component in your config: Your component can read the current route with TanStack Router: Use the sidebar snippet on `. It receives tree and isActive: Use the sidebar named slot: Use the #sidebar scoped slot: Tree Structure The tree prop has this shape: All types are exported from @farming-labs/docs for full type safety. Sidebar Style Breadcrumbs Enable breadcrumb navigation: Shows Parent / Current Page` with the parent being clickable. --- ## Sitemaps URL: https://docs.farming-labs.dev/docs/customization/sitemaps Generate sitemap.xml and sitemap.md for crawlers, agents, and static exports Sitemaps Generate both a standard sitemap.xml and a semantic sitemap.md from the same docs source. The XML file gives search crawlers a canonical URL list with freshness dates. The Markdown file gives agents, contributors, and automation a readable map of the docs tree with titles, descriptions, markdown URLs, last-updated dates, and related pages. Quick Start No config is required for the default runtime routes. Add sitemap only when you want to customize the public base URL or route prefix: That exposes these routes through the same docs server wrapper and public forwarding layer: - GET /sitemap.xml - GET /sitemap.md - GET /.well-known/sitemap.md - GET /api/docs?format=sitemap-xml - GET /api/docs?format=sitemap-md Next.js withDocs() adds the public rewrites automatically. TanStack Start, SvelteKit, Astro, and Nuxt use the same generated public forwarder that already handles .md, llms.txt, .well-known, and MCP routes. Static Export For static hosting, run the generator before your framework build: The command writes: - .farming-labs/sitemap-manifest.json - public/sitemap.xml - public/sitemap.md - public/.well-known/sitemap.md SvelteKit uses static/ instead of public/. docs sitemap generate is enough for static output. The --public flag is only an explicit spelling for the default behavior. Use --manifest-only when a server-rendered app only needs the generated manifest for accurate lastmod values and should keep the public route handled by the runtime. Use docs robots generate --append if the project already has its own robots.txt and should keep site-specific rules around the generated agent policy block. For Astro, SvelteKit, Nuxt, and TanStack Start deployments that preload docs content, bundle the manifest with the markdown files. Current init templates do this for you: That lets sitemap routes and per-page JSON-LD reuse the same stable lastmod dates in serverless and static-friendly builds. Use a custom config path for adapters whose config lives outside the project root: Common build scripts: For output: "export", drop --manifest-only so public/sitemap.xml and public/sitemap.md are written before next build. When you dogfood unpublished workspace packages, you may need to call the built CLI file directly after building the package. Published apps should use docs in package scripts or pnpm exec docs from the terminal. What lastmod Means lastmod is the last known content update date for a page. It appears as ` in sitemap.xml and as Last updated: in sitemap.md. The CLI resolves dates in this order: 1. the last git commit date for the page source file 2. the filesystem modified date for the page source file 3. no date, if neither source is available Git dates are preferred because they are stable across machines and CI runs. Filesystem dates are a fallback for new files, generated content, or projects that are not in a git checkout. Freshness is useful because crawlers and agents can: - detect pages that changed since the last crawl - prioritize recently edited documentation - avoid re-reading stable pages unnecessarily - show contributors where docs may have gone stale - reuse the same stable date in each page's JSON-LD dateModified when the adapter preloads the generated manifest If you do not want freshness dates, disable them per output: Route Prefix Use routePrefix when you want the sitemap routes under a custom prefix: This generates: - /docs-map/sitemap.xml - /docs-map/sitemap.md - /docs-map/.well-known/sitemap.md There is no separate well-known route setting. The prefix applies to all sitemap routes together so adapters only need one forwarding rule. Configuration Reference | Option | Type | Default | Description | | ------ | ---- | ------- | ----------- | | sitemap | boolean \| DocsSitemapConfig | true | Enable or configure sitemap routes. Set false to opt out. | | sitemap.enabled | boolean | true | Disable without removing the object. | | sitemap.routePrefix | string | "" | Prefix all public sitemap routes. | | sitemap.baseUrl | string | request origin at runtime, llmsTxt.baseUrl in the CLI | Absolute base URL for XML values. | | sitemap.manifestPath | string | .farming-labs/sitemap-manifest.json | Internal generated manifest path. | | sitemap.xml | boolean \| SitemapXmlConfig | enabled | Enable or configure sitemap.xml. | | sitemap.xml.includeLastmod | boolean | true | Include when a page date is known. | | sitemap.markdown | boolean \| SitemapMarkdownConfig | enabled | Enable or configure sitemap.md. | | sitemap.markdown.includeDescriptions | boolean | true | Include page descriptions in the Markdown sitemap. | | sitemap.markdown.includeLastmod | boolean | true | Include Last updated: lines when a page date is known. | | sitemap.markdown.linkTarget | "html" \| "markdown" \| "both" | "both" | Choose whether list items link to HTML pages, Markdown pages, or both. | Output Examples sitemap.xml sitemap.md Manifest Verification Check the public routes: In CI, use --check to fail when generated static output is stale: Use the agent doctor when you want the sitemap to count toward the broader machine-facing surface: The hosted doctor reads sitemap routes from /.well-known/agent.json, so custom routePrefix` values are checked through the configured routes instead of assuming root-level defaults. --- ## Typography URL: https://docs.farming-labs.dev/docs/customization/typography Fonts, heading sizes, weights, and spacing Typography Control fonts, heading sizes, weights, line heights, and letter spacing — all from config. Font Families Heading Styles Each heading level can be individually configured: Font Style Properties | Property | Type | Description | | --------------- | -------- | --------------------------------- | | size | string | CSS font-size (e.g. "2rem") | | weight | number | Font weight (e.g. 700) | | lineHeight | string | Line height (e.g. "1.2") | | letterSpacing | string | Letter spacing (e.g. "-0.02em") | Using Google Fonts Load fonts in your app/layout.tsx and reference them via CSS variables: Then reference them in your typography config: --- ## Guides URL: https://docs.farming-labs.dev/docs/guides Long-form playbooks for building docs that work well for humans, IDEs, and agents Guides These are the longer, more opinionated walkthroughs in the docs. They sit on top of the reference pages and show how to use @farming-labs/docs in a way that feels coherent end to end, not just technically correct. Featured Guide ", "llms.txt", "Sitemaps", "Robots", "MCP", "Doctor"]} /> --- ## How to Write Agent-Friendly Docs URL: https://docs.farming-labs.dev/docs/guides/agent-friendly-docs A practical playbook for writing documentation that reads well for humans while still giving agents enough structure to implement, verify, and recover How to Write Agent-Friendly Docs {"Guide 01"} {"·"} {"Apr 2026"} {"·"} {"@farming-labs/docs"} { "A good agent-friendly docs site is not one that stuffs in extra keywords or repeats every fact twice. It is one that lets a model discover the right page, understand what matters, verify the result, and recover when something breaks, without turning the human page into machine sludge." } Start With The Human Page The first mistake teams make is trying to write for crawlers before they write for users. That usually produces flat, repetitive pages that feel unnatural in the UI and still leave agents with too much ambiguity. Start with a page a human can actually follow: - what this page is for - when to use it - the exact steps - what success looks like - what usually goes wrong Once that page is solid, add the machine layer on top. @farming-labs/docs is built for that flow. The page UI stays human-first, while the machine-readable routes can include extra structure without polluting the visible article. Give Every Important Page A Contract Before you worry about agent.md, make sure the page already tells an agent what kind of page it is. For task pages, that means good frontmatter and a shape the runtime can expose consistently through .md routes and MCP. That alone gives the machine-readable route a stronger entry point: - Description: tells the model what problem the page solves - Related: gives it nearby pages without scraping the sidebar - agent.tokenBudget gives docs agent compact a per-page output target later - the normal body remains the human source of truth If a page is important enough to unblock implementation, it should also contain: 1. a short purpose statement 2. exact commands or file edits 3. a verification section 4. a troubleshooting section keyed to symptoms Write The Implementation Contract The most useful agent-friendly pages are not just shorter. They make the implementation contract obvious. After reading the page, an agent should know what to change, where to change it, and how to prove the change worked. For important task pages, include these signals in the visible page or in an additive ` block: - the task outcome in one sentence - framework and version assumptions when examples depend on them - exact package names, import paths, route paths, and file paths - copy-pasteable commands with the package manager you expect - a success check with expected route, file, status code, or visible UI state - common failure symptoms and the first place to inspect - related pages that an agent should read next That shape gives the agent the missing operational details without turning the human guide into a checklist dump. Use Agent Blocks For Machine-Only Hints When the human page is still correct but agents need extra steering, add an Agent block. It stays hidden in the normal docs UI and appears in the machine-readable layer. This is the sweet spot for most pages: - humans keep the full narrative page - agents get sharper instructions - you avoid maintaining two completely separate documents Treat <Agent> as the place for implementation hints, verification steps, and route-specific behavior that would feel noisy in the visible article. Do not duplicate the whole page there. Use agent.md When The Machine Page Needs A Real Split Some pages eventually need a different machine-readable contract than the human page can provide. A long conceptual article, for example, may still need a short operational document for agents. That is when a sibling agent.md becomes the right tool. Once agent.md exists, it becomes the source for: - {page}.md - GET /api/docs?format=markdown&path= - {page} with Accept: text/markdown in Next.js - {page} with Signature-Agent in Next.js - MCP read_page So use it when that stronger machine contract is genuinely worth owning. Ship The Discovery Layer Too Great page writing helps, but agents still need the routes that tell them how to use your site. With @farming-labs/docs, the goal is to expose a compact discovery layer around the docs tree. Most agent surfaces are enabled by default; the config below only adds site-specific details such as the public base URL and section-level llms.txt. That gives agents a much better workflow: - /.well-known/agent.json tells them which routes exist - /llms.txt links directly to page markdown routes, while /llms-full.txt exposes full machine-readable context - section-level files like /docs/guides/llms.txt give larger docs progressive disclosure without adding UI - custom static llms.txt files still win when present, so public/llms.txt or SvelteKit static/llms.txt can replace the generated index without extra config - /sitemap.xml exposes canonical URLs and lastmod freshness for crawlers and monitors - /sitemap.md exposes the same docs tree as a semantic, sectioned map for agents and contributors - spec.robots.route points agents to the static crawl policy, usually /robots.txt - HTML docs pages include Schema.org TechArticle JSON-LD with canonical URL, freshness, and breadcrumbs - when apiReference is enabled, /api/docs?format=openapi gives agents the OpenAPI schema before they scrape API reference pages - {page}.md gives them clean page markdown - canonical {page} URLs with Signature-Agent give agents the same markdown without appending .md - MCP lets tool-enabled agents search and read docs semantically - /api/docs/agent/feedback/schema and /api/docs/agent/feedback let agents report missing context without enabling the human feedback UI The big win is that the discovery layer comes from the same docs runtime instead of a parallel system you have to keep in sync by hand. Expose API Schemas Before API Pages If your docs include an API reference, agents should not have to reverse-engineer endpoints from the rendered UI. Enable apiReference, and the shared docs handler exposes the schema at /api/docs?format=openapi. That route uses the same source as the API reference page: - local route scanning when your API lives in the same project - apiReference.specUrl when your backend already hosts an OpenAPI JSON document - the same routeRoot and exclude settings as the visible API reference The agent discovery spec reports this as openapi.url, root llms.txt adds an API Schemas section, and generated AGENTS.md / skill.md tell agents to fetch the schema before scraping endpoint docs. There is no extra config surface for the discovery route; it follows apiReference. Know How Canonical Markdown Is Served In Next.js, agents can read markdown without changing the URL they already discovered from the human page, search result, sitemap, or browser history: Both requests are served by the existing shared docs API. withDocs() forwards the request into the same /api/docs handler that already powers search, markdown format routes, llms.txt, AGENTS.md, skill.md, sitemaps, MCP, and the agent discovery spec. It does not generate or require another /api/docs/markdown wrapper route. That detail matters for agent-friendly docs because there is only one page resolver to keep correct: - /docs/installation.md and /api/docs?format=markdown&path=installation are explicit markdown entry points - /docs/installation with Accept: text/markdown returns the same markdown and varies by Accept - /docs/installation with Signature-Agent returns the same markdown and varies by Accept, Signature-Agent - /docs/installation without those headers remains the normal HTML page All successful markdown page responses include a canonical Link response header pointing back to the normal HTML page. Agents still receive the full markdown body, but the header tells citation and deduplication systems that /docs/installation is the canonical URL for the content. If a markdown request misses, the response is still markdown. The 404 points the agent to /.well-known/agent.json, /.well-known/agent, /api/docs/agent/spec, search, the requested API markdown route, and sitemap routes so the agent can recover instead of treating the site as a dead end. Keep The Sitemap In The Build If the site is server-rendered, the shared docs handler serves sitemap routes at runtime by default. The generator is still useful because it writes .farming-labs/sitemap-manifest.json, which gives the runtime stable lastmod values based on each page source file's last git commit date. For static export, the generator is required because there is no server handler to answer /sitemap.xml or /sitemap.md: Use --manifest-only only when your deployment keeps the runtime route active: Then add a CI check when generated files are committed: That gives agents a trustworthy page inventory and lets freshness-aware crawlers avoid re-reading unchanged pages. Keep Structured Data Stable Every docs page also ships a hidden Schema.org JSON-LD script. Agents and search crawlers can use it for the page title, description, canonical URL, breadcrumbs, and dateModified without parsing the visible article. You do not need a config flag for this. What matters is giving the runtime stable inputs: - use frontmatter title and description on important pages - configure a public base URL through sitemap.baseUrl, llmsTxt.baseUrl, robots.baseUrl, or ai.docsUrl - keep .farming-labs/sitemap-manifest.json fresh when the adapter preloads docs content The framework escapes JSON-LD before inserting it into the page. For preloaded Astro, SvelteKit, Nuxt, and TanStack Start builds, dateModified comes from the generated sitemap manifest when that manifest is bundled with _preloadedContent; otherwise the runtime omits dateModified instead of claiming the page changed at request time. Write Verification Like You Expect Automation If you want agents to succeed, write setup pages like someone will actually run them without guessing. The strongest pattern is: 1. do the thing 2. check the exact route or file that proves it worked 3. name the most likely failure mode Example: This is the difference between a page that is merely informative and a page that is actually operational. Use Page Actions As A Human-To-Agent Bridge Agent-friendly docs should also help humans move between the normal page and the machine layer. That is where page actions matter. On this framework, the best pair is usually: - Copy Markdown for a clean page snapshot - Open in LLM for a direct handoff into ChatGPT, Claude, Cursor, or another tool Those features do not replace .md routes or MCP, but they make the same page contract visible to humans too. When the docs team uses the same flows agents use, bad page contracts become obvious much faster. Audit The Site, Not Just The Page After writing a few strong pages, run the doctor command and treat it as an ongoing quality loop. When you want the result to feed CI, automation, or another agent, use JSON output: This docs site dogfoods the same audit. A full local pass should report Score: 100% (Agent-optimized), with every docs page carrying either a sibling agent.md or an embedded block. After deployment, add --url to verify the public routes agents actually call: That hosted pass checks discovery, llms.txt, sitemap routes, AGENTS.md, skill.md, representative .md pages, canonical markdown response headers, robots.txt, JSON-LD structured data, markdown alternate head links, and MCP at /mcp, /.well-known/mcp, https://mcp./mcp, or https://mcp./. If you want the same public check without leaving the browser, use the hosted Agent readiness score page: The score page runs AFDocs-style checks for any public docs site, then adds @farming-labs/docs framework probes when the site exposes /.well-known/agent.json. The public score also adds a strict .md route probe that samples docs page routes and verifies that appending .md returns markdown, so llms.txt markdown mirrors do not hide missing /docs/foo.md routes. The framework probes cover the discovery spec, full-context files, sitemap routes, robots.txt, AGENTS.md, skill.md, same-domain or MCP-subdomain MCP, search, feedback, JSON-LD structured data on sampled pages, canonical Link headers on markdown responses, and the head links that point agents to each page's .md route. Existing leaderboard entries hydrate from the saved report, so a shared score URL can be reviewed without triggering a new calculation unless no saved result exists. Use the web score for demos, public comparisons, and quick regression checks. Use docs doctor --agent --url when you need CI-friendly JSON, local project context, or a command an agent can run while changing the repo. For static sites or projects that already own their public files, generate the crawl policy as part of release prep: The first command writes the resolved default path, usually public/robots.txt. The --append variant adds or updates the managed agent policy block when the project already has its own robots.txt. The agents generate command writes root AGENTS.md instructions plus public aliases for static exports; a hand-written root AGENTS.md remains in control unless you pass --force. What you want to see improve over time: - discovery routes passing - machine surfaces enabled - robots.txt allowing docs, markdown, sitemap, AGENTS.md, skill, MCP, and agent discovery routes - metadata quality rising, which also improves markdown output and JSON-LD - Explicit agent-friendly pages increasing on the pages that matter most - stale generated agent.md files dropping toward zero If a team wants to say their docs are agent-optimized, this kind of audit should be part of the definition, not an afterthought. Compact Only After The Contract Is Good docs agent compact is useful, but it should come after the page is already worth compressing. Compaction is a token optimization step, not a substitute for clear structure. Use it when: - pages are already accurate - the machine layer is too verbose - you want tighter agent.md files for .md, MCP, and API consumers The useful mental model is: - use positional page args when you already know the pages you want to compact - use --changed when you only want the pages touched in the current branch or working tree - use --stale when you want to refresh generated agent.md files whose source content or compact settings drifted - use --stale --include-missing when you also want to materialize missing agent.md files for pages that define agent.tokenBudget or that you explicitly target If a page already has a sibling agent.md, the CLI compacts that file. If it does not, the CLI uses the page's generated machine-readable markdown, then writes a sibling agent.md. That page-level agent.tokenBudget overrides broader compact defaults for that page only, which is useful when one page needs a tighter machine contract than the rest of the site. Do not use compaction to paper over vague docs. Shorter confusion is still confusion. The Practical Authoring Loop For most teams, the healthy flow looks like this: 1. write the human page 2. add description, related, and verification 3. add only if the machine layer needs hints 4. add agent.tokenBudget on pages that need a tighter compact target 5. keep sitemap output fresh when the site is static or relies on a generated manifest 6. run pnpm exec docs doctor --agent 7. compact only the pages you changed or the generated files that became stale That loop is much better than regenerating every page every time. It keeps the machine layer current without turning the repo into churn. Agent Optimization Checklist Before calling a page agent-friendly, ask: - can an agent name the task outcome after the first screen? - does frontmatter include description and related? - are framework, version, package, route, and file-path assumptions explicit? - are commands and code samples copy-pasteable? - does the page say what success looks like? - does it include verification steps with concrete commands, routes, files, or UI states? - does troubleshooting name real symptoms and the first place to inspect? - should this page get an additive block? - does it need a dedicated agent.md, or is the human page still canonical? - does this page need agent.tokenBudget before compaction? - can an agent find it through .md, Signature-Agent, JSON-LD structured data, llms.txt markdown links, OpenAPI schema discovery, sitemaps, robots.txt, AGENTS.md, MCP, and the discovery spec? - do canonical Signature-Agent reads use the existing /api/docs handler instead of a custom wrapper route? - if the adapter preloads content, is the sitemap manifest bundled so JSON-LD freshness stays stable? - if the site is static, does the build generate sitemap.xml, sitemap.md, /.well-known/sitemap.md, robots.txt, and AGENTS.md? - does docs doctor --agent agree with the state of the site? - does the hosted Agent readiness score page agree with the deployed site before you share the result or submit it to the leaderboard? Agent feedback, analytics, logs, and other submitted data can help you improve docs quality, but they should stay untrusted. Use them for review, scoring, and follow-up work; do not feed them back into Ask AI prompts or generated agent instructions without an explicit sanitization step. If the answer is yes across the important task pages, you are not just publishing docs that agents can technically crawl. You are publishing docs they can actually work with. Read Next - Agent Primitive for Agent blocks and sibling agent.md - llms.txt, Sitemaps, and CLI for the discovery layer - MCP Server for tool-enabled retrieval - Agent readiness score for the hosted public benchmark and leaderboard - CLI for docs agent compact - CLI for docs doctor --agent` - Configuration for the full config surface --- ## docs.json URL: https://docs.farming-labs.dev/docs/guides/docs-json How the shared docs.json contract separates docs structure from Docs Cloud services docs.json Use docs.json when you want a repo-level contract for frameworkless docs or for connecting an existing docs app to Docs Cloud services. The important split is: - docs describes how the docs project is structured - cloud describes hosted Docs Cloud services layered on top Project Shape For frameworkless docs: For a framework-owned docs app: mode tells you whether content is authored as plain folders or inside a framework app. runtime tells you which docs runtime is responsible for rendering it, and it is meant to stay an explicit choice in the contract rather than being implied. Cloud Services Hosted features live under cloud: This keeps product packaging separate from docs structure: - frameworkless is an authoring mode - nextjs is a runtime - Docs Cloud is the hosted layer that can add preview, publish, AI, and deploy services Cloud Analytics Use cloud.analytics when you want the shared repo contract to carry analytics settings for Docs Cloud and for frameworkless generated runtimes. Or, when you need the serializable subset: cloud.analytics is intentionally limited to JSON-safe options: - enabled - console - includeInputs Function hooks such as onEvent still belong in docs.config.ts, because docs.json is a repo contract and not an executable config file. For frameworkless projects, this serializable subset can be materialized into the generated hidden runtime even before the repo is fully connected to hosted Docs Cloud services. For framework-owned apps, docs.config.ts remains the canonical place for advanced runtime analytics behavior. Set cloud.enabled to false when the repo is using docs.json only as a local project contract. Set it to true once the repo is actually connected to Docs Cloud services. Current Note This example uses nextjs, but the config shape keeps docs.runtime explicit so the same contract can support other runtimes too. When using pnpm exec docs dev today, the local frameworkless preview is still generated through the Next.js runtime. --- ## Installation URL: https://docs.farming-labs.dev/docs/installation Get up and running in minutes Installation Option A: CLI (Recommended) Starting from scratch? Use --template with --name to bootstrap a new docs project (Next.js, TanStack Start, Nuxt, SvelteKit, or Astro). No prompts — the CLI creates the project folder and runs install. See the --template section below. Adding docs to an existing app? Run the init command inside your Next.js, TanStack Start, SvelteKit, Astro, or Nuxt project: The CLI will first ask: Are you adding docs to an existing project or starting fresh? Choose Existing project to add docs to the current directory. It will then: 1. Detect your framework (Next.js, TanStack Start, SvelteKit, Astro, or Nuxt), or ask you to pick one 2. Ask you to pick a theme (including Create your own theme, which prompts for a theme name and scaffolds themes/.ts and themes/.css) 3. Ask about path aliases and which global CSS file to use (defaults are offered; press Enter to accept) 4. Ask for the docs entry path (default: docs — press Enter to accept) 5. Optionally scaffold i18n by selecting locales, adding custom locale codes, and choosing a default locale TanStack Start currently skips the built-in i18n scaffold so the generated routes stay minimal and working 6. Generate config, layout, CSS, and sample pages 7. Install dependencies and optionally start the dev server Prompts that show a placeholder (e.g. entry path, theme name) use that value as the default — you can press Enter to skip typing. See CLI — Init flow for the full sequence. If you enable i18n, the CLI creates locale folders like docs/en and docs/fr, writes the i18n block to docs.config, and scaffolds the framework-specific files needed for routes like /docs?lang=en. To upgrade all docs packages to the latest later, run from your project root: npx @farming-labs/docs upgrade (framework is auto-detected). See CLI — Upgrade for options. Starting from scratch or want a full example? Use --template with --name to bootstrap a ready-to-run docs app. The CLI creates the project folder, bootstraps it with the chosen framework, and runs install: Replace my-docs with your project name. Then run cd my-docs and start the dev server (npm run dev, pnpm run dev, yarn dev, or bun run dev). Option B: Manual Setup For each framework below, add your theme's CSS to your global stylesheet (e.g. app/global.css, src/app.css, or nuxt.config). The import path must match the theme you use in docs.config — e.g. default, greentree, darksharp. Without it, docs pages will lack the correct styling. ### 1. Install packages > Monorepo note: If you are developing @farming-labs/docs and a TanStack Start app in the same workspace, keep @farming-labs/docs, @farming-labs/theme, and @farming-labs/tanstack-start linked locally (for example workspace:*). This avoids Node 22 / Vercel trying to load raw adapter TypeScript from node_modules. ### 2. Create docs.config.tsx ### 3. Create next.config.ts ### 4. Set up RootProvider in app/layout.tsx Wrap your app with RootProvider from @farming-labs/theme. This enables search, theme switching, and AI chat across all pages. > RootProvider automatically configures the search API endpoint (/api/docs) and handles theme persistence. The suppressHydrationWarning on ` prevents React warnings from the theme class injection. ### 5. Import theme CSS in app/global.css Add your theme's CSS to your global stylesheet so docs styling applies. Use the import that matches the theme in your config (e.g. default below; for greentree use @farming-labs/theme/greentree/css). ### 6. Write your first doc Create an MDX file under app/docs/: Each page uses frontmatter for metadata: That's it — the docs layout (app/docs/layout.tsx) is auto-generated by withDocs(). The framework handles routing, MDX compilation, and metadata from your config. > See CLI for all generated files including the full app/layout.tsx and app/docs/layout.tsx, or check Configuration for all available options. ### 1. Install packages > Monorepo note: If you are developing @farming-labs/docs and a TanStack Start app in the same workspace, keep @farming-labs/docs, @farming-labs/theme, and @farming-labs/tanstack-start linked locally (for example workspace:*). This avoids Node 22 / Vercel trying to load raw adapter TypeScript from node_modules. ### 2. Create docs.config.tsx ### 3. Update vite.config.ts ### 4. Create src/lib/docs.server.ts ### 5. Create src/lib/docs.functions.ts ### 6. Wrap the app with RootProvider ### 7. Render docs routes ### 8. Import the theme CSS in src/styles/app.css ### 9. Add the docs API route That is the full manual setup. The built-in TanstackDocsPage renderer handles MDX page modules for you, so there is no extra docs-page.tsx or doc-modules.ts file to maintain. ### 1. Install packages ### 2. Create src/lib/docs.config.ts ### 3. Create src/lib/docs.server.ts ### 4. Create route files src/routes/docs/+layout.svelte: src/routes/docs/+layout.server.js: src/routes/docs/[...slug]/+page.svelte: src/routes/api/docs/+server.js (for search and AI): > Production note: The docs.server.ts file uses import.meta.glob to bundle your markdown files at build time. This is required for serverless deployments (Vercel, Netlify, etc.) where the filesystem is not available at runtime. ### 5. Import theme CSS in src/app.css Add your theme's CSS to your global stylesheet so docs styling applies. Use the import that matches the theme in your config (e.g. fumadocs below; for greentree use @farming-labs/svelte-theme/greentree/css). ### 6. Write your first doc Create Markdown files under docs/: Each page uses frontmatter for metadata: ### 1. Install packages ### 2. Create src/lib/docs.config.ts ### 3. Create src/lib/docs.server.ts ### 4. Create page routes Import your theme's CSS in each docs page (or in a global CSS file and import that). The example below uses @farming-labs/astro-theme/css (default); for other themes use e.g. @farming-labs/astro-theme/greentree/css. src/pages/docs/index.astro and src/pages/docs/[...slug].astro: ### 5. Create API route src/pages/api/docs.ts: ### 6. Astro config Enable SSR in astro.config.mjs: ### 7. Write your first doc Create Markdown files under docs/: Each page uses frontmatter for metadata: ### 1. Install packages ### 2. Create docs.config.ts ### 3. Configure nuxt.config.ts Add your theme's CSS via the css array so docs styling applies. Use the path that matches the theme in your config (e.g. fumadocs below; for greentree use @farming-labs/nuxt-theme/greentree/css). ### 4. Create server/api/docs.ts ### 5. Create pages/docs/[...slug].vue > Nuxt uses Nitro's server assets to load markdown files at runtime. The serverAssets config in nuxt.config.ts tells Nitro where your docs directory is. ### 6. Write your first doc Create Markdown files under docs/`: Each page uses frontmatter for metadata: --- ## API Reference URL: https://docs.farming-labs.dev/docs/reference Complete reference for all configuration options API Reference Complete reference for every option in docs.config.ts. All types are exported from @farming-labs/docs. defineDocs(config) The entry point for configuring your docs. Returns a typed config object. DocsConfig Top-level configuration object passed to defineDocs(). | Property | Type | Default | Description | | -------------- | ----------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------ | | entry | string | required | URL path prefix for docs (e.g. "docs" → /docs) | | contentDir | string | same as entry | Path to content files. TanStack Start, SvelteKit, Astro, and Nuxt use it; Next.js uses app/{entry}/ | | staticExport | boolean | false | Set true for full static builds; hides search and AI (see Configuration) | | theme | DocsTheme | — | Theme preset from a factory (fumadocs(), darksharp(), pixelBorder(), etc.) | | github | string \| GithubConfig | — | GitHub repo config for "Edit on GitHub" links | | nav | DocsNav | — | Sidebar header title and URL | | themeToggle | boolean \| ThemeToggleConfig | true | Light/dark mode toggle | | breadcrumb | boolean \| BreadcrumbConfig | true | Breadcrumb navigation | | sidebar | boolean \| SidebarConfig | true | Sidebar visibility and customization | | icons | Record | — | Shared icon registry for frontmatter icon fields and built-ins like Prompt | | components | Record | — | Custom MDX component overrides, including built-ins like HoverLink and Prompt | | analytics | boolean \| DocsAnalyticsConfig | false | Product/usage event stream for docs UI, search, AI, feedback, agent reads, and MCP usage | | observability | boolean \| DocsObservabilityConfig | false | Trace event stream for Ask AI and MCP runs, including spans, timing, status, and errors | | onCopyClick | (data: CodeBlockCopyData) => void | — | Callback when the user clicks the copy button on a code block (runs in addition to copy) | | feedback | boolean \| FeedbackConfig | false for UI | Human page feedback UI; agent feedback endpoints are default-on unless opted out | | agent | DocsAgentConfig | — | Defaults for docs agent compact and related machine-docs workflows | | readingTime | boolean \| ReadingTimeConfig | false | Opt-in estimated reading-time label with per-page overrides | | pageActions | PageActionsConfig | — | "Copy Markdown" and "Open in LLM" buttons | | ai | AIConfig | — | RAG-powered AI chat | | search | boolean \| DocsSearchConfig | true | Built-in simple search, Typesense, Algolia, or a custom adapter | | mcp | boolean \| DocsMcpConfig | enabled | Built-in MCP server over stdio, /mcp, and /.well-known/mcp | | apiReference | boolean \| ApiReferenceConfig | false | Generated API reference pages from supported framework route conventions or a hosted OpenAPI JSON | | sitemap | boolean \| DocsSitemapConfig | true | Generated sitemap.xml, sitemap.md, and /.well-known/sitemap.md | | robots | boolean \| DocsRobotsConfig | true | Runtime/generated robots.txt policy for docs and agent-readable routes | | changelog | boolean \| ChangelogConfig | false | Generated changelog feed and entry pages from dated MDX entries | | ordering | "alphabetical" \| "numeric" \| OrderingItem[] | "alphabetical" | Sidebar page ordering strategy | | metadata | DocsMetadata | — | SEO metadata | | og | OGConfig | — | Open Graph image config | components is merged into the default MDX component map, so you can both add your own components and replace built-ins such as Callout, Tabs, HoverLink, or Prompt. For built-in defaults like theme.ui.components.HoverLink and theme.ui.components.Prompt, see Creating themes. For usage examples and a live demo, see Components. analytics and DocsAnalyticsConfig Built-in product and usage event stream. Use it for page views, search activity, Ask AI request counts, feedback, page actions, machine-readable markdown reads, agent feedback routes, and MCP usage. See Analytics for the full guide and event list. | Option | Type | Default | Description | | --------------- | ----------------------------------------- | ---------------------------- | ---------------------------------------------------------------- | | enabled | boolean | true when object is passed | Enable or disable the analytics stream | | console | boolean \| "log" \| "info" \| "debug" | true for analytics: true | Log analytics events to the console | | includeInputs | boolean | false | Include raw queries, AI questions, feedback comments, copied text | | onEvent | (event) => void \| Promise | — | Callback fired for every analytics event | Search queries, AI questions, feedback comments, and copied content are omitted by default. Set includeInputs: true only when your project is allowed to store that text. observability and DocsObservabilityConfig Built-in trace event stream for agent runtime debugging. Use it for Ask AI and MCP spans, timing, status, previews, errors, retries, and final handoffs. See Observability for the full guide and trace event list. | Option | Type | Default | Description | | --------------- | ----------------------------------------- | ---------------------------- | ------------------------------------------------------------------ | | enabled | boolean | true when object is passed | Enable or disable the observability stream | | console | boolean \| "log" \| "info" \| "debug" | true for observability: true | Log trace events to the console | | includeInputs | boolean | false | Include raw input fields on custom observability events | | onEvent | (event) => void \| Promise | — | Callback fired for every observability trace event | analytics.onEvent receives usage events like pageview, apiairequest, and mcptool. observability.onEvent receives trace events like run.start, model.call, and tool.result. Ordering Controls how pages are sorted in the sidebar. Three strategies are available: "alphabetical" (default) Pages are sorted alphabetically by folder name. No extra configuration needed. "numeric" Pages are sorted by the order field in frontmatter. Lower numbers appear first. Pages without order are sorted alphabetically after ordered pages. Then in your page frontmatter: Slug-based (OrderingItem[]) Pass an array of { slug, children? } objects for explicit control over page order, including nested pages. OrderingItem | Property | Type | Description | | ---------- | ---------------- | --------------------------------------------- | | slug | string | Folder name at this level (not the full path) | | children | OrderingItem[] | Ordering for child pages within this folder | Pages not listed in the array appear alphabetically after the listed ones. DocsNav Sidebar header configuration. | Property | Type | Default | Description | | -------- | --------------------- | ------------ | --------------------------------------------------------- | | title | string \| ReactNode | "Docs" | Sidebar header title. React elements supported in Next.js and TanStack Start | | url | string | "/{entry}" | URL the title links to | LlmsTxtConfig Generated llms.txt files for agent-readable docs indexes. The root files are enabled by default; section files are opt-in and only add route handling plus discovery metadata. If a native static file already exists at the same public route, such as public/llms.txt or SvelteKit static/llms.txt, that file takes precedence and the generated output remains the fallback. | Property | Type | Default | Description | | ----------------- | ------------------------ | -------------------------------- | ----------- | | enabled | boolean | true | Enable or disable llms.txt generation | | baseUrl | string | request origin at runtime | Base URL used for absolute links | | siteTitle | string | nav.title or "Documentation" | Heading shown in generated files | | siteDescription | string | undefined | Description shown below the heading | | maxChars | LlmsTxtMaxCharsConfig | { mode: "warn", chars: 50000 } | Budget for compact llms.txt files | | sections | LlmsTxtSectionConfig[] | [] | Optional section-level llms.txt files | Default routes: - /llms.txt - /llms-full.txt - /.well-known/llms.txt - /.well-known/llms-full.txt For sections, the route is derived from the first match value. For example, /docs/api/ creates /docs/api/llms.txt and /docs/api/llms-full.txt. Root /llms.txt lists configured sections first and keeps matching pages inside those section files. LlmsTxtMaxCharsConfig | Property | Type | Default | Description | | -------- | ---------------------------- | ------- | ----------- | | mode | "warn" \| "error" \| "off" | "warn" | Warn, fail the response, or disable budget checks | | chars | number | 50000 | Maximum recommended character count | LlmsTxtSectionConfig | Property | Type | Description | | ------------- | ----------------------- | ----------- | | title | string | Section title shown in root and section files | | description | string | Optional description shown next to the section link | | match | string \| string[] | Public URL matcher, such as /docs/api/ | | maxChars | LlmsTxtMaxCharsConfig | Optional section-specific budget | DocsMcpConfig Built-in MCP server configuration for AI clients, IDE agents, and local MCP tooling. | Property | Type | Default | Description | | ---------- | --------------------- | ----------------- | ----------- | | enabled | boolean | true | Enable the MCP server. MCP is on by default; set false to opt out. | | route | string | "/api/docs/mcp" | Streamable HTTP route used by the MCP endpoint | | name | string | nav.title or @farming-labs/docs | MCP server name reported to clients | | version | string | "0.0.0" | Version string reported to clients | | tools | DocsMcpToolsConfig | all enabled | Fine-grained tool toggles | DocsMcpToolsConfig | Property | Type | Description | | --------------- | --------- | ----------- | | listPages | boolean | Expose the list_pages tool | | getNavigation | boolean | Expose the get_navigation tool | | searchDocs | boolean | Expose the search_docs tool | | readPage | boolean | Expose the read_page tool | Default MCP surface: - Public HTTP route: /mcp - Well-known HTTP route: /.well-known/mcp - Canonical HTTP route: /api/docs/mcp - stdio command: pnpx @farming-labs/docs mcp - Built-in tools: listpages, getnavigation, searchdocs, readpage Framework notes: - Next.js: withDocs() auto-generates the default /api/docs/mcp route and public /mcp plus /.well-known/mcp rewrites - TanStack Start / SvelteKit / Astro / Nuxt: use the generated public forwarder for MCP aliases, or add an equivalent framework hook/middleware that forwards to the built-in MCP handler - Custom routes: set mcp.route in docs.config and update the framework public forwarder so the configured path and the actual endpoint stay aligned See MCP Server for the route snippets and examples. DocsRobotsConfig Runtime and generated robots.txt policy for docs and agent-readable routes. Server-rendered apps serve /robots.txt through the shared docs handler when no static file already owns that path; the CLI writes or appends a static file for static export and committed policies. | Property | Type | Default | Description | | -------------- | --------------------------------- | -------------------------------- | ----------- | | enabled | boolean | true | Enable or disable the robots policy | | path | string | public/robots.txt, or static/robots.txt for SvelteKit | File path used by docs robots generate | | baseUrl | string | sitemap.baseUrl or llmsTxt.baseUrl in the CLI | Public site URL used for the Sitemap: line | | ai | boolean \| "allow" \| "disallow" | "allow" | Explicitly allow or disallow common AI crawler user agents | | aiUserAgents | string \| string[] | built-in common AI crawler list | Extra AI user-agent names to include beside the defaults | | extraRules | DocsRobotsRule[] | [] | Extra robots rule blocks appended after the generated agent policy | Generated policy covers the docs entry, .md routes, llms.txt, sitemap routes, AGENTS.md, skill.md, MCP aliases, agent discovery routes, and common AI crawler user agents. Existing files are preserved by default; use docs robots generate --append to add or update the managed block, or --force to replace the file. The agent discovery JSON advertises this surface as robots.enabled, robots.route, and robots.defaultRoute, so agents can find the policy without guessing. DocsRobotsRule | Property | Type | Description | | ------------ | -------------------- | ----------- | | userAgent | string \| string[] | User-agent name or names for this rule block | | allow | string \| string[] | Routes to allow | | disallow | string \| string[] | Routes to disallow | | crawlDelay | number | Optional crawl-delay value emitted as-is | See CLI for docs robots generate and Configuration for the high-level setup guide. DocsSearchConfig Search is enabled by default. If you do not configure anything, the docs API uses the built-in simple adapter with section-based chunking. Built-in providers: - simple — zero-config docs search with section chunking - typesense — external Typesense collection with optional hybrid mode - algolia — external Algolia index - mcp — Streamable HTTP MCP endpoint that exposes a search_docs tool - custom — your own adapter implementation SimpleDocsSearchConfig | Property | Type | Default | Description | | ---------- | ----------------------- | ----------- | ----------- | | provider | "simple" | implied | Use the built-in search adapter | | enabled | boolean | true | Toggle search on or off | | maxResults | number | 10 | Maximum results to return | | chunking | DocsSearchChunkingConfig | { strategy: "section" } | Controls whether indexing happens per page or per section | TypesenseDocsSearchConfig | Property | Type | Default | Description | | ------------- | -------------------------- | ----------- | ----------- | | provider | "typesense" | — | Use Typesense as the search backend | | baseUrl | string | — | Typesense API base URL | | collection | string | — | Collection name used for docs documents | | apiKey | string | — | Search API key | | adminApiKey | string | — | Optional admin key used for on-demand indexing | | maxResults | number | 10 | Maximum results to return | | syncOnSearch | boolean | true when adminApiKey is present | Re-index docs on the first search request | | queryBy | string[] | built-in fields | Custom Typesense query_by fields | | mode | "keyword" \| "hybrid" | "keyword" | Keyword search only, or keyword + embeddings | | embeddings | DocsSearchEmbeddingsConfig | — | Semantic embeddings config for hybrid search | | chunking | DocsSearchChunkingConfig | { strategy: "section" } | Chunking strategy used before indexing/search | AlgoliaDocsSearchConfig | Property | Type | Default | Description | | -------------- | ----------------------- | ----------- | ----------- | | provider | "algolia" | — | Use Algolia as the search backend | | appId | string | — | Algolia application id | | indexName | string | — | Algolia index name | | searchApiKey | string | — | Search API key | | adminApiKey | string | — | Optional admin key used for on-demand indexing | | maxResults | number | 10 | Maximum results to return | | syncOnSearch | boolean | true when adminApiKey is present | Re-index docs on the first search request | | chunking | DocsSearchChunkingConfig | { strategy: "section" } | Chunking strategy used before indexing/search | McpDocsSearchConfig | Property | Type | Default | Description | | ---------- | ----------------------- | -------------- | ----------- | | provider | "mcp" | — | Use an MCP endpoint as the search backend | | endpoint | string | — | Streamable HTTP MCP endpoint | | headers | Record | — | Extra headers sent to initialize and tool calls | | toolName | string | "search_docs" | MCP tool name used for search | | protocolVersion | string | "2025-11-25" | MCP protocol version header | | maxResults | number | 10 | Maximum results to request | Relative endpoints are resolved against the current docs API request URL. That makes local setups like the Next example easy to test without hardcoding a host. CustomDocsSearchConfig | Property | Type | Description | | ---------- | ------------------------------ | ----------- | | provider | "custom" | Use your own adapter | | adapter | DocsSearchAdapter \| DocsSearchAdapterFactory | Search runtime implementation | | maxResults | number | Maximum results to return | | chunking | DocsSearchChunkingConfig | Chunking strategy used before your adapter runs | DocsSearchAdapter Custom adapters receive normalized source pages and chunked search documents so you can focus on retrieval and ranking instead of rebuilding the docs scan pipeline. DocsSearchChunkingConfig | Property | Type | Default | Description | | ---------- | ----------------------- | ----------- | ----------- | | strategy | "page" \| "section" | "section" | Chunk by whole page or split by headings | DocsSearchEmbeddingsConfig | Property | Type | Default | Description | | ---------- | ----------- | ------------------------- | ----------- | | provider | "ollama" | — | Embeddings provider for hybrid Typesense mode | | model | string | — | Embedding model id | | baseUrl | string | "http://127.0.0.1:11434" | Ollama base URL | Notes: - search: false disables search entirely - Search requires the docs API route; static export hides the UI because there is no server route - On Next.js, generated routes from withDocs() pass docsConfig directly into createDocsAPI - MCP-backed search works with relative endpoints like /mcp or /.well-known/mcp and absolute remote endpoints like https://docs.example.com/mcp - If MCP-backed search points at the same relative MCP route, the built-in search_docs tool falls back to simple search internally to avoid recursive loops - On custom/manual Next routes, import createDocsAPI from @farming-labs/next/api and pass the whole config: createDocsAPI(docsConfig) @farming-labs/theme/api remains supported as a compatibility import path today, but prefer @farming-labs/next/api in Next.js apps. The theme-level path will be deprecated. ApiReferenceConfig Generates API reference pages from framework route conventions or a hosted OpenAPI JSON document. Use route scanning when the API lives in the same project. Use specUrl when the backend is deployed elsewhere and already exposes an openapi.json. apiReference is supported in Next.js, TanStack Start, SvelteKit, Astro, and Nuxt. apiReference in docs.config controls scanning, theming, routeRoot, and exclude. In Next.js, withDocs() also generates the route automatically. In TanStack Start, SvelteKit, Astro, and Nuxt, you must additionally add the framework route handler for /{path}. specUrl is the easiest option when your docs app does not contain the backend route files. Point it at a hosted openapi.json and keep the same themed API reference UI. When apiReference is enabled, the shared docs API also serves GET /api/docs?format=openapi. The agent discovery spec advertises that OpenAPI schema URL, and generated llms.txt / AGENTS.md / skill.md output points agents there before they scrape API reference pages. | Property | Type | Default | Description | | ----------- | ---------- | ----------------- | ----------- | | enabled | boolean | true inside the object | Enable generated API reference pages | | path | string | "api-reference" | URL path where the generated API reference lives | | specUrl | string | — | Absolute URL to a hosted OpenAPI JSON document. When set, local route scanning is skipped | | routeRoot | string | "api" | Filesystem route root to scan. Bare values like "api" resolve inside app/ or src/app/; full values like "app/internal-api" and "src/app/v2/api" are supported | | exclude | string[] | [] | Route entries to omit from the generated reference. Accepts URL-style paths like "/api/hello" or route-root-relative values like "hello" / "hello/route.ts" | When specUrl is set, routeRoot and exclude are ignored and the API reference is rendered from the hosted spec instead. This does not remove the framework route requirement on non-Next adapters. TanStack Start, SvelteKit, Astro, and Nuxt still need their /{path} handler files so the docs app has a route that serves the generated API reference page. When output: "export" is used in Next.js, the generated API reference route is skipped automatically because it requires a server route handler. Scanned route conventions: - Next.js: app/api//route.ts and src/app/api//route.ts - TanStack Start: src/routes/api.*.ts and nested route files inside the configured route root - SvelteKit: src/routes/api//+server.ts or +server.js - Astro: src/pages/api//*.ts or .js - Nuxt: server/api//*.ts or .js Minimal route handlers for non-Next frameworks: Create a second src/routes/api-reference.$.ts file with the same handler and createFileRoute("/api-reference/$"). Create src/routes/api-reference/[...slug]/+server.ts with the same GET export. Create src/pages/api-reference/[...slug].ts with the same GET export. Create server/routes/api-reference/[...slug].ts with the same default export. ChangelogConfig Generates changelog listing and entry pages from dated MDX folders inside the docs content tree. The turn-key generated changelog pages are currently wired in Next.js when you use withDocs(). | Property | Type | Default | Description | | -------- | ---- | ------- | ----------- | | enabled | boolean | true inside the object | Enable generated changelog pages | | path | string | "changelog" | URL path where the changelog listing lives inside the docs layout | | contentDir | string | "changelog" | Source directory under the docs content root. Example: app/docs/changelog/2026-03-04/page.mdx | | title | string | "Changelog" | Listing page title | | description | string | — | Listing page description shown in the header and metadata | | search | boolean | true | Show the built-in changelog search field | | actionsComponent | ReactNode \| Component | — | Custom action content rendered in the changelog rail | With entry: "docs" and the config above, the public pages render at: - /docs/changelogs - /docs/changelogs/2026-03-04 When you use withDocs(), the route files are generated automatically. There is no separate changelog.generated.tsx file to maintain. ChangelogFrontmatter Optional frontmatter fields for each changelog entry: | Property | Type | Description | | -------- | ---- | ----------- | | title | string | Entry title shown in the feed and entry page | | description | string | Short summary shown in the feed and metadata | | image | string | Cover image path or URL | | authors | string \| string[] | Author name(s) shown in entry meta | | version | string | Compact version badge like v0.1.0 | | tags | string[] | Badge list shown in the feed and entry page | | pinned | boolean | Keep important entries pinned to the top of the listing | | draft | boolean | Hide the entry from generated routes and search indexes | GithubConfig GitHub repository configuration. Enables "Edit on GitHub" links in page footers. | Property | Type | Default | Description | | ----------- | -------- | ------------ | -------------------------------------------------------- | | url | string | required | Repository URL (e.g. "https://github.com/my-org/repo") | | branch | string | "main" | Branch name | | directory | string | — | Content subdirectory for monorepos | The "Edit on GitHub" link only appears when both url and directory are provided. onCopyClick and CodeBlockCopyData Optional callback on DocsConfig. Fired when the user clicks the copy button on a code block — in addition to copying to the clipboard. Use it for analytics, logging, or custom behavior. See Code block copy callback for setup (Next.js / TanStack Start vs SvelteKit / Astro / Nuxt). CodeBlockCopyData Argument passed to onCopyClick(data: CodeBlockCopyData). | Property | Type | Description | | ---------- | -------- | --------------------------------------------------------------------------- | | title | string \| undefined | Code block title (e.g. from fenced code meta), if present | | content | string | Raw code content (what was copied) | | url | string | Current page URL at the time of copy | | language | string \| undefined | Language/syntax hint (e.g. "tsx", "bash"), if present | feedback, FeedbackConfig, AgentFeedbackConfig, and feedback payloads Optional docs page feedback UI. When enabled, a built-in "Good / Bad" prompt is rendered at the end of the page and emits a feedback payload when the user clicks one of the buttons. FeedbackConfig | Property | Type | Default | Description | | --------------- | ---------------------------------------- | ----------------- | -------------------------------------------- | | enabled | boolean | true | Show or hide the feedback UI | | question | string | "How is this guide?" | Prompt shown above the buttons | | positiveLabel | string | "Good" | Label for the positive button | | negativeLabel | string | "Bad" | Label for the negative button | | onFeedback | (data: DocsFeedbackData) => void | — | Callback fired when the user clicks a button | | agent | boolean \| AgentFeedbackConfig | true | Agent feedback routes served through /api/docs | DocsFeedbackData | Property | Type | Description | | ------------- | -------------------------------------- | ----------------------------------------------------- | | value | "positive" \| "negative" | Which feedback button the user clicked | | title | string \| undefined | Current page title, when available | | description | string \| undefined | Current page description, when available | | url | string | Full current page URL | | pathname | string | Current pathname without origin | | path | string | Alias of pathname | | entry | string | Docs entry root, e.g. "docs" | | slug | string | Page slug relative to the docs entry | | locale | string \| undefined | Active locale when docs i18n is enabled | This docs site persists feedback through a local Next.js route at website/app/api/feedback/route.ts, backed by the DocsFeedback Prisma model in website/prisma/schema.prisma. See Page feedback for framework-specific notes. The built-in UI does not require a separate client bridge file. AgentFeedbackConfig Optional machine-facing feedback routes for agents, scripts, or evaluation flows. | Property | Type | Default | Description | | ------------- | --------------------------------------------------- | ---------------------------------- | ----------- | | enabled | boolean | true | Enable or disable the agent feedback endpoints | | route | string | "/api/docs/agent/feedback" | Public HTTP route for feedback submission | | schemaRoute | string | ${route}/schema | Public HTTP route for the machine-readable schema | | schema | Record | built-in payload schema | Schema used to validate the payload object | | onFeedback | (data: DocsAgentFeedbackData) => Promise \| void | — | Async callback fired after a valid submission | Notes: - agent feedback routes are enabled by default; set feedback: false or feedback: { agent: false } to opt out - feedback.agent does not enable the human footer UI by itself - the request body always uses { context?, payload } - without feedback.agent.onFeedback, valid submissions return { ok: true, handled: false } - submitted feedback is not added to Ask AI prompts or docs search context by the framework - in Next.js, withDocs() adds the public route rewrites automatically - Astro, SvelteKit, TanStack Start, and Nuxt expose the same feedback contract through the shared /api/docs?feedback=agent query route - the shared /api/docs handler is still the source of truth, so ?feedback=agent also works - agents should discover site identity, locale config, capability flags, search, active markdown routes, Accept: text/markdown, Signature-Agent support, JSON-LD structured data, llms.txt, OpenAPI schema routes, sitemap routes, robots.txt, AGENTS.md, skill.md, skills, MCP, and feedback routes with GET /.well-known/agent.json; GET /.well-known/agent is the public fallback and GET /api/docs/agent/spec is the canonical framework route DocsAgentFeedbackContext | Property | Type | Description | | -------- | ----------------------- | ----------- | | page | string \| undefined | Docs page path such as "/docs/installation" | | url | string \| undefined | Full docs URL when available | | slug | string \| undefined | Docs slug relative to the entry root | | locale | string \| undefined | Active locale when docs i18n is enabled | | source | string \| undefined | Arbitrary source label such as "md-route", "mcp", or "api" | DocsAgentFeedbackData | Property | Type | Description | | --------- | ------------------------------------- | ----------- | | context | DocsAgentFeedbackContext \| undefined | Optional docs/page transport context | | payload | Record | Agent feedback payload validated by feedback.agent.schema | Quick checks: ThemeToggleConfig Light/dark mode toggle in the sidebar. | Property | Type | Default | Description | | --------- | ------------------------------------- | -------------- | ---------------------------------- | | enabled | boolean | true | Show or hide the toggle | | default | "light" \| "dark" \| "system" | "system" | Forced theme when enabled: false | | mode | "light-dark" \| "light-dark-system" | "light-dark" | Toggle behavior | BreadcrumbConfig Breadcrumb navigation above page content. | Property | Type | Default | Description | | ----------- | ----------- | ------- | ------------------------------------------ | | enabled | boolean | true | Show or hide breadcrumbs | | component | Component | — | Custom breadcrumb component (Next.js only) | SidebarConfig Sidebar visibility and customization. See Sidebar for the full guide including custom sidebar components. | Property | Type | Default | Description | | ------------- | ------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------ | | enabled | boolean | true | Show or hide the sidebar | | component | (props: SidebarComponentProps) => ReactNode | — | Custom sidebar render function. Receives { tree, collapsible, flat }. Supported in Next.js and TanStack Start — other frameworks use slots. | | footer | ReactNode | — | Content rendered below nav items | | banner | ReactNode | — | Content rendered above nav items | | collapsible | boolean | true | Whether sidebar is collapsible on desktop | | flat | boolean | false | Render all items flat (Mintlify-style, no collapsible groups) | | folderIndexBehavior | "link" \| "toggle" \| "hidden" | adapter default | Whether folder parents with their own page navigate, only expand/collapse, or render as label-only groups | | folderIndexBehaviorOverrides | Record | — | Selective per-folder overrides keyed by the folder landing-page URL | Folder landing pages can still override both of those with frontmatter: SidebarComponentProps Props passed to a custom sidebar component: | Property | Type | Description | | ------------- | ------------- | ------------------------------------------------ | | tree | SidebarTree | Full page tree with all parent-child relationships | | collapsible | boolean | Whether folders are collapsible | | flat | boolean | Whether folders are rendered flat | SidebarTree | Property | Type | Description | | ---------- | ---------------- | ------------------------ | | name | string | Root name (e.g. "Docs") | | children | SidebarNode[] | Top-level sidebar items | SidebarNode A union of SidebarPageNode | SidebarFolderNode. SidebarPageNode | Property | Type | Description | | -------- | --------- | ------------------- | | type | "page" | Node type | | name | string | Display name | | url | string | Page URL | | icon | unknown | Icon from registry | SidebarFolderNode | Property | Type | Description | | ------------- | ------------------ | ----------------------------- | | type | "folder" | Node type | | name | string | Display name | | icon | unknown | Icon from registry | | index | SidebarPageNode | Folder's landing page | | children | SidebarNode[] | Child pages and sub-folders | | collapsible | boolean | Whether this folder collapses | | defaultOpen | boolean | Whether it starts open | DocsTheme Theme configuration returned by theme factories. | Property | Type | Description | | -------- | ---------- | --------------------------------------------------- | | name | string | Theme identifier (e.g. "fumadocs", "darksharp") | | ui | UIConfig | Visual configuration — colors, typography, layout | Created via theme factories: UIConfig Fine-grained visual configuration passed to theme factories. | Property | Type | Description | | ------------ | -------------------- | ----------------------------------------------- | | colors | ColorsConfig | Theme color tokens | | typography | TypographyConfig | Font families, heading styles | | radius | string | Global border-radius (e.g. "0px", "0.5rem") | | layout | LayoutConfig | Content width, sidebar width, TOC settings | | codeBlock | CodeBlockConfig | Syntax highlighting and line numbers | | sidebar | SidebarStyleConfig | Sidebar visual style | | card | CardConfig | Card component styling | ColorsConfig All color tokens. Accepts any CSS color value (hex, rgb, oklch, hsl). | Token | Description | | --------------------- | ----------------------------------- | | primary | Primary brand color | | primaryForeground | Text color on primary backgrounds | | background | Page background | | foreground | Default text color | | muted | Muted background (e.g. code blocks) | | mutedForeground | Text on muted backgrounds | | border | Default border color | | card | Card background | | cardForeground | Card text color | | accent | Accent color | | accentForeground | Text on accent backgrounds | | secondary | Secondary color | | secondaryForeground | Text on secondary backgrounds | | popover | Popover/dropdown background | | popoverForeground | Popover text color | | ring | Focus ring color | LayoutConfig | Property | Type | Default | Description | | --------------- | --------- | ------- | ------------------------------- | | contentWidth | number | — | Max width of content area in px | | sidebarWidth | number | — | Sidebar width in px | | tocWidth | number | — | Table of contents width in px | | toc.enabled | boolean | true | Show table of contents | | toc.depth | number | 3 | Max heading depth for TOC | | header.height | number | — | Header height in px | | header.sticky | boolean | true | Sticky header | CodeBlockConfig | Property | Type | Default | Description | | ----------------- | --------- | ------- | -------------------------- | | showLineNumbers | boolean | false | Show line numbers | | showCopyButton | boolean | true | Show copy button | | theme | string | — | Shiki theme for light mode | | darkTheme | string | — | Shiki theme for dark mode | SidebarStyleConfig | Property | Type | Default | Description | | ------------- | --------------------------------------- | ----------- | ------------------------- | | style | "default" \| "bordered" \| "floating" | "default" | Sidebar visual style | | background | string | — | Background color override | | borderColor | string | — | Border color override | TypographyConfig FontStyle Used for each heading level and body text. | Property | Type | Description | | --------------- | ------------------ | ------------------------------------------------------------------ | | size | string | CSS font-size (e.g. "2.25rem", "clamp(1.8rem, 3vw, 2.5rem)") | | weight | string \| number | CSS font-weight (e.g. 700, "bold") | | lineHeight | string | CSS line-height (e.g. "1.2", "28px") | | letterSpacing | string | CSS letter-spacing (e.g. "-0.02em") | AIConfig RAG-powered AI chat. See Ask AI for usage guide. | Property | Type | Default | Description | | -------------------- | ---------------------------------------------------- | ----------------------------- | ------------------------------------------------------- | | enabled | boolean | false | Enable AI chat | | mode | "search" \| "floating" | "search" | UI mode: integrated in search or floating widget | | position | "bottom-right" \| "bottom-left" \| "bottom-center" | "bottom-right" | Floating button position (only when mode: "floating") | | floatingStyle | "panel" \| "modal" \| "popover" \| "full-modal" | "panel" | Floating chat visual style | | triggerComponent | Component | — | Custom floating button component | | model | string | "gpt-4o-mini" | LLM model name (OpenAI-compatible) | | systemPrompt | string | auto-generated | Custom system prompt | | baseUrl | string | "https://api.openai.com/v1" | OpenAI-compatible API base URL | | apiKey | string | process.env.OPENAIAPIKEY | API key for the LLM provider | | maxResults | number | 5 | Number of doc pages to include as RAG context | | useMcp | boolean \| DocsAskAIMcpConfig | false | Route Ask AI retrieval through MCP search_docs without changing the search API | | suggestedQuestions | string[] | — | Pre-filled questions shown when chat is empty | | aiLabel | string | "AI" | Display name for the AI assistant | | feedback | boolean \| DocsAskAIFeedbackConfig | true | Copy, like, and dislike action row for completed answers | | onActions | (data: DocsAskAIActionData) => void \| Promise | — | Callback for copy, like, and dislike response actions | | packageName | string | inferred from docs context | Optional package-name override for import examples | | docsUrl | string | — | Base URL the AI uses for links | | loader | string | "shimmer-dots" | Loading indicator variant ("shimmer-dots", "circular", "dots", "typing", "wave", "bars", "pulse", "pulse-dot", "terminal", "text-blink", "text-shimmer", "loading-dots") | | loadingComponent | (props: { name: string }) => ReactNode | — | Custom loading component (overrides loader, Next.js only) | Floating styles | Style | Description | | -------------- | ----------------------------------------------------------------- | | "panel" | Tall panel sliding up from the button. No backdrop. | | "modal" | Centered modal with backdrop overlay, like Cmd+K search. | | "popover" | Compact popover near the button. | | "full-modal" | Full-screen immersive overlay with pills for suggested questions. | PageActionsConfig Action buttons shown on each docs page. See Page Actions for usage guide. | Property | Type | Default | Description | | -------------- | -------------------------------- | --------------- | --------------------------- | | position | "above-title" \| "below-title" | "below-title" | Where to render the buttons | | alignment | "left" \| "right" | "left" | Horizontal alignment of the action row | | copyMarkdown | boolean \| CopyMarkdownConfig | false | "Copy Markdown" button | | openDocs | boolean \| OpenDocsConfig | false | "Open in LLM" dropdown | CopyMarkdownConfig | Property | Type | Default | Description | | --------- | --------- | ------- | --------------- | | enabled | boolean | false | Show the button | OpenDocsConfig | Property | Type | Default | Description | | ----------- | -------------------- | ----------------- | ------------------ | | enabled | boolean | false | Show the dropdown | | providers | OpenDocsProvider[] | ChatGPT, Claude | LLM/tool providers | OpenDocsProvider | Property | Type | Description | | ------------- | ----------- | --------------------------------------------------------------------------------- | | name | string | Display name (e.g. "ChatGPT", "Claude") | | icon | ReactNode | Icon rendered next to the name | | urlTemplate | string | URL template. {url} is replaced with the page URL, {mdxUrl} with the raw MDX source URL, and {githubUrl} with the GitHub edit URL when github is configured. Use {url}.md when you want the public machine-readable page route. | | promptUrlTemplate | string | Optional URL template used by the built-in Prompt component. {prompt} is replaced with the prompt text. Known providers like ChatGPT, Claude, Cursor, Gemini, and Copilot fall back to built-in prompt templates by name when this is omitted. | ReadingTimeConfig Estimated reading-time label shown on docs pages. | Property | Type | Default | Description | | ---------------- | --------- | ------- | ----------- | | enabled | boolean | true | Show the computed read-time label when readingTime is configured | | wordsPerMinute | number | 220 | Word-count rate used for the estimate | Notes: - readingTime is disabled by default until you configure it in DocsConfig - the estimate strips code blocks, inline code, links, images, HTML, and URLs before counting - page frontmatter can disable or override the value with readingTime: false, readingTime: true, or readingTime: 8, even when the global config is off - placement follows the page title slot: it sits directly below the page actions row when page actions are shown above or below the title DocsMetadata SEO metadata configuration. Page title and description also feed the generated Schema.org JSON-LD structured data for each docs page. | Property | Type | Default | Description | | --------------- | ------------------------------------ | ------- | ---------------------------------------------------------- | | titleTemplate | string | — | Page title template. %s is replaced with the page title. | | description | string | — | Default meta description | | twitterCard | "summary" \| "summarylargeimage" | — | Twitter card type | Structured Data Every docs page emits Schema.org JSON-LD automatically. The generated object uses @type: "TechArticle" and includes the page title, description, canonical URL, dateModified, and a BreadcrumbList. No separate config flag is required. Absolute URLs are resolved from the first available public base URL in sitemap.baseUrl, llmsTxt.baseUrl, robots.baseUrl, or ai.docsUrl; otherwise the page route is emitted as a relative URL. The agent discovery spec advertises this as capabilities.structuredData and structuredData.schema: "https://schema.org/TechArticle". OGConfig Open Graph image configuration. When endpoint is set, each docs page gets a dynamic OG image: the framework passes the page’s title and description (from frontmatter) to the endpoint, and the layout injects the resulting image URL into og:image and twitter:image meta tags. For a full guide (how dynamic OG works, what context is passed, and how the docs website implements it), see OG Images. | Property | Type | Default | Description | | -------------- | ----------------------- | ------- | ----------------------------------- | | enabled | boolean | false | Enable OG image generation | | type | "static" \| "dynamic" | — | Static images or dynamic generation | | endpoint | string | — | API endpoint for dynamic OG images | | defaultImage | string | — | Fallback OG image URL | Example (Next.js): In docs.config.ts, set og.endpoint to your OG route path. Then implement app/api/og/route.tsx (or route.ts) that accepts title and description query params and returns a 1200×630 image (e.g. using ImageResponse from next/og). Testing your OG image 1. Start the dev server for the app that serves the OG route (e.g. cd website && pnpm dev or cd examples/next && pnpm dev). 2. Open the OG URL in the browser, e.g. http://localhost:3000/api/og?title=Test&description=My+description (use your app’s port if different). 3. After changing the OG route code, refresh the page. If the image doesn’t update, do a hard refresh (e.g. Cmd+Shift+R on macOS, Ctrl+Shift+R on Windows) or open the URL in a private/incognito window. You can also add a cache-busting query param (e.g. &=1, &=2) to force a new request. Page Frontmatter Each docs page (.mdx / .md) supports these frontmatter fields: | Property | Type | Description | | ------------- | ----------- | --------------------------------------------------------------------------------- | | title | string | Required. Page title, shown in sidebar and page heading | | description | string | Page description, used for meta tags and search | | related | string[] | Related doc URLs rendered as a comma-separated metadata line in .md routes and MCP page output | | agent | PageAgentFrontmatter | Per-page machine-docs metadata such as compaction token budgets | | sidebar | PageSidebarFrontmatter | Per-folder sidebar metadata such as folderIndexBehavior overrides | | readingTime | boolean \| number | Disable the estimate for one page (false) or force an exact minute count | | icon | string | Icon key from the icons registry. Shows in sidebar. | | order | number | Sort order in the sidebar (only when ordering: "numeric"). Lower numbers first. | | tags | string[] | Tags for categorization | | ogImage | string | Shorthand for a single OG image path. Ignored if openGraph is set. | | openGraph | PageOpenGraph | Full Open Graph object (e.g. images: [{ url, width?, height? }]). When set, replaces generated OG from the config (e.g. dynamic endpoint). | | twitter | PageTwitter | Full Twitter card object (e.g. card, images). When set, replaces generated twitter metadata. | | hidden | boolean | If true, hide the page from the sidebar (page remains reachable by URL). Optional; behavior may vary by adapter. | PageAgentFrontmatter Page-level metadata for machine-readable docs workflows. | Property | Type | Description | | ------------- | -------- | ----------- | | tokenBudget | number | Approximate output token target for docs agent compact on this page | Notes: - agent.tokenBudget overrides global agent.compact.maxOutputTokens defaults and CLI --max-output-tokens for that one page - if the page already has a sibling agent.md, docs agent compact compacts that file - if the page does not have agent.md, the command compacts the generated machine-readable page output and writes a new sibling agent.md - docs agent compact --changed only processes docs pages changed in the current git working tree, including staged, unstaged, and untracked docs changes - docs agent compact --stale --include-missing will also create missing agent.md files for pages that define agent.tokenBudget - inherited minOutputTokens is clamped down to tokenBudget when needed so the compression API never receives minoutputtokens > maxoutputtokens PageSidebarFrontmatter Page-level sidebar metadata used while building the docs navigation tree. | Property | Type | Description | | --------------------- | -------------------- | ----------- | | folderIndexBehavior | "link" \| "toggle" \| "hidden" | Override how this folder page behaves in the default sidebar when it has child pages | Notes: - this is only read from folder landing pages such as page.mdx / index.md - it overrides global sidebar.folderIndexBehavior - it also overrides sidebar.folderIndexBehaviorOverrides for that same folder - folderIndexBehavior: "hidden" keeps the folder route available but omits the folder index link from the sidebar tree; in flat mode this becomes a label-only group with child links only, and direct visits to the hidden parent route redirect to the first visible child Static OG example — use openGraph and twitter in frontmatter to serve a static image instead of the dynamic OG endpoint: Minimal example (shorthand ogImage or generated OG): Data types Frontmatter is YAML between the --- fences. Supported value types: | Type | Example in frontmatter | Use in framework fields | | --------- | ------------------------------ | -------------------------- | | string | title: "Hello" or title: Hello | title, description, icon, ogImage | | number | order: 1 | order | | boolean | hidden: true | Optional (e.g. hide from sidebar) | | string[]| tags: [a, b] or tags: ["a", "b"] | tags | You can use other YAML types (e.g. nested objects) for custom keys; only the fields in the table above are used by the framework for sidebar, SEO, JSON-LD structured data, OG, search, and machine-readable markdown. How frontmatter is passed - Next.js — The MDX pipeline uses remark-frontmatter and remark-mdx-frontmatter with name: "metadata". The parsed frontmatter is exposed as the metadata export from the page. The layout and generateMetadata receive it when resolving the page (e.g. via getPage(params)), and it is used for createPageMetadata, sidebar tree (title, icon, order), description maps, and JSON-LD structured data. - TanStack Start / SvelteKit / Astro / Nuxt — The docs loader (e.g. import.meta.glob or content layer) reads each .md/.mdx file, parses frontmatter, and builds the nav tree and page data. Frontmatter is passed to the layout as page data (e.g. page.data or the props your content component receives). The same fields (title, description, icon, order, tags, ogImage) are used for sidebar, meta tags, JSON-LD structured data, and search. So in all frameworks, the same frontmatter fields drive titles, descriptions, icons, ordering, JSON-LD, and OG; only the mechanism (MDX metadata vs. content loader page.data) differs. The related field is intentionally machine-readable first: add URL strings, and .md routes plus MCP page output render them next to Description as Related: /docs/a, /docs/b without adding a visible related-links block to the human page UI. A sibling agent.md remains a full override, so add a Related: line there manually if the override should include it. createTheme(options) Create a custom theme from scratch. See Creating Themes. extendTheme(base, overrides) Extend an existing theme with overrides. TanStack Start Server API createDocsServer(config) Creates the TanStack Start docs server adapter. Parameters: | Property | Type | Description | | --------- | ------------ | ----------- | | config | DocsConfig | The config object from defineDocs() | | rootDir | string | Optional project root used to resolve contentDir. Defaults to process.cwd() | Returns: | Property | Type | Description | | -------- | ---------------------------------------------------------- | ----------- | | load | ({ pathname, locale? }) => Promise | Loads docs page data and the sidebar tree for a pathname | | GET | ({ request }) => Response | Search / markdown / llms.txt / OpenAPI / sitemap / AGENTS.md / skill.md endpoint handler | | POST | ({ request }) => Promise | AI chat endpoint handler | TanStack Start Components | Component | Import | Description | | ------------------ | ------------------------------------ | ----------- | | TanstackDocsPage | @farming-labs/tanstack-start/react | Built-in page renderer that resolves the compiled MDX module and wraps it in the shared docs layout | | RootProvider | @farming-labs/theme/tanstack | Root provider for theme state, search, and AI UI | TanStack Start also uses the docsMdx() Vite plugin from @farming-labs/tanstack-start/vite so .mdx files compile without extra local module-loader glue. SvelteKit Server API createDocsServer(config) Creates all server-side functions for a SvelteKit docs site. Parameters: | Property | Type | Description | | ------------------- | ------------------------ | --------------------------------------------------------------------------- | | config | DocsConfig | The config object from defineDocs() | | _preloadedContent | Record | Pre-loaded markdown files from import.meta.glob. Required for serverless. | Returns: | Property | Type | Description | | -------- | ------------------------------ | ------------------------------------------------- | | load | (event) => Promise | Layout load function (use in +layout.server.js) | | GET | (event) => Response | Search endpoint handler | | POST | (event) => Promise | AI chat endpoint handler | SvelteKit Components Imported from @farming-labs/svelte-theme: | Component | Props | Description | | ------------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | | DocsLayout | tree, config, title?, titleUrl? | Main layout with sidebar, nav, search, AI | | DocsContent | data, config | Page content with TOC, breadcrumb, footer nav | | DocsPage | entry, tocEnabled, breadcrumbEnabled, previousPage, nextPage, editOnGithub, lastModified | Low-level page wrapper (used by DocsContent) | Astro Server API createDocsServer(config) Same as SvelteKit — returns { load, GET, POST }. Parameters: | Property | Type | Description | | ------------------- | ------------------------ | --------------------------------------------------------------------------- | | config | DocsConfig | The config object from defineDocs() | | _preloadedContent | Record | Pre-loaded markdown files from import.meta.glob. Required for serverless. | Returns: | Property | Type | Description | | -------- | ----------------------------------------- | ----------------------------------------------------- | | load | (pathname: string) => Promise | Takes a URL pathname string, returns page data | | GET | ({ request }) => Response | Search endpoint handler for GET /api/docs?query=... | | POST | ({ request }) => Promise | AI chat endpoint handler with SSE streaming | Astro Components | Component | Import | Description | | -------------- | ------------------------------------------------------------- | -------------------------------------------------- | | DocsLayout | @farming-labs/astro-theme/src/components/DocsLayout.astro | Main layout with sidebar, search, and theme toggle | | DocsContent | @farming-labs/astro-theme/src/components/DocsContent.astro | Page content with metadata | | DocsPage | @farming-labs/astro-theme/src/components/DocsPage.astro | Page structure (TOC, breadcrumb, nav) | | ThemeToggle | @farming-labs/astro-theme/src/components/ThemeToggle.astro | Light/dark toggle | | SearchDialog | @farming-labs/astro-theme/src/components/SearchDialog.astro | Search + AI dialog | CSS Imports Same pattern as SvelteKit: | Import | Description | | -------------------------------------------- | ------------------------ | | @farming-labs/astro-theme/css | Default (fumadocs) theme | | @farming-labs/astro-theme/pixel-border/css | Pixel border theme | | @farming-labs/astro-theme/darksharp/css | Darksharp theme | Theme Factories | Factory | Import | | --------------- | ---------------------------------------- | | fumadocs() | @farming-labs/astro-theme | | pixelBorder() | @farming-labs/astro-theme/pixel-border | | darksharp() | @farming-labs/astro-theme/darksharp | Nuxt Server API defineDocsHandler(config, useStorage) Creates a single Nitro event handler that serves docs loading, search, and AI chat for Nuxt. Parameters: | Property | Type | Description | | ------------ | ------------ | -------------------------------------------------------- | | config | DocsConfig | The config object from defineDocs() | | useStorage | Function | Nitro's useStorage utility for accessing server assets | Returns: A Nitro event handler that responds to GET (page loading + search) and POST (AI chat) requests. Nuxt uses Nitro's serverAssets to load markdown files. Configure nitro.serverAssets in nuxt.config.ts to point to your docs directory. Nuxt Components Imported from @farming-labs/nuxt-theme: | Component | Props | Description | | ------------- | ------------------------------------- | --------------------------------------------- | | DocsLayout | tree, config, triggerComponent? | Main layout with sidebar, nav, search, AI | | DocsContent | data, config | Page content with TOC, breadcrumb, footer nav | CSS Imports | Import | Description | | --------------------------------------- | ------------------------ | | @farming-labs/nuxt-theme/fumadocs/css | Default (fumadocs) theme | Theme Factories | Factory | Import | | ------------ | ----------------------------------- | | fumadocs() | @farming-labs/nuxt-theme/fumadocs | Next.js API RootProvider Client-side provider that wraps your entire app. Enables search, theme switching (light/dark), and AI chat. Must be placed in your root app/layout.tsx. | Prop | Type | Default | Description | | ---------- | --------------------- | ----------------------------------- | --------------------------------------------------------------- | | search | SearchProviderProps | { options: { api: "/api/docs" } } | Search configuration. API defaults to the unified docs handler. | | theme | ThemeProviderProps | — | Theme provider options (passed to next-themes) | | children | ReactNode | required | Your app content | suppressHydrationWarning on prevents React warnings caused by the theme class being injected before hydration. withDocs(nextConfig) Wraps your Next.js config with docs framework support. Handles MDX, routing, search index generation, and auto-generates mdx-components.tsx and app/docs/layout.tsx if missing. createDocsLayout(config) Creates the docs layout component from your config. Auto-generated by withDocs()` — you typically don't need to create this manually. --- ## Themes URL: https://docs.farming-labs.dev/docs/themes Built-in themes and how to create your own Themes @farming-labs/docs ships with eleven built-in theme entrypoints. Each is a preset factory function that you pass to defineDocs(). hardline stays as the original hard-edge preset, concrete is the louder brutalist variant, command-grid is the mono-first paper-grid preset inspired by the better-cmdk landing page, and ledger brings a Stripe Docs-inspired product documentation shell. Want to build a custom theme or publish one as a package? See Creating your own theme for the full guide. Built-in Themes | Theme | Import | Description | | ----------------------------------------- | ---------------------------------- | --------------------------------------- | | Default | @farming-labs/theme | Neutral colors, standard radius | | Colorful | @farming-labs/theme/colorful | Warm amber accent, Inter typography | | Darksharp | @farming-labs/theme/darksharp | All-black, sharp corners | | Pixel Border | @farming-labs/theme/pixel-border | Inspired by better-auth.com | | Shiny | @farming-labs/theme/shiny | Clerk-inspired, purple accents | | DarkBold | @farming-labs/theme/darkbold | Pure monochrome, Geist typography | | GreenTree | @farming-labs/theme/greentree | Mintlify-inspired, emerald green accent | | Concrete | @farming-labs/theme/concrete | Brutalist poster-style hard-edge theme | | Command Grid | @farming-labs/theme/command-grid | Mono-first paper-grid preset inspired by better-cmdk | | Ledger | @farming-labs/theme/ledger | Stripe Docs-inspired product docs shell | | Hardline | @farming-labs/theme/hardline | Original hard-edge theme with strong borders | Using a Theme Don't forget the matching CSS import: Overriding Theme Defaults Every theme accepts an overrides object: Creating a Custom Theme For a comprehensive guide including publishing your theme as a package, see Creating Your Own Theme. Use createTheme() to build your own: Extending a Theme Use extendTheme() to modify an existing preset: --- ## Colorful URL: https://docs.farming-labs.dev/docs/themes/colorful Warm amber accent with Inter typography and a clean layout Colorful Theme A warm, vibrant theme with an amber primary accent, Inter typography, and directional TOC styling. Usage Defaults | Property | Value | | ------------- | --------------------------- | | Primary | hsl(40, 96%, 40%) (Amber) | | Background | #ffffff | | Border | #e5e7eb | | Border radius | Standard | | Content width | 768px | | Sidebar width | 260px | | TOC style | Directional | Customizing --- ## Command Grid URL: https://docs.farming-labs.dev/docs/themes/command-grid Mono-first paper-grid theme inspired by the better-cmdk landing page Command Grid Theme A mono-first preset with a paper-grid light mode, lead-toned dark mode, sharper borders, and calmer surfaces inspired by the better-cmdk landing page. Usage Defaults | Property | Value | | ------------- | ----------------------------------- | | Primary | #141414 | | Background | #f8f6ed (light), #121212 (dark) | | Border radius | 0px | | Content width | 900px | | Sidebar width | 304px | Style Notes - Mono-first typography across the sidebar, TOC, and command surfaces - Paper-grid background in light mode with a quieter dark-mode grid - Sharper tables, callouts, search UI, and Ask AI surfaces - Better-cmdk-inspired light/dark palette without the heavier concrete shadow treatment Customizing --- ## Concrete URL: https://docs.farming-labs.dev/docs/themes/concrete Brutalist poster-style theme with offset shadows and square corners Concrete Theme A louder brutalist-style preset with poster typography, offset shadows, square corners, and bold contrast. Usage Defaults | Property | Value | | ------------- | ----------------------------------- | | Primary | #ff5b31 | | Background | #f6ead9 (light), #12100f (dark) | | Border radius | 0px | | Content width | 896px | | Sidebar width | 316px | Customizing --- ## Creating Your Own Theme URL: https://docs.farming-labs.dev/docs/themes/creating-themes Step-by-step guide to building, publishing, and sharing custom themes Creating Your Own Theme Everything starts with createTheme(). No CSS required — just a config object. Quick Start That's it. Users can override any of your defaults: Prompt: Match an Existing Website Use this prompt when you already have a product or marketing website and want the docs to feel like the same brand. Create or update an @farming-labs/docs theme so the documentation feels visually consistent with my existing website. Inputs: - Website URL: [WEBSITE_URL] - Docs route or app folder: [DOCS_LOCATION] - Framework: [Next.js, TanStack Start, SvelteKit, Astro, or Nuxt] First inspect the existing website and extract its design system: - brand colors, accent colors, background colors, border colors, and focus states - typography, font families, heading scale, body size, line height, and code font - border radius, shadows, dividers, spacing rhythm, and card/button treatment - navigation style, sidebar/header feel, search affordance, tabs, callouts, code blocks, and tables - light/dark mode behavior if the site supports it Then implement the docs theme: - choose the closest built-in @farming-labs/docs theme as the base, or create a custom theme with createTheme() / extendTheme() - update docs.config.ts[x] to use the theme - add or update the theme CSS import in the app's global stylesheet - add CSS overrides only where the config tokens are not enough - keep the docs interface readable, scannable, and documentation-first; do not copy marketing hero sections into docs pages - preserve agent-ready docs features such as .md routes, llms.txt, AGENTS.md, skill.md, sitemap, robots.txt, MCP, JSON-LD, and markdown alternate links Verification: - run the docs dev server - check desktop and mobile layouts - confirm text does not overflow buttons, cards, sidebars, or code blocks - confirm search, sidebar navigation, code blocks, callouts, tabs, and page actions still work - list the files changed and explain which website design tokens were mapped into the docs theme When asking an agent to use this prompt, replace the placeholders before sending it. If the website uses a very expressive landing page, ask for a docs interpretation of the brand instead of a literal copy. Documentation should feel connected to the product while staying calmer, denser, and easier to scan. Config Options Reference Here's everything you can configure in createTheme(). name Unique identifier for your theme. Used for debugging and CSS scoping. ui.colors Color tokens mapped to --color-fd-* CSS variables at runtime. Any valid CSS color value works (hex, rgb, hsl, oklch). You only need to set the ones you want to change — the rest are inherited from the base preset. ui.typography Font families, heading sizes, weights, line heights, and letter spacing. Each heading/text style accepts these properties: | Property | Type | Example | | --------------- | ------------------ | --------------------- | | size | string | "2.25rem", "36px" | | weight | number \| string | 700, "bold" | | lineHeight | string | "1.2", "28px" | | letterSpacing | string | "-0.02em" | Tip: Use CSS variables for fonts so users can load them with next/font: ui.radius Global border radius. Maps to CSS --radius. ui.layout Content area dimensions and structural options. ui.codeBlock Code block rendering options. ui.sidebar Sidebar visual style. | Style | Description | | ------------ | ------------------------------------------------------- | | "default" | Standard fumadocs sidebar | | "bordered" | Visible bordered sections (inspired by better-auth.com) | | "floating" | Floating card sidebar with subtle shadow | ui.card Card styling. ui.components Default props for built-in MDX components. ui.components changes the default props for built-in MDX components. For example, HoverLink can keep the built-in popover UI while changing its CTA label, indicator, or alignment across your docs. If you want to replace the component completely, use components.HoverLink in docs.config.tsx instead. Full Example Here's a complete theme with every option: Extending an Existing Theme Don't want to start from scratch? Use extendTheme() to build on top of a built-in preset: Works with any built-in theme: extendTheme() returns a DocsTheme directly (not a factory). Use createTheme() when building a reusable preset. Cherry-Picking from Built-in Themes Each built-in theme exports its defaults object: Publishing as an npm Package Users install and use: Advanced: CSS Customization The config options above cover most use cases. But if you need pixel-level control over specific components — like how the sidebar active state looks, code block borders, table styling, or callout appearance — you can write a CSS file alongside your theme. How it Works Every theme can ship a CSS file that sits on top of the config. The CSS file: 1. Imports a color preset (which includes fumadocs-ui core styles) 2. Overrides CSS variables for light (:root) and dark (.dark) modes 3. Targets specific component selectors to customize their appearance Starting Point Two presets are available: | Preset | Import | Best for | | ------- | ------------------------------------- | ------------------ | | Neutral | @farming-labs/theme/presets/neutral | Light-first themes | | Black | @farming-labs/theme/presets/black | Dark-first themes | Component CSS Selectors Here are all the selectors you can target to style individual components: Sidebar Code Blocks Inline Code Tables Callouts Tabs Prev/Next Navigation Cards Search Dialog Breadcrumb Page Action Buttons Scrollbar Selection & Misc Tips - Inspect in DevTools — the fastest way to find the right selector is to right-click an element, inspect it, and check its classes/attributes. - Use !important sparingly — fumadocs uses Tailwind utilities, so some overrides need !important. Check the built-in theme CSS files for examples. - Scope with .dark — use .dark for dark mode styles. Fumadocs uses class-based dark mode. - Reference CSS variables — always use var(--color-fd-*) in your styles so they respond to user color overrides. - Look at built-in themes — the pixel-border and darksharp CSS files are excellent references showing every component selector in use. --- ## DarkBold URL: https://docs.farming-labs.dev/docs/themes/darkbold Pure monochrome design with Geist typography and clean minimalism DarkBold Theme A pure monochrome design inspired by Vercel — featuring Geist typography, tight letter-spacing, and clean minimalism. Usage Defaults | Property | Value | | ------------- | ----------------- | | Primary | #000 (Black) | | Background | #fff | | Border | #eaeaea | | Border radius | Standard | | Content width | 768px | | Sidebar width | 260px | | Font | Geist, Geist Mono | Typography DarkBold uses the Geist font family with tighter letter-spacing for headings: - H1: 2.5rem, weight 600, letter-spacing -0.06em - H2: 2rem, weight 600, letter-spacing -0.04em - H3: 1.5rem, weight 600, letter-spacing -0.02em Customizing --- ## Darksharp URL: https://docs.farming-labs.dev/docs/themes/darksharp All-black theme with sharp corners Darksharp Theme An all-black theme with rounded-none styling — clean, minimal, and sharp. Usage Defaults | Property | Value | | ------------- | ---------------------- | | Primary | Near-white | | Background | #000000 (pure black) | | Border radius | 0px (no rounding) | | Content width | 860px | | Sidebar width | 286px | Customizing --- ## Default URL: https://docs.farming-labs.dev/docs/themes/default Clean, neutral palette with standard border radius Default Theme The default Fumadocs theme with a neutral color palette and standard border radii. Usage Defaults | Property | Value | | ------------- | ------------------ | | Primary | #6366f1 (Indigo) | | Background | #ffffff | | Border radius | Standard | | Content width | 768px | | Sidebar width | 280px | Customizing --- ## GreenTree URL: https://docs.farming-labs.dev/docs/themes/greentree Mintlify-inspired theme with emerald green accent and Inter typography GreenTree Theme Inspired by Mintlify — a clean, modern design with an emerald green accent, Inter typography, and a compact sidebar. Usage Defaults | Property | Value | | ------------- | ------------------- | | Primary | #0D9373 (Emerald) | | Background | #fff | | Muted | #505351 | | Border | #DFE1E0 | | Border radius | Standard | | Content width | 768px | | Sidebar width | 240px | | Header height | 56px | Customizing --- ## Hardline URL: https://docs.farming-labs.dev/docs/themes/hardline Hard-edge theme with square corners and strong borders Hardline Theme hardline() is the original hard-edge preset: square corners, bold borders, and sharp high-contrast surfaces. If you want something more brutalist and poster-like, use Concrete instead. Usage Defaults | Property | Value | | ------------- | ----------------------------------- | | Primary | #ffd335 | | Background | #f2efe8 (light), #0f0f10 (dark) | | Border radius | 0px | | Content width | 860px | | Sidebar width | 300px | --- ## Ledger URL: https://docs.farming-labs.dev/docs/themes/ledger Stripe Docs-inspired theme with tabbed navigation, blue-violet actions, and deep code panels Ledger Theme A product-docs preset inspired by Stripe Docs, with neutral branding for @farming-labs/docs projects. Usage Defaults | Property | Value | | ------------- | ---------------------------------- | | Primary | #5f6cf6 | | Background | #f6f8fb (light), #0f1424 (dark) | | Border | #dbe3ef | | Border radius | 0.5rem | | Content width | 820px | | Sidebar width | 292px | | Header height | 64px | Style Notes - Compact tab-like navigation and pill search controls - Light product-docs surfaces with subtle borders and restrained shadows - Blue-violet active states for sidebar, TOC, tabs, and actions - Deep navy code blocks designed to stand out from the light docs shell Customizing --- ## Pixel Border URL: https://docs.farming-labs.dev/docs/themes/pixel-border Inspired by better-auth.com — refined dark UI with visible borders Pixel Border Theme Inspired by better-auth.com — a clean dark UI with visible borders, pixel-perfect spacing, and a refined sidebar. The active sidebar indicator and text use your primary color. Usage Defaults | Property | Value | | ------------- | ----------------------------------------- | | Primary | Near-white (oklch(0.985 0.001 106.423)) | | Background | hsl(0 0% 2%) | | Border | hsl(0 0% 15%) | | Border radius | 0px | | Content width | 860px | | Sidebar width | 286px | Sidebar Features - Full-width border separators between top-level items - Collapsible folder groups with expand/collapse arrows - Active state: primary-colored text + left vertical indicator bar - Nested children are indented with smaller text - Folder parents use slightly muted text when collapsed Customizing The primary color flows to: - Active sidebar item text - Active sidebar indicator bar (::before) - Active TOC item - Links and other primary-colored elements --- ## Shiny URL: https://docs.farming-labs.dev/docs/themes/shiny Clerk-inspired theme with purple accents and a polished light/dark design Shiny Theme Inspired by Clerk — a clean, polished UI with purple accents and a professional light/dark design. Usage Defaults | Property | Value | | ------------- | ------------------------------ | | Primary | hsl(256, 100%, 64%) (Purple) | | Background | #f7f7f8 | | Border | #e5e5ea | | Border radius | Standard | | Content width | 768px | | Sidebar width | 280px | | Header height | 64px | Customizing --- ## Token Efficiency URL: https://docs.farming-labs.dev/docs/token-efficiency How @farming-labs/docs keeps your project lean for AI-assisted development — without sacrificing flexibility Token Efficiency AI tools like Cursor, Copilot, and Claude read your project files to help you write code. Every file they read costs tokens — the units that determine response speed, API cost, and how much of your project fits in a single conversation. @farming-labs/docs is built to keep the framework footprint small so more of your token budget goes to your actual documentation content — while still giving you the full flexibility of a modern docs framework. One Config, Full Control The core idea: everything about your docs site — theme, colors, typography, sidebar, AI chat, metadata — lives in a single docs.config.ts file. An AI agent reads this one file and immediately understands your entire docs setup — what theme you're using, where content lives, how AI chat is configured, how pages are titled. There's a provider wrapper (RootProvider) and a docs layout file, but they're minimal one-liners that the CLI generates for you. The real configuration surface is this single file. This matters because every additional config file, layout wrapper, or routing file is something an AI has to read, reason about, and avoid accidentally breaking. Fewer framework files means more room in the context window for what actually matters: your documentation content. The CLI Does the Heavy Lifting You don't have to set any of this up manually. The CLI scaffolds everything in seconds — for both new and existing projects. Starting from scratch Use --template to bootstrap a complete project with your framework and theme of choice: This creates a new my-docs/ folder with a fully working docs site — config, routes, CSS, sample pages, dependencies installed, dev server running. Pick from next, tanstack-start, nuxt, sveltekit, or astro. Want a specific theme? Add --theme: That's it — a beautiful themed docs site in one command. Adding to an existing project Already have a Next.js, TanStack Start, SvelteKit, Astro, or Nuxt project? Just run init inside it: The CLI auto-detects your framework from package.json, asks you to pick a theme, generates the config and minimal routing files, installs dependencies, and starts the dev server. Your existing code is untouched — it only adds what's needed for docs. See the CLI reference for all flags and options. Why This Matters for AI More context for your content AI models have limited context windows. Framework boilerplate eats into that budget. With @farming-labs/docs, the framework surface is roughly ~15 lines of config instead of hundreds of lines spread across many files. That leaves more room for your actual documentation pages when an AI is answering questions or making changes. Fewer files to reason about When an AI agent needs to modify your docs setup — change a theme, enable AI chat, adjust the sidebar — it reads your docs.config.ts and makes the change in one place. No hunting through layout files, provider trees, slug handlers, and CSS imports to figure out how everything connects. Less risk of breaking things A declarative config file is hard to break. An AI can add ai: { enabled: true } or change theme: darksharp() without worrying about import paths, component hierarchies, or routing logic. The framework handles all of that internally. Compact Page-Level Agent Docs The framework already gives agents clean page-level markdown through .md routes, canonical URLs with Signature-Agent in Next.js, hidden ... blocks, and sibling agent.md files. When you want that machine-readable layer to be even tighter, use docs agent compact. The command resolves the same page contract the runtime already uses: - existing agent.md - embedded Agent blocks - normal page markdown Then it sends that resolved document to the Token Company compression API and writes a sibling agent.md file for the page. The visible docs UI still renders page.mdx or page.md. The generated agent.md becomes the page-level source for .md routes, Signature-Agent markdown reads in Next.js, GET /api/docs?format=markdown&path=, and MCP read_page(). The CLI also loads .env and .env.local, so TOKENCOMPANYAPI_KEY can stay out of checked-in config. Use --dry-run when you want to preview the compression call without writing files. For the full command surface, see the CLI page. For the agent.compact config shape, see Configuration. Built-in AI Features Beyond being token-efficient to work with, @farming-labs/docs includes features designed for AI consumption: llms.txt Your docs are automatically served in LLM-optimized format — no extra routes needed: See the llms.txt docs for details. Sitemaps Enable sitemap when agents and crawlers need a compact inventory before reading pages: JSON-LD structured data is injected automatically on docs pages. See the Sitemaps docs for static export and manifest details. Robots.txt Generate robots.txt when crawlers and AI agents need an explicit public access policy for docs and machine-readable routes: Use docs robots generate, or docs robots generate --append when the project already owns a custom robots.txt. The agent doctor checks the resolved file path and warns when the policy blocks agent-readable routes or common AI crawlers. AI Chat with RAG Built-in AI chat with retrieval-augmented generation. A simple setup is just two lines: Need multiple models from different providers? Use the providers map and a model object with a selectable dropdown: Users get a model selector dropdown in the chat interface. The backend automatically routes each request to the correct provider with the right credentials. No separate API routes, no vector databases, no embedding pipelines — search indexing, context retrieval, and streaming responses are all handled internally. Works with any OpenAI-compatible provider: OpenAI, Groq, Together, Fireworks, OpenRouter, Ollama, or any vLLM deployment. See the AI Chat docs for the full configuration reference. Summary @farming-labs/docs keeps the framework lean — one config file, minimal routing, CLI-generated scaffolding — so you and your AI tools spend less time on plumbing and more time on content. And it does this without sacrificing flexibility: themes, multi-provider AI chat, search, custom components, and multi-framework support are all still there, just configured from one place. ---