# 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. Every docs site gets PR review automation by default. During next dev or next build, withDocs() can create a repo-root GitHub Actions wrapper that calls the hosted review workflow, checks changed docs files, scores agent-readiness, and reports GitHub annotations. Keep it as warn-only, or set review.ci.mode: "block" when docs quality should gate merges. See Docs Review CI and the docs review CLI. Looking Ahead: Cloud Docs 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 → Already have access? Start with the Docs Cloud integration guide to create an API key and sync docs.json. Use Deploy for hosted preview docs and Analytics for Cloud-managed event storage. 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. - Docs Review CI — Generated GitHub Actions wrapper plus hosted docs review workflow scoring for changed MDX, broken internal links, frontmatter, code fence metadata, and agent context. 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 - Docs Cloud — Understand the Cloud project contract - Deploy — Connect a docs project to hosted previews, API keys, and publish defaults - Analytics — Understand Cloud-managed analytics storage - 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, validate code blocks, 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 - review — review docs changes locally or in GitHub Actions - codeblocks validate — plan and validate runnable MDX code fences - 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, sitemap.md, and docs/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 6. optionally add Docs Cloud infrastructure support and print the auth plus .env.local steps 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, sitemap.md, and docs/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/docs/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 Docs Cloud compression 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 root agent.compact config, so apiKeyEnv: "DOCSCLOUDAPI_KEY" works without exporting the key manually. Keep the key in agent.compact.apiKey or agent.compact.apiKeyEnv; --api-key is available for one-off runs. 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. Use docs doctor --only agent or docs doctor --only site when scripts need an explicit single-suite selector. These forms are equivalent to --agent and --site; --only accepts only agent or site. Use docs doctor --fail-on warn when any warning should fail CI. Use docs doctor --fail-on fail when warnings should stay visible but only hard failures should exit non-zero. --strict remains a shortcut for --fail-on warn. 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, sitemap.md, and docs/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: Use docs doctor --fix or docs doctor --agent --fix when you want the agent doctor to repair issues it finds instead of only reporting them. Fix mode re-runs agent doc compaction for stale generated agent.md files and fills in token-budget missing outputs. Each repair is reported with a status of applied or skipped. --fix is agent-only, so docs doctor --site --fix exits with an error instead of modifying reader-facing docs. Add --dry-run alongside --agent --fix when you want to preview the agent compaction fix without writing generated files. The report includes the command that would run and leaves the stale or missing counts visible, but no agent.md files are written. Use --strict or --fail-on warn in CI when warnings should fail the job. Both forms set a non-zero exit code when any doctor check reports warn or fail. Use --fail-on fail when warnings should print without failing the job, while hard failures still exit non-zero. 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, /docs/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_docs - list_pages - get_navigation - search_docs - read_page - getcodeexamples - getconfigschema 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. The CLI checks the current directory and walks up parent directories looking for a lockfile (pnpm-lock.yaml, yarn.lock, bun.lock, bun.lockb, package-lock.json) or the packageManager field in package.json. The log message shows the relative path to the detected file. If no package manager signal is found anywhere in the directory tree, it prompts you to choose. 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 | | --cloud | Add Docs Cloud infrastructure support during init | | --no-cloud | Skip the Docs Cloud infrastructure prompt | 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 Docs Cloud infrastructure During init, the CLI asks whether you want Docs Cloud infrastructure support. If you answer yes, or pass --cloud, it adds the cloud block to your docs config, writes the safe docs.json contract, and prints the authentication steps. The CLI does not write secret values. Sign in to https://docs-app.farming-labs.dev, create a project API key from your Docs Cloud project settings, then add it to .env.local or your shell: Keep local env files out of git and set the same env names in production or CI. When ready, run: The check command verifies the generated docs.json, deploy key scope, Cloud analytics project id, Ask AI provider wiring, and any required browser CORS preflights before the deploy command requests a hosted preview. 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, beta channel, or an exact 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. In a monorepo, run from the app or package directory that contains your framework package.json (e.g. apps/web). The CLI will walk up to find the workspace lockfile automatically. The CLI also detects your package manager from lockfiles (pnpm-lock.yaml, yarn.lock, bun.lock, bun.lockb, or package-lock.json) or package.json#packageManager in the current directory or parent directories. If no package manager signal is found, it asks which package manager you want to use instead of defaulting to npm. Monorepo note: Run upgrade and downgrade from the app or package directory that contains the framework package.json (e.g. apps/web), not the workspace root. The CLI walks up parent directories automatically to find a lockfile (pnpm-lock.yaml, yarn.lock, bun.lock, bun.lockb, package-lock.json) or the packageManager field in package.json. The log message shows the relative path to the detected file, e.g. Detected pnpm from ../pnpm-lock.yaml. | Framework | Packages upgraded | | --------- | ------------------------------ | | 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, --beta to install beta versions, or --version to install a specific release: Use --dry-run when you want to preview the exact package manager command before changing package.json or the lockfile. 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. Downgrade Use downgrade when you need to move the docs packages back to a lower published version. The CLI auto-detects your framework and package manager the same way upgrade does. Without --version, it installs the published version immediately below your current installed @farming-labs/docs version: Pass --version to choose an exact lower version: Use --dry-run to preview the downgrade install command without changing dependencies. Without --version, dry-run still resolves the previous published version first. If the requested version is newer than the current installed version, the command stops and points you to upgrade --version instead. 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_docs - list_pages - get_navigation - search_docs - read_page - getcodeexamples - getconfigschema list_docs returns page summaries grouped by docs section, optionally narrowed by section. getcodeexamples extracts fenced code block metadata for agents, and getconfigschema returns structured docs.config.ts option metadata. For the HTTP endpoint version, see MCP Server. Print hosted MCP client config with mcp setup Use mcp setup when you want to print the MCP client configuration for a Docs Cloud hosted deployment instead of running a local stdio server. By default the command prints human-readable setup instructions. Use --json to print only the JSON block: Use --api-base-url to override the Docs Cloud API base URL (defaults to https://api.farming-labs.dev): Flags for mcp setup: | Flag | Description | | ---- | ----------- | | --deployment | (Required) Docs Cloud deployment ID. | | --api-base-url | Override the Docs Cloud API base URL. Defaults to https://api.farming-labs.dev. | | --json | Print only the MCP client JSON without human-readable instructions. | Code Blocks Use docs codeblocks validate to plan and validate fenced code blocks from your docs content. The command reads codeBlocks.validate from docs.config.ts[x]. --plan shows what would run without executing snippets. Code fence metadata gives the planner enough context: ts title="app/api/chat/route.ts" framework="nextjs" packageManager="pnpm" env="OPENAIAPIKEY" runnable const apiKey = process.env.OPENAIAPIKEY; Runner providers can be local, vercel-sandbox, e2b, daytona, or the reserved cloud runner. Remote runners skip with a clear reason when their token or optional SDK is missing instead of running the snippet locally. See Configuration for the config fields. Docs Review Use docs review to check changed docs files before a PR is merged. It scores the changed docs, checks default rules such as broken links and frontmatter, and prints GitHub annotations when run with --ci. withDocs() creates .github/workflows/docs-review.yml automatically during next dev or next build when review CI is enabled and the workflow does not already exist. The generated file is a small wrapper around the hosted reusable workflow at farming-labs/docs/.github/workflows/docs-review-reusable.yml@main, so existing projects get fixes to the review job without copying a large workflow into every repo. You can also create the wrapper explicitly: The workflow is repository-root aware. If your docs app lives in website/, the wrapper still lives at .github/workflows/docs-review.yml and passes working-directory: "website" to the hosted workflow. Set review.ci.name when the GitHub check should use a name other than docs-review. Supported Frameworks | Framework | Detection | init | upgrade | downgrade | | --------- | --------- | ------ | --------- | ----------- | | Next.js | next in dependencies | Supported | Supported | Supported | | TanStack Start | @tanstack/react-start in dependencies | Supported | Supported | Supported | | SvelteKit | @sveltejs/kit in dependencies | Supported | Supported | Supported | | Astro | astro in dependencies | Supported | Supported | Supported | | Nuxt | nuxt in dependencies | Supported | 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/theme/darksharp/css"; > - Pixel Border: import { pixelBorder } from "@farming-labs/svelte-theme" + @import "@farming-labs/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/theme/default/css for default; use @farming-labs/theme/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/theme/darksharp/css > - Pixel Border: import { pixelBorder } from "@farming-labs/astro-theme" + @farming-labs/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/theme/darksharp/css > - Pixel Border: import { pixelBorder } from "@farming-labs/nuxt-theme" + @farming-labs/theme/pixel-border/css Project Structure After running init, your project will look like this: --- ## Docs Cloud URL: https://docs.farming-labs.dev/docs/cloud Overview of Docs Cloud projects, deploys, analytics, and the docs.json contract Docs Cloud Docs Cloud is the hosted layer around @farming-labs/docs. Your repository stays the source of truth, your docs app still owns the runtime, and Cloud adds managed deploys, analytics, publish workflows, and agent operations around it. The Cloud contract has three parts: - docs.config.ts stays the executable config for the docs runtime. - docs.json is the serializable project contract that Cloud can read. - Docs Cloud owns hosted services such as deploys, analytics storage, and publish workflows. Docs Cloud is rolling out gradually. If your workspace does not have access yet, join the Cloud waitlist. Once access is enabled, create the API key from your Docs Cloud project settings. Cloud Sections | Page | Use it for | | ---- | ---------- | | Deploy | API keys, docs deploy, preview deploys, publish mode, and deploy troubleshooting | | Analytics | Cloud analytics storage, project identity, privacy defaults, and optional customer callbacks | | docs.json | The repo-level contract that Cloud reads | | CLI | The complete command reference | Minimal Config Add a cloud block when the project should participate in Cloud-aware CLI flows: cloud.apiKey.env names the environment variable that stores the project API key. The raw key stays outside source control. Sync The Contract Run cloud sync to write the serializable Cloud contract: docs.json is safe to commit because it contains configuration and environment variable names, not secret values. Check Before Deploy Run a read-only check when you want to validate Cloud wiring without creating a preview: Use --analytics, --ask-ai, or --deploy when you only want to validate one part of the Cloud setup. The check reports stale docs.json, missing project/API key env vars, hosted API reachability, and CORS issues before you spend time on a deploy. Next Step Use the deploy page for the full preview flow: See Deploy for API key setup, command options, preview behavior, publish mode, and troubleshooting. Related Docs - Deploy covers hosted preview deployments. - Analytics covers Cloud-managed analytics storage. - docs.json explains the repo-level contract that Cloud reads. - Configuration lists the cloud config fields. - CLI covers the full command surface. - Cloud landing page explains the managed product direction and waitlist. --- ## Analytics URL: https://docs.farming-labs.dev/docs/cloud/analytics How connected Docs Cloud projects store runtime analytics events Analytics Docs Cloud analytics stores usage events for a Docs Cloud project. The docs runtime emits the events; a Docs Cloud project ID tells Cloud which project should own those events; the dashboard reads the stored event stream. Teams do not need to configure internal Cloud transport or add an onEvent callback for Cloud storage. onEvent is only for sending an additional copy of the same events to a system the team owns. Project ID Requirement Cloud analytics requires a Docs Cloud project ID. Without one, Cloud cannot attach events to a workspace or dashboard. There are two ways to get the ID: | Source | How the ID is created | When to use it | | ------ | --------------------- | -------------- | | Imported repository | Import the repository in Docs Cloud. Cloud creates or reuses the project record and shows its project ID in the Analytics dashboard. | Normal Docs Cloud projects and managed previews | | Custom analytics project | Create a custom analytics project from the Docs Cloud Analytics dashboard. Cloud returns an analytics project ID for that source. | A docs site that is not tied to a GitHub import yet | Managed Docs Cloud deploys provide the project ID to the runtime for you. For a manually hosted or custom analytics source, copy the project ID from the Docs Cloud Analytics dashboard and set it in the runtime environment: Keep using the dashboard-provided ID. Do not invent one locally; the ID must match an imported repository project or a custom analytics project in Docs Cloud. The project ID is enough to turn on the built-in Cloud delivery path. To opt out from a manual runtime environment, set the analytics flag to false: Only set NEXTPUBLICDOCSCLOUDANALYTICS_ENDPOINT when the docs app should send events to a custom or self-hosted Docs Cloud API. The hosted Docs Cloud endpoint is the default. If your Cloud workspace issues a dedicated analytics key, set NEXTPUBLICDOCSCLOUDANALYTICSKEY for browser delivery or DOCSCLOUDANALYTICSKEY for server-only delivery. The project ID is still required; the key only authenticates the event write. Runtime Contract The runtime analytics stream is produced by @farming-labs/docs. Events cover product usage such as page views, search, Ask AI, feedback, page actions, markdown reads, agent reads, and MCP usage. For a connected Cloud project: 1. Docs Cloud creates or provides the project ID 2. the runtime receives that project ID 3. the runtime enables the built-in Cloud delivery path 4. Docs Cloud validates the project ID and analytics state 5. Docs Cloud stores accepted events 6. the dashboard reads stored events for counts, filters, and recent activity The public contract is the Docs Cloud project ID, the runtime analytics configuration, and the cloud.analytics setting. The internal transport is managed by Docs Cloud. Configuration For frameworkless projects, cloud.analytics can be stored in docs.json because it only contains JSON-safe options: | Option | Meaning | | ------ | ------- | | enabled | Enables or disables Cloud analytics for the project | | console | Controls runtime console logging for analytics events | | includeInputs | Allows raw user-authored inputs to be included in analytics events | Function callbacks do not belong in docs.json. If a project needs executable analytics logic, keep that logic in docs.config.ts. Custom onEvent Use analytics.onEvent when the project needs a second destination for analytics events: For connected Cloud projects, this callback is additive. Docs Cloud still stores its copy of the event through the managed Cloud path. Privacy Defaults By default, raw user-authored text is excluded from analytics events. That includes search queries, Ask AI questions, feedback comments, and copied content. Set includeInputs: true only when the project has the right consent, retention, and review policy for user-authored text: Stored Event Shape Docs Cloud stores event fields that are useful for dashboard queries: | Field group | Examples | | ----------- | -------- | | Project identity | project ID, optional analytics project ID | | Event shape | type, source, timestamp | | Page context | URL, path, referrer, locale | | Optional payloads | input, properties | | Agent traffic hints | trafficType, agentName, botProvider when Cloud can infer an agent or bot source | | Timing | event time, received time | The dashboard can use those fields for totals, recent event lists, source filters, event type filters, repository filters, and activity charts. Agent-readable routes and MCP events are marked as agent traffic when the runtime can infer that from the event source, event type, explicit properties, or known AI crawler user agents. That lets the dashboard separate human docs usage from ChatGPT, Codex, Copilot, Claude, MCP clients, and crawler activity without requiring a custom onEvent callback. Failure Behavior Analytics delivery should not break the docs runtime. If Cloud analytics delivery fails, rendering, search, Ask AI, MCP, and a customer-owned onEvent callback should continue independently. That behavior keeps analytics operationally useful without making analytics availability part of the critical path for serving docs. --- ## Deploy URL: https://docs.farming-labs.dev/docs/cloud/deploy Deploy a Docs Cloud preview from docs.config.ts, docs.json, and a project API key Deploy Docs Cloud deploy creates a hosted preview for a connected docs project. The repo remains the source of truth. The CLI syncs the serializable Cloud config into docs.json, validates the project API key, then asks Docs Cloud to create or update the preview. Requirements - Docs Cloud access for the workspace - a Docs Cloud project API key - DOCSCLOUDAPI_KEY available in your shell, .env.local, or CI secrets - a cloud block in docs.config.ts - a committed docs.json when the repo contract should be reviewed in Git Keep the raw API key in .env.local, your shell, or CI secrets. docs.config.ts should reference the environment variable name, and docs.json should only contain that environment variable name. Configure Cloud cloud.apiKey.env defaults to DOCSCLOUDAPI_KEY. Writing it explicitly makes the contract easier to review in CI and pull requests. Sync docs.json docs.json is the JSON contract that Docs Cloud reads. It mirrors safe Cloud settings from docs.config.ts; it does not store API key values. Example output: The generated cloud block intentionally omits enabled. The presence of the block opts the project into Cloud-aware CLI flows. Set "enabled": false only when the repo should keep the local contract but disable Cloud operations. Deploy A Preview Set the key, then run the deploy command from the project root: docs deploy performs the same sync step before requesting the preview: 1. read docs.config.ts 2. write or update docs.json 3. validate the Docs Cloud API key 4. request a hosted preview deployment The explicit Cloud command is equivalent: Check Cloud Setup Run a read-only health check before deploying or debugging Ask AI: The check validates docs.config.ts, docs.json freshness, the hosted Docs Cloud API target, cloud.apiKey.env, the Docs Cloud API key, required deploy scopes, analytics project envs, the public docs origin used for browser CORS, ai.provider: "docs-cloud" direct or proxied knowledge endpoint wiring, and browser CORS preflight for Cloud analytics/Ask AI. Use --json in CI or --no-network when you only want local config checks. Scope the report when you only want one integration area: --ask-ai checks the Docs Cloud provider path end to end. Direct browser mode needs PUBLICDOCSCLOUDPROJECTID plus PUBLICDOCSCLOUDAPIKEY or another browser-safe cloud.apiKey.env; Next.js projects can use the equivalent NEXTPUBLIC* names. The check also sends a browser-style CORS preflight to the hosted knowledge endpoint. If only the server-side project id and API key are available, the report marks the local docs API route as the proxy path and skips browser CORS for Ask AI because the browser only talks to the same-origin docs app. Example focused report: Use JSON output when CI or another tool needs the preview URL: Commands | Command | Use it when | | ------- | ----------- | | pnpm dlx @farming-labs/docs cloud sync | You only want to update docs.json | | pnpm dlx @farming-labs/docs cloud check | You want to validate config, hosted API access, analytics envs, API key scopes, CORS, and Ask AI wiring without deploying | | pnpm dlx @farming-labs/docs cloud check --analytics | You only want to validate Docs Cloud analytics wiring and analytics CORS | | pnpm dlx @farming-labs/docs cloud check --ask-ai | You only want to validate Docs Cloud Ask AI endpoint wiring and Ask AI CORS | | pnpm dlx @farming-labs/docs cloud check --deploy | You only want to validate deploy flags, API key presence, and deploy scopes | | pnpm dlx @farming-labs/docs cloud check --json | CI or automation needs the full check result as JSON | | pnpm dlx @farming-labs/docs cloud check --no-network | You only want local config, env, and docs.json checks | | pnpm dlx @farming-labs/docs cloud check --api-key-env | The deploy key lives in a custom env var | | pnpm dlx @farming-labs/docs deploy | You want to sync, validate the key, and request a preview | | pnpm dlx @farming-labs/docs cloud deploy | You prefer the explicit Cloud namespace | | pnpm dlx @farming-labs/docs deploy --json | CI needs machine-readable output | | pnpm dlx @farming-labs/docs deploy --config | The docs config file is outside the project root | | pnpm dlx @farming-labs/docs deploy --api-base-url | You are testing a staging or self-hosted Cloud API | --api-key is available for local testing. Prefer cloud.apiKey.env plus an environment variable for normal use so the key does not end up in shell history or logs. Publish Mode cloud.publish controls what Docs Cloud should do when generated docs changes need to go back to Git: | Mode | Behavior | | ---- | -------- | | "draft-pr" | Create a draft pull request targeting publish.baseBranch | | "direct-commit" | Commit directly to publish.baseBranch | Use draft-pr for normal team workflows. Reserve direct-commit for trusted automation where the Cloud project owns the branch policy. Troubleshooting Missing API key Set the environment variable named by cloud.apiKey.env: or add it to .env.local. Deploy disabled If cloud.deploy.enabled is false, docs deploy stops before requesting a hosted preview. Set it back to true or remove the override. Browser CORS blocked Make sure the public docs URL is set in one of the places the check can read: NEXTPUBLICBASEURL, NEXTPUBLICSITEURL, SITE_URL, site.url, sitemap.baseUrl, llmsTxt.baseUrl, or robots.baseUrl. For the hosted Docs Cloud API, you do not need to set a Docs Cloud API URL. If you are self-hosting the API, allow the same public docs origin in the API service CORS config. Wrong Cloud host For staging or self-hosted testing, pass: --- ## 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" | Docs source route and folder (e.g. "docs" -> app/docs) | | docsPath | string | same as entry | Public docs route prefix in Next.js. Use "" for root-mounted 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 | | codeBlocks | DocsCodeBlocksConfig | — | Validate fenced MDX code blocks with metadata planning and optional sandbox execution | | 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 | | review | boolean \| DocsReviewConfig | true | Docs Review CI workflow generation, scoring, and rule severity | | 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 | | cloud | DocsCloudConfig | — | Docs Cloud API key env, deploy, and publish defaults mirrored into docs.json | | 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, /docs/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) | Code block validation codeBlocks.validate lets the CLI inspect fenced MDX code blocks, build an execution plan from metadata, and optionally run executable snippets with a local or sandbox runner. Use metadata on the code fence to tell agents and validators how a snippet should be treated: ts title="app/api/chat/route.ts" framework="nextjs" packageManager="pnpm" env="OPENAIAPIKEY" runnable const apiKey = process.env.OPENAIAPIKEY; Small runnable blocks make the check easy to dogfood: Run the validator locally: The planner never writes secrets into docs. The env map means the snippet can keep using process.env.OPENAIAPIKEY, while validation injects that runtime variable from OPENAITESTAPIKEY. For Vercel Sandbox, VERCELTOKEN is enough in most projects; the runner uses .vercel/project.json when present and can auto-discover an accessible Vercel project from the token. Runner providers are explicit, so a remote sandbox never silently falls back to local execution: E2B uses the e2b package and Daytona uses @daytona/sdk. Install the provider SDK in the project that runs the CLI when you choose one of those runners. Public docs path In Next.js, entry controls where the docs live in your app directory. docsPath controls the public URL where readers see those pages. The default keeps both values aligned: With the default config, app/docs/quickstart/page.mdx is available at /docs/quickstart. Set docsPath: "" when the whole deployment is docs-only and should publish pages from the site root: The files still live under app/docs, but readers see root URLs: | Source file | Public URL | | ---------------------------------- | ------------- | | app/docs/page.mdx | / | | app/docs/quickstart/page.mdx | /quickstart | | app/docs/reference/auth/page.mdx | /reference/auth | This is useful for a docs subdomain. Attach docs.example.com to the docs deployment, then set docsPath: "" so the subdomain serves clean URLs like https://docs.example.com/quickstart instead of https://docs.example.com/docs/quickstart. withDocs({}) reads this from docs.config.tsx, so you do not need to add custom rewrites to next.config.ts. docsPath: "" makes the docs own / and other root-level page paths on that deployment. If the same Next.js app also serves a marketing site at /, keep the default /docs path or use a separate docs deployment for your subdomain. 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 - In Next.js, known AI user agents and conservative bot-like agent heuristics also receive markdown from normal docs URLs - These negotiated Next.js reads are handled by the existing /api/docs route; withDocs() does not create a separate /api/docs/markdown wrapper - Markdown responses start with YAML frontmatter for title, optional description, canonicalurl, markdownurl, and last_updated when a page freshness date is known - Successful markdown page responses append a ## Sitemap footer that links agents back to the configured markdown sitemap routes - Missing markdown pages return actionable markdown with HTTP 200, closest-match suggestions, recovery instructions, discovery links, and sitemap links - Very high-confidence missing slugs redirect to the closest .md page - 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 the page-content 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, send Signature-Agent, identify as a known AI user agent, or match a conservative bot-like agent heuristic 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. The markdown body starts with YAML frontmatter. Agents can read canonicalurl, markdownurl, title, description, and last_updated before parsing the page body. Negotiated markdown responses include cache-safe Vary headers: Accept for Accept: text/markdown, Accept, Signature-Agent for Signature-Agent, User-Agent for known AI user agents, and User-Agent, Sec-Fetch-Mode for heuristic agent detection. 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 recovery response is still markdown and returns HTTP 200 unless a very high-confidence match redirects to the corrected .md page. The body lists closest matches, recovery steps, the agent discovery spec, search endpoint, and sitemap routes so agents 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 - /docs/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. The default /docs/sitemap.md alias is emitted only when routePrefix is empty. 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: listdocs, listpages, getnavigation, searchdocs, readpage, getcodeexamples, getconfig_schema Useful built-in tool calls: - list_docs returns page summaries grouped by docs section, with optional section filtering - getcodeexamples returns fenced code blocks plus parsed metadata such as title, framework, packageManager, and runnable - getconfigschema returns structured docs.config.ts option metadata for agents editing config safely 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. Docs Review CI Docs Review is enabled by default. During next dev or next build, withDocs() creates .github/workflows/docs-review.yml when the workflow does not already exist. The generated workflow is intentionally tiny: it calls the hosted reusable workflow at farming-labs/docs/.github/workflows/docs-review-reusable.yml@main and passes the docs config path, package manager, check name, and working directory as inputs. In monorepos, the file is written at the repository root and the job runs from the docs app directory. The generated wrapper only triggers on docs-related PR changes such as docs.config.*, app/docs/, src/app/docs/, content/docs/, and the workflow files themselves. By default it runs in warn mode, posts GitHub annotations, uses docs-review as the GitHub check name, and keeps CI green. Use block mode when the team wants docs PRs to fail below the configured score threshold or when error-level findings exist: Set review: false to opt out of review and workflow generation. 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. Set requireComment: true to disable the submit button until the user types a non-empty comment: 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 root agent.compact config, so apiKeyEnv: "DOCSCLOUDAPI_KEY" works without exporting the key manually. Keep compression keys in agent.compact.apiKey or agent.compact.apiKeyEnv; 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: Last Updated Use lastUpdated to control the date shown at the bottom of each docs page. The optional label field lets you replace the default "Last updated" prefix with any string — useful for localization or rebranding. | Property | Type | Default | Description | | ---------- | --------------------------- | ---------------- | -------------------------------------------------------- | | enabled | boolean | true | Show or hide the last-updated date | | label | string | "Last updated" | Text prefix shown before the formatted date | | position | "footer" \| "below-title" | "footer" | Render beside "Edit on GitHub" or below the page title | Set label to an empty string to display only the formatted date with no prefix text. Reading Time Use readingTime when you want an estimated label like 5 min read on docs pages. For code-heavy guides where examples make up a large part of the page, set includeCode: true so fenced and inline code blocks are counted toward the estimate: Default behavior: - the feature is opt-in - the estimate uses 220 words per minute unless you override it - labels render as 5 min read by default; set format: "short" to render 5 min - fenced code blocks, inline code, links, images, and raw HTML are stripped before counting words - set includeCode: true in ReadingTimeConfig to count code block and inline code words toward the estimate instead of stripping them - 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 The global format setting applies to computed labels and exact per-page values. With format: "short", the same readingTime: 12 override renders as 12 min. 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 provider strings and target: "markdown" to send tools directly to the public .md route for the page. For deeper customization, use provider objects. This keeps the built-in preset but opens Cursor through the app deeplink: 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, telemetry, 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 - Telemetry — Farming Labs maintainer telemetry for package adoption and coarse agent-surface 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, sitemap.md, and /docs/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 - in Next.js, known AI user agents and conservative bot-like agent heuristics also receive markdown from normal docs 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 - agents can fetch a machine-readable map of the active docs config with GET /api/docs?format=config (returns docs-config-map.v1 JSON, X-Robots-Tag: noindex, CDN-cached at s-maxage=3600) /docs/installation stays the human HTML page by default. In Next.js, send Accept: text/markdown, send Signature-Agent, or identify with a known AI user agent on 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. The body starts with YAML frontmatter for title, description, canonicalurl, markdownurl, and last_updated when known, then ends with a ## Sitemap footer that links back to the configured markdown sitemap. Missing markdown pages return actionable markdown with HTTP 200, closest-match suggestions, recovery instructions, links to the agent discovery spec, search endpoint, and sitemap routes. Very high-confidence typo matches redirect to the corrected .md page. 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 - /docs/installation with User-Agent: ClaudeBot/1.0 for AI user-agent auto-detection - 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, sends Signature-Agent, identifies with a known AI user 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, Signature-Agent, or AI user-agent detection - 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, /docs/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 - where to fetch the machine-readable docs config map, including the format identifier (docs-config-map.v1) and endpoint (/api/docs?format=config), advertised under the top-level config field of the spec 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 spec also includes api.config: "/api/docs?format=config" and a top-level config object with format: "docs-config-map.v1" and endpoint: "/api/docs?format=config" so agents can discover and fetch the config map without hard-coding the route. 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. Hosted Docs Cloud provider Use Docs Cloud when you want the Ask AI UI to query the knowledge index built from your imported repository instead of running the local OpenAI-compatible RAG handler inside the docs app. Set the Docs Cloud project id and API key in environment variables: provider: "docs-cloud" keeps OpenAI, Anthropic, embeddings, and retrieval credentials on the Docs Cloud server. The Ask AI widget resolves one of two request paths: | Available runtime env | Request path | Use it when | | --------------------- | ------------ | ----------- | | PUBLICDOCSCLOUDPROJECTID plus PUBLICDOCSCLOUDAPIKEY or another browser-safe cloud.apiKey.env | The browser posts directly to /v1/projects/{projectId}/knowledge/ask with the project-scoped Docs Cloud API key | You want the lowest-latency Cloud path and the key is safe to expose to the browser | | DOCSCLOUDPROJECT_ID plus the server env named by cloud.apiKey.env | The browser posts to the local docs API route and the server proxies the Docs Cloud request | You want the API key to stay server-only or you need same-origin browser requests | The local docs API route is also the fallback when the project id or API key is not available to the browser. In both paths, Docs Cloud reads the same project knowledge index and returns either an OpenAI-compatible event stream or a JSON answer. Streaming is enabled by default for Docs Cloud; set stream: false if you need a single JSON answer. Next.js projects can use the equivalent NEXTPUBLICDOCSCLOUDPROJECT_ID and NEXTPUBLICDOCSCLOUDAPI_KEY names. SvelteKit, Astro, and Vite users should prefer the PUBLICDOCSCLOUD_* names. SvelteKit's createDocsServer() handles the local Docs Cloud proxy automatically for ai.provider: "docs-cloud", so the app does not need a custom POST handler. For staging or self-hosted Cloud APIs, direct browser requests read PUBLICDOCSCLOUD_URL or NEXTPUBLICDOCSCLOUDURL. Server-routed requests read DOCSCLOUDAPIBASEURL, DOCSCLOUDAPI_URL, then the public URL envs. 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. stream Whether Ask AI should request a streaming response when the provider supports it. Docs Cloud streams token deltas by default. | Type | Default | | --------- | ------- | | boolean | true | Streaming answers render as markdown while tokens arrive. Headings, tables, lists, horizontal rules, inline code, and fenced code blocks stay readable during the stream; fenced code blocks render with a language label and copy button. 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?" This page covers the runtime event stream and optional custom destinations. Connected Docs Cloud projects send events to Cloud automatically, store them in Cloud, and show activity in the dashboard. For the Cloud-specific contract, see Docs Cloud Analytics. Quick Start analytics: true logs usage events to the console with the [@farming-labs/docs:analytics] prefix. Send Events To Another Tool For connected Docs Cloud projects, this callback is optional and additive. Use it when you want a copy of events outside Docs Cloud; you do not need it for Cloud storage. The callback receives a normalized DocsAnalyticsEvent: properties carries safe operational details such as status, durations, result counts, model ids, provider names, message counts, content lengths, and route or delivery metadata. Raw user-authored text stays in input and is excluded unless includeInputs: true is set. 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. | | agents_request | An agent-facing generated instruction file was requested. | | 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, signatureagent, useragent, heuristic, apiformat, or mcp_tool. Ask AI API events include the provider, model, message count, question length, status or error reason, and durationMs when available. When ai.provider is "docs-cloud", those events identify the provider as docs-cloud even if the request is served through the local docs API proxy. 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 through getMDXComponents(). You can use them in MDX without imports or docs.config.ts registration: - 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 - CodeGroup — Mintlify-compatible tabbed code groups powered by the built-in code block UI - Callout — callout/admonition boxes Example: Use CodeGroup CodeGroup lets you wrap multiple fenced code blocks in one tabbed block. The tab label comes from title, filename, or the bare filename after the language, so Mintlify-style docs can move over without custom components or MDX component registration. bash npm npm install @farming-labs/docs bash pnpm pnpm add @farming-labs/docs ts title="docs.config.ts" entry: "app/docs", }); Use when you want to preserve Mintlify-authored MDX that includes the dropdown prop. It is accepted by the renderer and exposed as a data attribute for theme styling. 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, list sections, inspect code examples, and understand config options without scraping HTML. The current built-in surface includes: - list_docs - list_pages - get_navigation - search_docs - read_page - getcodeexamples - getconfigschema 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: To print the MCP client setup instructions for your own Docs Cloud deployment, use mcp setup: Add --json to print only the JSON config block, or --api-base-url to target a self-hosted Docs Cloud API. See CLI for the full flag reference. 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_docs - list_pages - get_navigation - search_docs - read_page - getcodeexamples - getconfigschema What to ask it Once your MCP client is connected, these are good first checks: - list_docs for getting-started before deciding which page to read - 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 - getcodeexamples for runnable Next.js or pnpm snippets - getconfigschema for mcp.tools before editing docs.config.ts 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_docs returns page summaries grouped by section; pass section such as getting-started to narrow the result - 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 - getcodeexamples returns fenced code blocks with parsed metadata like title, framework, packageManager, and runnable - getconfigschema returns docs.config.ts option metadata with paths, types, defaults, descriptions, and examples Example tool calls: The built-in resources mirror the navigation and page content 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 | The object form also lets you customize the button text. pageActions.copyMarkdown.label Button label shown before the page has been copied. | Type | Default | | -------- | -------------- | | string | "Copy page" | pageActions.copyMarkdown.copiedLabel Button label shown after a successful copy. | Type | Default | | -------- | ------------ | | string | "Copied!" | pageActions.copyMarkdown.format Content format that the button copies to the clipboard. | Type | Default | | -------------------------- | -------------- | | "markdown" \| "text" | "markdown" | - "markdown" — fetches the page's public .md route and copies its markdown source. - "text" — reads the visible text content of the rendered page without a network request. pageActions.copyMarkdown.includeTitle Whether to prepend the current page title to the copied content. | Type | Default | | --------- | ------- | | boolean | false | When enabled: - In "markdown" format a # Title heading is added before the page content. - In "text" format the plain title is prepended as a first line. The title is not added if the content already begins with it. Open in LLM The Open in... dropdown lets users send the current page to an LLM or tool. Built-in providers can target the public .md route, the rendered page, the source .mdx URL, or the GitHub edit URL. 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. Use provider strings for the built-in presets, or objects when you need icons, labels, per-provider prompts, or a custom URL template. | Type | Default | | -------------------- | ----------------- | | OpenDocsProvider[] | Built-in defaults | Built-in provider ids are "chatgpt", "claude", "cursor", "gemini", "copilot", "perplexity", and "github". pageActions.openDocs.target Controls which URL is inserted into {url} for the built-in provider prompt. | Value | Used for | | ------------ | --------------------------------------------------------------- | | "markdown" | Public .md route for the page. This is the default. | | "page" | Rendered docs page URL. | | "source" | Source-style .mdx URL. | | "github" | GitHub edit URL, when the top-level github config is present. | pageActions.openDocs.prompt Prompt text sent to built-in providers. It defaults to: You can make the handoff more specific: target: "markdown" sends providers to the public .md route for the current page. 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, or send Signature-Agent when they identify themselves as an agent. See Agent Primitive. Provider Objects Provider objects can customize a preset while keeping its built-in URL: Custom URL Templates Use urlTemplate when you need a provider that is not built in. This keeps the previous API working. The urlTemplate string supports these placeholders: | Placeholder | Replaced with | | --------------- | ----------------------------------------------------------------------------- | | {prompt} | The resolved prompt text | | {url} | The selected target URL, controlled by target | | {pageUrl} | The rendered docs page URL | | {markdownUrl} | The public .md route for the page | | {sourceUrl} | The source-style .mdx URL for the page | | {mdxUrl} | Alias for {sourceUrl} | | {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. | Existing providers that use name and urlTemplate are still supported. For custom URL templates, {url} keeps the previous page-URL behavior unless you explicitly set target. New configs should prefer target: "markdown" with provider strings. 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, sitemap.md, and docs/sitemap.md for crawlers, agents, and static exports Sitemaps Generate a standard sitemap.xml, semantic sitemap.md, and docs-scoped /docs/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 /docs/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/docs/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, public/sitemap.md, and public/docs/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 The default /docs/sitemap.md alias is emitted only when routePrefix is empty. There is no separate well-known route setting. The prefix applies to custom 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. --- ## Telemetry URL: https://docs.farming-labs.dev/docs/customization/telemetry Production maintainer telemetry for @farming-labs/docs adoption and agent-surface usage Telemetry @farming-labs/docs includes limited production telemetry so Farming Labs can understand which frameworks, deployments, and agent-optimized features are used in real projects. Telemetry is separate from Analytics. Analytics is a project-owned event stream for page views, search, Ask AI, feedback, and MCP usage. Telemetry is a low-volume maintainer signal for package adoption and coarse feature usage. Opt Out Disable telemetry with an environment variable: Or disable it in docs.config.ts: Environment opt-out wins over config. Telemetry delivery failures are ignored and never affect rendering, builds, routing, search, Ask AI, or MCP behavior. What Telemetry Includes Telemetry may include: - an anonymous project identity hash derived by the ingestion service from stable project metadata - package name and version - framework adapter, such as Next.js, TanStack Start, SvelteKit, Astro, or Nuxt - production/runtime and deployment provider signals - public site origin or deployment URL when available - enabled feature flags such as search, Ask AI, MCP, llms.txt, page actions, feedback, sitemap, robots, API reference, static export, changelog, Docs Cloud, and Docs Review - coarse agent-surface usage such as markdown, llms, agents, skill, agent_spec, agentfeedbacksubmit, ask_ai, mcp, and MCP tool names Telemetry does not collect: - visitor identities - end-user page views - device fingerprints - search queries - Ask AI questions - feedback comments - copied code or markdown content - documentation source content - cookies or cross-site tracking identifiers - per-user sessions Options | Option | Type | Default | | ------------ | --------- | ------------------------- | | enabled | boolean | production-only enabled | | siteOrigin | string | request/deployment origin | | endpoint | string | Farming Labs ingestion endpoint | Use siteOrigin when production requests should be grouped under a stable custom domain: Use endpoint only for self-hosting, local verification, or tests: If a self-hosted ingestion endpoint requires a shared key, set the same environment variable in the docs runtime and the ingestion app: When present, telemetry requests include the key as x-docs-telemetry-key. Failed telemetry requests are still ignored by the docs runtime. The ingestion service computes the anonymous identity hash server-side. Clients do not send a device id, cookie, browser fingerprint, or session id; the hash exists only to group repeated telemetry events from the same docs project/site. For self-hosted ingestion, set DOCSTELEMETRYIDENTITY_SALT on the ingestion service to derive the identity hash with HMAC-SHA256. Without it, ingestion uses a deterministic SHA-256 hash. --- ## 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." } {AGENTFRIENDLYDOCSWEBSITEPROMPT} The copy button asks for the website URL, docs entry folder, and framework, then fills those details into the prompt before copying. 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 - {page} with a known AI User-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 - /docs/sitemap.md exposes the same Markdown sitemap under the docs section for agents that probe docs-scoped routes first - 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 Accept: text/markdown, Signature-Agent, or a known AI user 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: These 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 with User-Agent: ClaudeBot/1.0 returns the same markdown and varies by User-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. Markdown responses also start with YAML frontmatter. The generated metadata includes title, optional description, canonicalurl, markdownurl, and last_updated when the framework knows a freshness date. If a markdown request misses, the response is still markdown. Uncertain misses return HTTP 200 with closest-match suggestions, recovery steps, /.well-known/agent.json, /.well-known/agent, /api/docs/agent/spec, search, the requested API markdown route, and sitemap routes. Very high-confidence typo matches redirect to the corrected .md page so the agent can continue without treating the site as a dead end. Every successful markdown page also ends with a ## Sitemap footer. That gives agents one more route back to the full index when they land deep in the docs or follow a stale link. 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, /sitemap.md, or /docs/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, AI user-agent detection, 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 and AI user-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, docs/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 Theme names, CSS imports, and component styling belong in docs.config.ts and the framework theme packages. Keep docs.json focused on repo shape and hosted Cloud services so the contract stays runtime-neutral. Cloud Analytics Use cloud.analytics when you want the shared repo contract to carry analytics settings for Docs Cloud and for frameworkless generated runtimes. Connected Cloud projects use these settings with the Cloud-provided project identity so events are stored by Docs Cloud without requiring an onEvent callback in the repo. 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. Use onEvent only when you want to send an extra copy to a tool you own; Cloud storage is handled by the managed Cloud delivery path. 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. The presence of a cloud block opts the project into Cloud-aware CLI flows. Leave cloud.enabled unset for normal connected projects. Set it to false only when the repo is using docs.json as a local project contract and should explicitly disable Cloud deploy and publish commands. To connect a repo, create an API key, and deploy hosted preview docs, see Docs Cloud and Deploy. For hosted analytics storage, see Docs Cloud Analytics. 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 Install only the Farming Labs packages for your chosen framework (listed in each tab below). Do not separately install fumadocs-openapi, fumadocs-ui, fumadocs-core, @scalar/core, or @scalar/nextjs-api-reference. The API reference renderers are bundled inside @farming-labs/docs and the framework adapters; adding those packages directly can cause version conflicts. 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. The default fumadocs() theme helper uses @farming-labs/theme/default/css; other built-in themes use their theme name, like @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. The default fumadocs() theme helper uses @farming-labs/theme/default/css; other built-in themes use their theme name, like @farming-labs/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/theme/default/css; for other themes use e.g. @farming-labs/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. The default fumadocs() theme helper uses @farming-labs/theme/default/css; other built-in themes use their theme name, like @farming-labs/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 | Docs source route and folder (e.g. "docs" -> app/docs) | | docsPath | string | same as entry | Public docs route prefix in Next.js. Use "" to serve docs from / | | 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 | | telemetry | boolean \| DocsTelemetryConfig | production-only enabled | Farming Labs maintainer telemetry for package adoption and coarse agent-surface 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 | | review | boolean \| DocsReviewConfig | true | Docs Review scoring, rule severity, and GitHub Actions workflow generation | | 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, /docs/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 | DocsConfigMap, DocsConfigMapPointer, and config-map constants The config-map endpoint (GET /api/docs?format=config) returns a DocsConfigMap JSON document built from the active docs config. All five framework server adapters handle this request automatically. Constants | Constant | Value | Description | | -------- | ----- | ----------- | | DEFAULTDOCSCONFIG_FORMAT | "docs-config-map.v1" | Schema version identifier included in the response and in the agent discovery spec | | DEFAULTDOCSCONFIG_ROUTE | "/api/docs?format=config" | Canonical route for the config-map endpoint | DocsConfigMap The JSON response shape. Contains a format field set to "docs-config-map.v1" and a pointers array of DocsConfigMapPointer entries. Sensitive config values are redacted before serialization. DocsConfigMapPointer | Property | Type | Description | | -------- | ---- | ----------- | | path | string | Dot-path to the config field, e.g. "mcp.tools.readPage" | | kind | string | Coarse value kind at this pointer, e.g. "boolean", "string", "object" | Response headers | Header | Value | | ------ | ----- | | Content-Type | application/json; charset=utf-8 | | Cache-Control | public, max-age=0, s-maxage=3600 | | X-Robots-Tag | noindex | Agent discovery spec The agent discovery spec (GET /.well-known/agent.json) now includes: Agents that read the discovery spec can use spec.config.endpoint and spec.config.format to fetch and identify the config map without hard-coding the route. entry and docsPath entry is the source route and app folder. In a Next.js app with entry: "docs", the framework expects docs pages under app/docs. docsPath changes the public route where those pages are exposed. It defaults to the same path as entry, so app/docs/quickstart/page.mdx is served at /docs/quickstart. For a docs-only deployment or a docs subdomain, set docsPath: "": That keeps the files under app/docs and serves them from the public root: | Source file | Public URL | | ------------------------------ | ------------- | | app/docs/page.mdx | / | | app/docs/quickstart/page.mdx | /quickstart | withDocs({}) reads docsPath from docs.config.tsx. You do not need host-specific rewrites in next.config.ts for a docs-only deployment. Attach the docs deployment to a subdomain like docs.example.com, then root URLs resolve on that subdomain. Use docsPath: "" when the deployment is dedicated to docs. If the same app needs a marketing homepage at /, keep docsPath unset so docs stay under /docs. 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. telemetry and DocsTelemetryConfig Farming Labs maintainer telemetry for production adoption and coarse agent-optimized feature usage. It is separate from analytics: telemetry is for framework/package insight, while analytics is for project-owned product events. Telemetry can be disabled with either: or: | Option | Type | Default | Description | | ------------ | --------- | ------------------------- | --------------------------------------------------------------- | | enabled | boolean | production-only enabled | Enable or disable maintainer telemetry | | siteOrigin | string | request/deployment origin | Stable public origin to include in telemetry events | | endpoint | string | Farming Labs endpoint | Override the ingestion endpoint for self-hosting or tests | Set DOCSTELEMETRYINGEST_KEY in both the docs runtime and self-hosted ingestion app when the endpoint should require a shared x-docs-telemetry-key header. Telemetry may include an anonymous project identity hash, package/framework versions, deployment provider signals, site origin, enabled docs features, and coarse agent-surface events such as llms, markdown, mcp, or skill. It does not collect visitor identities, page views, device fingerprints, search queries, Ask AI questions, feedback comments, copied content, docs source content, cookies, or per-user sessions. 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 | | ------------------- | --------- | ----------- | | listDocs | boolean | Expose the list_docs tool | | 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 | | getCodeExamples | boolean | Expose the getcodeexamples tool | | getConfigSchema | boolean | Expose the getconfigschema 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: listdocs, listpages, getnavigation, searchdocs, readpage, getcodeexamples, getconfig_schema 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. DocsReviewConfig Docs Review configuration for changed docs files, score thresholds, and GitHub Actions behavior. Omitted review config is enabled by default. Set review: false to opt out of the review command and generated workflow. The generated CI file is a small repository-root wrapper around the hosted reusable workflow at farming-labs/docs/.github/workflows/docs-review-reusable.yml@main. | Property | Type | Default | Description | | -------- | ---- | ------- | ----------- | | enabled | boolean | true | Enable Docs Review | | ci | boolean \| DocsReviewCiConfig | enabled | GitHub Actions behavior and workflow generation | | score | DocsReviewScoreConfig | { threshold: 80 } | Healthy score threshold and severity weights | | rules | DocsReviewRulesConfig | default severities | Per-rule severity overrides | DocsReviewCiConfig | Property | Type | Default | Description | | -------- | ---- | ------- | ----------- | | enabled | boolean | true | Enable review CI workflow generation | | name | string | "docs-review" | GitHub Actions job/check name for the generated workflow | | mode | "off" \| "warn" \| "block" | "warn" | Report only, warn without failing, or block unhealthy PRs | | annotations | boolean | true | Emit GitHub workflow annotations | | comment | boolean | true | Reserved for PR comments from the official action/bot | DocsReviewRulesConfig | Property | Default | Description | | -------- | ------- | ----------- | | brokenLinks | "error" | Check internal docs links | | frontmatter | "error" | Check title and description frontmatter | | duplicateSlugs | "error" | Check duplicate docs routes | | invalidMdx | "error" | Check markdown/MDX read and frontmatter parsing | | configExamples | "warn" | Check config examples when possible | | codeFenceMetadata | "warn" | Check code fences for metadata such as title | | runnableMetadata | "warn" | Check runnable snippets for package manager metadata | | agentContext | "suggestion" | Suggest ` blocks or sibling agent.md where helpful | Rule severities accept "off", "suggestion", "warn", or "error". 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. You do not need to install fumadocs-openapi, fumadocs-ui, @scalar/core, or related packages separately. The "fumadocs" renderer is bundled by @farming-labs/next; the "scalar" renderer is bundled by @farming-labs/docs and the other framework adapters. 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 | | renderer | "fumadocs" \| "scalar" | framework-aware | Which renderer to use: "fumadocs" is bundled by @farming-labs/next; "scalar" is bundled by @farming-labs/docs and the other adapters. Defaults to "fumadocs" for Next.js and "scalar" for other frameworks | | 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 | | requireComment | boolean | false | Require a non-empty comment before feedback can be submitted | | 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 | | ------------- | --------- | ------------- | -------------------------------------------------- | | format | "markdown" \| "text" | "markdown" | Content format copied by the button: "markdown" fetches the .md route; "text" reads visible page text | | enabled | boolean | false | Show the button | | label | string | "Copy page" | Button label shown before the page is copied | | copiedLabel | string | "Copied!" | Button label shown after a successful copy | | includeTitle | boolean | false | Prepend the current page title to copied content. In "markdown" format this produces a # Title heading; in "text" format it prepends the plain title. Skipped if the content already begins with the title. | OpenDocsConfig | Property | Type | Default | Description | | ----------- | -------------------- | ---------------------------------- | -------------------------------------------------------- | | enabled | boolean | false | Show the dropdown | | target | "markdown" \| "page" \| "source" \| "github" | "markdown" | URL inserted into {url} for built-in provider prompts | | prompt | string | "Read this documentation: {url}" | Prompt text for built-in providers | | providers | OpenDocsProvider[] | ChatGPT, Claude | LLM/tool providers | OpenDocsProvider | Property | Type | Description | | ------------------- | ----------- | --------------------------------------------------------------------------------- | | id | string | Built-in provider preset ("chatgpt", "claude", "cursor", "gemini", "copilot", "perplexity", "github") | | name | string | Display name override | | icon | ReactNode | Icon rendered next to the name | | target | "markdown" \| "page" \| "source" \| "github" | Per-provider target override | | prompt | string | Per-provider prompt override | | mode | "web" \| "app" | Cursor-only option. "app" uses the Cursor app deeplink | | urlTemplate | string | Custom URL template. Supports {prompt}, {url}, {pageUrl}, {markdownUrl}, {sourceUrl}, {mdxUrl}, and {githubUrl}. | | promptUrlTemplate | string | Optional URL template used by the built-in Prompt component. {prompt} is replaced with the prompt text. | LastUpdatedConfig Controls the "Last updated" date shown on docs pages. | Property | Type | Default | Description | | ---------- | --------------------------- | ---------------- | --------------------------------------------------------------- | | enabled | boolean | true | Show or hide the last-updated date | | label | string | "Last updated" | Text prefix shown before the formatted date | | position | "footer" \| "below-title" | "footer" | Where to render the date: next to "Edit on GitHub" or below the page title | Set label to an empty string to show only the formatted date with no prefix. Use any locale-appropriate string to rebrand the prefix text (e.g. "Dernière mise à jour" for French docs). 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 | | format | "long" \| "short" | "long" | Label style: 3 min read for "long" or 3 min for "short" | | includeCode | boolean | false | Whether fenced and inline code blocks count toward the reading time estimate. Disabled by default so the label reflects human prose. Set to true for code-heavy guides. | 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 - set includeCode: true to preserve fenced and inline code word counts in the estimate (useful for code-heavy guides where examples make up a significant portion of the page) - format changes only the rendered label; it does not affect word counting or per-page overrides - 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 the page-content 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/theme/default/css | Default theme | | @farming-labs/theme/pixel-border/css | Pixel border theme | | @farming-labs/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/theme/default/css | Default 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 twelve 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, ledger brings a Stripe Docs-inspired product documentation shell, and threadline adds compact neutral chrome for chat and agent docs. 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 | | Threadline | @farming-labs/theme/threadline | Compact neutral shell for chat and agent docs | | 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 entry folder: [DOCS_ENTRY] - Framework: [FRAMEWORK] - Brandfetch brand context: [BRAND_CONTEXT] Use the @farming-labs/docs markdown routes as implementation context before coding: - Framework docs: https://docs.farming-labs.dev/docs.md - Theme guide: https://docs.farming-labs.dev/docs/themes/creating-themes.md - Components guide: https://docs.farming-labs.dev/docs/customization/components.md - CLI guide: https://docs.farming-labs.dev/docs/cli.md Use the Brandfetch brand context as brand hints, then inspect the existing website directly 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 The copy button asks for the website URL, fetches Brandfetch brand details, defaults the docs entry folder to docs, defaults the framework to Next.js, and fills those placeholders before copying. 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 text my-fumadocs-theme/ src/ index.ts ← createTheme() + exported defaults theme.css ← CSS overrides (optional) package.json json title="package.json" { "name": "my-fumadocs-theme", "version": "1.0.0", "type": "module", "exports": { ".": { "import": "./dist/index.mjs", "types": "./dist/index.d.mts" }, "./css": "./src/theme.css" }, "peerDependencies": { "@farming-labs/docs": ">=0.0.1" } } bash title="terminal" npm install my-docs-theme tsx title="docs.config.tsx" css title="app/global.css" @import "tailwindcss"; @import "my-docs-theme/css"; bash title="terminal" npm install my-fumadocs-theme tsx title="docs.config.tsx" css title="app/global.css" @import "tailwindcss"; @import "my-fumadocs-theme/css"; css title="my-theme/theme.css" / Pick a color preset as your base / @import "@farming-labs/theme/presets/black"; / Or: @import "@farming-labs/theme/presets/neutral"; / / Override CSS variables / .dark { --color-fd-primary: #22d3ee; --color-fd-background: #0a0a0f; --color-fd-border: #1e293b; --radius: 0.5rem; } css title="radius.css" / Container / .dark aside#nd-sidebar { background: hsl(0 0% 2%); border-color: hsl(0 0% 12%); } / All sidebar links / .dark aside a[data-active] { font-size: 0.875rem; border-radius: 0.2rem; color: hsl(0 0% 50%); } / Active link (current page) / .dark aside a[data-active="true"] { background: hsl(0 0% 14%); color: var(--color-fd-primary); font-weight: 600; } / Hover on inactive links / .dark aside a[data-active="false"]:hover { color: hsl(0 0% 90%); } / Folder toggle buttons / .dark aside button.text-fd-muted-foreground { color: hsl(0 0% 90%); font-weight: 500; } / Child links inside folders / .dark aside div.overflow-hidden[data-state] a[data-active] { font-size: 0.785rem; padding-left: 2rem; } / Search button / .dark aside button[class*="bg-fd-secondary"] { background: transparent; border-top: 1px solid hsl(0 0% 12%); border-bottom: 1px solid hsl(0 0% 12%); } / Active link indicator bar / .dark aside a[data-active="true"]::before { background-color: var(--color-fd-primary); width: 2px; } css title="example.css" / Container / figure.shiki { border: 1px solid var(--color-fd-border); border-radius: 0.5rem; } / Title bar / figure.shiki > div:first-child { border-radius: 0.5rem 0.5rem 0 0; } / Copy button / figure.shiki button { border-radius: 0.25rem; } css title="inline-code.css" code:not(pre code) { border-radius: 0.25rem; } .dark code:not(pre code) { background: hsl(0 0% 10%); border: 1px solid hsl(0 0% 16%); color: hsl(0 0% 85%); } css title="tables.css" .dark table { border: 1px solid hsl(0 0% 15%); } .dark th { background: hsl(0 0% 6%); color: hsl(0 0% 80%); } .dark td { border-color: hsl(0 0% 12%); } css title="callouts.css" [class*="bg-fd-card"], [role="alert"] { border-radius: 0.5rem; } .dark [class*="bg-fd-card"] { border-color: hsl(0 0% 14%); } css title="tabs.css" .dark [role="tablist"] { border-radius: 0.5rem; } .dark [role="tab"] { border-radius: 0.25rem; } css title="prev-next-navigation-cards.css" .dark article a[class*="border"] { border-color: hsl(0 0% 15%); border-radius: 0.5rem; } .dark article a[class*="border"]:hover { background: hsl(0 0% 6%); } css title="search-dialog.css" [role="dialog"] { border-radius: 0.5rem; } css title="breadcrumb.css" .fd-breadcrumb { font-size: 0.75rem; } .fd-breadcrumb-link { text-decoration: none; } .fd-breadcrumb-current { font-weight: 500; } css title="page-action-buttons.css" .fd-page-action-btn { border-radius: 0.375rem; background: var(--color-fd-secondary); border: 1px solid var(--color-fd-border); } .fd-page-action-menu { border-radius: 0.5rem; background: var(--color-fd-popover); } css title="scrollbar.css" .dark { --scrollbar-thumb: var(--color-fd-border); --scrollbar-thumb-hover: var(--color-fd-ring); --scrollbar-track: transparent; } css title="selection-misc.css" ::selection { background: var(--color-fd-foreground); color: var(--color-fd-background); } .dark hr { border-color: hsl(0 0% 15%); } `` 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 --- ## Threadline URL: https://docs.farming-labs.dev/docs/themes/threadline Compact neutral theme with right-aligned page actions Threadline Theme A compact neutral preset for chat, agent, and product documentation. It uses a 48px header, compact Geist typography, muted shadcn surfaces, and ghost-style page actions for dense, readable docs. Usage Page Actions threadlinePageActions enables: - Copy page - Open in GitHub - Open in ChatGPT - Open in Claude - Open in Cursor - Open in T3 Chat The GitHub action needs top-level github config so the theme can resolve {githubUrl} for the current page. Defaults | Property | Value | | ------------- | -------------------------------------- | | Primary | oklch(0.205 0 0) | | Background | oklch(1 0 0) (light), oklch(0.145 0 0) (dark) | | Border | oklch(0.922 0 0) | | Border radius | 0.625rem | | Content width | 912px | | Sidebar width | 260px | | TOC width | 224px | | Header height | 48px | Style Notes - Geist-first typography with smaller headings and dense prose - Transparent sidebar with soft hover and active states - Muted cards, tables, inline code, and code blocks - Blue documentation links and info callouts on an otherwise neutral shell - Ghost-style page-action buttons with a compact Open in menu 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 Docs Cloud 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 DOCSCLOUDAPI_KEY can stay out of checked-in config while agent.compact.apiKeyEnv remains the root source of truth. 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. ---