midbrain-memory-mcp
v0.3.2
Published
Persistent AI memory for coding agents (MCP server) — episodic capture and semantic search
Maintainers
Readme
MidBrain Memory MCP
Persistent AI memory for coding agents. An MCP server exposes
memory_search, grep, get_episodic_memories_by_date, list_files,
read_file, and memory_setup_project for context retrieval and project
configuration; companion hooks auto-capture every message as episodic memory.
Works with OpenCode and Claude Code.
API: https://memory.midbrain.ai
Prerequisites
- Node >= 20
- OpenCode and/or Claude Code
- A MidBrain API key (get one at https://memory.midbrain.ai)
Quick Start
Install
The recommended install is a single-line npx command that auto-updates on
every MCP client cold start:
npx -y midbrain-memory-mcp@latestYou don't run this directly — your MCP client (OpenCode / Claude Code) runs it. The configuration examples below embed that exact command.
Get Your API Key
- Sign up at memory.midbrain.ai
- Create an agent in the dashboard
- Generate an API key for the agent
Configure MCP
OpenCode (~/.config/opencode/opencode.json):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"midbrain-memory": {
"type": "local",
"command": ["npx", "-y", "midbrain-memory-mcp@latest"],
"environment": {
"MIDBRAIN_CONFIG_DIR": "<absolute-path>/.config/opencode"
},
"enabled": true
}
}
}Claude Code (~/.claude.json):
{
"mcpServers": {
"midbrain-memory": {
"type": "stdio",
"command": "npx",
"args": ["-y", "midbrain-memory-mcp@latest"],
"env": {
"MIDBRAIN_CONFIG_DIR": "<absolute-path>/.config/claude"
}
}
}
}Replace <absolute-path> with your home directory (e.g.
/Users/alice/.config/opencode). JSON values do not expand ~.
Run Setup
The automated installer detects both clients, prompts for your API key, writes per-client key files (chmod 600), and patches the configs above for you:
npx midbrain-memory-mcp install # interactive install
npx midbrain-memory-mcp install --project /abs/path # per-project setupHow auto-update works
Pinning the spec to @latest changes how npx resolves the package:
npx -y midbrain-memory-mcp@latest(recommended) — resolves the latest published version from the npm registry on every cold start. When a new version ships, your next restart of OpenCode / Claude Code picks it up automatically.- Unpinned package name (no
@latest, no version) — looks auto-updating but is actually sticky.npxcaches the first resolved version under a hash keyed by the full spec string and reuses the cache until npm's own staleness heuristic triggers. That heuristic is unreliable and you can end up stuck on an old version for weeks without noticing. Always pin to@latestor a specific version. npx -y [email protected]— pinned to an exact version for reproducibility. You are responsible for bumping the version yourself.
Run npx -y midbrain-memory-mcp@latest --version to print the resolved
version, or watch the MCP server stderr on startup — it logs its name
and version in the format MCP server running (<package> v<version>).
Verify
# 1. Health check
curl https://memory.midbrain.ai/health
# Expected: {"status":"ok"}
# 2. Start a session in OpenCode or Claude Code
# 3. The memory_search tool should be available
# 4. Send a few messages, then search — your messages should appearPer-Project Setup
By default, all memory goes to a single agent (your global key). Per-project setup scopes memory to a project-specific agent, so each project has its own isolated memory space.
Option A: CLI (Recommended)
# 1. Place your API key
mkdir -p .midbrain && echo "sk-your-key-here" > .midbrain/.midbrain-key && chmod 600 .midbrain/.midbrain-key
# 2. Run project setup
npx midbrain-memory-mcp install --project /absolute/path/to/your/projectNon-interactive. Resolves the API key from existing key files (no prompts), creates the key file and MCP configs for all detected clients, outputs JSON to stdout. All progress goes to stderr.
# Example output (stdout)
{
"success": true,
"project_dir": "/Users/you/myproject",
"key_file": "/Users/you/myproject/.midbrain/.midbrain-key",
"key_created": true,
"key_source": "/Users/you/.config/opencode/.midbrain-key",
"configs_written": ["opencode.json", ".mcp.json"],
"restart_required": true,
"warnings": []
}Option B: MCP Tool
Warning: Never include your API key in a chat prompt. API keys in prompts are sent to the model provider (Anthropic, OpenAI, etc.) and may be logged, stored in conversation history, or leaked. Always place the key in a file first (step 1 below), then call the tool without the key.
First, place your API key in the project:
mkdir -p .midbrain && echo "sk-your-key-here" > .midbrain/.midbrain-key && chmod 600 .midbrain/.midbrain-keyThen tell the AI assistant to configure the project:
OpenCode:
Set up midbrain memory for this projectClaude Code (name the tool explicitly due to lazy tool loading):
Use the memory_setup_project tool to configure this project
The tool creates:
opencode.jsonor.mcp.json— project-level MCP config withMIDBRAIN_PROJECT_DIR
After setup, restart the application for the project memory to take effect.
Option C: Manual
# 1. Create the key file
mkdir -p /path/to/project/.midbrain
echo "sk-your-key" > /path/to/project/.midbrain/.midbrain-key
chmod 600 /path/to/project/.midbrain/.midbrain-key
# 2. Add .midbrain-key to .gitignore
echo ".midbrain-key" >> /path/to/project/.gitignoreThen create a project-level MCP config (see Configuration Reference below).
Important: Always use absolute paths for the node binary and server.js in
MCP configs. Bare node fails when the client's shell environment doesn't
include PATH.
Architecture
OpenCode session
|
|-- MCP stdio -----> server.js ----> POST /api/v1/memories/search
| memory.midbrain.ai
|-- Plugin hook ---> midbrain-memory.ts -> POST /api/v1/memories/episodic
memory.midbrain.ai
Claude Code session
|
|-- MCP stdio -----> server.js ----> POST /api/v1/memories/search
| memory.midbrain.ai
|-- Hook scripts --> capture-user.mjs -----> POST /api/v1/memories/episodic
| capture-assistant.mjs memory.midbrain.ai- server.js -- MCP server (Node 20, stdio transport). Exposes six tools:
memory_search,grep,get_episodic_memories_by_date,list_files,read_file, andmemory_setup_project. Plain JavaScript, no build step. - plugin/midbrain-memory.ts -- OpenCode plugin (Bun/TS). Hooks into
chat.messageandmessage.updatedevents. POSTs every message to the episodic endpoint. Fire-and-forget, never blocks. - claude-code/ -- Standalone Node 20 scripts wired to Claude Code's hook system. Same episodic capture, no dependencies beyond Node builtins.
- shared/midbrain-common.mjs -- Shared utilities (
loadApiKey,storeEpisodic,makeDebugLogger) consumed by all components. Single source of truth for key resolution, API endpoints, and constants.
How It Works
- Search -- The LLM invokes
memory_searchvia MCP. The server queries the search API and returns scored results as formatted text. - Capture (OpenCode) -- The plugin hooks into OpenCode's message lifecycle.
User messages are captured from
chat.message; assistant messages frommessage.updatedafter completion. Each is POSTed to the episodic endpoint. - Capture (Claude Code) -- Hook scripts fire on
UserPromptSubmitandStopevents. Each reads the message from stdin JSON and POSTs to the same episodic endpoint. Async, fire-and-forget. - Project Setup -- The LLM invokes
memory_setup_projectvia MCP. The server creates the key file and project-level MCP config, then instructs the LLM to tell the user to restart.
Configuration Reference
Environment Variables
| Variable | Purpose | Set by |
|---|---|---|
| MIDBRAIN_CONFIG_DIR | Client config directory for key resolution | MCP config environment/env block |
| MIDBRAIN_PROJECT_DIR | Project directory for per-project key resolution | Project-level MCP config |
| MIDBRAIN_API_KEY | API key (CI/debug fallback only) | User environment |
API Key Resolution
Keys are stored in files with chmod 600. The resolution chain (in order):
| # | Location | Source |
|---|---|---|
| 1a | <projectDir>/.midbrain-key | Per-project (flat) |
| 1b | <projectDir>/.midbrain/.midbrain-key | Per-project (recommended) |
| 2a | $MIDBRAIN_PROJECT_DIR/.midbrain-key | Per-project via env (flat) |
| 2b | $MIDBRAIN_PROJECT_DIR/.midbrain/.midbrain-key | Per-project via env |
| 3 | <configDir>/.midbrain-key | Per-client config dir |
| 4 | $MIDBRAIN_CONFIG_DIR/.midbrain-key | Per-client via env |
| 5 | $MIDBRAIN_API_KEY | Environment variable (CI only) |
| 6 | ~/.config/midbrain/.midbrain-key | Global default |
Rules:
- Permission denied (
EACCES) on any key file is a hard error (not silent fallthrough) - Empty key files are a hard error naming the file path
- When a project dir is specified but no project key found, a warning is emitted if resolution falls through to the global key
Key File Locations
| Purpose | Path |
|---|---|
| Global default | ~/.config/midbrain/.midbrain-key |
| OpenCode client | ~/.config/opencode/.midbrain-key |
| Claude Code client | ~/.config/claude/.midbrain-key |
| Project (flat) | <projectDir>/.midbrain-key |
| Project (recommended) | <projectDir>/.midbrain/.midbrain-key |
Project-Level MCP Configs
OpenCode -- <project>/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"midbrain-memory": {
"type": "local",
"command": ["npx", "-y", "midbrain-memory-mcp@latest"],
"environment": {
"MIDBRAIN_CONFIG_DIR": "<absolute-path>/.config/opencode",
"MIDBRAIN_PROJECT_DIR": "<absolute-project-dir>"
},
"enabled": true
}
}
}Claude Code -- <project>/.mcp.json:
{
"mcpServers": {
"midbrain-memory": {
"command": "npx",
"args": ["-y", "midbrain-memory-mcp@latest"],
"env": {
"MIDBRAIN_CONFIG_DIR": "<absolute-path>/.config/claude",
"MIDBRAIN_PROJECT_DIR": "<absolute-project-dir>"
}
}
}
}Important:
- All paths must be absolute. JSON does not expand
~. - Pin the spec to
@latestfor auto-updates, or@X.Y.Zfor a frozen version. Never use the bare, unpinned package-name form — it looks auto-updating but is actually sticky on the first resolved version. - OpenCode uses the
mcpkey. Claude Code usesmcpServers. Using the wrong key for either client will silently fail.
LLM Rules for Your Project
Add these rules to your project's AGENTS.md (or CLAUDE.md for Claude Code)
so the AI assistant uses memory correctly:
## MidBrain Memory Rules
- Use memory_search at session start to load relevant context
- Use grep for exact pattern matches (names, IDs, code, URLs)
- Use list_files and read_file to browse semantic memory documents
- Use get_episodic_memories_by_date for conversation history by date
- NEVER create semantic memories. Semantic is managed by dream consolidation.
- NEVER create episodic memories. Episodic capture is automatic.
- The only memory tools available are search and setup. Use them proactively.
- When the user asks to set up, configure, or initialize MidBrain memory for a
project, ALWAYS use the memory_setup_project tool. NEVER manually create
.midbrain-key files, .mcp.json, or opencode.json with shell commands.
The tool handles permissions, config merging, and path resolution correctly.
Manual setup will break.Troubleshooting
MCP server not connecting
Symptom: memory_search tool not available in your session.
Check:
# Verify the server starts
node /path/to/midbrain-memory-mcp/server.js
# Should print "MCP server running" to stderrCommon causes:
- Bare
nodein MCP config instead of absolute path (e.g.,/usr/local/bin/node) server.jspath is relative instead of absolute- Missing
npm install(MCP SDK not installed) - Claude Code: check
~/.claude.jsonfor the MCP entry (NOT~/.claude/settings.json-- MCP servers in settings.json are silently ignored)
Memory going to wrong agent
Symptom: Messages appear in the global agent dashboard instead of the project agent.
Cause: The session started before the project key was created. The plugin/hooks resolve the API key at init time and cache it.
Fix: Restart the application after running project setup. The restart message from memory_setup_project reminds you of this.
Claude Code doesn't use the setup tool
Symptom: Claude Code web-searches for "Midbrain" instead of calling memory_setup_project.
Cause: Claude Code lazy-loads MCP tools. If your message doesn't trigger tool loading, it doesn't know the tool exists.
Fix: Name the tool explicitly:
Use the memory_setup_project tool to configure this projectPermission denied on key file
Symptom: EACCES error when the server starts or searches.
Fix:
chmod 600 /path/to/.midbrain-keyEmpty key file error
Symptom: Error message naming a specific key file as empty.
Fix: Either add a valid key to the file or remove it so the resolution chain falls through to the next source.
API Reference
Base URL: https://memory.midbrain.ai
All endpoints use Authorization: Bearer <key> (except /health).
| Method | Endpoint | Body / Params | Returns |
|---|---|---|---|
| GET | /api/v1/memories/search/semantic | ?query=...&limit=10 | [{role, text, memory_metadata, score, occurred_at}] |
| GET | /api/v1/memories/search/lexical | ?pattern=...&source=...&limit=50 | [{source, line_number, text}] |
| GET | /api/v1/memories/episodic | ?page=1&limit=100&start_date=...&end_date=... | {items, total, page, limit} |
| GET | /api/v1/memories/semantic/files | -- | [{source, chunk_count}] |
| GET | /api/v1/memories/semantic/files/{path} | ?start_line=1&num_lines=200 | {path, start_line, content} |
| POST | /api/v1/memories/episodic | {"text": "...", "role": "user\|assistant"} | Created memory object |
| GET | /health | -- | {"status": "ok"} |
Manual Setup
If you prefer not to use the automated installer, follow these steps.
All paths below must be absolute — JSON values do not expand ~.
Global API Key
mkdir -p ~/.config/midbrain
echo "sk-your-api-key" > ~/.config/midbrain/.midbrain-key
chmod 600 ~/.config/midbrain/.midbrain-keyOpenCode
Add to ~/.config/opencode/opencode.json:
{
"mcp": {
"midbrain-memory": {
"type": "local",
"command": ["npx", "-y", "midbrain-memory-mcp@latest"],
"environment": {
"MIDBRAIN_CONFIG_DIR": "<absolute-path>/.config/opencode"
},
"enabled": true
}
}
}The OpenCode episodic-capture plugin is delivered as a separate bundled
file; the automated installer (npx midbrain-memory-mcp install) copies it
into ~/.config/opencode/plugins/ for you. Manual-setup users who want
episodic capture should run the installer once or follow the
Development section to copy the plugin files by hand.
Claude Code
Register the MCP server in ~/.claude.json:
{
"mcpServers": {
"midbrain-memory": {
"type": "stdio",
"command": "npx",
"args": ["-y", "midbrain-memory-mcp@latest"],
"env": {
"MIDBRAIN_CONFIG_DIR": "<absolute-path>/.config/claude"
}
}
}
}Note: MCP servers must be in ~/.claude.json, not
~/.claude/settings.json. Entries in settings.json are silently
ignored for MCP server registration.
Add hooks and permissions to ~/.claude/settings.json. The hook
commands need an absolute path to the hook scripts — use the installer
(npx midbrain-memory-mcp install) to emit these automatically, or follow the
Development section below if you want to point at a
local clone.
File Structure
midbrain-memory-mcp/
server.js MCP server (Node 20, plain JS, 6 tools)
install.mjs Automated installer + --project CLI mode
shared/
midbrain-common.mjs Shared utilities: key loading, store, logging
plugin/
midbrain-memory.ts OpenCode plugin (Bun/TS)
claude-code/
common.mjs Re-exports shared utils + readStdinJSON
capture-user.mjs UserPromptSubmit hook
capture-assistant.mjs Stop hook
tests/
midbrain-common.test.mjs Unit tests for shared utilities
server-integration.test.mjs Integration tests for MCP tools
eslint.config.js ESLint flat config (ESM)
package.json
AGENTS.md LLM project instructions
README.mdDevelopment
Getting Started
git clone https://github.com/MidbrainAI/midbrain-memory-mcp.git
cd midbrain-memory-mcp/
npm run bootstrap # installs deps + sets up git hooksnpm run bootstrap is a one-time command. It runs npm install to fetch
dependencies, then husky to install pre-commit hooks.
Install from a local clone
Regular users should install via npx @latest (above). If you are hacking
on this repo and want your MCP clients to run your working tree, pass
--dev to the installer. It will write absolute paths into the configs
instead of npx @latest:
node install.mjs --dev # interactive
node install.mjs --project /abs/path/to/project --dev # per-projectRun node install.mjs --help for the full flag reference.
Manual dev setup without the installer:
cp shared/midbrain-common.mjs ~/.config/opencode/plugins/midbrain-common.mjs
cp plugin/midbrain-memory.ts ~/.config/opencode/plugins/midbrain-memory.tsThen in any MCP config, replace the npx -y midbrain-memory-mcp@latest
command with an absolute path:
- OpenCode:
"command": ["<absolute-node>", "<abs>/server.js"] - Claude:
"command": "<absolute-node>", "args": ["<abs>/server.js"]
Use process.execPath as the node path — bare node fails when MCP
clients spawn the server without a PATH export.
Commands
| Command | Purpose |
|---|---|
| npm run bootstrap | First-time setup: install deps + git hooks |
| npm test | Run full test suite (vitest) |
| npm run test:watch | Run tests in watch mode |
| npm run lint | Run ESLint |
| npm run lint:fix | Auto-fix ESLint issues |
| npm run check | Lint + test in one command |
Pre-commit Hook
Every git commit automatically runs:
- lint-staged -- ESLint on staged
.js/.mjsfiles (zero warnings) - npm test -- full test suite (52 tests)
If either step fails, the commit is rejected. Fix the issues and commit again.
Test Architecture
Tests use vitest (ESM) and live in tests/:
Unit tests (
midbrain-common.test.mjs) -- pure function tests forloadApiKey,isNewerVersion,storeEpisodic, and constants. Uses temp directories andvi.spyOn(globalThis, "fetch")for mocking.Integration tests (
server-integration.test.mjs) -- self-contained, in-process. ImportscreateServer()directly, connects via MCP SDKInMemoryTransport.createLinkedPair(), and mocksglobalThis.fetchto simulate API responses. No child process, no stdio, no network.
Dependencies
Production dependencies are kept minimal (only what ships to users):
| Package | Purpose |
|---|---|
| @modelcontextprotocol/sdk | MCP protocol |
| zod | Schema validation |
Everything else (eslint, vitest, husky, lint-staged) is in devDependencies
and is not installed by end users.
