Public docs MCP

Let anyone's agent read your published docs, with no login.

Agents & MCP4 min read

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:

EndpointSurfaceAuth
POST /api/mcpEverything an agent can doRequired
POST /mcpDocumentation onlyNone

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.

ToolPurpose
list_collectionsWhat kinds of documents exist
list_contentThe documents in one collection
get_contentOne document's frontmatter and body
search_contentFull-text search across the published content
explain_errorWhat 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 outReason
describe_schemaCarries the project's functions
list_registry, describe_itemCarry the project's owned primitives
list_branches, list_compilations, list_approvalsCarry the project's operations
graft://<branch>/schemaSame payload as describe_schema
PromptsThree of the four instruct write_content, which is not here
write_content, delete_content, put_asset, run_function, decide_approvalWrites 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:

src/pages/mcp.tsAstro
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:

SurfaceWhat it is
/llms.txtAn index of pages with descriptions and .md links
/llms-full.txtEvery page's content in one file
/docs/<slug>.mdAny 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