Summary
Three MCP write paths treat a document path that does not exist as a brand-new document instead of refusing.
For an agent the practical failure is that edit blames the find string for what is actually a wrong path, so the agent re-reads the file from disk, sees the text plainly there, and retries forever. Meanwhile write ... position: "append" against the same wrong path reports success and creates a phantom file elsewhere in the tree.
Environment
- MCP server:
npx -y @inkeep/open-knowledge@latest mcp → 0.75.0 (this is the stdio registration ok init writes)
- Project server: 0.68.6 (CLI on PATH), started with
ok start --only server --idle-shutdown off
- macOS 27.0.0 arm64,
content.dir: ., client is Claude Code
The version split is what the default registration produces: the MCP entry pins @latest while the CLI on PATH is whatever the package manager installed. I could not retest with matched versions — other live sessions held collaboration clients, and ok stop correctly refused to kill them.
Reproduction
1. Body find/replace against a path with no document
edit({ cwd, document: { path: "wiki/zz-probe", find: "a word that is definitely here", replace: "replaced" } })
→ Error: Text not found in document.
$ ls wiki/zz-probe.md
ls: wiki/zz-probe.md: No such file or directory
Nothing was created, but the error names the text, not the missing document. It is byte-identical to the error you get when the document exists and the find string is stale.
2. Frontmatter patch against the same nonexistent path
edit({ cwd, document: { path: "wiki/zz-probe", frontmatter: { title: "probe" } } })
→ Frontmatter patched (1 set).
$ cat wiki/zz-probe.md
---
title: probe
---
A new file, with nothing in the response saying this was a create rather than a patch.
3. Append against a nonexistent path
write({ cwd, document: { path: "wiki/zz-probe-2", content: "appended line\n", position: "append" } })
→ Written successfully (append).
New file created. The only signal is an advisory orphan hint ("This doc has no backlinks yet"), which reads as routine.
4. Control — the same find/replace on a document that does exist
edit({ cwd, document: { path: "wiki/zz-probe-2", find: "appended line", replace: "appended line (control passed)" } })
→ Edit applied successfully.
So find/replace itself is fine. The defect is entirely in how a missing document is reported.
What this cost in practice
Our content root is the repository root; a knowledge bundle lives under .okf/. Several agents addressed documents bundle-relative — system/ways-of-working/operations — instead of .okf/system/ways-of-working/operations.
The dotted path is correctly refused (docName path segments must not start with "."), which is clear. The un-prefixed path is the problem: it silently resolves to a new document at the repository root. Result:
- every body find/replace returned "Text not found in document", so the agents concluded the document's content had drifted and kept re-reading it from disk, where the text was plainly present;
- the documented append workaround then reported success and wrote 6,945 bytes into a phantom
system/ways-of-working/operations.md at the repo root;
- nobody noticed for a day, and 2,119 bytes of that content never reached the real document.
Suggested fix
edit with find/replace against a path that has no document should fail with a distinct error — e.g. No document at "<path>" — rather than Text not found in document. Today the two are indistinguishable and send the agent to debug the wrong thing.
- A call that creates a document as a side effect (
edit + frontmatter, write + position: append/prepend) should say so in the response text — something like Created new document <path> — so an agent aiming at an existing document can detect the miss on the first call.
- Optionally: when a nonexistent path's trailing segments match an existing document elsewhere in the tree, suggest it.
Happy to test a fix against the same setup.
Summary
Three MCP write paths treat a document path that does not exist as a brand-new document instead of refusing.
For an agent the practical failure is that
editblames the find string for what is actually a wrong path, so the agent re-reads the file from disk, sees the text plainly there, and retries forever. Meanwhilewrite ... position: "append"against the same wrong path reports success and creates a phantom file elsewhere in the tree.Environment
npx -y @inkeep/open-knowledge@latest mcp→ 0.75.0 (this is the stdio registrationok initwrites)ok start --only server --idle-shutdown offcontent.dir: ., client is Claude CodeThe version split is what the default registration produces: the MCP entry pins
@latestwhile the CLI on PATH is whatever the package manager installed. I could not retest with matched versions — other live sessions held collaboration clients, andok stopcorrectly refused to kill them.Reproduction
1. Body find/replace against a path with no document
Nothing was created, but the error names the text, not the missing document. It is byte-identical to the error you get when the document exists and the find string is stale.
2. Frontmatter patch against the same nonexistent path
A new file, with nothing in the response saying this was a create rather than a patch.
3. Append against a nonexistent path
New file created. The only signal is an advisory
orphanhint ("This doc has no backlinks yet"), which reads as routine.4. Control — the same find/replace on a document that does exist
So find/replace itself is fine. The defect is entirely in how a missing document is reported.
What this cost in practice
Our content root is the repository root; a knowledge bundle lives under
.okf/. Several agents addressed documents bundle-relative —system/ways-of-working/operations— instead of.okf/system/ways-of-working/operations.The dotted path is correctly refused (
docName path segments must not start with "."), which is clear. The un-prefixed path is the problem: it silently resolves to a new document at the repository root. Result:system/ways-of-working/operations.mdat the repo root;Suggested fix
editwithfind/replaceagainst a path that has no document should fail with a distinct error — e.g.No document at "<path>"— rather thanText not found in document. Today the two are indistinguishable and send the agent to debug the wrong thing.edit+frontmatter,write+position: append/prepend) should say so in the response text — something likeCreated new document <path>— so an agent aiming at an existing document can detect the miss on the first call.Happy to test a fix against the same setup.