Local, free, and private
This is the default recommended way to use Spomory: no registration, your memory graph never leaves your machine, encrypted at rest by default. It runs as a stdio MCP server — every client below spawns memory-core-mcp as a subprocess and passes it the same environment variables; pick yours below.
1. Install (or skip this step)
Requires Python 3.11+. The simplest path is to not install anything ahead of time — uvx (bundled with uv) fetches and runs the package on demand, and step 3's client config can point straight at it. For a fixed, pinned install instead, the published PyPI package is the next-simplest path — nothing to clone. A Dockerfile is also in the repo for MCP directories/hosts that deploy from a container image instead.
# simplest: nothing to install ahead of time, point your client's config at this
uvx --from 'spomory[llm,embedding,mcp]' spomory-mcp
# or, a fixed install:
pip install "spomory[llm,embedding,mcp]"
# or, for local development from a cloned repo:
uv pip install -e ".[llm,embedding,mcp]" ⚠️ On macOS, don't install the runtime under ~/Documents (or Desktop/Downloads)
A real gotcha we actually hit: if the command path points inside ~/Documents/<project>/.venv/..., the client's MCP server subprocess fails with "Server disconnected." The real error, in that client's own MCP log, is a PermissionError reading the venv's own pyvenv.cfg. The cause is macOS's TCC privacy protection: ~/Documents, ~/Desktop, and ~/Downloads are protected by default against apps (and their subprocesses) without explicit Full Disk Access — this has nothing to do with the code or where the repo lives, it's an OS-level restriction tied to where the venv sits, regardless of which client spawns it. Using uvx from step 1 sidesteps this entirely — uv's cache was never under ~/Documents to begin with. This only applies if you installed into your own virtual environment.
The fix (for a manual virtual-environment install): Install the virtual environment actually used to run the MCP server somewhere outside ~/Documents (e.g. ~/mcp-servers/), non-editable:
mkdir -p ~/mcp-servers
uv venv --python 3.11 ~/mcp-servers/memory-core-venv
uv pip install "/path/to/memory-core[llm,embedding,mcp]" \
--python ~/mcp-servers/memory-core-venv/bin/python 2. Configure environment variables
Every client below passes these to the spawned process. Defaults to OpenAI; swap LLM_BASE_URL / LLM_MODEL for any other OpenAI-compatible provider (DeepSeek, Qwen, etc.):
export LLM_API_KEY=...
export LLM_BASE_URL=https://api.deepseek.com # optional, defaults to OpenAI
export LLM_MODEL=deepseek-v4-flash # optional, defaults to gpt-4o-mini
export EMBEDDING_MODEL=BAAI/bge-m3 # optional, defaults to bge-m3 Optional: run the LLM and embedding fully local too
Embedding already runs locally by default. To make the LLM call local as well, point LLM_BASE_URL at any local OpenAI-compatible server — vLLM, Ollama, llama.cpp, and MLX all expose one. Add EMBEDDING_PROVIDER=openai_compatible to route embedding the same way instead of the local sentence-transformers model, which drops the torch dependency entirely (pip install spomory[llm,mcp] is then enough, no [embedding] extra needed):
# example: Ollama
export LLM_BASE_URL=http://localhost:11434/v1
export LLM_API_KEY=not-needed
export LLM_MODEL=llama3.1
export EMBEDDING_PROVIDER=openai_compatible
export EMBEDDING_BASE_URL=http://localhost:11434/v1 # defaults to LLM_BASE_URL if unset
export EMBEDDING_MODEL=nomic-embed-text 3. Connect your client
Every client talks to the same local server the same way: point its "command" (or equivalent) at the absolute path to memory-core-mcp from step 1, and pass it the environment variables from step 2. The exact config for Claude Desktop, Cursor, Codex CLI, Doubao, and any other MCP client — plus how to verify it's actually working — lives on the Platforms page.
See exact config for your client on the Platforms page →
Where your data lives
Local SQLite files (memory graph data + usage stats) live in ~/.memory-core/ by default (override with MEMORY_CORE_DATA_DIR) — deliberately outside Documents/Desktop/Downloads. Every entity and relation is stored as ciphertext, not plaintext: the first startup generates an encryption.key file (mode 600) next to the database, and reuses it on every later run. Upgrading from an older, unencrypted database migrates it automatically on first launch (the original file is backed up, not deleted).
4. Make it actually get used
The six tools are purely reactive on their own — nothing makes an AI think to call them, so being connected doesn't mean it's actually recalling or saving anything. skill/spomory/SKILL.md in the repo is an instruction file that teaches "recall first, store last": search memory at the start of a task instead of assuming it doesn't know something, and save durable facts as they come up without being asked. How you load it is specific to each client — Claude Code and Cursor auto-discover a skills folder at .claude/skills/spomory/ or .cursor/skills/spomory/; several other clients (Codex CLI, Gemini CLI, Zed, and more) have their own equivalent folder, and some have no such feature at all and need the content pasted into a custom-instructions field instead. For a genuinely empty account, SKILL.md also runs a brief onboarding flow — a few lightweight questions instead of starting cold.
See the exact loading mechanism for your client on the Platforms page →
Already using Claude Code, Codex, or Cursor?
A brand-new account's biggest problem is that it's empty. If you already have real session history in one of those clients, this CLI tool reads it and runs it through the exact same extraction pipeline add_memory uses — turning real history into a real starting memory graph instead of teaching it one sentence at a time. It genuinely reads your local conversation content and sends it to whichever LLM you've configured, so it's a deliberate step, not something that runs on its own:
spomory-import-sessions --client claude-code # or codex / cursor The six tools
| add_memory | Extracts facts from text and writes them into the memory graph. |
| search_memory | Matches the query against triples, expands via personalized PageRank, returns a natural-language context. |
| forget_memory | Finds the single best-matching relation and physically deletes it, cleaning up orphaned entities and logging the deletion. |
| forget_all_memory | Physically deletes the entire memory graph in one call, including archived history — the bulk counterpart to forget_memory. |
| get_graph | Returns the subgraph around an entity, as JSON. |
| export_memory | Exports the entire memory graph as a memory passport. |