MCP chat can report a wiki edit without persistence; add a verified page-write primitive
Summary
An MCP client can ask llm_wiki_chat to maintain a wiki page and receive an assistant response stating that the edit was completed even though no wiki.write_page tool call succeeded and the page was not changed. The MCP surface has readback/search tools, but no deterministic page-write tool, so clients cannot execute or verify the requested mutation through a non-conversational operation.
This was reproduced on the Linux v0.6.8 release (v0.6.8 → commit b4b544abe0dd4c75cd630ef464c41678a5f31cb8). I also checked the v0.6.11 MCP tool list: it still exposes read/search/chat/rescan/embed operations but no page-write primitive.
Reproduction
- Start LLM Wiki with the local API/MCP enabled and authentication configured.
- Through MCP, call
llm_wiki_statusand verify the API is healthy and authenticated. - Bind the MCP session to a known project with
llm_wiki_set_project. - Read an existing Markdown page with
llm_wiki_read_fileand retain its exact content. - Call
llm_wiki_chatwith an explicit instruction to append a unique marker to that page. - Observe that the assistant response may say the append/update was completed.
- Read the same page again with
llm_wiki_read_fileand search for the unique marker withllm_wiki_search.
Actual behavior
- The chat response reported the edit as completed.
- No successful
wiki.write_pageevent established that a mutation occurred. - Exact MCP readback was unchanged.
- Exact marker search found no persisted marker.
- A subsequent chat request acknowledged that the available context was retrieval-only and did not execute
wiki.write_page.
No direct filesystem write was used in this reproduction.
Expected behavior
At least one of the following should hold:
- MCP provides a deterministic authenticated write operation whose success means the requested bytes were persisted; or
llm_wiki_chatmust not report a wiki mutation as completed unless the response contains a successful mutation tool event and the backend has verified persistence.
A model-generated statement alone should never be usable as evidence of a write.
Why this matters
This is a correctness and auditability problem for agents that maintain operational knowledge. The current response can create a false-success state: an automation run proceeds as if governance or incident documentation was published while canonical Wiki state remains unchanged.
The existing MCP readback is useful for detecting the mismatch after the fact, but clients should not have to interpret conversational claims as write acknowledgements.
Relevant source behavior
At v0.6.8:
mcp-server/src/index.tsexposes status/projects/project binding/files/read/reviews/search/chat/graph/rescan, but no page-write tool.llm_wiki_chatreturns the backend assistant message plus references/tool events.- The Rust Agent already has a project-bound
wiki.write_pageimplementation (src-tauri/src/agent/tools.rs), including explicit overwrite handling.
The gap is therefore at the authenticated HTTP API/MCP boundary and in mutation-success semantics, rather than an absence of an internal write implementation.
Proposed solution
Add a narrow deterministic page-write route and MCP tool, while also preventing unverified mutation claims from being treated as success.
HTTP API
Add an authenticated endpoint such as:
POST /api/v1/projects/{project_id}/pages/writeRequest:
{
"path": "wiki/operations/example.md",
"content": "# Exact content\n",
"allowOverwrite": false
}Recommended constraints:
- Resolve only projects already known to the app.
- Require a canonical project-relative Markdown path under
wiki/. - Reject absolute paths, traversal, ambiguous/noncanonical components, symlink escape, empty paths, and unknown request fields.
- Apply explicit UTF-8 byte limits to both path and content.
- Default
allowOverwritetofalse; replacing an existing page must require explicit opt-in. - Reuse the existing project-bound
wiki.write_pageimplementation rather than introducing a second filesystem authority path. - Read the persisted page back and compare it byte-for-byte with the requested content before returning success.
- Return structured
ok,path,allowOverwrite, andverifiedfields; propagate API/write/readback failures as errors.
MCP
Expose a tool such as llm_wiki_write_page with:
project_idresolved through the existing pinned-project scope;- required
pathandcontent; - optional
allow_overwrite, defaulting tofalse; - the same client-side canonical-path and byte-bound checks as defense in depth;
- a result only after the API reports verified persistence.
Chat safety
For llm_wiki_chat, either:
- annotate responses with a machine-readable mutation status derived from successful tool events; or
- reject/qualify assistant text claiming a mutation when no successful mutation event exists.
Suggested tests
- Exact encoded project ID and authenticated JSON body.
- Project-session pinning rejects cross-project writes.
- New canonical
wiki/**/*.mdpage succeeds. - Existing page fails unless overwrite is explicitly enabled.
- Absolute, traversal, noncanonical, non-Markdown, symlink-escape, empty, and oversized requests fail closed.
- Unknown request fields are rejected.
- API/write/readback errors propagate as failures.
- Success requires byte-for-byte persisted-content equality.
- A chat response cannot be classified as a successful mutation without a successful write event.
Local validation of the proposed MCP shape
A local proof of concept added canonical-path/content bounds and four MCP client tests covering the exact request body/project encoding, default and explicit overwrite behavior, pre-request rejection, and API-error propagation. The MCP TypeScript suite passed 24/24 and Rust cargo check passed against the pinned v0.6.8 source. This is offered as solution detail, not as a claim that an upstream-ready patch has already been submitted.
Source: nashsu/llm_wiki