#754·llm_wiki

MCP chat can report a wiki edit without persistence; add a verified page-write primitive

Author: gregoryhornCreated Sep 14, 2026Updated Sep 14, 2026

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

  1. Start LLM Wiki with the local API/MCP enabled and authentication configured.
  2. Through MCP, call llm_wiki_status and verify the API is healthy and authenticated.
  3. Bind the MCP session to a known project with llm_wiki_set_project.
  4. Read an existing Markdown page with llm_wiki_read_file and retain its exact content.
  5. Call llm_wiki_chat with an explicit instruction to append a unique marker to that page.
  6. Observe that the assistant response may say the append/update was completed.
  7. Read the same page again with llm_wiki_read_file and search for the unique marker with llm_wiki_search.

Actual behavior

  • The chat response reported the edit as completed.
  • No successful wiki.write_page event 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:

  1. MCP provides a deterministic authenticated write operation whose success means the requested bytes were persisted; or
  2. llm_wiki_chat must 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.ts exposes status/projects/project binding/files/read/reviews/search/chat/graph/rescan, but no page-write tool.
  • llm_wiki_chat returns the backend assistant message plus references/tool events.
  • The Rust Agent already has a project-bound wiki.write_page implementation (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/write

Request:

json
{
  "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 allowOverwrite to false; replacing an existing page must require explicit opt-in.
  • Reuse the existing project-bound wiki.write_page implementation 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, and verified fields; propagate API/write/readback failures as errors.

MCP

Expose a tool such as llm_wiki_write_page with:

  • project_id resolved through the existing pinned-project scope;
  • required path and content;
  • optional allow_overwrite, defaulting to false;
  • 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/**/*.md page 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.