Xanther memory Engine
OfficialExports captured memory data to Obsidian for use as a knowledge base.

pip install xanther-xme
xme hook install . # 30 seconds โ auto-captures every session
xme start my-project # memory starts nowWant code intelligence too? Install XME bundled with the Xanther Context Engine (XCE) in one command:
pip install "xanther-xce[all]" # XCE + XME together # or run instantly, no install: uvx --from "xanther-xce[all]" xanther --help
๐ Why XME
Most LLM sessions are ephemeral. The agent solves a problem, then forgets it. The usual workarounds fall short:
Chat history captures conversations but isn't structured or searchable knowledge.
Bigger context windows still reset every session and cost tokens to refill.
Built-in agent memory is small, vendor-owned, and usually invisible โ gone if you switch tools.
RAG / vector DBs need infra and usually live in someone else's cloud.
XME takes a simpler path: three memory layers you own, on your machine, queryable over MCP.
Verbatim episodes so you can audit exactly what happened.
An extracted fact graph (decisions, attempts, preferences) with vector dedup.
Live working context that's always current, injected at the start of each session.
Facts link to the code they affect when XCE is installed alongside.
Related MCP server: evermemos-mcp
๐ง How it works
You are mid-refactor and the agent tried a Redis distributed lock last week that timed out under load. Without memory, it suggests the same thing again. With XME:
1. Capture (automatic). As you work, an IDE hook buffers every turn to .xanther/turns/ in under 5ms. Nothing blocks.
2. Persist on stop. When the agent stops, XME drains the buffer, extracts facts, and updates working context:
[ATTEMPT ยท FAILED] Redis distributed lock โ timeout under high load
[DECISION ยท VALIDATED] Use FastAPI โ async support required
Current task: Refactor auth module3. Prime the next session. On xme_session_start, the agent gets a context block injected into its prompt:
Current task: Refactor auth module
Known failed approaches:
- Redis distributed lock โ timeout under high load
Recent decisions:
- [VALIDATED] Use FastAPI โ async support required4. No repeated mistakes. The agent sees the failed Redis attempt and proposes something else โ building on history instead of relearning it.
The files stay yours (.xanther/xme.db, plus optional Neo4j/OpenSearch), inspectable and local.
Architecture
graph TB
subgraph "AI Agent (Claude Code / Kiro / Cursor)"
AGENT[Agent]
HOOKS[IDE Hooks<br/>agentStop ยท promptSubmit]
end
subgraph "XME Memory Engine"
ENGINE[MemoryEngine<br/>xme/engine.py]
subgraph "Layer 1 โ Episodic"
EP[EpisodicStore<br/>Verbatim session transcripts]
end
subgraph "Layer 2 โ Facts"
FG[FactGraphStore<br/>Decisions ยท Attempts<br/>Preferences ยท Conventions]
EXT[FactExtractor<br/>LLM or regex]
EMB[LocalEmbedder<br/>all-MiniLM-L6-v2]
EXT --> FG
EMB --> FG
end
subgraph "Layer 3 โ Context"
CTX[ContextStore<br/>Working state per project+user<br/>UPSERT semantics]
end
ENGINE --> EP & FG & CTX
end
subgraph "Storage"
OS[(OpenSearch<br/>port 9200<br/>Full-text + k-NN)]
NEO4J[(Neo4j<br/>port 7687<br/>Fact graph + vectors)]
SQLITE[(SQLite<br/>.xanther/xme.db<br/>Context + fallback)]
end
subgraph "Outputs"
MCP[MCP Server<br/>11 tools]
DASH[Dashboard<br/>port 8001]
EXP[Exports<br/>Obsidian ยท Wiki ยท Graphify]
end
HOOKS -- buffer files --> ENGINE
AGENT -- MCP tool calls --> MCP
EP --> OS & SQLITE
FG --> NEO4J & SQLITE
CTX --> SQLITE
ENGINE --> DASH & EXP
ENGINE --> MCPLocal Infrastructure
graph LR
subgraph "Your Machine"
subgraph "Docker Compose"
NEO4J[(Neo4j:7687<br/>Fact knowledge graph)]
OS[(OpenSearch:9200<br/>Episodic search)]
end
subgraph "XME Process"
CLI[xme CLI]
DASH[xme dashboard<br/>:8001]
MCP_SRV[MCP Server]
end
subgraph "Hook Files"
BUF[.xanther/turns/<br/>Buffer files<br/>written per turn]
DB[.xanther/xme.db<br/>SQLite warm store]
end
subgraph "IDE"
KIRO[Kiro / Claude Code]
MCP_CFG[mcp.json]
end
end
subgraph "External APIs (optional)"
OR[OpenRouter API<br/>LLM fact extraction]
end
KIRO -- agentStop hook --> BUF
KIRO -- promptSubmit hook --> BUF
CLI -- drain buffer --> DB
CLI -- index to --> NEO4J & OS
MCP_CFG -- spawn --> MCP_SRV
MCP_SRV -- read --> NEO4J & OS & DB
KIRO -- MCP tool calls --> MCP_SRV
CLI -. LLM extraction .-> OR
DASH -- read --> NEO4J & OS & DBSession lifecycle
sequenceDiagram
participant IDE as Kiro / Claude Code
participant HOOK as Hook Handler<br/>.xanther/hook.py
participant BUF as Buffer<br/>.xanther/turns/
participant XME as XME Engine
participant DB as Neo4j + SQLite
IDE->>HOOK: promptSubmit (user message)
HOOK->>BUF: write turn JSON (< 5ms)
IDE->>HOOK: promptSubmit (next message)
HOOK->>BUF: write turn JSON
Note over IDE,DB: ... more turns ...
IDE->>HOOK: agentStop (response finished)
HOOK->>BUF: write session_end marker
Note over BUF,DB: On next xme start or xme_session_end MCP call
XME->>BUF: drain all buffer files
XME->>XME: extract facts (LLM or regex)
XME->>DB: upsert facts with vector dedup
XME->>DB: save episode to OpenSearch
XME->>DB: update working context (UPSERT)
Note over IDE,DB: Next session
IDE->>XME: xme_session_start
XME->>DB: load working context
XME->>DB: load recent facts
XME->>DB: load last episode summary
XME-->>IDE: primed context block (inject into prompt)Three memory layers
flowchart LR
subgraph "Layer 1 โ Episodic"
direction TB
E1[Full session transcripts<br/>verbatim]
E2[Searchable by:<br/>full-text ยท semantic ยท date ยท user]
E3[Backend: OpenSearch<br/>Fallback: SQLite FTS5]
E1 --> E2 --> E3
end
subgraph "Layer 2 โ Facts"
direction TB
F1[Extracted knowledge nodes]
F2[Types:<br/>Decision ยท Attempt<br/>Preference ยท Convention ยท Entity]
F3[UPSERT dedup<br/>cosine similarity > 0.85]
F4[Backend: Neo4j graph<br/>+ vector index]
F1 --> F2 --> F3 --> F4
end
subgraph "Layer 3 โ Context"
direction TB
C1[Live working state<br/>per project + user]
C2[Fields:<br/>current_task ยท next_steps<br/>recent_decisions ยท blockers]
C3[UPSERT only โ always current<br/>Backend: SQLite]
C1 --> C2 --> C3
end
EP[Episodic\nStore] --> L1(Layer 1)
FG[Fact\nGraph] --> L2(Layer 2)
CTX[Context\nStore] --> L3(Layer 3)
style L1 fill:#dbeafe
style L2 fill:#dcfce7
style L3 fill:#fef9c3โก Getting Started
1. Install
pip install xanther-xme
# Or bundled with the Xanther Context Engine (code graph + memory):
pip install "xanther-xce[all]"
# Run instantly without installing:
uvx --from "xanther-xce[all]" xanther --help2. Choose an infrastructure mode
XME works with or without Docker. Pick one:
Zero infrastructure โ SQLite only, no Docker, works offline:
XME_FALLBACK_MODE=true xme start my-projectFull infrastructure โ Neo4j (fact graph) + OpenSearch (episodic search):
cp .env.example .env # set NEO4J_PASSWORD (and OPENROUTER_API_KEY for LLM extraction)
docker-compose up -d # starts Neo4j (:7687) + OpenSearch (:9200)
xme start my-projectNo OpenRouter key? Fact extraction falls back to regex heuristics โ everything still works, just with slightly coarser facts.
3. Install the auto-capture hooks
This is the step that makes memory automatic. Hooks capture every agent turn and persist a session when the agent stops โ no manual recording needed.
# Install Kiro + Claude Code hooks into a repo (defaults to current dir)
xme hook install .
# Preview what would be written without changing anything
xme hook install . --dry-run
# Remove the hooks later
xme hook uninstall .What gets installed:
Hook | IDE event | What it does |
|
| Buffers each user turn to |
|
| Buffers tool calls to the same journal |
|
| Drains the buffer โ extracts facts โ updates context โ saves the session |
The installer writes IDE-native config:
Kiro โ hook files under
.kiro/hooks/Claude Code โ hook entries in the project's Claude settings
4. Wire up the MCP server (optional but recommended)
So your agent can query and prime memory directly, add XME as an MCP server. Pick your client:
Add to ~/.kiro/settings/mcp.json (global) or .kiro/settings/mcp.json (workspace):
{
"mcpServers": {
"xme": {
"command": "xme",
"args": ["serve"],
"env": { "NEO4J_PASSWORD": "your-password" },
"autoApprove": ["xme_session_start", "xme_search", "xme_get_context"]
}
}
}claude mcp add xme -- xme serveAdd to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):
{
"mcpServers": {
"xme": {
"command": "xme",
"args": ["serve"],
"env": { "NEO4J_PASSWORD": "your-password" }
}
}
}Add to your User Settings (JSON):
{
"mcp": {
"servers": {
"xme": {
"command": "xme",
"args": ["serve"],
"env": { "NEO4J_PASSWORD": "your-password" }
}
}
}
}If your client speaks MCP over stdio, point it at the xme serve command above. The tool names and behavior are identical across clients.
At the start of a session the agent calls xme_session_start to get a primed context block
(current task, recent decisions, known-failed approaches) injected into its prompt.
5. Verify it's working
xme stats my-project # memory health: fact / episode / context counts
xme dashboard # visual timeline at http://localhost:8001After a session or two, xme stats should show growing fact and episode counts. If they stay at
zero, see Troubleshooting hooks below.
Troubleshooting hooks
Nothing captured? Confirm hooks installed: check
.kiro/hooks/(Kiro) or your Claude Code settings. Re-runxme hook install . --dry-runto see expected paths.Buffer never drains? Facts are extracted on
agentStopor on the nextxme start/xme_session_endMCP call. Runxme start my-projectto force a drain.Neo4j errors? You can run fully local with
XME_FALLBACK_MODE=true(SQLite only).Buffer files live in
.xanther/turns/; the warm store is.xanther/xme.db. Both are safe to inspect. Add.xanther/to your.gitignore(memory is per-developer runtime state).
What gets captured automatically
After xme hook install .:
Every prompt is buffered to
.xanther/turns/(< 5ms, no blocking)On
agentStop: buffer drains โ facts extracted โ context updatedNext session: agent gets a primed context block injected automatically
**Current task**: Refactor auth module
**Last session**: Moved JWT to dedicated auth service โ success
**Recent decisions**:
- [VALIDATED] Use FastAPI โ async support required
- [VALIDATED] PostgreSQL โ ACID compliance
**Known failed approaches**:
- Redis distributed lock โ timeout under high load
**Next steps**: Deploy auth service to staging๐งฐ MCP tools (11)
Tool | Description |
| Start session, get primed context block |
| End session: persist episode, extract facts, update context |
| Add content โ Mem0-style UPSERT with deduplication |
| Search across all 3 layers simultaneously |
| Get working context for prompt injection |
| Query fact graph (filter by type, user, keyword) |
| Full-text + semantic search over past sessions |
| Explicitly store a typed fact |
| Soft-delete a memory node |
| Export to Obsidian vault / wiki / Graphify JSON |
| Partial UPSERT of working context fields |
Add to MCP config:
{
"mcpServers": {
"xme": {
"command": "xme",
"args": ["serve"],
"env": {
"NEO4J_PASSWORD": "your-password"
}
}
}
}Deduplication
Facts are stored once, not repeated across sessions:
flowchart TD
A[New content added] --> B[Embed with\nall-MiniLM-L6-v2]
B --> C{Similar fact exists?\ncosine > 0.85}
C -- Yes --> D[Merge into existing fact\nupdate content + metadata]
C -- No --> E[Create new fact node]
D --> F[Update Neo4j + SQLite]
E --> F๐ How It Compares
Mem0 | Zep | MemPalace | XME | |
Episodic memory | โ | โ | โ | โ |
Fact graph | partial | โ | โ | โ |
Working context UPSERT | โ | โ | โ | โ |
Multi-user scoping | โ | โ | โ | โ |
Deduplication | โ | โ | โ | โ |
Local-first / open source | โ | โ | โ | โ |
MCP tools | โ | โ | โ | โ (11) |
Obsidian export | โ | โ | โ | โ |
Dashboard UI | โ | โ | โ | โ |
Code graph integration | โ | โ | โ | โ via XCE |
CLI
xme start <project> # init + show stats
xme add <project> <user> <text> # add content to memory
xme search <project> <query> # search all layers
xme facts <project> # list facts
xme stats <project> # memory health metrics
xme export <project> # export (obsidian/wiki/graphify)
xme dashboard # launch web UI (port 8001)
xme hook install [path] # install Kiro + Claude Code hooks
xme hook uninstall [path] # remove hooksConfiguration
# LLM for better fact extraction (optional โ regex works without it)
OPENROUTER_API_KEY=sk-or-...
XME_LLM_MODEL=openai/gpt-4o-mini
# Neo4j โ fact graph (recommended, free tier at console.neo4j.io)
NEO4J_URI=bolt://localhost:7687
NEO4J_PASSWORD=your-password
# OpenSearch โ episodic search (optional, falls back to SQLite FTS5)
XME_OPENSEARCH_URL=http://localhost:9200
# Zero-infrastructure mode
XME_FALLBACK_MODE=false # set true for SQLite-only, no Docker neededSee .env.example for the complete reference.
Related
Xanther Context Engine (XCE) โ code graph intelligence. When installed alongside XME, decisions link directly to the code they affect.
pip install "xanther-context-engine[memory]" # XCE + XME togetherโญ Star Us on GitHub
If XME saves your agent from relearning your codebase every session, a star helps other developers find it and helps us keep building in the open.
License
Apache 2.0. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Shared memory for coding agents. Stop re-explaining your codebase every session.
Persistent cross-session memory shared by Codex, Claude Code, ChatGPT, and other AI agents.
Persistent memory and cross-session learning for AI coding assistants (hosted remote MCP).
Local-first, governed memory and session continuity for AI coding agents. No cloud, no telemetry.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenancePersistent memory for AI coding agents735 PyPI217Apache 2.0
- AlicenseAqualityCmaintenanceLong-term memory for AI coding assistants. Remembers context once and recalls it across sessions.721MIT
- AlicenseAqualityDmaintenanceEnables AI coding assistants to store and retrieve persistent long-term memory across sessions, remembering project preferences, build steps, and architecture decisions.4MIT
- AlicenseNot gradedqualityCmaintenancePersistent memory for AI coding tools, enabling AI assistants to store and recall project decisions, conventions, and context across sessions.33 npmMIT