brain-graph
Provides read-only query tools over a Neo4j graph of wikilinked markdown notes, including finding everything within 1 to 3 hops of a note, computing the shortest path between two notes, listing orphan notes, retrieving recently updated notes by label, and running arbitrary read-only Cypher (with write clauses rejected before execution).
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., "@brain-graphshow me what's connected to projects/acme within two hops"
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.
Overview
brain-graph reads a folder of markdown notes linked with [[wikilinks]]. An Obsidian vault, a team wiki and
an AI agent's memory folder all fit this shape. It writes the notes and their links into Neo4j, then exposes
the graph through a read-only MCP server.
Grep answers "which files mention Acme". brain-graph answers "what is connected to Acme, and how far away is
it". Every sync also lists orphan notes, dangling links and notes missing a level: field.
It is for people who keep a markdown knowledge base and want an AI client to traverse it, not just search it.
flowchart LR
V[(markdown vault<br/>one or more)] -->|graph-lib.mjs<br/>parse · resolve| S[graph-sync.mjs]
S -->|full rebuild per source| N[(Neo4j)]
S -->|stdout| R[report:<br/>orphans · dangling links · missing level:]
N -->|read-only session| M[mcp-server.mjs]
M -->|stdio| C[Claude / any MCP client]Related MCP server: graph-mcp
Quick start
Requires Node 20.6 or newer (for --env-file) and Docker.
npm install
npm run verify # assertions against the bundled example-vault/
npm run dry # parse example-vault/ and print the report. No database needed.The dry run prints a report like this (trimmed):
sources : brain
nodes : 12 (8 governed by the orphan/level rules)
edges : 12
orphans : 1 (12.5% of governed)
dangling : 1 (wikilinks pointing at nothing)
missing level: 2 (25.0% of governed)
...
DANGLING WIKILINKS (target does not exist, fix the link or write the note):
brain:ideas/someday -> [[ideas/not-written-yet]]Then point it at your own vault and write the graph:
cp .env.example .env # set NEO4J_PASSWORD and GRAPH_SOURCES
npm run up # Neo4j 5 with APOC; browser UI on http://localhost:7474
npm run syncSync is a full rebuild. It deletes every node whose
sourcematches a name inGRAPH_SOURCESand writes them again. Do not share a source name with anything else living in the same database.
Connect Claude
Add the server to .mcp.json (Claude Code) or your client's MCP config:
{
"mcpServers": {
"brain-graph": {
"command": "node",
"args": ["/path/to/brain-graph/mcp-server.mjs"]
}
}
}It reads NEO4J_PASSWORD from the environment or from the .env file beside it.
Tool | What it answers |
| Everything within 1 to 3 hops of a note |
| How two notes relate, as a chain of slugs |
| Notes with no links in or out |
| Most recently updated notes with a given label |
| Counts by label and edge type, orphans, notes missing |
| Any read-only Cypher |
Every Cypher statement is checked for write clauses before it reaches the driver, and the session is opened in read mode.
Configuration
Set these in .env. Names only here; never commit real values.
Variable | Required | Default | Purpose |
| yes | none | Password for the |
| no |
| Bolt address of the Neo4j instance. |
| no |
| Neo4j user name. |
| no | bundled | Comma-separated |
| no | none | Slugs of machine-generated notes, exempt from the orphan and |
| no |
| Where the Neo4j container keeps its data. Keep it outside any synced folder. |
What becomes a node and an edge
Nodes. Every
.mdfile, keyed by its path without the extension (builds/example-app). Its label comes from frontmattertype:, or failing that from its top folder (people/becomesPerson,companies/becomesCompany; the table isFOLDER_TYPEingraph-lib.mjs).INDEX.mdfiles are labelledIndex. Scripts (.sh,.mjs,.ps1) undersystems/orscripts/become nodes too, so links to them resolve.LINKS_TOedges. Every[[wikilink]]in a note's body. Links inside code spans, fenced blocks and HTML comments are ignored, so a note that documents the link syntax does not link to a note called "wikilink".Typed edges.
company:becomesWORKS_AT,client:becomesFOR_CLIENT,supersedes:becomesSUPERSEDES, anduses_skill:becomesUSES_SKILL. A field only becomes an edge when its value is written as a wikilink (company: "[[companies/acme]]").client: internalstays plain text.Resolution. For each link the resolver tries, in order: the exact slug, a case-insensitive match, an alias declared on the target (
aliases:,alias:orslug:), the folder'sREADME,INDEXorSKILLnote, then a basename that matches exactly one note. When two notes match, the link is reported as dangling rather than guessed, because a wrong edge is worse than a reported one.Exempt from the rules.
skills/**, anything with a_-prefixed path segment, and the slugs inGRAPH_GENERATEDare indexed but never reported as orphans or as missinglevel:. Dot-folders are not read.archive/agent-outputs/andbackups/are indexed as single nodes, and their contents are never parsed.
Each vault in GRAPH_SOURCES is resolved on its own. A link in one vault never resolves to a note in another.
Design notes
The markdown is the source of truth. The graph is derived from it and rebuilt from scratch on every sync. If the graph and the files disagree, run the sync again rather than editing the graph. This is why the MCP server cannot write.
No YAML dependency. The frontmatter reader handles flat
key: valuepairs and lists, which covers how notes are actually written.CRLF is tested. In JavaScript,
$does not match before a\r. A regex-based frontmatter parser returned nothing for every CRLF file and reported no error, so a vault checked out on Windows gave different answers than on Linux.verify.mjschecks that both line endings parse identically, and.gitattributeskeeps the CRLF fixture CRLF.
Project structure
brain-graph/
├── graph-lib.mjs # Parsing, link resolution and node/edge mapping
├── graph-sync.mjs # Full rebuild of the graph, and the report
├── mcp-server.mjs # Read-only MCP server (stdio)
├── verify.mjs # Assertions run against example-vault/
├── docker-compose.yml # Neo4j 5 with APOC
├── example-vault/ # Bundled sample vault used by verify and dry run
└── .env.exampleTesting
npm run verifyverify.mjs runs its assertions against example-vault/, covering parsing, link resolution, LF and CRLF
handling and the report. It does not need a running Neo4j instance.
Contributing
Issues and pull requests are welcome. Please run npm run verify before opening a pull request.
License
Released under the MIT License. See LICENSE. Built by Dime Data.
This server cannot be deployed
Maintenance
Related MCP Connectors
Serve a folder of Markdown notes as an MCP server: hybrid search, reading, and sourced answers.
Read-only search of your Sortio knowledge graph (files and entities) for Claude and ChatGPT.
Search, read, and safely update Markdown notes in your connected Phasoric knowledge vaults.
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceThis lets you use Claude Desktop, or any MCP Client, to use natural language to accomplish things with Neo4j and your Aura account.988MIT
- FlicenseAqualityBmaintenanceExposes a Neo4j knowledge-graph index over personal markdown notes to LLM agents via MCP, enabling semantic search, tag navigation, and relationship discovery.6-
- AlicenseNot gradedqualityAmaintenanceMCP server exposing fast full-text and semantic search over an Obsidian vault, plus safe structured note read and write access for Claude.2Apache 2.0
- FlicenseNot gradedqualityBmaintenanceExposes a Neo4j knowledge graph to AI assistants via MCP, enabling entity search, neighbor traversal, and read-only Cypher queries.-