Public docs MCP
Let anyone's agent read your published docs, with no login.
If your Graft project hosts documentation, people will ask their coding agents about it. A public docs endpoint lets those agents search and read your docs directly, instead of scraping web pages. It is read-only and needs no authentication.
It is a separate server from your project's main MCP endpoint, not a setting on it:
| Endpoint | Surface | Auth |
|---|---|---|
POST /api/mcp | Everything an agent can do | Required |
POST /mcp | Documentation only | None |
Why a separate endpoint
Docs platforms already publish this shape, and agents arrive expecting it.
Mintlify generates a docs MCP at /mcp for every site it hosts: public,
unauthenticated, strictly read-only. Cloudflare runs a documentation server
separately from its authenticated API server.
Keeping it separate also keeps it safe. If it were a flag on the main endpoint, which also handles writes, one wrong setting could publish more than your docs. As its own server, it can only ever serve the tools listed below.
What it serves
Five tools and the document resources.
| Tool | Purpose |
|---|---|
list_collections | What kinds of documents exist |
list_content | The documents in one collection |
get_content | One document's frontmatter and body |
search_content | Full-text search across the published content |
explain_error | What a Graft error code means |
Documents are also addressable as
graft://<branch>/<collection>/<slug> resources, so a client can attach a page
rather than paste it.
What it leaves out, and why
It leaves out every write, as you would expect. It also leaves out several reads, because they reveal how your project works rather than what your docs say:
| Left out | Reason |
|---|---|
describe_schema | Carries the project's functions |
list_registry, describe_item | Carry the project's owned primitives |
list_branches, list_compilations, list_approvals | Carry the project's operations |
graft://<branch>/schema | Same payload as describe_schema |
| Prompts | Three of the four instruct write_content, which is not here |
write_content, delete_content, put_asset, run_function, decide_approval | Writes and execution |
So the test for what belongs on a public endpoint is not "is it read-only?" but "is it documentation?".
Set one up
Create the handler with createDocsMcpHandler and mount it at /mcp on your
docs domain, which is where agents look. This is the route this site runs, on
the static engine:
import type { APIRoute } from "astro";
import { resolve } from "node:path";
import { createDocsMcpHandler, type GraftMcpHandler } from "@usegraft/mcp";
import { collections } from "../../graft.config";
import { staticIndexPath } from "../lib/graft";
export const prerender = false;
let handler: GraftMcpHandler | null = null;
function getHandler(): GraftMcpHandler {
handler ??= createDocsMcpHandler({
name: "graft-docs",
contentDir: resolve(import.meta.dirname, "../..", "content"),
collections,
staticIndexPath, // or db: createDb(url).db on Postgres
});
return handler;
}
export const POST: APIRoute = ({ request }) => getHandler()(request);
export const GET: APIRoute = ({ request }) => getHandler()(request);staticIndexPath is the same absolute path your app's own reads open, imported
from where you resolve it. Avoid a relative path such as ".graft/index.db":
it is resolved from the working directory, and on a serverless host that is not
your project folder, so the handler would not find the index.
Export both POST and GET. Agents only send POST, but a GET should
answer 405 rather than 404, which tells a client the endpoint exists and
only accepts POST.
Options it does not accept
The docs handler only accepts contentDir, collections, db,
staticIndexPath, branchId, scope, name, and version. It has no option
for functions, identities, file uploads, or approvals, so they cannot be turned
on by mistake. Even if JavaScript code passes functions anyway, the handler
ignores it. Setting up the public endpoint can never expose the private one.
Choose what is published
The server publishes the collections you hand it. To publish your docs but not your product catalog, pass only the collections you mean:
createDocsMcpHandler({
contentDir,
collections: { docs },
db,
});
Other ways agents can read your docs
An MCP endpoint is one of several ways an agent can read documentation without scraping rendered HTML. This site also serves:
| Surface | What it is |
|---|---|
/llms.txt | An index of pages with descriptions and .md links |
/llms-full.txt | Every page's content in one file |
/docs/<slug>.md | Any page as markdown |
They are generated from the same content and grouping as the sidebar, so they
always match the site. Each page also answers Accept: text/markdown with its
Markdown version.
Next steps
- Connecting agents documents the private endpoint.
- MCP tools lists the full tool surface.
- Agents overview explains what agents can do.