Use case · decision recall Prerelease 0.3.0 · Claude Code via MCP

Recall a decision record, cited to its file and bytes.

You write down a decision in a Markdown note and move on. A week 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, if the judge confirms
01 · Scenario

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

The vault is a folder of Markdown you already keep. 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 source of record.

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 you move the queue to a different backend and edit 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.

Some steps are server behavior. Others are conventions the agent follows because the tool descriptions and llms.txt ask it to. Each node is labelled with which one it is.

How the question travels
Server tool list_vaults

Returns the vaults this server can reach: name, whether this connection uses it, and backend. Never paths.

Agent convention .okto-neuron-vault pin

A JSON file in the project names the vault that applies. The server does not read it; the agent does, and passes the name as vault=.

Server tool ask, include_sources=true

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

Agent convention Read synthesis_status first

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

Agent convention Cite path + bytes + hash

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

Server, at ingest Edit, then remember again

If the edit replaced the old text and the correction judge confirms the change, the old claim is dated, dropped from recall, and linked by an edge.

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.

Tool, parameter and key names below match the 0.3.0 MCP tools. Payloads are abridged. 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. Each entry has a name, a current flag for the vault this connection would use, and the backend. Paths are never returned, so nothing about the machine's layout leaks into the transcript.

    list_vaults
    // illustrative: two vaults reachable from this daemon
    {
      "vaults": [
        { "name": "platform-notes", "current": false, "backend": "grafx" },
        { "name": "personal", "current": false, "backend": "grafx" }
      ]
    }
  2. Step2

    Pin the vault for this project

    The repository carries a .okto-neuron-vault file in its working directory, JSON of the form {"vault": "<name>"}. The server does not read this file. The agent reads it, as the tool descriptions tell it to, and passes the name on every later call, so a question asked from this project is answered from this project's vault.

    cat .okto-neuron-vault → {"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(question="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)
    {
      "status": "ok",
      "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

    A month later Redis Streams is retired. You replace the decision line in the same note: the queue now runs on NATS JetStream, dated 2026-04-11. The file is re-ingested with remember. The block that held the old sentence no longer exists, so the claim read from it becomes a candidate for correction. Extraction proposes the new claim, and the relation gate commits it once its endpoint types, predicate and grounding in the new block check out.

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

    The correction judge retires the old claim

    The ingest asks an LLM correction judge whether the new claim corrects the one whose text was removed. The judge leans towards no correction when it is unsure. When it confirms, and both claims come from the same document, the old claim is dated and dropped from recall, and a supersedes edge goes 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. If the judge says no, nothing is superseded.

    judge confirms → 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, only for claims whose text the edit removed, and only within the same document or resolved subject. A correction that lives in a different document is deferred for you to review in Curation. There is no sweep over the whole vault.
04 · The honest branch

When the model is 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

With provider_error, empty text means the model was not reached.

synthesis_status is always present and has six values: ok, empty, provider_error, truncated, abnormal_stop, no_llm. 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.

{
  "status": "degraded",
  "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 and returns the claims and their block_ids as structure rather than prose.

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

Who does what: you, the agent and the daemon.

The daemon enforces its own limits, such as loopback-only writes and vault-relative paths. The agent's habits, such as reading synthesis_status and citing the pointer, come from the tool descriptions and llms.txt, and the agent has to follow them.

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 (by instruction)
  • Cites path, byte range, and content hash in its reply (by instruction)
Does not
  • See absolute paths. Vault-relative paths only
  • Answer from a vault other than the one named in retrieval.vault
ND
Okto Neuron daemon
One process, two ports
Does
  • Chunks files into byte-anchored blocks and runs the six-stage write path
  • Commits entities at confidence 0.75 or higher and gates claims on grounding; the rest wait for review
  • Attaches the status fields to every ask, and sources[] when include_sources=true
Does not
  • Accept remember or init_vault from anything but the local machine
  • Send your text anywhere except the LLM endpoint you configure and, only if you set them up, a remote Neo4j or an MLflow tracking server
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
    Linux; macOS not yet verified on 0.3.0. 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 create a .okto-neuron-vault file containing {"vault": "<name>"} 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. Entities at or above confidence 0.75 commit, claims pass a grounding gate, and anything held back waits in Curation.

  4. 04

    Ask from Claude Code

    If Claude Code is not registered yet, follow the Claude Code setup guide. 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
Read all of these before trusting the text. They are how the tool reports a failed model call.

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 if the correction judge confirms the change
exploreFallback when the model is unreachable: claims and relationships with block_id, no LLM call
07 · Why it matters

What the payload lets you check.

The pointer is in the payload

Path, byte range, and content hash ride with the answer. A reviewer can open the file and read those bytes without asking 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 stays the source of record. Replace a fact, re-ingest, and once the correction judge confirms it, the old claim is dated and dropped from recall with an edge to the new one.

Only the LLM call left the machine

The daemon binds 127.0.0.1 and the writes are loopback-only. In this walkthrough your text went only to the LLM endpoint you configured, so if that endpoint is hosted, it left the machine there. The full list is under what leaves the machine.

Run it on a note you wrote yourself.

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