<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harry Smart</title>
    <description>The latest articles on DEV Community by Harry Smart (@songofhawk).</description>
    <link>https://dev.to/songofhawk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4082466%2F62ecba2f-ba30-47dc-805a-0df64a5c3585.png</url>
      <title>DEV Community: Harry Smart</title>
      <link>https://dev.to/songofhawk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/songofhawk"/>
    <language>en</language>
    <item>
      <title>Designing a Document API That AI Agents Cannot Break</title>
      <dc:creator>Harry Smart</dc:creator>
      <pubDate>Tue, 08 Sep 2026 05:57:35 +0000</pubDate>
      <link>https://dev.to/songofhawk/designing-a-document-api-that-ai-agents-cannot-break-3fh6</link>
      <guid>https://dev.to/songofhawk/designing-a-document-api-that-ai-agents-cannot-break-3fh6</guid>
      <description>&lt;p&gt;You cannot make an AI agent incapable of proposing a bad edit, but you can design a document API so one stale, oversized, duplicated, or partially authorized request cannot silently corrupt the source of truth. The core controls are stable block identity, version preconditions, atomic operations, idempotent retries, least privilege, audit evidence, and recoverable history.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Doco series · Article 14 · API engineering guide&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“Agents cannot break documents” is a useful design goal only after it is translated into failure modes. No API can guarantee that a grammatically valid change is factually wise. It can guarantee that concurrency, retries, partial failures, and permission mistakes behave predictably.&lt;/p&gt;

&lt;p&gt;Doco is the document system I am building, so the examples below describe its first-party contract. The principles apply to any API that lets autonomous or semi-autonomous clients maintain important text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define “break” before defining endpoints
&lt;/h2&gt;

&lt;p&gt;An agent breaks a document system when it can cause one of these outcomes without an explicit, recoverable signal:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure&lt;/th&gt;
&lt;th&gt;Unsafe behavior&lt;/th&gt;
&lt;th&gt;Required control&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lost update&lt;/td&gt;
&lt;td&gt;Old content silently replaces a newer edit&lt;/td&gt;
&lt;td&gt;Version precondition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong target&lt;/td&gt;
&lt;td&gt;A moved paragraph can no longer be addressed&lt;/td&gt;
&lt;td&gt;Stable block ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oversized mutation&lt;/td&gt;
&lt;td&gt;One sentence change replaces the whole document&lt;/td&gt;
&lt;td&gt;Bounded block operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial batch&lt;/td&gt;
&lt;td&gt;Two of five edits apply before the third fails&lt;/td&gt;
&lt;td&gt;Atomic transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate retry&lt;/td&gt;
&lt;td&gt;Network retry creates the same section twice&lt;/td&gt;
&lt;td&gt;Idempotency key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Excess authority&lt;/td&gt;
&lt;td&gt;Read workflow can delete or publish&lt;/td&gt;
&lt;td&gt;Narrow scopes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invisible origin&lt;/td&gt;
&lt;td&gt;Nobody can explain who changed what&lt;/td&gt;
&lt;td&gt;Audit event and provenance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bad recovery&lt;/td&gt;
&lt;td&gt;A mistake is permanent&lt;/td&gt;
&lt;td&gt;Snapshots and rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This table is more valuable than a long endpoint list. It turns a slogan into acceptance tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give every editable block a stable identity
&lt;/h2&gt;

&lt;p&gt;A path identifies a file. A line number identifies a temporary position. A heading identifies a label that users routinely rename. None is a durable paragraph address.&lt;/p&gt;

&lt;p&gt;Store a stable block ID in the canonical structured document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block_01K0Y7R5F…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"paragraph"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rollback begins after five minutes."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moving the block preserves the ID. Copying it creates a new one. Deleting it makes the old address explicitly absent rather than accidentally selecting whatever text now occupies line 42.&lt;/p&gt;

&lt;p&gt;Stable IDs also improve audit logs and citations: the same identifier can connect a search result, read response, write operation, change event, and browser highlight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make every write conditional on what the agent read
&lt;/h2&gt;

&lt;p&gt;A safe edit begins with a read that returns a version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /v1/documents/doc_123?view=tiptap
ETag: "sha256:abc123"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The write must include that version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;PATCH /v1/documents/doc_123/blocks/block_456
If-Match: "sha256:abc123"
Content-Type: application/json

{"text":"Rollback begins after three minutes."}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If another actor changed the document, the server rejects the request and returns the current version. &lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-if-match" rel="noopener noreferrer"&gt;RFC 9110&lt;/a&gt; defines &lt;code&gt;If-Match&lt;/code&gt; specifically for conditional requests and avoiding lost updates.&lt;/p&gt;

&lt;p&gt;Do not hide the conflict behind an automatic last-write-wins retry. The agent must reread the new state, reconsider its intended change, and either stop or submit a new proposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep edits narrow but version the whole document
&lt;/h2&gt;

&lt;p&gt;Block-level operations reduce the mutation surface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;insert after &lt;code&gt;block_A&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;replace &lt;code&gt;block_B&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;delete &lt;code&gt;block_C&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;move &lt;code&gt;block_D&lt;/code&gt; under heading &lt;code&gt;block_E&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yet the precondition should represent the canonical document content, not only the target block. A change elsewhere may alter the meaning of the planned edit. For example, a teammate may add an exception above a policy paragraph without touching the paragraph itself.&lt;/p&gt;

&lt;p&gt;Use &lt;a href="https://www.rfc-editor.org/rfc/rfc6902" rel="noopener noreferrer"&gt;JSON Patch&lt;/a&gt; only when its positional path semantics match the document model. In tree-shaped collaborative documents, domain operations over stable IDs are often clearer than array indexes such as &lt;code&gt;/content/17&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make batches atomic
&lt;/h2&gt;

