Recall
# Recall
[](https://github.com/jerrl10/recall/actions/workflows/ci.yml)
[](LICENSE)
[](https://www.python.org/downloads/)
**Durable engineering memory for AI coding assistants.**
You solve a hard problem with an AI assistant on Tuesday. On Friday the context
window is gone, the session is closed, and the reasoning went with it.
Recall is an [MCP](https://modelcontextprotocol.io/) server that turns those
conversations into structured notes in your Obsidian vault — and hands them
back to your assistant the next time they matter.
```text
you: "this visibility timeout thing is important — save it"
│
▼
/learn ──▶ Recall MCP ──▶ Obsidian vault
├── Concepts/Visibility Timeout.md
└── Daily/2026-09-05.md
│
▼
next session: /recall ──▶ the knowledge is back in context
```
Built for **Claude Code**, **Codex**, and **OpenCode** from one shared
configuration. *(Verified end-to-end on Claude Code; see
[provider support](#provider-support).)*
---
## Why
Most AI memory tools store conversation history. Recall stores *conclusions*.
- **Your notes, your files.** Plain Markdown in your own Obsidian vault. No
database, no lock-in, no service. Delete Recall tomorrow and every note still
opens.
- **Structured, not dumped.** Each note is classified, templated by kind, tagged,
cross-linked, and logged to a daily timeline.
- **Merges instead of duplicating.** Capturing the same subject twice extends
the existing note. A near-identical title is refused outright; a different
title covering the same ground — "Backoff strategy" against "Retry policy" —
comes back flagged, because search reads bodies, not just headings.
- **Provider-neutral.** One canonical skill and command set, installed into
whichever assistants you use.
- **No LLM inside the server.** Your assistant already has the conversation and
does the reasoning. Recall does storage, structure, and retrieval — so it
needs no API key and makes no network calls.
## What a captured note looks like
````markdown
---
title: Azure Storage Queue visibility timeout
kind: concept
created: 2026-09-05
updated: 2026-09-05
tags:
- azure
- queue
- distributed-systems
projects:
- recall
source: claude-code
---
# Azure Storage Queue visibility timeout
> [!summary]
> A dequeued message is hidden from other consumers for a set window,
> not deleted.
## How it works
Dequeue hides the message for the visibility timeout. Delete it explicitly
or it reappears.
## Gotchas
Slow consumers cause duplicate processing.
## Related
- [[Idempotency]]
````
Obsidian-native throughout: frontmatter properties, callouts, wiki links, tags.
### See it for yourself
Seed a throwaway vault with a set of interlinked notes and open it in Obsidian:
```bash
python scripts/demo_vault.py /tmp/recall-demo
```
Seven notes across all five kinds, written through the normal capture path —
so what you see is what a real session produces. Open `/tmp/recall-demo` in
Obsidian as a vault and look at the graph view: the notes reference each other,
which is what turns a folder of files into something you can navigate.
---
## Install
Requires Python 3.12+, [uv](https://docs.astral.sh/uv/), and an existing
Obsidian vault.
```bash
git clone https://github.com/jerrl10/recall.git
cd recall
uv sync
```
> Packaging for `uvx obsidian-recall` is in place and the release workflow is
> ready; it is not on PyPI yet. Install from source until then.
Point it at your vault — this finds your Obsidian vaults for you:
```bash
uv run recall setup
```
It asks which vault to use and whether Recall may read your existing notes
(see [reading and writing](#reading-and-writing-are-separate)), then writes a
config file. If something later stops working:
```bash
uv run recall doctor
```
Then register Recall with your assistant — run this from the project you want
memory in:
```bash
# Claude Code
python scripts/install.py claude --vault ~/Documents/Obsidian/MyVault
# Codex
python scripts/install.py codex --vault ~/Documents/Obsidian/MyVault
# OpenCode
python scripts/install.py opencode --vault ~/Documents/Obsidian/MyVault
# or all three
python scripts/install.py all --vault ~/Documents/Obsidian/MyVault
```
Add `--dry-run` to see exactly what would be written first. Existing MCP
configuration is merged, never overwritten — a config that already registers
`recall` is left untouched.
Restart your assistant, then confirm the connection by asking it to run
`vault_health`.
### Provider support
One canonical skill and command set in `ai/` is rendered into each assistant's
native layout, so the workflows cannot drift apart.
| Provider | Status | MCP config | Commands |
| --- | --- | --- | --- |
| Claude Code | Verified end-to-end, in daily use | `.mcp.json` | `.claude/commands/` |
| Codex | Config verified; not yet driven from a live session | `.codex/config.toml` | `~/.codex/prompts/` *(global only)* |
| OpenCode | Config verified; not yet driven from a live session | `opencode.json` | `.opencode/commands/` |
Three layers are covered by CI:
- **the protocol** — the server is launched as a subprocess and driven through
a real MCP handshake, tool discovery, and tool calls by the official client.
MCP is the contract, so a server a compliant client can drive is one every
compliant client can drive;
- **the configuration** — each provider's generated config is asserted against
that vendor's documented format, including that installing never disturbs
servers you already have;
- **the workflows** — one canonical skill and command set in `ai/` is rendered
into each layout, so they cannot drift apart.
What that leaves untested is a live model on Codex or OpenCode actually
choosing to call the tools. If you run Recall there, reports are welcome.
## Use
| Command | Does |
| --- | --- |
| `/learn` | Extract everything worth keeping from this conversation |
| `/recall` | Pull relevant prior knowledge back into context |
| `/decision` | Record an architectural decision and its trade-offs |
| `/lesson` | Record a debugging or operational lesson |
Or just say it: *"this is worth remembering — save it to my notes."* The skill
picks it up.
## Vault layout
```text
YourVault/
└── Recall/
├── Concepts/ mechanisms, terminology, reusable ideas
├── Decisions/ choices made, and what they rule out
├── Lessons/ what broke, why, and the fix
├── Questions/ open threads worth returning to
├── Projects/ durable per-project context
├── Daily/ dated log linking each day's captures
└── Archive/ withdrawn notes, kept out of search
```
Topic notes hold the knowledge; the daily log gives you the timeline.
### Reading and writing are separate
Recall **only ever writes** beneath its own folder. What it *reads* is your
choice:
| `RECALL_SEARCH_SCOPE` | Recall searches |
| --- | --- |
| `recall` (default) | Only notes Recall wrote |
| `vault` | Your whole vault |
Set it to `vault` and everything you have already written becomes recallable
on day one, instead of after weeks of building a corpus. Notes Recall did not
create are read-only to it — they can be found, quoted, and linked, never
modified.
Keep the default if your vault mixes work with anything personal: `vault`
scope means an assistant can surface any note in it. `RECALL_SEARCH_EXCLUDE`
skips folders by name at any depth.
## Configuration
Set via environment or a `.env` file — see [`.env.example`](.env.example).
| Variable | Default | Purpose |
| --- | --- | --- |
| `RECALL_VAULT_PATH` | *required* | Path to your Obsidian vault |
| `RECALL_ROOT` | `Recall` | Folder inside the vault that Recall owns |
| `RECALL_DAILY_FOLDER` | `Daily` | Subfolder for dated logs |
| `RECALL_ARCHIVE_FOLDER` | `Archive` | Subfolder for withdrawn notes |
| `RECALL_SEARCH_SCOPE` | `recall` | `recall` or `vault` — see below |
| `RECALL_SEARCH_EXCLUDE` | `[".obsidian", ".trash", "Templates"]` | Folders never searched |
| `RECALL_MAX_SEARCH_RESULTS` | `10` | Default result cap |
| `RECALL_EXCERPT_CHARS` | `320` | Search excerpt length |
| `RECALL_CONTEXT_CHAR_BUDGET` | `8000` | Hard cap on text `note_context` returns |
Recall creates its own folder inside an existing vault. It never creates a
vault, and never writes outside `RECALL_ROOT`.
## MCP tools
| Tool | Purpose |
| --- | --- |
| `note_capture` | Write a note, or fold new material into an existing one |
| `note_search` | Ranked search across the vault, with excerpts |
| `note_read` | Read one note in full |
| `note_context` | Assemble relevant prior knowledge for the current task |
| `note_archive` | Withdraw a note captured in error — moved, never deleted |
| `vault_health` | Verify configuration, reachability, and note counts |
## How it works
Markdown files are the source of truth. There is no database and no index to
rebuild — every search walks the vault and ranks in memory, so a note you edit
by hand in Obsidian is simply the current state.
That is a deliberate trade: ranked search is weaker than a real index would
give, in exchange for a vault that is fully portable, hand-editable, and
outlives the tool. See [`docs/decisions/`](docs/decisions/) for the reasoning,
and [`docs/architecture.md`](docs/architecture.md) for the module map.
## Development
```bash
uv sync
uv run ruff format .
uv run ruff check .
uv run mypy src tests
uv run pytest
```
Optionally, run the same checks before each commit:
```bash
uv run pre-commit install
```
CI runs across Linux, macOS, and Windows on Python 3.12 and 3.13, plus MCP
protocol conformance against a real stdio subprocess and a coverage floor.
[CONTRIBUTING.md](CONTRIBUTING.md) covers the constraints worth knowing before
you write code; [CLAUDE.md](CLAUDE.md) is the condensed version an AI assistant
loads automatically.
## Status
Working and in daily use on Claude Code. CI covers formatting, types, and
smoke checks over capture, merge, search, context, and the installer; a proper
unit test suite is the next piece of work, followed by verifying the Codex and
OpenCode paths against live sessions.
## License
MIT © Chang Liu
TDQS
Scored across 5 tools
note_search and note_context both retrieve relevant notes, but their use cases are clearly separated: one is for pre-capture lookup with excerpts, the other is for session-start context gathering. note_capture, note_read, and vault_health have distinct, non-overlapping purposes.
Four tools follow a predictable note_* prefix pattern with clear action or intent, while vault_health is a reasonable diagnostic outlier. The naming is consistent enough that an agent can infer behavior, with only minor deviation from a pure action-oriented convention.
Five tools is well-scoped for a personal-knowledge recall server: search, read, capture, context, and health cover the essential workflow without bloat or redundancy. Each tool earns its place.
The tool set covers the full knowledge lifecycle: discover existing notes, read them, capture new material while folding duplicates, and pull relevant context into the conversation. vault_health also addresses the diagnostic dead-end when something fails, and deletion is reasonably absent for a durable-note system.