nohead CLI. Its --json output and exit codes cost an agent less context than a list of tools.
Connect
The server speaks Streamable HTTP athttps://mcp.nohead.io/mcp. Authenticate with a project API key as the bearer token. Give the agent a key of its own, with only the scopes it needs, so its changes are attributed to it in history. New keys are published-only by default: an agent that changes content needs a key with Published records only off and schema:read, records:read and records:write, plus search:read to search, and assets:read and assets:write for images and files. The server offers only the tools a key’s scopes allow, and an agent starts by reading the schema.
Codex and Cursor read the key from the NOHEAD_API_KEY environment variable, and VS Code asks for it once and stores it, so none of their config files holds the key itself, and a project’s .cursor/mcp.json or .vscode/mcp.json can be committed. Claude Code saves the key in its own settings, outside the project, when you run its command: run it with NOHEAD_API_KEY set.
~/.codex/config.toml. Cursor reads .cursor/mcp.json in the project, or ~/.cursor/mcp.json for every project; it doesn’t load an env file for remote servers, so set NOHEAD_API_KEY in your shell profile. Copilot in VS Code reads .vscode/mcp.json in the workspace.
Other clients name these fields differently (some want "type": "http", some take the header as a separate setting): check your client’s documentation for remote servers, and send the key as Authorization: Bearer <key>.
Chat apps that only connect through a sign-in (OAuth), such as Claude.ai connectors, aren’t supported.
Tools
An agent sees only the tools its key’s scopes allow, and a published-only key reads published records only: its
list_revisions and diff_record calls are refused (403), since history can hold unpublished content, and so are its get_schedule calls. Each tool’s arguments carry the API reference’s own descriptions. Schema changes aren’t tools: make those as reviewed schema files with the CLI (nohead schema pull, diff and push).
schedule_record (Schedule publishing) takes publish_at, unpublish_at or both, as ISO 8601 times with a zone such as 2026-11-06T09:00:00Z, and replaces the whole schedule: a time left out is cleared, so an agent adding an unpublish time passes the publish time again. A publish time needs an unpublished record, and an unpublish time a published one or a publish time before it. The change is made within a minute of its time, as its own revision; one that can’t be made then (the record is live with a pending draft, say) is dropped, and get_schedule gives the reason in last_error. Scheduling makes no revision, so these tools take no note or expected_revision, and a schedule change can’t be reverted: the last write wins, so read the schedule with get_schedule just before replacing it. Schedule changes are in the activity log.
Agents read and write rich text as Markdown: tools that return records, diff_record and revert_record (its changes too, with dry_run) give rich text fields as Markdown unless the agent asks for rich_text: "html" or "document", and create_record, update_record, create_draft and update_draft take Markdown (Rich text).
Images and files
Withassets:read and assets:write, an agent can find a project’s assets, fix missing alt text, and upload an image to put on a record. Asset tools take no note or expected_revision, since assets have no history.
An upload takes the same three requests as over HTTP: two tools, with the agent sending the file itself in between.
start_uploadwith the file’sfilename,content_typeand exactbyte_size. It returns the pending asset and itsupload: amethod,urlandheaders, and when itexpires_at. A file larger than your plan allows, or on Free one that would go over the organization’s storage, is refused with402 plan_limit_exceeded.- The agent sends the file’s bytes to
upload.urlwithupload.methodand every header inupload.headers, beforeupload.expires_at: for examplecurl -X PUT "$UPLOAD_URL" -H "Content-Type: image/png" --data-binary @cover.png. The bytes go straight to storage, not through the MCP server. complete_uploadchecks the file and marks the assetready.
id (ast_…) is then the value of an asset field, set with update_record ("data": {"cover": "ast_…"}, or a list of IDs with multiple on), or an image in rich text, . get_schema shows which fields are asset fields and the types they accept.
Guardrails
The tools add guardrails for agents:- A note on every change to a record. Each one takes a required
notesaying why, up to 500 characters. It is stored in the record’s history next to the key’s name and the client, MCP. - No overwriting. Changes to an existing record take the
expected_revisionthe agent read, and the server’s instructions ask agents to pass it. If someone changed the record since, the change is refused withprecondition_failed, and the agent should read it again. Without it, the last write wins. - Safe retries. Every write takes an optional
idempotency_key. Retrying with the same key never applies a change twice. - Review before it goes live. A change to a published record shows on your sites at once, so the server’s instructions ask agents to propose it as a draft instead, unless you asked for a direct edit. The draft waits until a person merges it in the editor (Publish changes) or with
nohead drafts merge: no tool merges or schedules a draft. A record has at most one draft, andcreate_drafton a record that has one is refused withdraft_existsand itsdraft_id, so the agent changes that one withupdate_draft, passing the draft’srevisionasexpected_revision. - Nothing irreversible. Every record change is a revision you can revert, and
revert_recordpreviews withdry_run: true. New records start unpublished.
type, message and details (each with its code) the API reference documents.