Give AI agents the smallest exact slice of a Markdown knowledge base that answers their question — and let them edit that slice back, safely.
context is an MCP server for Markdown vaults. It recursively indexes .md files when it starts,
exposes their heading structure and wikilink graph, and returns byte-exact sections with source
provenance. Agents can discover the right section before retrieving content instead of loading an
entire document into model context, then replace that section's body with a hash-guarded write
that leaves every other byte of the file untouched.
This makes retrieval more context-efficient by omitting unrelated body text. The actual token
savings depend on the document, selected section, MCP client, and model tokenizer; context does
not promise a fixed reduction.
From this repository, start the stdio MCP server with a vault directory:
cargo run -- serve /absolute/path/to/vaultcontext serve is not an interactive prompt. It indexes the vault, then waits for an MCP client on
stdin and writes protocol responses to stdout. The client normally starts and owns this process.
To run a built or installed binary instead:
context serve /absolute/path/to/vaultMCP clients wrap server settings differently, but a source-checkout configuration needs the same command, arguments, working directory, and vault path:
{
"command": "cargo",
"args": [
"run",
"--quiet",
"--",
"serve",
"/absolute/path/to/vault"
],
"cwd": "/absolute/path/to/context-repository"
}For a built binary, use its absolute path as command, omit cwd, and pass
["serve", "/absolute/path/to/vault"] as args.
See the complete MCP and CLI guide for lifecycle details, full request and response examples, and troubleshooting.
Choose the shortest discovery path for what the agent already knows:
topic only -> search -> choose result -> get_section
known file -> outline -> choose path -> get_section
If the agent knows only the topic, search headings and heading paths:
{
"query": "gun skill"
}If it already knows the vault-relative file, inspect that document without loading its body:
{
"file": "player.md"
}Both discovery tools return exact provenance. Copy the selected result's file and
heading_path directly into exact retrieval:
{
"file": "player.md",
"heading_path": "Skills > Gun"
}get_section returns only that section's byte-exact Markdown plus its file, heading path, byte
range, line range, and content hash — the token the next edit is guarded by.
Every retrieval result carries a content_hash: the BLAKE3 hash of that section's exact source
bytes. To edit, pass it back as expected_hash along with the new body:
{
"file": "player.md",
"heading_path": "Skills > Gun",
"body": "Deal 3 damage per shot.",
"expected_hash": "…the hash from get_section…"
}edit_section re-reads the file from disk, checks the hash against its current bytes (not the
possibly-stale in-memory index), and only then splices in the new body — the heading line is
never touched. If the section changed since it was read, the call fails with a conflict naming
the current hash instead of silently overwriting. On success it returns the edited document's
outline with a fresh hash per section, so a follow-up edit needs no extra read.
An edit can only replace a section's body; the new content may not contain a heading at or above the edited section's own level (that would restructure the document, which this tool deliberately refuses to do).
| Tool | Use it when | Input |
|---|---|---|
search |
You know the topic, but not the file or exact heading | query |
outline |
You know the file and need its heading tree without body text | file |
get_section |
You have the exact result provenance and want source content | file, heading_path |
edit_section |
You want to replace a section's body, guarded by its content hash | file, heading_path, body, expected_hash |
backlinks |
You want every wikilink pointing at a file (or one section in it) | file, heading_path (optional) |
reindex_vault |
You want the server to notice changes made outside it and tell you what changed | none |
search is deterministic, case-insensitive matching over heading text and heading paths. It is
not semantic or embedding-based search. outline returns nested headings and line ranges without
section bodies. get_section requires the exact, case-sensitive breadcrumb returned by a
discovery tool.
The vault's [[target]] wikilinks are resolved and indexed vault-wide, so backlinks can answer
"what links here" without an agent grepping raw file content itself:
[[Note]]— a whole-file link, resolved by matchingNoteagainst every indexed file's filename stem.[[Note#Heading]]— resolved to a specific section, matched by heading text withinNote.[[#Heading]]— a self-link, resolved within the file the link appears in.
A target that matches no file, or more than one, is unresolved rather than an error — the vault still indexes, and the unresolved link shows up as a diagnostic. Resolution is exact, not fuzzy: a typo in a link target does not get corrected or suggested.
edit_section keeps the index current for its own writes, but the server otherwise indexes the
vault once at startup and does not watch the filesystem. If something else touches the vault — a
human editing in Obsidian, another tool, a git checkout — call reindex_vault to re-walk the
vault root and get back exactly what changed:
{
"files_added": ["lore/armor.md"],
"files_removed": [],
"files_changed": [
{
"file": "player.md",
"sections_added": [],
"sections_removed": [],
"sections_modified": ["Skills > Gun"]
}
]
}A file with no differences is omitted entirely. Within a changed file, only the root cause of
each difference is reported, not every path that merely inherited a changed hash: editing a
deeply nested section reports only that section, not its ancestors (whose hashes changed purely
by cascade); adding a new heading with children of its own reports only the new heading, not each
child beneath it. A renamed file or heading is always reported as one path removed and a
different path added — context never guesses that a rename occurred.
This is an approximation, not a precise diff: because a section's hash covers its entire subtree,
a parent's own body changing in the same window as one of its children is indistinguishable from
pure cascade, and only the child is reported. If that matters for a specific section, follow up
with get_section and compare against a previously known hash.
- The vault root can contain nested directories;
.mdextensions are matched case-insensitively. - Tool calls use vault-relative paths such as
lore/weapons.md, never absolute source paths. - The in-memory index is built once at startup.
edit_sectionwrites through to disk atomically and refreshes the in-memory index for the edited file in the same call — no restart needed for edits made through the tool.- Changes made outside the server are not picked up automatically; call
reindex_vault(or restart the server) to see them. - The wikilink/backlink index is vault-wide and is fully recomputed after every
edit_sectionorreindex_vaultcall, since editing one file's headings can change whether a link in a different file resolves.
The current release does not include:
- file watching —
reindex_vaultis a manual trigger, not automatic; - a persistent index or any history across server restarts;
- semantic search or embeddings;
- section-body full-text search;
- inserting, deleting, or renaming sections (only replacing a section's body); or
- rewriting wikilinks when the section they point to is renamed or removed.
context-enginebuilds section trees, indexes vaults, and performs structural retrieval.context-lexerprovides the low-level streaming Markdown lexer.context-parserbuilds the source-backed Markdown document tree.
Run the standard development checks from the repository root:
cargo fmt --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace