The same workspace, over MCP
notdocs ships a Model Context Protocol server with one tool per operation in the API, generated from the same contract that the web app and the CLI use. An agent lists, reads, creates and updates documents, runs query blocks, searches, follows the change feed and works through comment threads, and every write goes through the same three-way merge a person's save does.
Locally the server runs over stdio. Hosted, it runs over streamable HTTP at https://notdocs.dev/api/mcp. Claude and other MCP clients that can sign you in connect with OAuth; an agent that runs unattended, in CI or on a server, uses a project token.
Connect Claude
Claude connects to your account, not to one project: you sign in to notdocs, choose read or read and write, and Claude then works as you in every workspace you can open.
On claude.ai, in the desktop app or on mobile, add a custom connector under Settings › Connectors with the URL https://notdocs.dev/api/mcp. Claude opens notdocs to ask for your consent; allow it and you are sent back.
claude mcp add --transport http notdocs https://notdocs.dev/api/mcpThen run /mcp in Claude Code and pick notdocs to sign in.
A client that supports MCP authorization finds everything from the endpoint's 401: the protected resource metadata at /.well-known/oauth-protected-resource/api/mcp names notdocs as the authorization server, which takes dynamic client registration and client ID metadata documents, with PKCE. Scopes are read and write.
Tokens
A token belongs to one project and has a scope: read or write. Create one in the project's settings or with the CLI; the secret starts with nd_ and is shown once.
notdocs token create --name "claude on my laptop" --scope write
notdocs token create --name "changelog bot" --path docs/changelog --path notes
notdocs token list
notdocs token revoke <id>A write token can be limited to folders (--path, repeatable, or "Folders it may write" in the settings): it still reads the whole project, but writes only documents, comments and images under those folders, as paths from the repository root. A move must start and end inside them, and is refused when a document outside them links to the moved one, since that link would be rewritten in the same commit. Operations that write across the project (markdown.fix, index.rebuild, docs.moveMany) are refused to such a token.
On GitHub, a commit an agent makes is authored by its token's name as <name>@agent and committed by notdocs, so the repository's history says which agent changed what; a person's commits carry their name and email address, and a commit made by an app they connected (Claude, say) is authored by them with a Via: <app> trailer in its message.
Connect a client
Local folder, no account:
claude mcp add notdocs -- notdocs mcp --serve ./my-projectHosted workspace, from a checkout that is linked to it:
notdocs link --url https://acme.notdocs.dev --project handbook
claude mcp add notdocs -- notdocs mcpOr connect straight to the hosted endpoint:
claude mcp add --transport http notdocs https://acme.notdocs.dev/api/mcp \
--header "Authorization: Bearer nd_…"{
"mcpServers": {
"notdocs": {
"url": "https://acme.notdocs.dev/api/mcp",
"headers": { "Authorization": "Bearer nd_…" }
}
}
}For a local folder, use a stdio entry with the command notdocs and the arguments mcp --serve ./my-project.
Streamable HTTP: POST https://acme.notdocs.dev/api/mcp with Authorization: Bearer nd_….
Stdio: the command is notdocs mcp, with --serve <folder> to run an in-memory daemon on a local folder at the same time, or a linked checkout to reach a hosted workspace.
notdocs link writes a git-ignored .notdocs/link.json; the token stays in NOTDOCS_TOKEN unless you pass --token.
Tools
Every operation is a tool with the same input as in the API, named with an underscore for the dot (docs_list). Over HTTP, with a token or an OAuth connection, the read and write tools are listed and the admin ones left out, since neither may run them. Each tool has a title and says whether it only reads and whether it can overwrite or remove something, so a client can run the reads without asking and confirm the rest. The ones an agent uses most:
| Tool | What it does |
|---|---|
docs.list | Every document on the branch, optionally filtered by type or path prefix |
docs.get | One document by id or path, markdown body included |
docs.create | A document from a template, or blank, at a path |
docs.update | Text or frontmatter changes, merged against the hash the agent last read |
docs.delete | Remove a document |
query.run | A query block: type, where, sort, groupBy |
search | Full-text plus semantic search |
changes.since | The change feed after a cursor, cheaper than re-reading everything |
comments.list, comments.add, comments.reply, comments.resolve | Comment threads |
templates.list, config.get, branches.list | What the repository offers |
Retrying a write
A write whose answer never arrived (a timeout, a dropped connection) may or may not have happened. Send it again with the same idempotency key and it is not made twice: the answer of the first attempt comes back instead. Every tool that changes something takes an optional idempotencyKey, and a tool that only reads takes none, even one that needs admin scope such as members_list; over HTTP the key is the Idempotency-Key header on POST /api/ops/<name>, and GET /api/ops marks the operations that honour it with idempotent.
- A key is 1 to 255 letters, digits and
-_.:~, belongs to the caller (a person or a token) and lasts 24 hours. - The same key with another operation or another input is refused with
conflict: a key names one request. - While the first attempt is still running, a repeat is answered
conflictwithRetry-After: 1. - A success is kept, and so is a refusal about the request itself (
invalid,forbidden,not_found,plan_limit). Aconflict, a rate limit or a server failure is not, so a retry with the same key runs the write again. tokens_createignores the key: its answer is a secret that is never stored.
The TypeScript SDK and the web app send a new key with every write, and the CLI does too unless --idempotency-key names one. Reads ignore the header.
Resources and prompts
Two resources: notdocs://<projectId>/docs is the index of the branch as JSON, and notdocs://<projectId>/docs/<docId> is one document as markdown.
Three prompts: Plan from a spec reads a spec document and drafts task documents from it; Review comments lists the unresolved threads on a document and acts on them; Review an organized docs root runs links_check after markdown was moved under the docs root, fixes the links it left broken, and suggests types and folder names without changing them.
SKILL.md and llms.txt
An agent that has never seen notdocs can set it up by itself. notdocs.dev/SKILL.md is an agent skill: how to install the CLI, initialise a repository, connect the MCP server, and the rules for editing documents without overwriting a person. notdocs.dev/llms.txt is the map of this site for a language model.
Read https://notdocs.dev/SKILL.md and set up notdocs in this repository.Published sites
A project's published site is readable by an agent without any credential, as it is by a visitor, and sees exactly what a visitor sees. Each published page is served as markdown at its address with .md (https://acme.notdocs.dev/handbook/p/guides/setup.md), or to a request whose Accept header prefers text/markdown; llms.txt under the project's base lists every page that way; and /api/public/search?org=acme&project=handbook&q=<words> searches the listed pages and answers JSON. A document shared by link is served at <id>.md to whoever holds the id and never listed or found. On a page, a browser that offers WebMCP registers three read-only tools for its own agent: notdocs_site_pages, notdocs_site_page and notdocs_site_search. Writing needs a token or a connected app as above.
Instructions for the agent
notdocs init appends a short section to your CLAUDE.md or AGENTS.md: where the documents are, which types exist, and that changes.since is cheaper than re-reading the workspace. The workspace assistant inside the web app uses the same operations with the permissions of the person who is signed in, and asks before it writes.