Document write tools (writeDoc, editDoc)
Tool names: writeDoc, editDoc
Scopes: knowledge:read, knowledge:write
When to use
This family has two tools. writeDoc creates a document or replaces a whole document. editDoc replaces one exact passage. Use editDoc when you can: it changes the least.
The two tools are in the tool list only when all of these are true:
- The instance has document writing turned on. It is off by default, and the instance's operator turns it on. Writing to a member home and writing to a shared project are two separate switches.
- The person who connected is a member. An admin or staff connection never gets these tools, even with the
knowledge:writescope. The tools are absent, not refused. - The connection holds
knowledge:readandknowledge:write. A member's default grant includes both. A connection made beforeknowledge:writeexisted does not gain it: the person must reconnect, or mint a new token.
A member can write in two places:
- Their own member home, when home writing is on.
- An ordinary shared project where they hold a
writegrant, when shared-project writing is on. Areadgrant refuses the write. Another member's home answers as not found.
Every change is checked again against the person's current permissions. There is no confirmation step, no history and no undo. A replacement discards the old text.
The loop:
- Call
readDocfor the document. Keep theversionit returns. - Call
editDocorwriteDocwith thatversionasexpectedVersion. The change is refused if the file changed after your read. - To create a document, call
writeDocwithoutexpectedVersion. It is refused if a document already exists at that path.
Arguments that both tools take:
operationId: an id you make for this change, 8 to 64 characters of letters, digits,.,_or-. A UUID is good. A retry with the sameoperationIdand the same arguments returns the first result and does not write twice. The sameoperationIdwith different arguments is refused.project: a project id fromlistProjects.path: the path in the project. The file types are.md,.txt,.csv,.json,.xml,.yamland.yml, up to 1 MiB.reason: one line that says why, 1 to 100 characters after surrounding spaces are removed, with no control characters.
writeDoc also takes content (the complete new text) and an optional expectedVersion. editDoc also takes oldString, newString and a required expectedVersion. oldString must occur exactly once in the current file.
The result is JSON text with operationId, operation, path, bytes, created, reason, the new version, and project.
Example
A member creates a note in their own home with writeDoc:
{
"operationId": "7c1e4b2a-9f3d-4e6a-8b10-5d2c7f9e0a41",
"project": "member-14",
"path": "notes/2026-10-02-client-call.md",
"content": "# Client call, 2 October 2026\n\nThe client asked for the diagnostic at the standard price.\n",
"reason": "Notes from the client call"
}
{
"operationId": "7c1e4b2a-9f3d-4e6a-8b10-5d2c7f9e0a41",
"operation": "writeDoc",
"path": "notes/2026-10-02-client-call.md",
"bytes": 90,
"created": true,
"reason": "Notes from the client call",
"version": "AQ-example-opaque-version",
"project": "member-14"
}
Refusals and what to do
A refusal is an error result with the text Error in <tool>: <code>:<reason>. For a code that a retry can fix, the text adds (recoverable: …) with the right way to retry.
version_conflict:stale_version: the file changed after your read. CallreadDocagain, decide whether your change still applies to the new text, and retry with the newversionand a newoperationId. Never retry without the version.version_conflict:target_exists: you calledwriteDocwithoutexpectedVersion, and a document already exists at that path. Read it, and replace it only if that is what the person wants.version_conflict:target_absent: you passedexpectedVersion, but there is no document at that path.invalid_input:edit_no_matchorinvalid_input:edit_not_unique:oldStringdoes not occur in the file, or occurs more than once. Include more surrounding text.idempotency_conflict:operation_id_reused: theoperationIdwas used before with different arguments. Use a newoperationIdfor a new change.forbidden:home_writes_disabled: the target is a member home, but this instance has home writing turned off. Only shared-project writing is on. Write to a shared project where the person has awritegrant, or ask the operator.forbidden:not_a_member_home: the target is a shared project, but this instance has shared-project writing turned off. Only home writing is on. Write to the person's own home instead, or ask the operator.payload_too_large:content_too_large: the new document would be larger than 1 MiB. Make it smaller, or split it into two documents.invalid_input:no_change: the change would leave the document as it is, for examplenewStringis the same asoldString. Do not send it.forbidden:path_denied: the person does not have write permission on that project, for example only areadgrant.not_found:project_not_visible: the project does not exist, or this person cannot see it. Use a project fromlistProjects.repository_busy:…: another change to the same file is in progress. Wait a short time, then retry with the sameoperationIdand the same arguments.invalid_input:arguments_invalid (<fields>): the arguments do not match the schema, for example an extra argument or anoperationIdwith the wrong form. The named fields are wrong.- If
writeDocandeditDocare not in your tool list, a call returnsMCP error -32602: Tool writeDoc not found. One of the conditions above is not true. Read thewritingandpermissionssections ofgetDocs. Do not tell the person that Sasha cannot write.
Related
writeDocandeditDocare the two tools of this family.readDocgives theversionto pass asexpectedVersion.listProjectsandlistDocsgive the project ids and paths.getDocshas thewritingandpermissionssections.