An MCP (Model Context Protocol) server that provides persistent semantic memory backed by PostgreSQL + pgvector. Store "thoughts" with vector embeddings and organize them via a flexible dimensional model.
This repo provides two MCP servers with a shared codebase:
- brain-mcp (
dist/index.js) — General-purpose knowledge store with ADR support. Use this for non-code contexts. - brain-code-mcp (
dist/code.js) — Superset of brain-mcp with code-aware tools. Use this for software projects.
You only need to configure one — brain-code-mcp includes all brain-mcp tools.
- Semantic search — find thoughts by meaning using cosine similarity over vector embeddings
- Dimensional organization — tag thoughts with typed dimensions (person, project, topic, tag, file, symbol, etc.)
- Thought temporality — thoughts have types (
fact,decision,observation,question) and can be superseded while preserving history - Multi-brain support — isolated knowledge spaces via the
BRAIN_NAMEenvironment variable - Conflict detection — automatically surfaces similar existing thoughts when capturing new ones
- Architecture Decision Records — structured ADR capture with auto-numbering, alternatives, and consequences
- Code-linked knowledge — link thoughts to repositories, files, and symbols (brain-code-mcp)
- Knowledge freshness — detect stale knowledge when referenced code changes (brain-code-mcp)
| Tool | Description |
|---|---|
capture_thought |
Store a thought with type, dimensions, metadata, and embedding. Surfaces conflicts with similar active thoughts. |
search |
Semantic vector search with optional filters (dimension, thought type, etc.) |
list_recent |
Chronological listing with optional filters |
explore_dimension |
All thoughts linked to a given dimension |
list_dimensions |
All dimensions with thought counts |
list_brains |
List all brains with optional thought counts. Respects BRAIN_ACCESSIBLE. |
supersede_thought |
Replace an existing thought, preserving history. Auto-preserves ADR metadata. |
capture_adr |
Record an Architecture Decision Record with context, alternatives, and consequences |
list_adrs |
List and filter ADRs by status or dimension |
| Tool | Description |
|---|---|
capture_code_context |
Capture knowledge linked to specific files, symbols, or repositories |
search_code |
Semantic search filtered to code-linked knowledge |
check_freshness |
Check if code-linked knowledge is stale by comparing git state |
refresh_stale_knowledge |
Find stale thoughts with git diffs for review |
| Prompt | Description |
|---|---|
brain_overview |
Comprehensive orientation: thought counts, dimensions, recent thoughts, ADR summary, open questions |
deep_dive |
Deep dive into a dimension with all linked thoughts, co-occurring dimensions, and ADRs |
decision_review |
Review active decisions and ADRs, flagging overdue revisit dates |
capture_session |
Set up a knowledge capture session with existing taxonomy and related knowledge |
| Prompt | Description |
|---|---|
codebase_knowledge |
All knowledge about a repo grouped by file/symbol, with optional freshness checks |
file_context |
All knowledge about a specific file with freshness and semantically related unlinked knowledge |
- Node.js
- PostgreSQL with pgvector extension
- An OpenRouter API key (for generating embeddings)
Set OPENROUTER_API_KEY in your shell environment (e.g. in ~/.bashrc or ~/.zshrc):
export OPENROUTER_API_KEY="your-key-here"Then add to your project's .mcp.json:
{
"mcpServers": {
"brain": {
"command": "npx",
"args": ["-y", "github:markschaake/brain-mcp"],
"env": {
"DATABASE_URL": "postgresql://user:pass@host:5432/brain",
"BRAIN_NAME": "personal"
}
}
}
}For brain-code-mcp (includes code-aware tools):
{
"mcpServers": {
"brain": {
"command": "npx",
"args": ["-y", "-p", "github:markschaake/brain-mcp", "brain-code-mcp"],
"env": {
"DATABASE_URL": "postgresql://user:pass@host:5432/brain",
"BRAIN_NAME": "my-project"
}
}
}
}Note: Do not put
OPENROUTER_API_KEYin.mcp.json— it is often checked into version control. The server reads it from the environment automatically.
The database schema is automatically created on first run.
Option 1: Use the included docker-compose (easiest for local development)
git clone https://github.com/markschaake/brain-mcp.git
cd brain-mcp
docker compose up -d # starts PostgreSQL+pgvector on port 5488With docker-compose, the default DATABASE_URL (postgresql://brain:brain@localhost:5488/brain) works without any configuration.
Option 2: Bring your own PostgreSQL
Any PostgreSQL instance with the pgvector extension installed will work. Set DATABASE_URL in your MCP config. The schema is auto-applied on first server startup.
pnpm install
pnpm run build
pnpm run dev # watch mode (tsc --watch)
pnpm run lint # run ESLint
# Run directly
OPENROUTER_API_KEY=your-key node dist/index.js # brain-mcp
OPENROUTER_API_KEY=your-key node dist/code.js # brain-code-mcp| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgresql://brain:brain@localhost:5488/brain |
OPENROUTER_API_KEY |
Required for embedding generation | — |
EMBEDDING_MODEL |
Override the embedding model | openai/text-embedding-3-small |
BRAIN_NAME |
Which brain (knowledge space) to use | personal |
BRAIN_ACCESSIBLE |
Comma-separated whitelist of brain names this instance can access. Empty = all brains accessible. | (empty) |
All tools and prompts accept an optional brain parameter to target a specific brain by name at runtime, without restarting the server. Omit it to use the default brain (BRAIN_NAME).
Read-only tools (search, list_recent, explore_dimension, list_dimensions, list_adrs, search_code, check_freshness, refresh_stale_knowledge) also accept brain: "*" to query across all accessible brains.
Write tools (capture_thought, supersede_thought, capture_adr, capture_code_context) reject "*" — you must specify a brain name for writes.
Use BRAIN_ACCESSIBLE to restrict which brains a server instance can access:
{
"mcpServers": {
"brain": {
"command": "npx",
"args": ["-y", "github:markschaake/brain-mcp"],
"env": {
"DATABASE_URL": "postgresql://user:pass@host:5432/brain",
"BRAIN_NAME": "personal",
"BRAIN_ACCESSIBLE": "personal,work,shared"
}
}
}
}When BRAIN_ACCESSIBLE is empty (default), all brains are accessible.
| File | Purpose |
|---|---|
src/index.ts |
brain-mcp entry point |
src/code.ts |
brain-code-mcp entry point (superset) |
src/tools.ts |
Shared tool registration (core + ADR tools) |
src/db.ts |
PostgreSQL connection pool and helpers |
src/migrate.ts |
Auto-migration runner (applies migrations/*.sql on startup) |
src/embeddings.ts |
Embedding generation via OpenRouter |
src/git.ts |
Git operations for freshness detection |
src/prompts.ts |
MCP prompt registration (core prompts for both servers) |
Migrations are in migrations/ and are auto-applied on server startup.
- brains — isolated knowledge spaces
- thoughts — content + vector(1536) embedding + metadata (jsonb) + thought type + status
- dimensions — typed categories with metadata, unique per (brain, name, type)
- thought_dimensions — many-to-many links with optional context
Embeddings are indexed with HNSW for fast cosine similarity search.
brain-code-mcp uses these dimension types to link knowledge to code:
| Type | Name convention | Metadata |
|---|---|---|
repo |
Repository name | {} (extensible) |
file |
Repo-relative path | {repo, line_start, line_end, git_sha} |
symbol |
Symbol name | {repo, file, kind} |
ADRs are stored as decision thoughts with structured metadata:
ISC
{ "adr": true, "adr_number": 7, "adr_title": "Use pgvector for semantic search", "adr_status": "accepted", // proposed | accepted | deprecated | superseded "adr_context": "Why this decision was needed...", "adr_alternatives": [{ "name": "Pinecone", "pros": [...], "cons": [...] }], "adr_consequences": ["Must run PostgreSQL with pgvector"], "adr_decided_date": "2026-03-01" }