agentic-memory
A persistent, bitemporal, injection-safe memory MCP server for coding agents, offering unified retrieval across six memory layers, non-destructive belief revision, and multi-tenant namespace isolation.
README
<p align="center"> <img src="assets/banner.svg" alt="Agentic Memory — A Persistent, Bitemporal, Injection-Safe Memory MCP for AI Agents, by Verace Pvt. Ltd." width="100%"> </p>
<p align="center"><b>A Persistent, Bitemporal, Injection-Safe Memory MCP for AI Agents</b></p>
A memory server for coding agents (Claude Code, Claude Desktop, Cursor, Antigravity, Codex CLI, or any MCP-compliant client) that goes beyond the naive "five separate buckets" model of agent memory: non-destructive belief revision, injection-safe context synthesis, real embeddings with ANN search, multi-tenant namespace isolation, and a choice of a zero-dependency SQLite backend or a Postgres + pgvector + Row-Level-Security backend for real deployments.
🏛️ Architecture
Six memory layers behind one unified retrieval call. Diagram renders natively on GitHub (Mermaid) — open this file on github.com if you're viewing raw markdown:
flowchart TD
ENV(["Real-Time Environment<br/>user queries / tool output"])
subgraph L1["1 · SENSORY MEMORY"]
L1D["Ring buffer, saliency-scored<br/>Retention: ms → minutes"]
end
subgraph L2["2 · WORKING MEMORY"]
L2D["Token-budgeted session context<br/>chronological, auto-compressing<br/>Retention: minutes → hours"]
end
subgraph L3["3 · EPISODIC MEMORY"]
L3D["Causal event trajectories<br/>vector similarity retrieval<br/>Retention: weeks → months"]
end
subgraph L4["4 · SEMANTIC MEMORY"]
L4D["Subject-predicate-object graph<br/>non-destructive belief revision<br/>hybrid vector + text + PPR search"]
end
subgraph L5["5 · LONG-TERM MEMORY"]
L5D["Ebbinghaus-decay retrievability<br/>Truth Maintenance System<br/>Retention: days → years"]
end
subgraph L6["6 · PROCEDURAL MEMORY"]
L6D["Registered skills / action recipes<br/>success-rate tracked"]
end
UQ{{"unified_query()<br/>ranks + fuses all 6 layers by one<br/>relevance function (similarity +<br/>recency + importance + graph proximity)"}}
OUT(["One synthesized,<br/>injection-safe context block"])
ENV --> L1
L1 -- "promotion on high saliency" --> L2
L2 --> L3
L2 --> L4
L3 <-.-> L4
L3 --> L5
L4 --> L5
L1 --> UQ
L2 --> UQ
L3 --> UQ
L4 --> UQ
L5 --> UQ
L6 --> UQ
UQ --> OUT
classDef sensory fill:#d1f5d3,stroke:#2e7d32,color:#1b1b1b
classDef working fill:#e3d9f7,stroke:#6a3fb5,color:#1b1b1b
classDef episodic fill:#c9dcf7,stroke:#2451a8,color:#1b1b1b
classDef semantic fill:#c3ecd9,stroke:#1b7a4d,color:#1b1b1b
classDef longterm fill:#cfe8ff,stroke:#1565c0,color:#1b1b1b
classDef procedural fill:#ffe6cc,stroke:#e07b00,color:#1b1b1b
classDef fusion fill:#111111,stroke:#111111,color:#ffffff
class L1,L1D sensory
class L2,L2D working
class L3,L3D episodic
class L4,L4D semantic
class L5,L5D longterm
class L6,L6D procedural
class UQ,OUT fusion
A single unified_query call ranks and fuses all six layers by one consistent relevance function
(embedding similarity + recency + importance + graph proximity), then returns one synthesized,
injection-safe context block — not six separate results the caller has to stitch together itself.
What makes this different from a naive per-layer memory store
- Non-destructive belief revision. Updating a fact never silently overwrites it. The old belief is
marked superseded (with a timestamp and a pointer to what replaced it) rather than deleted — full
audit history via
get_belief_history()/get_semantic_belief_history(). - Injection-safe context synthesis. Every retrieved memory item is wrapped in
<memory trust="..." source="...">tags with an explicit preamble telling the LLM that memory content is untrusted historical data, not instructions. All angle brackets in stored content are escaped (not just the<memory>tag itself) — so a stored item can't forge any tag, including ones like<system>, to break out of its fence. - Write-time trust isn't blind. Every stored item carries a
trustlevel. Self-reported, unverified claims (an agent's own confidence/success/reward score) are ranking-discounted relative to system- or user-provided facts — an agent can't inflate its own memory's influence just by claiming high importance. Averify_memory()call lets a separate, more-trusted reviewer corroborate an item after the fact and restore full ranking weight; the original writer can never call it on its own write. - Contradiction flagging, not silent overwrite. A cheap antonym-predicate heuristic flags directly conflicting facts about the same subject (e.g. "Dan LIKES pizza" vs "Dan HATES pizza") for review instead of guessing which one is right — this is not general semantic contradiction detection (an open NLP problem), just a fast, honest, zero-model check for the specific opposite-predicate case.
- Real embeddings, with a safety net. Defaults to
sentence-transformers(all-MiniLM-L6-v2) when installed; falls back to a deterministic hash embedder otherwise. The vector index dimension is auto-detected from whichever embedder is actually active — mixing a database built with one embedder and a process running another raises a clear, actionable error at startup instead of silently corrupting or crashing mid-session. - Real ANN search, with a safety net. Uses
hnswlibHNSW when installed; falls back to brute-force NumPy cosine search otherwise — and near-exhaustive queries route to brute-force even when HNSW is available, since HNSW is optimized for k ≪ n and is measurably slower than a vectorized brute-force pass once k approaches the corpus size. - Multi-tenant namespace isolation. Every memory item carries a
namespace. On the Postgres backend this is enforced by Postgres Row-Level Security — not just an app-layer filter that a bug could skip. Consolidation, reflection, and graph export are all namespace-scoped too — none of them mix or leak data across tenants. - Two backends, one API. SQLite by default (zero external dependencies, single-process). Postgres +
pgvector + RLS for real multi-writer, multi-tenant deployments — same
MemoryEngineAPI either way. Optional Qdrant and Milvus vector-store connectors are also included for teams that want a dedicated vector database instead.
📦 Installation
One package, every feature included by default (real sentence-transformers embeddings, hnswlib ANN
search, and Postgres/pgvector support are all installed out of the box — no extras to pick). Not on
PyPI yet — install straight from GitHub for now:
pip install "agentic-memory @ git+https://github.com/Verace-Pvt-Ltd/agentic-memory.git"
Or clone and install locally:
git clone https://github.com/Verace-Pvt-Ltd/agentic-memory.git
cd agentic-memory
pip install -e .
Connect it to your AI coding tool
Claude Code (recommended — plugin marketplace, gets autonomous hooks too):
/plugin marketplace add Verace-Pvt-Ltd/agentic-memory
/plugin install agentic-memory@agentic-memory
Same two steps non-interactively (CI, setup scripts) via the CLI:
claude plugin marketplace add Verace-Pvt-Ltd/agentic-memory
claude plugin install agentic-memory@agentic-memory
This registers the MCP server through Claude Code's plugin marketplace system
and auto-registers 9 autonomous hooks (hooks/hooks.json) that read and write memory on every turn
without the agent needing to call an MCP tool explicitly — see
Autonomous operation via hooks below. Verify with
claude plugin details agentic-memory@agentic-memory, which should list Status: enabled and
Hooks (9). The Python package still needs to be installed separately first (see
Installation above) — the plugin distributes the MCP registration, not a bundled
Python runtime.
Any other tool (Cursor, Antigravity, Codex CLI, Claude Desktop, or any other MCP-capable agent): see SETUP.md — copy one prompt into your agent's chat and it detects your tool and configures itself, including autonomous hooks where that tool supports them.
<details> <summary>Manual installation per tool (if you'd rather not use the plugin marketplace or SETUP.md prompt)</summary>
Claude Code (manual, no plugin system — MCP tools only, no autonomous hooks):
claude mcp add agentic-memory -- agentic-memory --transport stdio
Any other MCP client (Claude Desktop, Cursor, Antigravity, Codex CLI — MCP tools only, no autonomous hooks unless configured per SETUP.md):
# Auto-install into Claude Desktop's config
agentic-memory --setup-claude
# Print copy-paste config snippets for Claude Desktop, Cursor, Antigravity, and Codex CLI
agentic-memory --print-config
</details>
MCP (Model Context Protocol) is a shared standard across all of these — the same server works with any of them via:
{
"mcpServers": {
"agentic-memory": {
"command": "agentic-memory",
"args": ["--transport", "stdio"]
}
}
}
🪝 Autonomous operation via hooks
MCP tools require the agent to decide to call them. Hooks make memory read/write happen
automatically, tied to the host tool's own lifecycle events (a new session starting, a prompt
being submitted, a tool call succeeding or failing, the session stopping) — no explicit tool
call needed. Four tools have a real hook/lifecycle system this project integrates with, each
adapter living in its own module (memory/hooks.py, memory/hooks_antigravity.py,
memory/hooks_cursor.py, memory/hooks_codex.py) with a matching config template under
hooks/. Confidence differs per platform — verified against real installed binaries where one
was available in this environment, doc-derived only where it wasn't:
| Tool | Events wired | Confidence |
|---|---|---|
| Claude Code (plugin marketplace) | SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, Stop, TaskCompleted, PreCompact, SessionEnd | Confirmed working end-to-end — verified via claude plugin details, which reports Status: enabled and Hooks (9) against a real installed instance. |
Antigravity (agy) |
PreToolUse, PostToolUse, PreInvocation, Stop | Confirmed to execute — a real tool-triggering prompt through a live agy install produced the hook's own stderr output (model load) in agy's logs, with no error. The resulting database write location wasn't independently confirmable from outside agy's process (possible subprocess sandboxing), so treat as working-but-not-fully-proven rather than broken. |
Cursor (cursor-agent / Cursor IDE) |
sessionStart, beforeSubmitPrompt, beforeShellExecution, afterShellExecution, beforeMCPExecution, afterMCPExecution, afterFileEdit, afterAgentResponse, stop, sessionEnd | Schema confirmed valid against Cursor's own docs; not confirmed to fire — cursor-agent requires an interactive login this environment didn't have. A community report also states several of these (sessionStart, beforeSubmitPrompt, stop, afterAgentResponse) are documented but don't currently execute in Cursor CLI specifically, only in the Cursor IDE — CLI users may see partial coverage until this is independently confirmed. |
| Codex CLI | SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop, PreCompact, SessionEnd | Doc-derived only — no codex binary was available in this environment to verify against, so neither the schema nor the firing behavior has been live-tested. |
Claude Desktop and any other generic MCP client have no hook/lifecycle system to integrate with — they get the 16 MCP tools only, called explicitly by the agent.
See SETUP.md for the exact install steps per platform, including where each
hooks/*.json template needs to be copied.
🐘 Optional: Postgres / Supabase backend
For a real deployment with concurrent multi-process writers and enforced multi-tenant isolation, run Agentic Memory against Postgres + pgvector instead of SQLite. Self-hosted via Docker, no cloud account required:
docker compose up -d # starts Postgres + pgvector (bare, unconfigured)
python -m memory.backends.postgres --migrate --dsn postgresql://postgres:postgrespassword@localhost:5433/memory
The migration step auto-detects the correct vector column dimension from whichever embedder is active
in your Python environment (via get_embedding_dim()) and generates a secure app-role password if you
don't supply one — nothing about the schema is hand-edited or hardcoded.
from memory.backends.postgres import PostgresBackend
from memory.engine import MemoryEngine
backend = PostgresBackend(dsn="postgresql://postgres:postgrespassword@localhost:5433/memory")
engine = MemoryEngine(backend=backend)
Row-Level Security policies enforce namespace isolation at the database engine level — a query for
namespace="tenant_a" genuinely cannot see tenant_b's rows, even if application code has a bug.
Known limitations of the self-hosted Docker setup: no automated backups/point-in-time recovery, no high-availability/failover (single-container Postgres), and no encryption-at-rest beyond whatever the underlying disk provides — all buildable, none included out of the box today.
🔌 MCP Tools
| Tool | Purpose |
|---|---|
sensory_ingest |
Ingest a real-time event into sensory memory |
working_memory_update |
Add a turn to the active session's working memory |
episodic_record |
Log a task trajectory; pass episode_id from an earlier call to update it instead of creating a duplicate |
semantic_upsert |
Add/revise a subject-predicate-object fact (flags antonym contradictions) |
long_term_store |
Store a durable preference or fact (Truth Maintenance on update) |
procedural_register |
Register a reusable skill/action recipe (upserts by name — won't duplicate) |
unified_query |
Fused, ranked, injection-safe context across all layers; optional top_k cap |
query_as_of |
Point-in-time retrieval using bitemporal fields |
get_belief_history |
Full revision history for a long-term memory key |
get_semantic_belief_history |
Full revision history for a semantic fact |
verify_memory |
Mark a fact/episode/skill as corroborated by a trusted reviewer — never by its own writer |
forget |
GDPR-style hard delete (distinct from non-destructive supersede) |
evict |
Namespace-scoped capacity/decay-based pruning |
reflect |
Heuristic pattern synthesis over recent episodes/facts, namespace-scoped, deduplicated |
graph_export_dot |
Export the semantic knowledge graph as Graphviz DOT, namespace-scoped |
trigger_consolidation |
Run the sensory→working→episodic→semantic→long-term promotion cycle, namespace-scoped |
inspect_memory_health |
Per-layer counts and operational metrics |
Resources: memory://sensory/stream, memory://working/active, memory://semantic/graph.dot,
memory://longterm/user. Prompt: synthesize_context.
🐍 Python SDK Quickstart
from memory import MemoryEngine
engine = MemoryEngine(db_path="memory.db") # SQLite by default
engine.sensory_ingest("User clicked urgent alert", source="ui_event")
engine.working_update("user", "Can you deploy the payments service to prod?")
engine.working_update("assistant", "Running the blue-green deploy pipeline now.")
engine.episodic_record(
title="Payments deploy",
goal="Deploy payments service to prod",
context="prod cluster",
steps=[{"action": "run_pipeline", "input": {}, "output": {"status": "ok"}, "success": True}],
outcome="success",
)
engine.semantic_add("User", "PREFERS", "blue-green deploys", confidence=0.9)
engine.long_term_store(
"user_preference", "deploy_strategy", "blue-green",
summary="User always wants blue-green deploys for prod",
importance=0.9, is_pinned=True,
)
result = engine.query("deploy payments service to production")
print(result.synthesized_prompt_context) # one injection-safe, ranked context block
stats = engine.consolidate() # promote sensory -> working -> episodic -> semantic -> long-term
Every write accepts namespace= (default "default") and trust= (default TrustLevel.USER) for
multi-tenant scoping and provenance tracking.
🧪 Testing
python3 -m pytest tests/ -v
98 tests covering persistence across restart, non-destructive belief revision, prompt-injection
fencing (including non-<memory> tag names), namespace isolation across every subsystem (query,
consolidation, reflection, DOT export, belief history), trust-weighted ranking and the verification
mechanism, contradiction flagging, ANN index correctness (with and without hnswlib installed),
embedding-dimension mismatch fail-fast behavior, eviction, Postgres dialect translation logic, and
the Claude Code / Antigravity / Cursor / Codex CLI hook adapters. The
Postgres backend has been verified against a real running instance (writes, reads, RLS enforcement,
forget, evict) — not just reviewed statically.
⚠️ Known limitations
- No published benchmark comparison (LoCoMo, LongMemEval, or similar) against Mem0, Zep/Graphiti, or
MemGPT/Letta exists yet — retrieval quality claims should be treated as unvalidated against other
systems until that exists. Internal, judge-free retrieval measurements against the MemFail benchmark
(arXiv:2605.26667) are available in
benchmarks/. reflect()and episodic→semantic extraction (trigger_consolidation) use real LLM synthesis when called live through an MCP client that supports MCP sampling — the server asks the connected client's own model to synthesize insights / extract triples (ctx.sample(...)inmcp_server.py), so no separate API key or subscription is needed. This only works for live tool calls, since sampling requires an active client connection: headless paths (autonomous hooks, the background consolidation loop) have no live client to sample from and fall back toHeuristicReflector/HeuristicExtractor(template sentences / regex patterns) — real coverage, but not LLM-quality synthesis. If a sampling call fails or the connected client doesn't support it, the same heuristic fallback applies automatically, soreflect()/trigger_consolidation()never produce nothing.LLMReflector/LLMExtractor/StructuredPromptExtractor(inconsolidator.pyandutils.py) remain available as a separate, explicitly-opt-in path for anyone who wants headless LLM-backed extraction/reflection backed by their ownCallable[[str], str](e.g. a direct API call) — not wired in by default.- Contradiction detection only catches the specific antonym-predicate-on-same-subject case (Tier 1). It does not detect general semantic contradictions between differently-worded facts — that remains an open problem no memory system solves generically without an expensive, unreliable LLM pass at write time.
- Postgres ANN search currently loads embeddings into an in-process index rather than querying pgvector's native index directly — works well at moderate scale, doesn't yet solve the "dataset too large for one process's memory" case that's part of the reason to use Postgres at all.
- Qdrant/Milvus connectors are real (genuine client calls, no fallback stub) but have only been exercised
in local/embedded mode (
:memory:Qdrant,milvus-lite) — not against a remote/distributed cluster.
🤝 Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md for guidelines.
📜 License
MIT License — see LICENSE. © Verace Pvt. Ltd.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。