&lt;p&gt;Agents frequently perform multi-step changes: create a heading, insert two paragraphs, and update a summary. Applying each request independently can leave half a result when the third operation fails.&lt;/p&gt;

&lt;p&gt;Accept an ordered batch under one document version and execute it in one transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"insert_after"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"after"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"block_A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"block"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"heading"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Recovery"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"insert_after"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"after"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"$0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"block"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"paragraph"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Restore the latest snapshot."&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"replace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"block_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"block_SUMMARY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Recovery procedure added."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Either every operation validates and applies, or none does. References such as &lt;code&gt;$0&lt;/code&gt; can target the stable ID allocated by an earlier operation in the same batch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design retries before production traffic does it for you
&lt;/h2&gt;

&lt;p&gt;Networks fail after the server commits but before the client receives the response. The agent retries because it cannot know whether the first request succeeded. Without idempotency, “append this checklist” creates two checklists.&lt;/p&gt;

&lt;p&gt;Creation and batch endpoints should accept an idempotency key bound to the authenticated actor and normalized request body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Idempotency-Key: task-8f4a-create-recovery-section
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replaying the same successful request returns the original result. Reusing the key with different input returns an explicit conflict. Failed validation should not become a permanently cached result that blocks a corrected retry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate capability from intent
&lt;/h2&gt;

&lt;p&gt;An agent prompt may say “do not delete,” but the server should not issue delete authority to a workflow that only reads.&lt;/p&gt;

&lt;p&gt;Useful scope boundaries include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;documents:read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;documents:write&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;documents:create&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;documents:delete&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sharing:manage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;knowledge_bases:admin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Begin with read-only tokens, short lifetimes, and the smallest workspace boundary. Store token hashes, support revocation, and rate-limit write bursts separately from reads.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://modelcontextprotocol.io/specification/draft/server/tools" rel="noopener noreferrer"&gt;MCP tools specification&lt;/a&gt; recommends human visibility and the ability to deny tool invocations. Tool annotations can describe read-only or destructive behavior, but enforcement belongs in authentication and authorization, not labels alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Return errors an agent can act on
&lt;/h2&gt;

&lt;p&gt;An HTML error page or ambiguous &lt;code&gt;400&lt;/code&gt; invites guesses. Return stable codes and recovery data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"version_conflict"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Document changed after it was read."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"current_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:def789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"request_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"req_01K0…"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Distinguish at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing precondition;&lt;/li&gt;
&lt;li&gt;stale version;&lt;/li&gt;
&lt;li&gt;missing scope;&lt;/li&gt;
&lt;li&gt;unknown or deleted block;&lt;/li&gt;
&lt;li&gt;invalid document structure;&lt;/li&gt;
&lt;li&gt;rate limit with retry guidance;&lt;/li&gt;
&lt;li&gt;idempotency-key conflict;&lt;/li&gt;
&lt;li&gt;incomplete or stale search cursor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model may still choose poorly. It should not need to infer the state transition from prose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preserve provenance and recovery
&lt;/h2&gt;

&lt;p&gt;Every successful mutation should record actor, token or agent identity, request ID, target blocks, source version, resulting version, timestamp, and origin channel. W3C's &lt;a href="https://www.w3.org/TR/prov-overview/" rel="noopener noreferrer"&gt;PROV overview&lt;/a&gt; provides a general model for describing entities, activities, and agents; a document API does not need the full standard to benefit from the same provenance questions.&lt;/p&gt;

&lt;p&gt;Snapshots should be taken before consequential writes or at a bounded cadence. Rollback should create a new version rather than erase the history that explains why recovery happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the hostile cases
&lt;/h2&gt;

&lt;p&gt;A happy-path integration test proves almost nothing. The acceptance suite should include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;two writers using the same initial version;&lt;/li&gt;
&lt;li&gt;a block moved between read and write;&lt;/li&gt;
&lt;li&gt;a duplicate request after a simulated timeout;&lt;/li&gt;
&lt;li&gt;failure in the middle of a five-operation batch;&lt;/li&gt;
&lt;li&gt;a read-only token attempting a write;&lt;/li&gt;
&lt;li&gt;a write token attempting to manage sharing;&lt;/li&gt;
&lt;li&gt;malformed rich-text nodes and duplicate block IDs;&lt;/li&gt;
&lt;li&gt;rollback after a valid but unwanted edit;&lt;/li&gt;
&lt;li&gt;a search cursor used after the index changes;

&lt;ol&gt;
&lt;li&gt;a retry storm hitting per-document rate limits.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The API is safe only when each failure has a deterministic, observable result.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can an API prevent an agent from writing incorrect information?
&lt;/h3&gt;

&lt;p&gt;Not completely. Structural controls prevent silent corruption and limit blast radius. Factual quality still requires source evidence, review rules, and human judgment for consequential content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not use last-write-wins for simplicity?
&lt;/h3&gt;

&lt;p&gt;Because it makes a successful HTTP response indistinguishable from silent data loss. Explicit conflicts cost an extra read but preserve both actors' work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are block IDs enough to make writes safe?
&lt;/h3&gt;

&lt;p&gt;No. They identify targets. Version preconditions, authorization, atomicity, idempotency, validation, audit logs, and recovery address different failure modes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should every tool call require human approval?
&lt;/h3&gt;

&lt;p&gt;Not necessarily. Read-only, low-risk operations can run autonomously within a narrow scope. Publication, deletion, permission changes, and ambiguous conflict resolution deserve explicit review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;An unbreakable document API does not promise perfect agent judgment. It makes dangerous state transitions narrow, conditional, atomic, attributable, and reversible. When every stale write fails visibly and every successful edit has a durable address and history, agents can participate without turning collaboration into data-loss roulette.&lt;/p&gt;




