obsidian-local-mcp
This server gives your AI assistant a local, graph-based memory of your codebase and Obsidian notes: it indexes repos into a queryable graph, lets you curate notes and relationships by hand, and returns ranked context (must-read, must-test, danger) for a task.
Graph CRUD: upsert/get/delete nodes and edges, rename nodes, set node status, batch operations.
Context retrieval:
retrieve_context(FTS+PageRank, classified into MUST_READ/MAY_CHANGE/MUST_TEST/KNOWLEDGE/DANGER),impact_of,path_between.Search & traversal:
search_nodes,neighbors,graph_stats.Repo indexing:
index_repo(full reindex),reindex_paths(incremental after edits),index_git_coedits(co-change edges from git history),detect_divergence(drift report),clear_graph.Vault note management:
write_note,read_note,update_note_frontmatter,promote_node_to_note,list_notes,search_vault,archive_note.Maintenance:
consolidation_report(decayed/orphaned notes, divergence),sync_note_links(add wikilinks for graph neighbors),get_status(config/health).
Indexes git history to derive co-edit relationships between files, using git log and git ls-files to build graph edges that help identify related code changes.
Connects the code graph to an Obsidian vault for persistent memory, enabling note creation, frontmatter management, vault search, note promotion from graph nodes, and automatic sync of Graph View with reindexed code relationships.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@obsidian-local-mcp retrieve context for handling payments"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
██████╗ ██████╗ ███████╗██╗██████╗ ██╗ █████╗ ███╗ ██╗
██╔═══██╗██╔══██╗██╔════╝██║██╔══██╗██║██╔══██╗████╗ ██║
██║ ██║██████╔╝███████╗██║██║ ██║██║███████║██╔██╗ ██║
██║ ██║██╔══██╗╚════██║██║██║ ██║██║██╔══██║██║╚██╗██║
╚██████╔╝██████╔╝███████║██║██████╔╝██║██║ ██║██║ ╚████║
╚═════╝ ╚═════╝ ╚══════╝╚═╝╚═════╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝
██╗ ██████╗ ██████╗ █████╗ ██╗ ███╗ ███╗ ██████╗██████╗
██║ ██╔═══██╗██╔════╝██╔══██╗██║ ████╗ ████║██╔════╝██╔══██╗
██║ ██║ ██║██║ ███████║██║ ██╔████╔██║██║ ██████╔╝
██║ ██║ ██║██║ ██╔══██║██║ ██║╚██╔╝██║██║ ██╔═══╝
███████╗╚██████╔╝╚██████╗██║ ██║███████╗ ██║ ╚═╝ ██║╚██████╗██║
╚══════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝Gives your AI coding assistant a memory of your codebase. Instead of grepping around blindly every session, it asks a local graph "what does this touch, what breaks if I change it, and what did we already learn about it?"
No API keys, no network calls, no embeddings. Everything runs on your machine.

