All use cases
Use case · decision recall Prerelease 0.3.0 · Claude Code via MCP

One decision. Recalled with its byte range.

A team writes down a decision in a Markdown note and moves on. Days later an agent is asked about it. This is the sequence of tool calls it makes, the shape of what comes back, and why the answer carries a path, a byte range, and a content hash instead of just a paragraph.

4
MCP tools touched
1
Markdown note, edited once
0
Absolute paths returned
1
supersedes edge written
01 · Scenario

A decision in a note, then a question a week later.

The vault is a folder of Markdown the team already keeps. One file in it, decisions/0012-queue-backend.md, records that the job queue will run on Redis Streams, with the date and the reasoning. Nobody tags it, links it, or moves it. The graph is derived from the file on ingest, and the file stays the trust root.

01

The note is written.

A short decision record: what was chosen, when, and why. Ingested once with remember, either from the CLI or from the agent.

02

The question arrives.

Someone asks the agent in Claude Code: what did we decide for the job queue, and why? The agent has no memory of the note. It has the MCP tools.

03

The note changes.

A month later the team moves to a different queue and edits the same note. The old fact has to stop being the answer without anyone deleting anything.

02 · Flow

Pick the vault. Ask with sources. Check the status.

Every node below is a real mechanism in the product, not a convention the agent has to remember.

How the question travels
Agent list_vaults

Returns the vault names this server can reach. Names only, never paths.

Working directory .okto-neuron-vault pin

A marker file in the project names the vault that applies. The agent passes it as vault=.

Agent ask, include_sources=true

One-shot retrieval and synthesis. The answer comes back with sources[] attached.

Gate on the client side Read synthesis_status first

ok is the only success value. Anything else changes what the agent is allowed to say.

Agent Cite path + bytes + hash

The reply names the vault-relative path, byte_start, byte_end, and content_hash.

Later Edit, then remember again

The changed fact supersedes the old claim at ingest. Old claim dated, dropped from recall, edge written.

Failure branch provider_error

Empty text, honest status. The agent retries or fixes the LLM config. It does not conclude the graph is empty.

Shown in section 04
03 · The calls

Six steps, with the payloads.

Key names below are the real ones. Values (vault names, block ids, byte offsets, hashes, the answer text) are illustrative and would differ in your vault.

  1. Step1

    Discover the vaults

    The agent asks the server which vaults it can reach. The response is a list of names. Paths are never returned, so nothing about the machine's layout leaks into the transcript.

    list_vaults
    // illustrative: two vaults reachable from this daemon
    ["platform-notes", "personal"]
  2. Step2

    Pin the vault for this project

    The repository carries a .okto-neuron-vault marker in its working directory. The agent reads it and passes the name on every later call, so a question asked from this project is always answered from this project's vault.

    cat .okto-neuron-vault → platform-notes
  3. Step3

    Ask, with sources

    One call. include_sources=true asks for the block pointers alongside the text. The context the model sees carries an [EXCERPT ...] line per source, stating which bytes of which file it is looking at and how many bytes the file has in total, so it can say when the excerpts do not cover a period instead of extrapolating.

    ask(query="What did we decide for the job queue, and why?", vault="platform-notes", include_sources=true)
    // the excerpt line the model sees for this source
    [EXCERPT source=decisions/0012-queue-backend.md bytes=0-388,880-1364 of 2210]
    
    // what comes back (abridged)
    {
      "text": "On 2026-03-04 the team chose Redis Streams for the job queue, citing a smaller operational footprint than Kafka for the current team size.",
      "citations": ["..."],
      "subgraph_evidence_ids": [],
      "retrieval": {
        "synthesis_status": "ok",
        "finish_reason": "stop",
        "vault": "platform-notes"
      },
      "sources": [
        {
          "block_id": "blk_4e11c0",
          "path": "decisions/0012-queue-backend.md",
          "byte_start": 880,
          "byte_end": 1364,
          "content_hash": "sha256:7c0e...91af"
        }
      ]
    }
    retrieval.synthesis_status is ok and retrieval.finish_reason is stop, so the text is complete and came from the model. retrieval.vault confirms which vault answered. Had the agent's vault argument been discarded, retrieval.vault_override_ignored would be present.
  4. Step4

    Cite the exact lines

    The agent replies with the decision and, under it, the pointer: vault-relative path, byte range, content hash. A reviewer opens the file at those bytes. If the text there matches, and the hash of the block matches, the claim is checked without trusting the agent's paraphrase.

    decisions/0012-queue-backend.md · bytes 880-1364 · sha256:7c0e...91af
    Decided 2026-03-04: Redis Streams for the job queue. Reason given: smaller
    operational footprint than Kafka for the current team size.
    
    Source: decisions/0012-queue-backend.md, bytes 880-1364 (sha256:7c0e...91af)
  5. Step5

    The note is edited

    Weeks later the team retires Redis Streams and appends an update to the same note: the queue now runs on NATS JetStream, dated 2026-04-11. The file is re-ingested with remember. Extraction proposes the new claim; the gate commits it because it clears confidence 0.75.

    remember(path="decisions/0012-queue-backend.md", vault="platform-notes")
  6. Step6

    The old claim is superseded

    Because the changed fact lives in the same document lineage, the ingest dates the old claim and drops it from recall, writing a supersedes edge from the new claim to the old one. Nobody deletes anything. The next ask answers with NATS JetStream and a new byte range; the Redis Streams claim is still in the graph, reachable through the edge, no longer in the answer.

    new claim → supersedes → old claim
    [EXCERPT source=decisions/0012-queue-backend.md bytes=0-388,1402-1790 of 2638]
    
    {
      "text": "As of 2026-04-11 the job queue runs on NATS JetStream; Redis Streams was retired.",
      "retrieval": { "synthesis_status": "ok", "finish_reason": "stop", "vault": "platform-notes" },
      "sources": [
        { "block_id": "blk_a90d3e", "path": "decisions/0012-queue-backend.md", "byte_start": 1402, "byte_end": 1790, "content_hash": "sha256:c41b...0e77" }
      ]
    }
    Supersession happens at ingest, within the same document lineage or resolved subject. A correction that lives in a different document is detected and deferred for human triage in Curation rather than applied automatically.