&lt;p&gt;Originally published on &lt;a href="https://doco.page/s/0c7aZszxqm-Z87HUH0J_Kw7OtXeT_HO_" rel="noopener noreferrer"&gt;Doco&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Doco is an open-source document workspace where humans and AI agents write together. &lt;a href="https://doco.page/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=content_series" rel="noopener noreferrer"&gt;Explore Doco&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>architecture</category>
      <category>security</category>
    </item>
    <item>
      <title>Why I Moved My Agent Knowledge Base Off Local Markdown</title>
      <dc:creator>Harry Smart</dc:creator>
      <pubDate>Tue, 01 Sep 2026 01:32:13 +0000</pubDate>
      <link>https://dev.to/songofhawk/why-i-moved-my-agent-knowledge-base-off-local-markdown-2lj3</link>
      <guid>https://dev.to/songofhawk/why-i-moved-my-agent-knowledge-base-off-local-markdown-2lj3</guid>
      <description>&lt;p&gt;I moved the shared, agent-maintained part of my knowledge base off local Markdown because synchronization, paragraph identity, permissions, and concurrent writes had become application problems. I still use Markdown as an open import/export format; I no longer ask a folder of files to behave like a live multi-user database.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Doco series · Article 13 · Migration retrospective&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not a breakup letter to Markdown. Plain text remains one of the best formats for notes: readable, portable, diffable, and supported almost everywhere. Obsidian's documentation is explicit that a vault stores notes as local Markdown files in a folder, which is exactly why it is durable and easy to inspect; see &lt;a href="https://obsidian.md/help/Files%2Band%2Bfolders/How%2BObsidian%2Bstores%2Bdata" rel="noopener noreferrer"&gt;How Obsidian stores data&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The boundary changed when the knowledge base stopped being only mine. I wanted people to edit in a browser while an agent searched, cited, and maintained the same material. At that point, every advantage of “just files” remained—but several missing behaviors became my responsibility.&lt;/p&gt;

&lt;p&gt;Doco is the system I am building in response, so this is a first-party migration account rather than a neutral product comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first warning was copy ambiguity
&lt;/h2&gt;

&lt;p&gt;On one computer, a Markdown vault feels canonical. Add a laptop, a phone, a cloud-sync folder, a Git remote, and an automation job, and “the file” becomes several replicas connected by workflows.&lt;/p&gt;

&lt;p&gt;Most of the time, the replicas converge. The difficult moments are the ones that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a device edits an old copy after waking from sleep;&lt;/li&gt;
&lt;li&gt;a sync provider creates a conflict file;&lt;/li&gt;
&lt;li&gt;a teammate sends an exported folder that immediately diverges;&lt;/li&gt;
&lt;li&gt;an agent reads before a pull and writes after a human change;&lt;/li&gt;
&lt;li&gt;a path changes and an external citation stops resolving.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Git makes divergence explicit and auditable. Its &lt;a href="https://git-scm.com/docs/git-merge" rel="noopener noreferrer"&gt;merge documentation&lt;/a&gt; also explains the operational truth: overlapping changes can stop the merge and leave conflicts for the user to resolve. That is a good engineering workflow, but it was not the real-time writing experience I wanted for every teammate and agent action.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second warning was paragraph identity
&lt;/h2&gt;

&lt;p&gt;Agents rarely need to replace an entire document. They need to update a deadline, add one observation, or fix a policy paragraph.&lt;/p&gt;

&lt;p&gt;In a local file, the common addresses are path, heading, line number, or surrounding text. Each is positional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;move the file and the path changes;&lt;/li&gt;
&lt;li&gt;rename the heading and the selector changes;&lt;/li&gt;
&lt;li&gt;insert a paragraph and line numbers move;&lt;/li&gt;
&lt;li&gt;edit the surrounding sentence and a text-match patch may fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted a paragraph to retain an identity independent of where it appeared. In Doco, document blocks carry stable IDs in the ProseMirror/Yjs state. Moving a block preserves its ID; copying it creates a new one. A citation can point to the same logical paragraph after an outline is reorganized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://prosemirror.net/docs/guide/" rel="noopener noreferrer"&gt;ProseMirror's guide&lt;/a&gt; describes documents as structured nodes rather than an undifferentiated text buffer. That structure is a better substrate for tables, callouts, tasks, code blocks, and bounded agent edits than pretending every operation is a line-oriented patch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The third warning was the read-write race
&lt;/h2&gt;

&lt;p&gt;The dangerous workflow looked harmless:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the agent reads &lt;code&gt;policy.md&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;I edit and save &lt;code&gt;policy.md&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the agent writes a complete replacement based on its older read;&lt;/li&gt;
&lt;li&gt;my change disappears or survives only in history.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A Git diff may reveal the loss later. A conflict-aware write prevents it at the boundary.&lt;/p&gt;

&lt;p&gt;The online system returns a content version with the read. The agent must present that version when it writes. If the document changed, the update fails and the agent must reread. This follows the conditional-request model of &lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-if-match" rel="noopener noreferrer"&gt;HTTP &lt;/a&gt;&lt;code&gt;If-Match&lt;/code&gt;, which is designed to prevent accidental overwrites of changed resources.&lt;/p&gt;

&lt;p&gt;The important shift was psychological: a conflict became an expected result, not an exceptional mess to hide with “last writer wins.”&lt;/p&gt;

&lt;h2&gt;
  
  
  The fourth warning was permissions
&lt;/h2&gt;

&lt;p&gt;A process that can open a local vault often inherits broad filesystem access. Restricting it usually means sandboxing directories or creating copies. Neither model naturally expresses “search this knowledge base, read those two documents, and update only documents in this workspace until Friday.”&lt;/p&gt;