What it does, in plain words
You ask your assistant to change something. Without this server, it searches for text and hopes for the best. With it:
You: "the daily podcast is generating twice, fix it"
Assistant calls retrieve_context("daily podcast cron") and gets back:
MUST_READ the files that actually implement it
MAY_CHANGE what depends on them and would break
MUST_TEST the tests that cover it
KNOWLEDGE your note from three weeks ago explaining why the cron
runs at 540s and what already went wrong once
DANGER anything flagged as risky to touchTwo things make that possible:
A graph of your code, built mechanically by reading your files (Python
ast, TypeScript tree-sitter) and your git history. Files, symbols, who imports whom, who calls whom, what gets edited together.An Obsidian vault of notes — plain
.mdfiles where you (or the assistant) write down the why: decisions, incidents, gotchas. Things that are nowhere in the code.
The code half gets rebuilt whenever you reindex. The notes half is yours and never gets overwritten.
Related MCP server: Threadnote
What you need
uv — handles Python for you (it downloads Python 3.13 automatically; that version is pinned because
tree-sitter-typescriptwheels lag newer ones).A folder for your vault. That is all a vault is: a folder of
.mdfiles. Installing Obsidian is optional and only needed if you want to browse your notes and see the graph visually.Git repos you want indexed. Only files tracked by git are scanned.
Optional: a GPU or Apple Silicon, if you want the decision model. It is too slow on a CPU.
Setup
1. Install
git clone <this repo>
cd obsidian-local-mcp
uv sync2. Set up your vault
uv run obsidian-local-mcp-setupIt asks two questions — whether to install the decision model (say no for
now) and where your vault is — then writes
~/.obsidian-local-mcp/vaults.yaml and prints the block for step 3.
Copy vaults.example.yaml to somewhere outside this repo — it contains
real paths from your machine, so it must never be committed:
default_vault: main
vaults:
main:
path: /path/to/your/vault-folder
conventions: generic # "generic" = no rules about note format
repos:
frontend:
path: /path/to/your/frontend
include: ["src/**"]
exclude: ["**/node_modules/**"]
languages: [ts, tsx]
backend:
path: /path/to/your/backend
include: ["app/**", "tests/**"]
languages: [py]repos names (frontend, backend) are yours to pick — you will use them
when indexing. Use conventions: englora instead of generic if you want
strict note contracts enforced (see Vault profiles).
3. Connect it to your assistant
Create a .mcp.json in each repo you work from:
{
"mcpServers": {
"obsidian": {
"command": "uv",
"args": ["run", "--directory", "/path/to/obsidian-local-mcp", "obsidian-local-mcp"],
"env": {
"OBSIDIAN_MCP_CONFIG": "/path/to/your/vaults.yaml",
"OBSIDIAN_MCP_VAULT": "main"
}
}
}
}Restart your assistant. In Claude Code, /mcp should now list obsidian.
4. Build the graph (once)
Just ask your assistant, in plain language:
"index the frontend and backend repos, then index the git co-edits"
Or, if you prefer to know what it runs under the hood:
index_repo(repo="frontend")
index_repo(repo="backend")
index_git_coedits(repo="frontend")
index_git_coedits(repo="backend")Then ask for graph_stats() to confirm. A few thousand nodes for a
mid-sized repo is normal. This takes seconds, not minutes.
The CLI
One command, three subcommands. Run them from this repo with uv run.
uv run obsidian-local-mcp-setup # set up your vault (and optionally the model)
uv run obsidian-local-mcp-setup serve # run the decision model, if you installed one
uv run obsidian-local-mcp-setup doctor # check everything and print the triage latencydoctor is the one to run when something looks wrong — it checks the
config, your vaults, whether the graph is built and whether the model
answers, and says what to do about anything it finds.
To set up without the questions:
uv run obsidian-local-mcp-setup install --vault ~/notes --no-model --force
| |
| use this vault, skip all prompts |
| install this model without asking |
| skip the model |
| overwrite an existing config |
| port for the model (default 8009) |
Config lives at ~/.obsidian-local-mcp/vaults.yaml and the model in
~/.obsidian-local-mcp/kev-env/. OBSIDIAN_MCP_CONFIG and
OBSIDIAN_MCP_HOME move them.
The decision model (optional)
Skip this unless you have a GPU or Apple Silicon.
retrieve_context ranks candidates by how your code is wired together,
which cannot tell that a well-connected file is irrelevant to the question
you asked. Your assistant finds that out by opening them — and every one
that did not matter is now in its context for good.
triage_candidates(query) runs the same search, then asks
Kev, a small model running on your
machine, which candidates are worth reading. You get those in full and the
rest as one-line stubs.
uv run obsidian-local-mcp-setup install --model 0.8b # once
uv run obsidian-local-mcp-setup serve # leave running
uv run obsidian-local-mcp-setup doctor # check the latencyTune it in vaults.yaml under kev: (enabled, base_url, timeout_ms,
threshold, max_candidates — see vaults.example.yaml).
It cannot break anything. If the model is down, slow or disabled,
triage_candidates returns every candidate with triaged: false and a
reason — exactly what retrieve_context would have given you. Never an
error. So it is safe to leave configured, and safe to kill serve.
But check the latency. Triage has to cost less than the reading it
saves. We measured kev-4b scoring 15 candidates on a CPU with no GPU:
~73 seconds. Unusable — it would just time out and fail open every
time. 0.8b is ~5x smaller and still probably too slow. Without a GPU,
leave it off and use retrieve_context.
The judgement itself is good, which was the part in doubt: on a Spanish query against Spanish notes, kev-4b kept exactly the five candidates that mattered and dropped eight structural neighbours and an unrelated billing module at 0.00–0.07.
Using it day to day
You do not call these tools yourself. You work exactly as you always did — the assistant decides when to reach for the graph, because every tool describes when it should be used.
You: "why does the newsletter send the wrong level sometimes?"
→ assistant calls retrieve_context, reads your notes, then the codeThree things worth knowing:
Ask it to use the graph when it forgets. A nudge works ("check the graph
first"). To make it permanent, put one line in your CLAUDE.md:
Before using Grep/Glob to understand code or relationships, call
retrieve_contextfirst. Grep is only for locating a known literal string.
Reindex after you move things. Adding, deleting, renaming files or changing imports makes the graph drift. A stale map is worse than no map:
"reindex the files I just changed" →
reindex_paths(repo, paths=[...])
Let it write notes. When you explain something non-obvious ("we cap it at 540s because Cloud Run was OOMing"), tell it to save that:
"write that down in the vault" →
write_note(...)
That note is what shows up in KNOWLEDGE next month, long after you and the
assistant have both forgotten the conversation.
Searching well
Seeding is lexical (SQLite FTS5 + bm25), not semantic. So queries work best with few, distinctive words:
Query | Result |
| excellent |
| good |
| poor — common words match everything |
If the buckets come back unrelated to what you asked, retry with fewer and rarer words (a file name, a feature name, a symbol).
The two kinds of data
This distinction matters, and it is the one thing to get right:
Code nodes (mechanical) | Curated notes (yours) | |
Looks like |
|
|
Written by |
|
|
To change it | re-run the indexer | edit the note |
Never | hand-edit it — the indexer will overwrite you | gets touched by an indexer |
For a note to surface as KNOWLEDGE when someone touches a file, it needs a
documents edge pointing at that file:
upsert_edge(src="feature:daily-podcast", dst="file:back/app/podcast.py", type="documents")Without that edge the note only appears when your query happens to match its text. With it, the note appears whenever that file is in play — which is the whole point.
Troubleshooting
Changed the server's code and nothing happened. The running process still
has the old code loaded. Restart the MCP server (in Claude Code: restart the
session, or reconnect via /mcp).
KNOWLEDGE is always empty. Your notes probably have no documents
edges yet. See The two kinds of data.
Results point at files that no longer exist. The graph drifted. Run
detect_divergence() to see what broke, then reindex_paths or a full
index_repo.
A note says something that is no longer true. Notes carry status and
confidence in their frontmatter. status: "assumed" means a past session
inferred it without verifying — always confirm against the code before
trusting it. consolidation_report() lists stale and orphaned notes.
Nothing gets indexed. Only git-tracked files are scanned. Check your
include/exclude globs and that the files are actually committed.
Anything looks wrong. Run obsidian-local-mcp-setup doctor first. It
checks the config, your vaults, the graph and the model, and says what to
do about whatever it finds.
triage_candidates always says triaged: false. The model is not
answering; the reason field says why. Your results are unaffected —
kept holds every candidate.
Vault profiles
Set per-vault with conventions::
generic— no rules. Notes are plain markdown; write whatever you want. Good for a personal wiki.englora— enforces a note contract (id,type,area,status,confidence,source,created,updated,decay,supersedes,links) and refuses to mark inferred claims asverified. Good when you want the assistant held to a standard.
Multiple vaults can be configured at once; OBSIDIAN_MCP_VAULT picks the
default per repo, and every tool takes an optional vault= argument.
Internals
Everything below is for working on this server rather than with it.
Architecture
src/obsidian_local_mcp/
├── server.py FastMCP instance + all tools (thin: validate → call → model)
├── instructions.py server-level instructions handed to the connecting assistant
├── config.py vaults.yaml → Config/VaultConfig/RepoConfig/KevConfig
├── models.py pydantic request/response types for every tool
├── setup_cli.py the obsidian-local-mcp-setup command (install/serve/doctor)
├── triage/
│ ├── client.py stdlib-only HTTP client for the Kev sidecar
│ └── candidates.py RetrievalResult → one Kev request → keep/drop verdicts
├── graph/
│ ├── store.py SQLite (<vault>/.graph/graph.db): nodes, edges, FTS5
│ ├── ids.py file:<repo>/<path>, fn:<repo>/<path>#<symbol>, feature:, ext:, note:
│ ├── weights.py edge-type weight / reverse-factor / origin table
│ └── retrieve.py seed_fts() + personalized_pagerank() + classify()
├── index/
│ ├── scan.py repo file listing (git ls-files) + parser dispatch + test-edge derivation
│ ├── python_parser.py ast: contains/call/import, two-pass (forward refs resolve)
│ ├── ts_parser.py tree-sitter: contains/call/import + JSX-usage-as-call
│ └── git_coedit.py co_edit edges from `git log --name-only`
└── vault/
├── conventions.py vault "profiles": englora (strict) vs generic (unenforced)
├── notes.py frontmatter read/write, wikilink-tolerant YAML, secret guard
└── maintenance.py decay/orphan/divergence reports, additive sync_note_linksThe origin rule
Every node/edge carries origin: scanner | git | manual. Reindexing only
ever rewrites rows it owns:
index_repo/reindex_pathsdelete+rewritescanner-origin rows for the paths touched, then purge anyscanner-origin edge left dangling.index_git_coeditsdelete+rewritegit-origin edges for the repo.manual-origin rows (written byupsert_node/upsert_edge— curatedfeature:/ext:nodes,depends_on/documents/decided_by/dangerousedges) are never touched by either indexer.
This is what lets an assistant curate knowledge on top of a graph that gets mechanically rebuilt.
Retrieval
retrieve_context(query): FTS5 bm25 over node id/label/path/curated-note
body seeds a personalized-PageRank walk (restart α=0.15, both edge
directions at their type's weight/reverse_factor). The ranked result is
then classified into MUST_READ / MAY_CHANGE / MUST_TEST / KNOWLEDGE /
DANGER by walking the same edges. impact_of(node_id) runs the same
classification anchored on one explicit node instead of a query.
documents/decided_by edges run note → code, so the note explaining an
anchor is reached by walking into the anchor (by_dst), not out of it.
Those notes are surfaced even with no lexical hit of their own — curated
prose exists precisely for the queries whose wording does not match it.
No embeddings by default — seed_fts() is the one seam where a vector
search could be substituted later without touching the ranking/
classification code.
Triage
retrieve_context ranks by topology. Topology cannot tell that a
well-connected file is irrelevant to this particular question, so the
assistant settles that by opening the candidates — and every one that turns
out not to matter has already been paid for and is in its context for good.
triage_candidates(query) runs the same search, then asks
Kev — a small local decision model —
which candidates are worth reading. Kev is a pointer model: one state
plus N typed questions go in, one prefill pass runs, and the answers are
read off the logits. Nothing is generated. Questions share the state's
activations and cannot see each other, so 25 candidates cost about what one
costs and no candidate's verdict is dragged around by its neighbours'.
Consequences that shaped the code, all in triage/candidates.py:
One
Noulper candidate, never one bigChoice. Choice options are read in order and the ranking moves when you permute them; independent yes/no questions have no order to be biased by, and they let several candidates be essential at once.must_readis never dropped. The graph put it there off the query's own seeds; a model wrong on that one would hide the file you came for.A missing or malformed answer keeps the candidate. Silence must not be indistinguishable from rejection.
Fail-open, always. Kev down, slow, disabled or throwing returns every candidate with
triaged: falseand areason. A filter that can hide results is never allowed to be the reason a lookup fails.
Kev is reached over HTTP and never imported: it needs torch and a multi-GB
checkpoint, and this server is loaded by a stdio client that would
otherwise wait on that at every start. That is why the sidecar lives in its
own virtualenv and why setup_cli.py exists at all.
The user-facing half — installing it, running it, configuring it, and the measured latency that decides whether it is worth running — is in The decision model.
Tools
Graph write: upsert_node[s], upsert_edge[s], delete_node,
delete_edge, rename_node, set_node_status.
Graph read: get_node, neighbors, search_nodes, retrieve_context,
triage_candidates, impact_of, path_between, graph_stats.
Indexing: index_repo, reindex_paths, index_git_coedits,
detect_divergence, clear_graph (requires confirm=True).
Vault notes: read_note, write_note, update_note_frontmatter,
promote_node_to_note (scaffold graph nodes into vault notes),
list_notes, search_vault, archive_note.
Maintenance: consolidation_report, sync_note_links, get_status.
Tests
uv run pytestAll 116 tests pass (one skips unless kev is importable). Key fixtures:
tests/fixtures/mini_repo/— a small backend (5 Python files) that reproduces a complete indexing workflow:index_repo→index_git_coedits→retrieve_context.tests/fixtures/mini_vault/— a minimal vault used to test note read/write, frontmatter validation, andpromote_node_to_note.Dynamic
mini_repo_gitfixture — builds a throwaway git repo with two commits to test co-edit detection.
Available Tools
30 toolsarchive_noteArchive NoteB
Move a note to 99-Archive/ with a dated filename and a header recording why -- nothing in the vault is ever hard-deleted.
Inputs: reason (required), relative_path (required), vault. Returns: result
clear_graphClear GraphA
Wipe every node and edge for a vault. Irreversible; requires confirm=True.
Inputs: vault, confirm. Returns: result
consolidation_reportConsolidation ReportA
The Instructions.md Sec.7 consolidation ritual, as a read-only report: decayed notes, isolated graph nodes, and divergence issues. Run this every ~10 sessions, or when asked to 'consolida'.
Inputs: vault. Returns: result
delete_edgeDelete EdgeB
Remove one edge by its (src, dst, type) key.
Inputs: dst (required), src (required), type (required), vault. Returns: result
delete_nodeDelete NodeA
Remove a node. Soft delete (default) hides it from reads but keeps the row; hard=True also deletes every edge touching it, irreversibly.
Inputs: id (required), hard, vault. Returns: result
detect_divergenceDetect DivergenceA
Find nodes/notes pointing at paths that no longer exist, and manual/git edges whose endpoint node vanished -- read-only, reports only; nothing here is deleted automatically.
Inputs: vault. Returns: result
get_nodeGet NodeC
Fetch one node by its exact id.
Inputs: id (required), vault. Returns: result
get_statusGet StatusA
Report which vaults are configured and whether the requested (or default) vault's graph store is reachable.
Inputs: vault. Returns: status, vaults, message, default_vault
graph_statsGraph StatsC
Node/edge counts by kind and type.
Inputs: vault. Returns: result
impact_ofImpact OfB
Impact analysis for one specific node: what it needs (MUST_READ), what depends on it (MAY_CHANGE), what exercises it (MUST_TEST), the knowledge that documents it, and anything flagged DANGER.
Inputs: id (required), vault. Returns: result
index_git_coeditsIndex Git CoeditsB
Derive co_edit edges from this repo's git history: files that keep changing together, even without an import connecting them.
Inputs: repo (required), vault, min_shared_commits. Returns: result
index_repoIndex RepoA
Full (re)index of one configured repo: wipes and rewrites every scanner-origin node/edge for it, leaving manual/git-origin rows alone.
Inputs: repo (required), vault. Returns: result
list_notesList NotesC
List note paths under the vault (or under subdir).
Inputs: vault, subdir. Returns: result
neighborsNeighborsC
One-hop neighbors of a node. direction is 'out', 'in', or 'both'.
Inputs: id (required), vault, direction, edge_types. Returns: result
path_betweenPath BetweenB
Shortest connection between two nodes through the graph (either direction), for "how are these two things related" questions.
Inputs: dst (required), src (required), vault, max_hops. Returns: result
promote_node_to_notePromote Node To NoteA
Materialize a graph node (file/symbol/feature) as a curated note under 20-Knowledge/codebase/{files,symbols,features}, pre-filled with its neighbors from the graph. Written as status:"assumed" (it is a skeleton, not verified prose) -- promote sparingly, per Instructions.md Sec.12: not every file earns a note.
Inputs: node_id (required), body, vault, extra_frontmatter. Returns: result
read_noteRead NoteC
Read one note's frontmatter and body.
Inputs: relative_path (required), vault. Returns: result
reindex_pathsReindex PathsB
Incremental reindex of just these relpaths within a repo -- call this after editing files so the graph does not drift from the code.
Inputs: repo (required), paths (required), vault. Returns: result
rename_nodeRename NodeB
Move a node to a new id, carrying every edge with it (e.g. after a file move index_repo would otherwise treat as delete-then-create, losing manual/git edges attached to the old id).
Inputs: new_id (required), old_id (required), vault. Returns: result
retrieve_contextRetrieve ContextB
The main entry point: FTS5-seeded, PageRank-expanded context for a natural-language description of a task. Read MUST_READ first, note DANGER, expect MAY_CHANGE/MUST_TEST before you consider a change done.
Inputs: query (required), top_k, vault, seed_k. Returns: result
search_nodesSearch NodesC
Full-text search over node id/label/path/curated-note body. If
query is itself a valid node id, that exact node is included first.
Inputs: query (required), limit, vault. Returns: result
search_vaultSearch VaultC
Substring search over every note's id/body, with a short snippet per hit.
Inputs: query (required), limit, vault. Returns: result
set_node_statusSet Node StatusB
Update a node's status (verified/probable/assumed/outdated/refuted) and optionally its confidence, without touching anything else.
Inputs: id (required), status (required), vault, confidence. Returns: result
sync_note_linksSync Note LinksB
Add wikilinks to curated notes for graph neighbors that also have a note (documents/decided_by/depends_on/dangerous/test edges). Additive only -- never removes a link you added by hand.
Inputs: vault. Returns: result
update_note_frontmatterUpdate Note FrontmatterB
Merge updates into a note's existing frontmatter, leaving the body
untouched. created is preserved; updated is bumped automatically.
Inputs: updates (required), relative_path (required), vault. Returns: result
upsert_edgeUpsert EdgeB
Create or update one edge. weight/origin default from the edge type (see graph.weights) when omitted -- pass origin='manual' explicitly when in doubt; that is the one origin a reindex never overwrites.
Inputs: dst (required), src (required), type (required), vault, +4 more. Returns: result
upsert_edgesUpsert EdgesC
Create or update several edges at once, in the (src, dst, type) shape from starter_edges (each entry may also set weight/origin/etc).
Inputs: edges (required), vault. Returns: result
upsert_nodeUpsert NodeB
Create or update one graph node. Prefer this for curated nodes (feature:, ext:, note: ids, or a manual correction to a code node) -- code nodes (file:/fn:) are normally written by index_repo instead, so a later reindex does not immediately overwrite what you just wrote.
Inputs: id (required), kind (required), label (required), path, +8 more. Returns: result
upsert_nodesUpsert NodesB
Create or update several nodes at once, in the (id, kind, label) shape (each entry may also set repo/path/status/confidence/etc).
Inputs: nodes (required), vault. Returns: result
write_noteWrite NoteB
Create or update a note. Enforces the vault's note contract (id, type, area, status, confidence, source, ...) when the vault's profile requires it, and refuses content that looks like a live secret unless allow_secrets=True.
Inputs: body (required), frontmatter (required), relative_path (required), vault, +1 more. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 30 tools
The set is mostly well-differentiated, with clear splits between node/edge CRUD, note operations, indexing, and context queries. Minor overlaps remain, especially between search_vault and search_nodes, and between consolidation_report and detect_divergence, but descriptions usually clarify the intended choice.
All tool names use snake_case consistently, and most follow recognizable verb_noun or verb_object patterns. A few tools are noun-only or prepositional phrases (neighbors, graph_stats, path_between, impact_of), which is a minor deviation from a strict verb_noun convention.
With 30 tools, the surface is above the 25+ threshold that typically indicates an overloaded set. The domain is complex, but the many granular single/batch and lifecycle operations could likely be consolidated or grouped more tightly.
The set covers node/edge lifecycle, note lifecycle, indexing, retrieval, impact analysis, pathfinding, stats, divergence detection, and consolidation. Gaps are minor: no batch delete, no explicit edge listing/search, no note rename, and no wikilink removal beyond additive sync.
Maintenance
Related MCP Connectors
Search indexed code, trace dependencies, assess change impact, and recall repository memory.
Shared memory for coding agents. Stop re-explaining your codebase every session.
Deterministic context layer for your codebase: change impact, blast radius, answers with receipts.
Give your AI agent a persistent map of your project's structure, dependencies, and bugs.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to retrieve hyper-targeted, token-efficient context from a codebase by querying a locally built AST knowledge graph, and to persist cross-session agent memory through 17 MCP tools. All indexing and retrieval run entirely offline, so proprietary code never leaves the machine.24 PyPI1MIT
- AlicenseBqualityAmaintenanceLocal-first engineering memory and code-graph tools for coding agents. Supports private session handoffs, code-cited knowledge, repository graph inspection, and optional Git-backed team sharing.2532 npm29AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables AI coding agents to persist and retrieve notes, code entities, and typed links in a local graph database across sessions, with hybrid offline search, graph traversal, and staleness checks. It also supports export/import and CLI access for backup and transfer.MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI coding agents to query a locally indexed structural code graph — source entities, callers/callees, Git relationships and recorded mistakes — so they retrieve focused, token-bounded context instead of reading entire files. It runs over local SQLite and can be served through a CLI, agent hooks, an MCP executable and an authenticated local dashboard.28 npmMIT