MCP Server
@farming-labs/docs can expose your docs as a built-in MCP server, so AI clients can search the docs, read pages, and inspect the nav tree without scraping HTML.
The current built-in surface includes:
list_pagesget_navigationsearch_docsread_page
It also exposes resources for:
docs://navigation- one
docs://…resource per page
Enable it
import { defineDocs } from "@farming-labs/docs";
import { pixelBorder } from "@farming-labs/theme/pixel-border";
export default defineDocs({
entry: "docs",
theme: pixelBorder(),
mcp: {
enabled: true,
},
});That enables the built-in MCP surface with the default Streamable HTTP route:
/api/docs/mcpDefault HTTP route
/api/docs/mcp is the default MCP endpoint across the framework adapters.
Minimal route behavior
Next.js auto-generates the default /api/docs/mcp route when mcp is enabled and you use
withDocs().
TanStack Start, SvelteKit, Astro, and Nuxt still need the framework route file, but they all reuse the built-in MCP handler from the docs server helper.
Next.js
With withDocs(), no extra route file is needed for the default path.
import { withDocs } from "@farming-labs/next/config";
export default withDocs();TanStack Start
import { createFileRoute } from "@tanstack/react-router";
import { docsServer } from "@/lib/docs.server";
export const Route = createFileRoute("/api/docs/mcp")({
server: {
handlers: {
GET: async ({ request }) => docsServer.MCP.GET({ request }),
POST: async ({ request }) => docsServer.MCP.POST({ request }),
DELETE: async ({ request }) => docsServer.MCP.DELETE({ request }),
},
},
});SvelteKit
import { MCP } from "$lib/docs.server.js";
export const GET = ({ request }) => MCP.GET({ request });
export const POST = ({ request }) => MCP.POST({ request });
export const DELETE = ({ request }) => MCP.DELETE({ request });Your src/lib/docs.server.ts can keep using the normal helper:
import { createDocsServer } from "@farming-labs/svelte/server";
import config from "./docs.config";
export const { load, GET, POST, MCP } = createDocsServer({
...config,
});Astro
import type { APIRoute } from "astro";
import { MCP } from "../../lib/docs.server";
export const GET: APIRoute = async ({ request }) => MCP.GET({ request });
export const POST: APIRoute = async ({ request }) => MCP.POST({ request });
export const DELETE: APIRoute = async ({ request }) => MCP.DELETE({ request });Nuxt
import { defineDocsMcpHandler } from "@farming-labs/nuxt/server";
import config from "../../docs.config";
export default defineDocsMcpHandler(config, useStorage);Custom route
If you want a custom MCP route, set it in docs.config and add the route file yourself.
export default defineDocs({
entry: "docs",
mcp: {
enabled: true,
route: "/api/internal/docs/mcp",
},
});Custom routes are explicit
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 add the matching route file manually so the app
and the MCP client point at the same endpoint.
Example custom Next.js route:
import docsConfig from "@/docs.config";
import { createDocsMCPAPI } from "@farming-labs/theme/api";
export const { GET, POST, DELETE } = createDocsMCPAPI({
entry: docsConfig.entry,
contentDir: docsConfig.contentDir,
nav: docsConfig.nav,
ordering: docsConfig.ordering,
mcp: docsConfig.mcp,
});
export const revalidate = false;Stdio transport
The same docs graph is available locally over stdio:
pnpx @farming-labs/docs mcpOr with pnpm in an installed project:
pnpm exec docs mcpBy default the CLI reads docs.config.ts[x] from the project root. If your config lives somewhere else:
pnpm exec docs mcp --config src/lib/docs.config.tsTest the Next example
This repo's Next example exposes the default MCP endpoint directly.
Start the example:
pnpm --dir examples/next devThen connect your MCP client or inspector to:
http://127.0.0.1:3000/api/docs/mcpThe built-in HTTP route exposes the full MCP surface:
list_pagesget_navigationsearch_docsread_page
What the tools return
list_pagesreturns page titles, slugs, and URLsget_navigationreturns the docs tree in a readable text outlinesearch_docsranks pages by title, description, and content matchesread_pageaccepts either a slug likeinstallationor a full docs path like/docs/installation
The built-in resources mirror that same data:
docs://navigationdocs://docsdocs://docs/installationdocs://docs/guides/quickstart
When to use this vs llms.txt
- Use
llms.txtwhen you want a static, crawler-friendly docs summary - Use MCP when you want a structured, queryable docs interface for agents and IDE tools
They complement each other well. llms.txt is lightweight and public; MCP is interactive and tool-based.
How is this guide?