&lt;p&gt;An online API can issue hashed, revocable tokens with separate read and write scopes, rate limits, and finite lifetimes. It can also record which actor changed which block from which source version.&lt;/p&gt;

&lt;p&gt;That does not make online access automatically safe. It moves safety into an explicit capability boundary that can be reviewed and tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I kept from Markdown
&lt;/h2&gt;

&lt;p&gt;The migration did not require converting every note into a proprietary dead end.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Markdown remains an import format.&lt;/li&gt;
&lt;li&gt;A single document or complete knowledge base can be exported back to Markdown.&lt;/li&gt;
&lt;li&gt;Images are exported alongside the documents with relative links.&lt;/li&gt;
&lt;li&gt;Self-hosting keeps the service boundary under the operator's control.&lt;/li&gt;
&lt;li&gt;Local browser persistence supports temporary disconnection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Markdown changed roles. It became a durable interchange and backup format while the collaborative document became the active source of truth.&lt;/p&gt;

&lt;p&gt;This separation is useful even outside Doco. A database can be authoritative without trapping users if it supports complete, documented exports. A file can remain portable without carrying all concurrency, authorization, and indexing responsibilities itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  A migration path that did not require a big bang
&lt;/h2&gt;

&lt;p&gt;The practical sequence was incremental:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Classify the vault.&lt;/strong&gt; Personal scratch notes stayed local; shared operational knowledge became the migration target.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Import without deleting.&lt;/strong&gt; The original Markdown remained a rollback source while headings, links, tables, and attachments were checked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose canonical documents.&lt;/strong&gt; Duplicate policies were resolved before automation could cite them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start agents read-only.&lt;/strong&gt; Search and citations were verified against the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable one bounded write.&lt;/strong&gt; A reversible block update tested identity and version conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test export.&lt;/strong&gt; The exit path was verified before declaring the online workspace authoritative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive the old shared vault.&lt;/strong&gt; Read-only history is less confusing than two writable sources of truth.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The hardest work was not format conversion. It was deciding which copy was canonical and which workflows still assumed local paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I would stay with local Markdown
&lt;/h2&gt;

&lt;p&gt;I would keep local Markdown as the source of truth when the notes are primarily personal, offline access is central, Git review is acceptable, and automation operates inside the same trusted device boundary.&lt;/p&gt;

&lt;p&gt;I would move a collection online when several people and agents need one current state, paragraph-level addresses, permissioned tools, live edits, and conflict-protected writes.&lt;/p&gt;

&lt;p&gt;The decision is not Markdown versus rich text. It is personal files versus a shared operational system.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Did you stop using Markdown?
&lt;/h3&gt;

&lt;p&gt;No. Markdown remains useful for authoring, exchange, versioned exports, and offline archives. I stopped using a local folder as the only authoritative state for shared human-agent work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Git a bad collaboration system for documents?
&lt;/h3&gt;

&lt;p&gt;No. Git is excellent when contributors are comfortable with branches, diffs, and explicit merges. It is less suitable for teammates who expect live browser editing or for frequent small agent updates that should appear immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does moving online sacrifice ownership?
&lt;/h3&gt;

&lt;p&gt;It can if the service lacks exports or self-hosting. The migration should include a tested exit path. Doco supports Markdown export and an MIT-licensed self-hosted deployment for that reason.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should every personal note move to an online knowledge base?
&lt;/h3&gt;

&lt;p&gt;No. Move only the material whose collaboration and agent requirements justify the operational service. Keeping private scratch notes local reduces complexity and exposure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Local Markdown did not fail at being a file format. I asked it to provide stable block identity, shared live state, permissions, indexing, and concurrency control. Moving the shared agent knowledge base online let each layer do one job well—and keeping open Markdown export made the decision reversible.&lt;/p&gt;




&lt;p&gt;Originally published on &lt;a href="https://doco.page/s/oRWI5x5gjrTDUjfRNGeII838WVTsJJNE" rel="noopener noreferrer"&gt;Doco&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Doco is an open-source document workspace where humans and AI agents write together. &lt;a href="https://doco.page/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=content_series" rel="noopener noreferrer"&gt;Explore Doco&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>database</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Your AI Agent Does Not Need RAG. It Needs a Readable Knowledge Base.</title>
      <dc:creator>Harry Smart</dc:creator>
      <pubDate>Sun, 30 Aug 2026 08:51:54 +0000</pubDate>
      <link>https://dev.to/songofhawk/your-ai-agent-does-not-need-rag-it-needs-a-readable-knowledge-base-1ho</link>
      <guid>https://dev.to/songofhawk/your-ai-agent-does-not-need-rag-it-needs-a-readable-knowledge-base-1ho</guid>
      <description>&lt;p&gt;An AI agent does not automatically need a custom RAG pipeline to use organizational knowledge. If the corpus is maintained, searchable, structurally readable, citable, and available through bounded tools, direct retrieval can be the simpler system. Use RAG when ranking and synthesizing across a large or heterogeneous corpus is genuinely the problem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Doco series · Article 12 · Architecture opinion&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“We need the agent to answer from our documents” often becomes “we need RAG” before anyone writes down the actual job.&lt;/p&gt;

&lt;p&gt;That jump confuses a technique with a requirement. Retrieval-augmented generation is a valuable family of architectures. The original &lt;a href="https://arxiv.org/abs/2005.11401" rel="noopener noreferrer"&gt;RAG paper by Lewis et al.&lt;/a&gt; combines a generator's parametric memory with retrieved non-parametric memory for knowledge-intensive tasks. It is especially relevant when the system must rank evidence from a large corpus and generate an answer from it.&lt;/p&gt;

