graft. docs

The agent surface

MCP tools and self-teaching errors. Operate without an admin UI.

Day-to-day content work goes through MCP and the CLI — the same ops a dashboard would expose, without locking agents behind a mouse UI.

Every error carries a fix. Tools expose schema, content, functions, and the registry. CI enforces a cold-agent contract: tool descriptions and error messages alone are enough to author content and call functions.

Install

graft mcp        # stdio for local agents (.mcp.json)
# HTTP: POST /api/mcp  (Streamable HTTP, stateless)
TransportWhoAuth
StdioIDE / local agentsServer default from GRAFT_DEV_TOKEN
HTTPRemote agentsBearer on the connection; anonymous callers refused outside loopback

Tools

ToolsRole
list_collections, describe_schemaDiscover collections, fields, and functions
list_content, get_content, search_contentRead authored content (search hits include sourcePath)
write_content, delete_contentAuthor or remove MDX; compile in the same call
put_assetUpload binaries for field.asset
list_functions, describe_function, run_functionSame gates as POST /api/fn/<name>
list_registry, describe_itemBrowse owned primitives before graft add
explain_errorMap any GraftError code to causes + recovery

run_function reuses the functions handler. Access rules, audit rows, rate limits, and approval gates apply identically. MCP is a door, not a bypass.

delete_content is destructive and human-gated. First call files an approval; retry with approval: "<id>" after graft approve.

Tools also carry MCP annotations, so a client can tell a read from a deletion without parsing a description. Results carry structuredContent alongside the text, and the sixteen tools with a fixed shape declare an outputSchema the server validates against before answering.

Resources and prompts

Documents are addressable as resources at graft://<branch>/<collection>/<slug>, so a client can attach one as context instead of spending a turn on get_content. Reads come from the authored files, so a resource reflects the working tree rather than the last compile. graft://<branch>/schema serves the describe_schema payload for attaching once.

Prompts carry the workflows filled in from the live project: author-document and plan-migration include the collection's real field list, revise-document the document's URI, fix-error this build's recovery text. Prompt arguments and resource variables autocomplete from what exists.

Two endpoints

A Graft project can serve two MCP endpoints, and they are different servers rather than one server with a setting.

SurfaceAuth
POST /api/mcpEverything aboveRequired
POST /mcpDocumentation onlyNone

The public one is built by createDocsMcpHandler, and it exists because docs platforms already publish this: Mintlify generates a docs MCP at /mcp for every site it hosts, and Cloudflare runs a documentation server separately from its authenticated API server. Agents arrive looking there.

It serves list_collections, list_content, get_content, search_content, explain_error and the document resources. Nothing else, and mostly for reasons that are not about writes: describe_schema carries the project's functions, list_registry its owned primitives, and the branch, compilation and approval listings its operations. Those are all reads, which is why "read-only" is the wrong test for what belongs on a public endpoint.

It is a separate factory rather than a flag on the authenticated one. There is no option on it that widens the surface and no allowAnonymous escape, so standing up the public endpoint cannot open the private one.

Proof

The test suite includes offline cold-agent runs (stdio and remote HTTP) under pnpm test:cold-agent. A live off-repo exercise authored a page and walked a human-gated delete with only the network MCP endpoint as context.

Next steps