Warning
This is a personal project. I maintain it for my own use and share it because others might find it useful. Feature PRs are unlikely to be merged — if you have an idea, start a discussion first. Fork freely — it's MIT licensed.
AI-accessible knowledge you actually own. Plain markdown files on your disk, searchable by AI via MCP.
Your notes live on your disk as plain markdown with YAML frontmatter — readable without mor, portable to any tool, git-syncable across machines. The MCP server gives AI assistants (Claude Code, Claude Desktop, Cursor, etc.) persistent memory that survives context windows. You also get a CLI and HTTP API.
npm install -g morRequires Node.js 20+.
# Add memories
echo "Always use snake_case for Python variables" | mor add -t "Python naming"
mor add notes.md -t "Meeting notes" --tags "meeting,project-x"
mor add https://raw.githubusercontent.com/owner/repo/main/config.ts
# Search (FTS5 — tokenized, stemmed)
mor find python naming
# Grep (literal substring or regex)
mor grep snake_case
mor grep -i todo
mor grep -E "async\s+function"
mor grep -w Beer -n -C 2
# Read, edit, copy, remove
mor cat python naming
mor edit python naming
mor cp -o ./out.md python naming
mor rm python naming
# List
mor ls
mor ls -l| Command | Description |
|---|---|
find <query> |
Full-text search (--limit, -s threshold, --json) |
grep <pattern> |
Substring or regex search (-i, -E regex, -w word, -n line numbers, -l files only, -A/-B/-C context) |
add [file|url] |
Add from file, URL, stdin, or $EDITOR (-t title, -d description, --tags, --type) |
cat <query> |
Print content (--raw for frontmatter, --links for cross-references) |
cp <query...> |
Copy content to file (-o <dest>) |
edit <query> |
Open in $EDITOR (--raw to edit frontmatter) |
update <query> |
Update metadata or content (-t title, -d description, --tags, --type, --content-from) |
rm <query> |
Remove a memory |
links [query] |
Show cross-references for a memory (--broken to find dangling links) |
ls |
List all (--limit, -l long, --tags, --types) |
sync |
Pull, commit, and push the memory folder via git |
reindex |
Rebuild search index |
import <dir> |
Import .md files from a directory |
mcp |
Start MCP server (stdio) |
serve |
Start HTTP server (-p port, -H host, --token, --mcp) |
login |
Authenticate with a remote server via OAuth (-s server URL) |
Queries resolve in order: full UUID, UUID prefix (8+ chars), filename, FTS search. Multi-word queries don't need quoting — options go before the query: mor find --limit 5 python naming.
find, grep, and ls support shared filters: --type, --tag, --repo, --ext (all support glob patterns).
Add to your Claude Code or Claude Desktop config:
{
"mcpServers": {
"memory": {
"command": "mor",
"args": ["mcp"]
}
}
}Tools: memory_search, memory_read, memory_create, memory_update, memory_remove, memory_list, memory_grep.
To make sure Claude Code checks mor first when you ask it to recall something, add this to ~/.claude/CLAUDE.md:
## Memory
When the user asks to recall, find, check, or reuse something they previously saved
or remembered — use the `mor` MCP server tools (`memory_search`, `memory_read`,
`memory_list`). This is the user's primary memory store containing code snippets,
files, and reference notes. Always check mor before saying something wasn't found.Run the server on one machine, access from anywhere:
# Server
mor serve --port 7677 --token mypassphrase --mcpPoint your MCP client at the server URL — no secret in the config:
{
"mcpServers": {
"memory": {
"type": "url",
"url": "http://mybox.tail1234.ts.net:7677/mcp"
}
}
}The client discovers auth via WWW-Authenticate → OAuth metadata → browser passphrase flow, all automatic.
# OAuth login — saves server URL to config and credentials to credentials.json
mor login -s http://mybox.tail1234.ts.net:7677
# All commands now proxy to the remote server
mor find "python naming"Or configure a direct token instead:
OAuth tokens auto-refresh on expiry.
When --token is set, all routes require auth. Two methods work everywhere:
- Bearer token:
Authorization: Bearer <passphrase>or?token=<passphrase> - OAuth access token: obtained via the OAuth flow (
mor loginor MCP client auto-discovery)
Unauthenticated requests get a 401 with a WWW-Authenticate header pointing to the OAuth discovery endpoint.
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/memories?limit=N&offset=N |
List all |
GET |
/memories/search?q=...&limit=N&offset=N |
FTS search |
GET |
/memories/grep?q=...&limit=N&offset=N&ignoreCase=1®ex=1 |
Substring or regex search |
GET |
/memories/:query |
Read one |
GET |
/memories/:query/links |
Get forward and backlinks |
POST |
/memories |
Create ({title, content, description?, tags?, type?, repository?}) |
PUT |
/memories/:query |
Update ({title?, description?, content?, tags?, type?}) |
DELETE |
/memories/:query |
Remove |
POST |
/mcp |
MCP protocol (streamable HTTP) |
Optionally augment FTS search with vector similarity. Configure in ~/.config/mor/config.json:
{
"embedding": {
"provider": "openai",
"model": "text-embedding-3-small",
"dimensions": 1536
}
}Providers: openai (or compatible API via baseUrl), azure-openai, ollama. Run mor reindex after configuring.
Azure OpenAI uses AZURE_OPENAI_API_KEY (or apiKey in config) and requires a deployment name (defaults to model name).
Memories are markdown files with YAML frontmatter, stored in ~/.config/mor/memories/ with a SQLite index at ~/.config/mor/index.db. Override with MOR_HOME.
~/.config/mor/
config.json
credentials.json # OAuth tokens (created by `mor login`)
index.db # search index
oauth.db # OAuth clients and tokens (server-side)
memories/
python-naming-a1b2.md
meeting-notes-c3d4.md
Files are human-readable and git-friendly. Use mor sync to pull, commit, and push if the memory folder is a git repo. Enable autosync to sync automatically after every add, update, or remove:
{
"autosync": true
}MIT