&lt;p&gt;But many workplace agents are not open-domain question-answering systems. They need to find a policy, inspect the relevant section, cite it, and sometimes update one paragraph. For that workflow, the first problem is often not model training, embeddings, or chunk orchestration. It is that the knowledge base is difficult for software to read safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the job, not the acronym
&lt;/h2&gt;

&lt;p&gt;Consider four requests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;“What is the current refund window? Show me the policy.”&lt;/li&gt;
&lt;li&gt;“Update the on-call owner in the release checklist.”&lt;/li&gt;
&lt;li&gt;“Compare every customer interview about onboarding confusion.”&lt;/li&gt;
&lt;li&gt;“Answer arbitrary questions across ten million mixed-format documents.”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first request needs search, a canonical source, and a citation. The second needs structured mutation and concurrency protection. The third may benefit from ranking, clustering, or semantic retrieval. The fourth is a strong candidate for a serious retrieval pipeline.&lt;/p&gt;

&lt;p&gt;Calling all four “RAG” hides the differences that determine the architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a readable knowledge base exposes
&lt;/h2&gt;

&lt;p&gt;Readable does not mean “the model can receive a giant Markdown export.” It means the system provides a deliberate set of operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browse:&lt;/strong&gt; list knowledge bases, folders, documents, and outlines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search:&lt;/strong&gt; find relevant blocks with paths and surrounding context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read:&lt;/strong&gt; request Markdown, plain text, or lossless structured content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget:&lt;/strong&gt; limit output and continue from an explicit cursor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cite:&lt;/strong&gt; return a stable document and block address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check freshness:&lt;/strong&gt; identify the source and index versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit safely:&lt;/strong&gt; patch a bounded unit against the version that was read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These operations let the agent progressively disclose context. It can inspect an outline before loading a section, and search before reading a whole document. The result is not magical retrieval. It is a knowledge interface whose behavior can be tested.&lt;/p&gt;

&lt;p&gt;Doco is the system I am building, so this is a first-party architecture argument. Doco currently uses structured full-text search rather than claiming universal semantic search. SQLite's official &lt;a href="https://www.sqlite.org/fts5.html" rel="noopener noreferrer"&gt;FTS5 documentation&lt;/a&gt; describes a full-text search virtual table with phrase, prefix, NEAR, and boolean query support. That kind of retrieval is often enough for known policies, identifiers, names, and operational terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chunking is not neutral
&lt;/h2&gt;

&lt;p&gt;A typical RAG pipeline copies source documents, divides them into chunks, embeds those chunks, and builds a retrieval index. Each step introduces a projection that can drift from the source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a heading may be separated from the paragraph it qualifies;&lt;/li&gt;
&lt;li&gt;a table row may lose its headers;&lt;/li&gt;
&lt;li&gt;a chunk ID may change after a small source edit;&lt;/li&gt;
&lt;li&gt;the index may lag behind the document;&lt;/li&gt;
&lt;li&gt;a generated answer may cite a chunk that has no durable user-facing address.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are solvable engineering problems. They are still problems. If the source system already has stable blocks, heading paths, versions, and search, preserve those semantics before inventing another identity layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Direct retrieval is not “stuff everything into context”
&lt;/h2&gt;

&lt;p&gt;The alternative to RAG is not a 200,000-token dump. A tool-using agent can follow a staged loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;list → inspect outline → search → read relevant blocks → verify source → answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Model Context Protocol separates resources and tools so a server can expose contextual data and executable operations through a negotiated interface; see the &lt;a href="https://modelcontextprotocol.io/specification/2025-06-18/server/index" rel="noopener noreferrer"&gt;MCP server concepts&lt;/a&gt;. That makes direct, structured retrieval portable across agent clients without requiring the knowledge base to pretend it is a chat model.&lt;/p&gt;

&lt;p&gt;The agent should also know when the result is incomplete. “No match in the first page” must not become “the knowledge base contains no answer.” Search cursors, completeness flags, and index freshness are mundane metadata with large reliability consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  When RAG is the right choice
&lt;/h2&gt;

&lt;p&gt;Use RAG—or another dedicated retrieval architecture—when one or more of these conditions dominate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the corpus is too large for deterministic browsing and lexical search alone;&lt;/li&gt;
&lt;li&gt;users ask broad natural-language questions with weak keyword overlap;&lt;/li&gt;
&lt;li&gt;evidence comes from heterogeneous external sources without shared structure;&lt;/li&gt;
&lt;li&gt;ranking quality is a core product capability worth evaluating continuously;&lt;/li&gt;
&lt;li&gt;the system must synthesize several passages for every answer;&lt;/li&gt;
&lt;li&gt;you can operate ingestion, deduplication, access control, freshness, and retrieval evaluation as real production systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even then, RAG does not replace a readable source of truth. It becomes a projection over that source. Every retrieved passage should retain enough provenance to reach the authoritative document.&lt;/p&gt;

&lt;h2&gt;
  
  
  When direct knowledge tools are enough
&lt;/h2&gt;

&lt;p&gt;A simpler tool layer is often sufficient when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the corpus is a bounded team workspace;&lt;/li&gt;
&lt;li&gt;documents have useful titles, headings, and stable structure;&lt;/li&gt;
&lt;li&gt;queries contain operational vocabulary found in the source;&lt;/li&gt;
&lt;li&gt;the agent must cite or edit the exact source;&lt;/li&gt;
&lt;li&gt;freshness matters more than fuzzy recall;&lt;/li&gt;
&lt;li&gt;the team values debuggable behavior over opaque ranking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, an incident assistant looking up “payments rollback timeout” may perform better with full-text search over current runbooks than with a complex semantic stack that was indexed yesterday. The important comparison is empirical: measure whether the correct source block is found, whether its version is current, and whether the answer cites it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture can grow incrementally
&lt;/h2&gt;

