Make your docs answerable by people and by agents, from one static index: a cited Ask box, an MCP server and llms.txt, on your own function and key, with no vector database and no vendor. Agent traffic costs you no model tokens.
Ask these docs from your editor, through the MCP server this site runs on ondocs:
Website: ondocs.vercel.app, whose docs answer questions about this library with OpenAI, under a rate limit and a daily budget, and serve agents at /api/mcp. Formerly ask-my-site (what changed).
ondocs.mp4
The live demo: a question, a streamed answer with citations, and a citation opening the section it came from. On that docs page, the Ask button answers a setup question with the exact config. Then the one-line setup for Docusaurus, Starlight and any other site.
One index, ask-index.json, built with your site from the pages it serves, feeds three things:
- An Ask box for people. A ⌘I dialog that streams a short answer from your model, with numbered citations to the exact section. When nothing relevant is found, it says "I don't know" without calling the model. A page carries only its button, a few KB of JavaScript; the dialog loads the first time it is wanted.
- An MCP server for agents.
search,fetchandlist_pagesover the same index, for Claude Code, Cursor, VS Code, ChatGPT and Claude. The agent brings its own model, and search is keyword-only by default, so it calls no model at all. llms.txt,llms-full.txtand a.mdcopy of every page, written with the build.
Search runs in memory inside one function you deploy: BM25 and vectors, merged with reciprocal rank fusion. There is no vector database, no hosted service and no account. MIT licensed.
| Your site | What you add | Guide |
|---|---|---|
| Docusaurus 3 | A plugin | Docusaurus |
| Starlight, or any Astro site | A plugin, or an integration | Astro and Starlight |
| VitePress | One wrapper in its config | VitePress |
| Next.js or another React app | A route and a component | Next.js and React |
| Fumadocs | A route and a component | Fumadocs |
| Nextra | A route and a component | Nextra |
| Hugo, Jekyll, MkDocs or plain HTML | One script tag; for plain HTML, init --out <dir> |
Script tag |
Each answers from an endpoint you deploy as one function next to the site, rate-limited by default. npx ondocs init writes it for Vercel, Netlify, Cloudflare or GitHub Pages (as a Cloudflare Worker of its own, answering with Workers AI and no API key), and an Astro site with an SSR adapter needs none: see Deploying.
Docusaurus. npm i ondocs ai @ai-sdk/openai @radix-ui/react-dialog cmdk, then:
// docusaurus.config.ts
export default { plugins: ['ondocs/docusaurus'] };After docusaurus build, it indexes the pages into build/ask-index.json, writes llms.txt and the .md pages, and adds an "Ask AI" button with ⌘I.
Starlight. npm i ondocs ai @ai-sdk/openai react react-dom @radix-ui/react-dialog cmdk, then:
// astro.config.mjs
integrations: [starlight({ title: 'Acme Docs', plugins: [ondocs()] })],Next.js. With OPENAI_API_KEY set, index your content with npx ondocs index ./content --base-url /docs --llms-txt public, then:
// app/api/ask/route.ts
export const POST = createAskHandler({
index,
model: openai('gpt-5.4-mini'),
embeddingModel: openai.embedding('text-embedding-3-small'),
});and render the dialog in your layout, with its two stylesheets:
// app/layout.tsx
import { AskDialog } from 'ondocs/react';
import 'ondocs/react/styles.css';
import 'ondocs/embed/launcher.css';
// …then <AskDialog launcher /> in the bodyAny other site. Index the HTML it builds, write the endpoint with npx ondocs init, and add the script tag. For a site built into public:
npx ondocs index public -o public/ask-index.json -e none # keyword-only; leave out -e none to embed with OPENAI_API_KEY
npx ondocs init --out public # plain HTML needs --out; Hugo, Jekyll, MkDocs and Eleventy are detected<script src="https://cdn.jsdelivr.net/npm/ondocs@0.10/dist/embed.global.js" defer></script>Try it without a key. npx ondocs index ./docs -e mock, then npx ondocs dev, serves the ask endpoint and the MCP server on localhost, with offline embeddings and a mock model that quotes your docs. See Try it locally.
The same index, served to agents with createMcpHandler:
// app/api/mcp/route.ts
const handler = createMcpHandler({ index, siteName: 'the Acme docs' });
export { handler as POST, handler as GET, handler as DELETE, handler as OPTIONS };claude mcp add --transport http acme-docs https://docs.acme.dev/api/mcp- Protocol. MCP 2026-07-28 (stateless,
server/discover), and for clients that start withinitialize, 2025-11-25 back to 2024-11-05. Both are tested with the official TypeScript client. - Tools.
searchreturns ranked sections with a snippet and a URL to the anchor, andfetchthe page or section as Markdown: the shape ChatGPT's connectors expect. - Quality. On docusaurus.io's own docs, keyword search puts the right page in the top three for 22 of 22 agent-style queries, checked in CI. An
embeddingModeladds one embedding call per search. - On your docs. With the plugins'
mcpoption, a page can show an "Add to Cursor / VS Code / Claude" block. - llms.txt. The plugins write it by default, the CLI with
--llms-txt <dir>, and they leave alone what another llms plugin already writes.
See MCP server and llms.txt and Markdown pages.
- A question costs one embedding call and one model call, with at most 8,000 characters of sources in and 800 tokens out, and a follow-up that leans on the conversation one more short call to rewrite it. A question your docs don't cover stops before the model.
- An agent's search costs no model call, or one embedding call with an
embeddingModel. - Rate limits are on by default: 10 questions and 60 tool calls a minute per client IP, per server instance. Name your platform's client IP header outside Vercel.
budgetcaps questions and model tokens per UTC day for the whole endpoint,answerCacheanswers repeated questions without a model call, andchallengeadds an optional Turnstile bot check.- Requests must be JSON, which forces a CORS preflight, and bodies are capped. Model errors are masked.
- Answers render as text, never HTML, with links allow-listed.
links: 'sources'keeps only links to the pages cited. - Set a spend limit with your model provider. It is the only hard cap. See Rate limits and security.
- Algolia DocSearch Ask AI adds answers to DocSearch's search box. Choose it if you already use DocSearch and are fine with an Algolia account and its crawler.
- Kapa.ai and Inkeep are hosted. They take more sources than your docs (forums, issues, chat) and come with analytics dashboards and support tooling. ondocs gives you
ondocs evalfor CI, readers' ratings and a log of unanswered questions you wire yourself, not a dashboard. - Fumadocs has first-party AI chat and an MCP route. On a Fumadocs site, they fit its framework natively.
- docusaurus-plugin-mcp-server builds the same kind of index for an MCP endpoint, more widely used today, with no Ask box for people.
- The llms.txt plugins (docusaurus-plugin-llms, @signalwire/docusaurus-plugin-llms-txt, starlight-llms-txt, vitepress-plugin-llms) have more output options, such as custom sets and versions. ondocs writes the basics, and steps aside when one of them is installed.
What ondocs adds is one index you host serving both people and agents, on Docusaurus, Starlight, VitePress, Next.js or any static site, with no vendor account. See Limits and trade-offs for where it is the wrong tool.
- Getting started, Deploying, API, CLI, Benchmarks and FAQ
- Changelog, Contributing, Security
Requires Node.js 22.12 or later, and any AI SDK provider. MIT © Diogo Esteves