04 · The honest branch

The model was down. The answer says so.

Same question, same vault, but the LLM endpoint the daemon points at is unreachable. This is what comes back, and what the agent should do with it.

synthesis_status: provider_error

Empty text is a report, not an answer.

synthesis_status is always present and has five values: ok, empty, provider_error, truncated, abnormal_stop. Only ok means the text can be trusted as complete. provider_error with empty text means the model could not be reached. It says nothing about whether the graph holds the answer.

{
  "text": "",
  "citations": [],
  "subgraph_evidence_ids": [],
  "retrieval": {
    "synthesis_status": "provider_error",
    "vault": "platform-notes"
  },
  "sources": []
}

What the agent should do: say the model was unreachable, not that no decision was recorded. Check the configured LLM endpoint (the default is http://127.0.0.1:8123/v1, a server you run) and retry once it is up. If an answer is needed now, call explore with the same topic: it makes no LLM call, returns the claims and their block_ids as structure rather than prose, and works fully offline.

What the agent should not do: treat empty text as an empty graph, paraphrase from its own memory, or drop the status field from its reply. truncated and abnormal_stop deserve the same care; the text is incomplete and should be labelled as such.

A degraded answer never looks like a successful one
05 · Who does what

One author. One agent. One daemon on loopback.

Three parties, and the boundaries between them are enforced by the product, not by instruction.

AU
Author
Human, edits the vault
Does
  • Writes and edits Markdown notes in the vault
  • Runs remember from the CLI or lets the agent do it
  • Reviews held candidates in the Curation view
Does not
  • Maintain the graph by hand. It is derived and can be rebuilt from the same files
CC
Claude Code
MCP client, bearer token
Does
  • Calls list_vaults, ask, explore, remember over MCP on 127.0.0.1:8201
  • Reads synthesis_status before trusting any text
  • Cites path, byte range, and content hash in its reply
Does not
  • See absolute paths. Vault-relative paths only
  • Answer from a vault other than the one named in retrieval.vault
MD
Okto Neuron daemon
One process, two ports
Does
  • Chunks files into byte-anchored blocks and runs the six-stage write path
  • Commits at confidence 0.75 or higher, holds the rest for review
  • Attaches sources[] and the honesty fields to every ask
Does not
  • Accept remember or init_vault from anything but the local machine
  • Send text anywhere except the LLM endpoint you configure
06 · Reproduce it

Install it. Ingest one note. Ask.

The installer is the only supported install path. It starts the daemon and registers Claude Code as the MCP client.

  1. 01

    Install and start the daemon

    $ curl -fsSL https://raw.githubusercontent.com/OktoLabsAI/okto-neuron/main/install.sh | bash
    macOS and Linux. Installs uv, pins Python 3.12, starts one daemon (UI and REST on 7777, MCP on 8201), opens the UI, and runs claude mcp add --scope user.
  2. 02

    Create a vault and pin it

    Create a managed vault in the UI (or with init_vault), then write its name into a .okto-neuron-vault file at the root of the project you will ask from.

  3. 03

    Write the note and ingest it

    Put a decision record in the vault, then call remember with its path. Candidates at or above confidence 0.75 commit; the rest wait in Curation.

  4. 04

    Ask from Claude Code

    Ask the question. The agent should call ask with include_sources=true, check synthesis_status, and cite the block pointer. The rules it follows are in llms.txt.

// 1. is the answer healthy?
retrieval.synthesis_status == "ok"
retrieval.finish_reason    == "stop"

// 2. did the right vault answer?
retrieval.vault == the pinned name
// and retrieval.vault_override_ignored is absent

// 3. can a human check it?
sources[].path          // vault-relative
sources[].byte_start
sources[].byte_end
sources[].content_hash
None of these fields is optional to read. They are how the tool tells the truth about itself when the model behind it fails.

The MCP tools this walkthrough uses, and what each one did:

ToolUsed for
list_vaultsDiscover the vault names this daemon can reach. Names only.
askRetrieve and synthesize the decision with include_sources=true; returns text, citations, retrieval.synthesis_status, retrieval.finish_reason, retrieval.vault, and sources[]
rememberIngest the note the first time, and again after the edit. Loopback-only. The second ingest writes the supersedes edge
exploreFallback when the model is unreachable: claims and relationships with block_id, no LLM call
07 · Why it matters

Checkable by structure, not by trust.

The pointer is in the payload

Path, byte range, and content hash ride with the answer. A reviewer checks the bytes, not the agent.

Failure has its own shape

An unreachable model returns empty text and provider_error, never a paragraph that looks finished.

Edits supersede, on ingest

The note is the trust root. Change it, re-ingest, and the old claim is dated and dropped from recall with an edge to the new one.

Nothing left the machine

The daemon binds 127.0.0.1, the writes are loopback-only, and the only outbound call is to the LLM endpoint you configured.

Run it on a note you wrote yourself.

Prerelease 0.3.0. Free to run locally. No account required.