&lt;p&gt;Starting with a readable knowledge base does not prevent semantic retrieval later. It creates a better foundation for it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make canonical documents structurally accessible.&lt;/li&gt;
&lt;li&gt;Add stable block IDs and source versions.&lt;/li&gt;
&lt;li&gt;Implement deterministic search with complete, testable results.&lt;/li&gt;
&lt;li&gt;Record real queries that deterministic retrieval misses.&lt;/li&gt;
&lt;li&gt;Add semantic ranking where those failures justify it.&lt;/li&gt;
&lt;li&gt;Keep citations bound to the canonical blocks, not only vector-store chunks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sequence turns RAG from a fashionable default into a targeted response to measured retrieval failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is this an argument against RAG?
&lt;/h3&gt;

&lt;p&gt;No. RAG is useful for knowledge-intensive generation, especially across large or heterogeneous corpora. The argument is against treating it as the automatic first requirement for every agent that needs documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is full-text search enough for every knowledge base?
&lt;/h3&gt;

&lt;p&gt;No. It can miss paraphrases and concepts that share little vocabulary. It is strong for names, identifiers, policies, and operational phrases. Evaluate it against real queries before adding or rejecting semantic retrieval.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can an agent use both direct tools and RAG?
&lt;/h3&gt;

&lt;p&gt;Yes. A semantic retriever can identify candidate blocks, while direct tools fetch current canonical content, versions, and surrounding structure. The two layers should not create competing sources of truth.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should I measure before building RAG?
&lt;/h3&gt;

&lt;p&gt;Measure source-block recall, citation correctness, freshness, incomplete-search handling, latency, and the share of real queries that simpler retrieval cannot answer. Those results reveal whether the bottleneck is retrieval, document quality, or workflow design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Your agent needs a dependable way to find, read, cite, and sometimes update knowledge. RAG may be part of that system, but it is not the definition of it. Build a readable source of truth first; add specialized retrieval when evidence shows that you need it.&lt;/p&gt;




&lt;p&gt;Originally published on &lt;a href="https://doco.page/s/Z26ILMVEwQZqCI7J5W6RQl4Ye0GwpWsa" rel="noopener noreferrer"&gt;Doco&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Doco is an open-source document workspace where humans and AI agents write together. &lt;a href="https://doco.page/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=content_series" rel="noopener noreferrer"&gt;Explore Doco&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>rag</category>
    </item>
    <item>
      <title>Give Claude Code a Knowledge Base It Can Actually Edit</title>
      <dc:creator>Harry Smart</dc:creator>
      <pubDate>Sun, 30 Aug 2026 08:51:52 +0000</pubDate>
      <link>https://dev.to/songofhawk/give-claude-code-a-knowledge-base-it-can-actually-edit-5ae8</link>
      <guid>https://dev.to/songofhawk/give-claude-code-a-knowledge-base-it-can-actually-edit-5ae8</guid>
      <description>&lt;p&gt;Give Claude Code an editable knowledge base by connecting a narrowly scoped MCP server that exposes structured search, versioned reads, and block-level writes. Start read-only, verify citations, then grant write access only after stale writes fail safely and every change remains visible to humans.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Doco series · Article 11 · Claude Code tutorial&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude Code can already read files in a repository. That is enough for source code and temporary project notes. It is not enough when the source of truth is a living team handbook, product specification, or research library that people are editing in a browser at the same time.&lt;/p&gt;

&lt;p&gt;The missing piece is not “more context.” It is a reliable boundary between Claude Code and maintained knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What “actually edit” means
&lt;/h2&gt;

&lt;p&gt;An agent can technically edit a knowledge base if it can overwrite a file or call a generic update endpoint. A useful editing contract is stricter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;search should return evidence, not only generated answers;&lt;/li&gt;
&lt;li&gt;a paragraph should keep the same address when it moves;&lt;/li&gt;
&lt;li&gt;a read should identify the document version it observed;&lt;/li&gt;
&lt;li&gt;a write based on an old version should fail visibly;&lt;/li&gt;
&lt;li&gt;a small change should not require replacing the entire document;&lt;/li&gt;
&lt;li&gt;a person watching the document should see the same state;&lt;/li&gt;
&lt;li&gt;permissions should separate reading from writing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Claude Code uses the &lt;a href="https://code.claude.com/docs/en/mcp" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; to connect to external tools and data sources. Anthropic recommends HTTP for remote services and stdio for local processes. MCP provides the connection; the quality of the knowledge contract still depends on the server behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: connect Doco in read-only mode
&lt;/h2&gt;

&lt;p&gt;Doco is the product I am building, so treat the recommendation here as a first-party tutorial rather than an independent comparison. It is MIT licensed, and its relevant distinction is that browser users and API clients operate on the same collaborative documents.&lt;/p&gt;

&lt;p&gt;For the hosted MCP endpoint, create a Doco token with read scopes only, then add it to Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http doco https://api.doco.page/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_DOCO_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a local stdio connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add doco &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--package&lt;/span&gt; doco-agent-cli doco mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the token outside committed configuration. Claude Code supports environment-variable expansion in project-scoped &lt;code&gt;.mcp.json&lt;/code&gt; files, so a shared configuration can reference a local secret without storing it in Git.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;claude mcp list&lt;/code&gt;, then open &lt;code&gt;/mcp&lt;/code&gt; inside Claude Code. Confirm that the Doco server is connected and inspect the tools before asking the model to use them. The official Claude Code documentation also warns that servers which fetch external content can expose clients to prompt injection; knowledge-base text is data, not trusted instruction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: prove retrieval before enabling writes
&lt;/h2&gt;

