Skip to content

Architecture

Denys Kashkovskyi edited this page Jun 5, 2026 · 2 revisions

Architecture

Threadnote is a thin local workflow around OpenViking. It puts one stdio MCP adapter between every supported agent and a single local OpenViking store, so Codex, Claude Code, Cursor, and Copilot all read and write the same memory layer through stable viking:// pointers.

The layered model

Threadnote stacks four layers between the agent you are talking to and the bytes on disk:

  1. Agents — Codex (CLI), Claude Code, Cursor, and GitHub Copilot (VS Code). Each is configured with one MCP server entry pointing at the bundled adapter.
  2. The MCP adapter — a single bundled stdio binary, threadnote-mcp-server. Every agent launches its own instance over stdio and calls the same set of tools.
  3. The local serveropenviking-server, a Python process managed with uv, bound to 127.0.0.1:1933. The adapter forwards recall, read, list, remember, compact, and share operations to it.
  4. Storage — plain Markdown on disk under THREADNOTE_HOME/data/viking/<account>/..., plus a regenerable semantic index (a LevelDB vectordb). The Markdown is the source of truth; the vectordb only accelerates recall.

Nothing in this stack reaches the network on its own. The only egress path is an explicit share publish, which pushes a curated durable memory to a team git repo.

How the pieces connect

flowchart TD
    A1[Codex CLI]
    A2[Claude Code]
    A3[Cursor]
    A4[Copilot VS Code]

    MCP[threadnote-mcp-server stdio]
    OV[openviking-server 127.0.0.1:1933 python uv]

    MD[Markdown data tree THREADNOTE_HOME/data/viking/account]
    VDB[vectordb LevelDB semantic index]

    A1 --> MCP
    A2 --> MCP
    A3 --> MCP
    A4 --> MCP
    MCP --> OV
    OV --> MD
    OV --> VDB
Loading

Each agent speaks MCP over stdio to its own threadnote-mcp-server process; the adapter talks to the one local openviking-server, which owns both storage nodes.

One adapter, one URI scheme

The value of the layering is that everything above the storage layer is uniform:

  • Every agent calls the same MCP tools. The Threadnote-named tools are recall_context, read_context, list_context, remember_context, compact_context, and share_publish. Older adapters expose the compatibility aliases search, read, list, and store. Arguments are always JSON.
  • viking:// URIs are stable pointers. A memory addressed as viking://user/<you>/memories/durable/projects/<project>/<topic>.md is the same pointer from any session, on any agent, in any worktree. Recall returns candidate URIs plus abstracts; the agent then reads or lists the selected URI rather than treating the search hit as the final payload.
  • Storage is plain Markdown, and the index is disposable. The on-disk Markdown tree is authoritative. The LevelDB vectordb is a derived semantic index that can be regenerated from the Markdown, so reindexing (for example after a shared pull) never risks the canonical content.

For recall queries that mention "current repo" or "this branch", pass the workspace path as callerCwd (for example recall_context({"query":"current repo latest handoff","callerCwd":"/absolute/workspace/path"})) so the server resolves branch and workspace terms instead of guessing from the adapter's launch directory.

The memory namespace

All addressable content lives under the viking:// scheme. The tree below shows the main subtrees.

viking://
  user/<you>/memories/
    durable/projects/<project>/<topic>.md   # long-lived feature knowledge (shareable)
    handoffs/active/<project>/<topic>.md     # current work logs
    handoffs/archived/<project>/<topic>.md   # provenance-only handoffs
    preferences/                             # personal workflow preferences (local only)
    incidents/active/                        # in-flight incident notes (local only)
    incidents/archived/                      # past incident notes (local only)
    shared/<team>/...                        # memories published to a team git repo
  resources/repos/<project>                  # seeded repo guidance: README, AGENTS.md, CLAUDE.md, SKILL.md, docs/**
  agent/<agent-id>/memories                  # shared agent identity (default id: threadnote)

A project + topic pair maps to a single stable lifecycle path, so updates replace the file in place instead of accreting timestamped notes. Only the durable/ subtree is shareable; handoffs, preferences, and incidents stay local by construction.

Account, user, and agent identity

Three identity dimensions shape where content lands and how it is namespaced:

Dimension Env var Default Role
Account THREADNOTE_ACCOUNT local Top-level on-disk partition under data/viking/<account>/.
User THREADNOTE_USER local username Owns the user/<you>/memories/ subtree; each teammate clones shared repos into their own user-namespaced path.
Agent THREADNOTE_AGENT_ID threadnote Shared agent identity for viking://agent/<agent-id>/memories, so all four agents share one memory surface.

Why stdio

The default install uses the bundled stdio adapter because OpenViking 0.3.12 does not expose a native /mcp HTTP route. Each agent is registered with a command-style MCP entry, for example:

codex mcp add threadnote -- threadnote-mcp-server
claude mcp add threadnote -- threadnote-mcp-server

Cursor and Copilot use the equivalent stdio entry in their own MCP config files. If a future OpenViking build ships a healthy native endpoint, it can be installed explicitly with threadnote mcp-install claude --native-http --apply, but stdio remains the supported default today.

See also: Memory Lifecycle, Recall and Read, Configuration, Sharing Memories.

Clone this wiki locally