-
Notifications
You must be signed in to change notification settings - Fork 0
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.
Threadnote stacks four layers between the agent you are talking to and the bytes on disk:
- Agents — Codex (CLI), Claude Code, Cursor, and GitHub Copilot (VS Code). Each is configured with one MCP server entry pointing at the bundled adapter.
-
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. -
The local server —
openviking-server, a Python process managed withuv, bound to127.0.0.1:1933. The adapter forwards recall, read, list, remember, compact, and share operations to it. -
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.
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
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.
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, andshare_publish. Older adapters expose the compatibility aliasessearch,read,list, andstore. Arguments are always JSON. -
viking://URIs are stable pointers. A memory addressed asviking://user/<you>/memories/durable/projects/<project>/<topic>.mdis 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.
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.
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. |
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-serverCursor 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.
GitHub · npm · walkthrough deck · OpenViking · AGPL-3.0-or-later
Start here
Concepts
Workflows
Reference