&lt;p&gt;Use a question whose answer you already know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search Doco for the release rollback procedure. Return the document title,
heading path, stable block ID, and the exact evidence you relied on.
Do not modify anything.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is not a polished summary. You are checking the retrieval contract:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Did Claude search the intended knowledge base?&lt;/li&gt;
&lt;li&gt;Did it land on a relevant block rather than dump a whole document?&lt;/li&gt;
&lt;li&gt;Can you open the cited document and verify the evidence?&lt;/li&gt;
&lt;li&gt;Does the result disclose incomplete or stale search state?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This matters because a confident answer without an address is hard to audit. A useful knowledge tool should let the agent browse structure, search to a block, and read enough surrounding context to interpret it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: perform one bounded edit
&lt;/h2&gt;

&lt;p&gt;After read-only retrieval is dependable, create a token with the minimum write scope needed for the test. Choose a disposable document or a clearly reversible paragraph.&lt;/p&gt;

&lt;p&gt;Ask Claude to follow an explicit read-edit-verify loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open the document “Release checklist.” Read the block under “Rollback owner.”
Change only that block to add the on-call alias. Preserve the rest of the
document. If the version changed after your read, stop and show the conflict.
Then read the block again and report its stable ID and resulting version.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Doco, reads expose a version fingerprint and protected writes use an &lt;code&gt;If-Match&lt;/code&gt;-style precondition. That follows the lost-update protection described by &lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-if-match" rel="noopener noreferrer"&gt;RFC 9110&lt;/a&gt;: a state-changing request proceeds only when the selected representation still matches the condition supplied by the caller.&lt;/p&gt;

&lt;p&gt;The version check is the important part. Without it, an agent can read version A, a teammate can create version B, and the agent can silently write an update based on A over B.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: watch the same edit in the browser
&lt;/h2&gt;

&lt;p&gt;Open the target document before running the edit. The changed block should update in the collaborative editor because the API and browser share the same Yjs document state. Yjs describes shared editing as peers exchanging document updates that merge without a central ordering assumption; see its &lt;a href="https://docs.yjs.dev/getting-started/a-collaborative-editor" rel="noopener noreferrer"&gt;collaborative editor guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This check catches a common integration failure: the agent updates a shadow database or exported file while the human-facing workspace continues to show something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: test the failure path on purpose
&lt;/h2&gt;

&lt;p&gt;A safe demo must include a conflict, not only a successful write.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have Claude read a block and retain its version.&lt;/li&gt;
&lt;li&gt;Change the same document in the browser.&lt;/li&gt;
&lt;li&gt;Ask Claude to submit the original planned write.&lt;/li&gt;
&lt;li&gt;Confirm that the server rejects the stale precondition.&lt;/li&gt;
&lt;li&gt;Let Claude reread, explain the difference, and propose a new edit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do not automatically retry semantic conflicts. A retry is safe only after the caller has incorporated the newer state. “Last writer wins” is convenient until the last writer is an unattended agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  A production-ready prompt pattern
&lt;/h2&gt;

&lt;p&gt;The prompt below is deliberately procedural:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search the Doco knowledge base for the current API deprecation policy.
Use source blocks, not generated summaries, as authority.
If you find conflicting policies, cite both and stop.
Otherwise update only the “Timeline” block in the canonical policy document.
Do not write without a version precondition. If the version is stale, do not
retry automatically. Report the document URL, changed block ID, and new version.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt does not create safety by itself. It tells Claude how to use safety properties enforced by the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Permission and review checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Begin with &lt;code&gt;documents:read&lt;/code&gt;; do not issue a broad write token for exploration.&lt;/li&gt;
&lt;li&gt;Scope the token to the smallest knowledge base and lifetime available.&lt;/li&gt;
&lt;li&gt;Review the server's tools before approving them. The MCP specification says tools are model-controlled and recommends a human in the loop who can deny invocations; see the &lt;a href="https://modelcontextprotocol.io/specification/draft/server/tools" rel="noopener noreferrer"&gt;MCP tools specification&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Require explicit confirmation for publication, permission changes, deletion, and other consequential actions.&lt;/li&gt;
&lt;li&gt;Preserve an audit record containing the actor, target block, source version, and result.&lt;/li&gt;
&lt;li&gt;Revoke test tokens when the integration is no longer in use.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can Claude Code edit a normal Markdown knowledge base?
&lt;/h3&gt;

&lt;p&gt;Yes. It can edit files it is allowed to access. That is appropriate when the file is the source of truth and Git-style review is the intended collaboration model. A shared live knowledge base needs additional identity, concurrency, and permission semantics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need MCP to connect Claude Code to knowledge?
&lt;/h3&gt;

&lt;p&gt;No. A CLI or direct API can work. MCP is useful when you want discoverable tools, resources, authentication, and a reusable connection inside Claude Code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I give Claude Code a write token immediately?
&lt;/h3&gt;

&lt;p&gt;No. Prove search, evidence, and citations with a read-only token first. Add narrow write scope for a reversible test, then verify both a successful update and a stale-write rejection.&lt;/p&gt;

&lt;h3&gt;
  
  
  What makes a block-level edit safer than replacing a document?
&lt;/h3&gt;

&lt;p&gt;It limits the mutation surface and gives the changed paragraph a durable address. Document-level version protection is still required because other blocks may have changed since the agent read the document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Connecting Claude Code is the easy part. The real acceptance test is whether it can find evidence, edit one stable block, lose a race safely, and show the same result to a human in the browser. If those properties hold, the knowledge base is not merely readable by an agent—it is maintainable by one.&lt;/p&gt;




&lt;p&gt;Originally published on &lt;a href="https://doco.page/s/OrWQqXi-hm42qjNA6Hind_fzWBMbhATF" rel="noopener noreferrer"&gt;Doco&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Doco is an open-source document workspace where humans and AI agents write together. &lt;a href="https://doco.page/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=content_series" rel="noopener noreferrer"&gt;Explore Doco&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>mcp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why Knowledge Bases Are Better Hosted Online Rather Than as Local Markdown</title>
      <dc:creator>Harry Smart</dc:creator>
      <pubDate>Fri, 21 Aug 2026 02:16:12 +0000</pubDate>
      <link>https://dev.to/songofhawk/why-knowledge-bases-are-better-hosted-online-rather-than-as-local-markdown-37e3</link>
      <guid>https://dev.to/songofhawk/why-knowledge-bases-are-better-hosted-online-rather-than-as-local-markdown-37e3</guid>
      <description>&lt;h1&gt;
  
  
  Why Knowledge Bases Are Better Hosted Online Rather Than as Local Markdown
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Doco series · Article 6&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Local Markdown is an excellent medium for personal notes. It is portable, inspectable, offline-friendly, and supported by a deep ecosystem. Obsidian is very good at that job.&lt;/p&gt;

&lt;p&gt;But a personal note vault and a shared knowledge base have different users. The moment an AI agent must search, cite, and maintain the same material as you, the limitations of a folder full of files become architectural rather than cosmetic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes are for the future you; a knowledge base serves others
&lt;/h2&gt;

&lt;p&gt;A personal note can be messy as long as its author can find it. A knowledge base creates value when another actor can use it reliably. Today, that actor may be a teammate—or an agent that must answer from it, follow changes, and update it.&lt;/p&gt;

&lt;p&gt;The question is no longer “Can a model read Markdown?” Of course it can. The question is whether the storage model gives the agent stable identity, current state, safe writes, and an efficient way to navigate.&lt;/p&gt;

&lt;h2&gt;
  
  
  A local vault belongs to a device
&lt;/h2&gt;

&lt;p&gt;Move to another computer and the vault must be transported or synchronized. Git works well for engineers but remains a workflow. Cloud drives create their own conflict semantics. Sending a file produces a copy that begins aging immediately.&lt;/p&gt;

&lt;p&gt;Mobile access is often access to a synchronized file rather than a shared live workspace. An online knowledge base reverses the relationship: the knowledge has one managed home, and each device is a window onto it.&lt;/p&gt;

&lt;p&gt;That convenience has a cost. Online systems depend on network availability and an operator. A responsible system needs exports and self-hosting so access does not become lock-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Files are not the same unit as knowledge
&lt;/h2&gt;

&lt;p&gt;The conclusion you need is usually one paragraph inside a file. The value that goes stale may be one number. The sentence an agent should change is one block.&lt;/p&gt;

&lt;p&gt;In a plain file system, paragraph-level work falls back to file-level work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read the whole file to change one sentence;&lt;/li&gt;
&lt;li&gt;cite “somewhere in this file” instead of a durable block;&lt;/li&gt;
&lt;li&gt;rewrite the file after a path- and line-based edit;&lt;/li&gt;
&lt;li&gt;lose the old address when content moves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doco gives blocks stable IDs independent of position. An agent can cite and patch the same unit a person sees in the editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;head&lt;/code&gt; are tools, not a knowledge interface
&lt;/h2&gt;

&lt;p&gt;An agent can search local files quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"release window"&lt;/span&gt; ./notes
&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 50 ./notes/operations/release.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those commands answer where characters occur and what appears near the beginning of a file. They do not answer whether the search covered the current corpus, which heading contains the match, what the surrounding block means, or whether the index and source agree.&lt;/p&gt;

&lt;p&gt;A knowledge interface should let an agent browse structure, inspect an outline, search to a block, read around it under a token budget, and return a citation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrent edits need more than conflict copies
&lt;/h2&gt;

&lt;p&gt;The file model assumes one writer at a time. Once you and an always-on agent maintain the same vault, that assumption fails.&lt;/p&gt;

&lt;p&gt;An agent can read a file, you can save a change, and the agent can write its older copy back. A synchronization tool may create two conflict files, but it cannot explain which paragraph each actor changed or which version the agent used.&lt;/p&gt;

&lt;p&gt;Doco uses a shared collaborative document for browser and API writes. Reads return version fingerprints. Stale writes fail explicitly. Stable blocks limit the edit, transactions prevent partial batches, and snapshots support rollback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown still matters—its role changes
&lt;/h2&gt;

&lt;p&gt;Moving knowledge online does not require abandoning Markdown. Doco imports it and exports individual documents or complete knowledge bases. Word and PDF exports are available as well. Browser-local persistence also lets editing continue through temporary disconnection and merge after reconnecting.&lt;/p&gt;

&lt;p&gt;Markdown becomes an exchange format rather than the only authoritative storage shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    MD[Markdown files] -- import --&amp;gt; KB[Structured online knowledge base]
    KB -- export --&amp;gt; OUT[Markdown, Word, PDF]
    HUMAN[Human] &amp;lt;--&amp;gt; KB
    AGENT[Agent] &amp;lt;--&amp;gt; KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A practical decision rule
&lt;/h2&gt;

&lt;p&gt;Choose local Markdown when the material is primarily for you, offline control dominates, and agents do not need to maintain it. Choose a structured online workspace when humans and agents must search, cite, edit, and observe the same evolving knowledge.&lt;/p&gt;

&lt;p&gt;This is not an anti-Obsidian argument. It is a scope boundary. The strongest personal notebook and the strongest collaborative agent workspace do not need to be the same product.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The important difference is not the file extension. It is where knowledge lives, what unit has an identity, and who can safely work on it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Shareable version:&lt;/strong&gt; Local Markdown is excellent for personal notes. An agent-maintained knowledge base needs more: stable paragraph addresses, structured search, version-protected writes, and one live state across devices. Doco keeps Markdown as an open import/export format while letting humans and agents work on structured blocks online.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
