Skip to content
Conduit

mcp server

thinkingmemory

The memory database for AI agents. One recall primitive: intent in, the right context out (hybrid retrieval, token-budget packing, in-engine lifecycle, bitemporal audit, per-tenant RLS). MCP-native. O

mplusm/thinkingmemorycommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "thinkingmemory": {
      "command": "uvx",
      "args": [
        "thinkingmemory"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 26, 2026

ThinkingMemory

The memory database agents recall from. Point your agent at one endpoint; it stores experience and gets back the right context - hybrid-retrieved, ranked, and packed to a token budget - with recall that improves as the agent runs.

ThinkingMemory is agent-agnostic (works with any framework or LLM) and runs on Postgres + pgvector with a local, offline embedding model by default.

Why a "memory database" and not a vector store

A raw vector store gives you nearest-neighbours. An agent needs the useful context for what it's doing right now, within a token budget. ThinkingMemory's query primitive is recall:

  • Hybrid retrieval - vector similarity + keyword (Postgres full-text) + recency, fused with Reciprocal Rank Fusion and weighted by salience.
  • Token-budget packing - returns a ready-to-use, deduped context string with [n] citations, not a pile of rows.
  • Lifecycle - each recall boosts the salience of what it surfaced, so useful memories rise over time.
  • Server-side embeddings - you send text, not vectors.

One unified Memory substrate replaces the old four-layer split; the "layer" (episodic / semantic / procedural / working) is now just a mtype tag on a row.

Architecture

        any agent / framework / LLM
                  │   REST /v1   +   MCP tools
                  ▼
        ┌──────────────────────────┐
        │  Recall engine            │  embed → (vector | keyword | recency)
        │  hybrid → fuse → pack     │  → RRF + salience → token-budget packer
        └────────────┬─────────────┘
                     ▼
        ┌──────────────────────────┐
        │  memory (Postgres)        │  one table: content+text+embedding,
        │  pgvector HNSW + FTS GIN  │  salience, bitemporal, provenance
        └──────────────────────────┘
  • Storage: PostgreSQL 16+ / pgvector (memory table; HNSW + GIN indexes).
  • Embeddings: fastembed + BAAI/bge-small-en-v1.5 (384-dim, CPU, offline) by default; OpenAI optional. Pluggable via EMBEDDING_PROVIDER.
  • Working memory: a separate Redis TTL scratchpad (/working).

Getting Started

Prerequisites

  • Python 3.10+, PostgreSQL 16+ with pgvector, Redis 7+

Install

git clone https://github.com/mplusm/thinkingmemory.git
cd thinkingmemory
python3 -m venv venv && source venv/bin/activate
pip install -e .            # installs fastembed + tiktoken
cp .env.example .env        # set DATABASE_URL etc.
python scripts/init_db.py   # creates the memory table + HNSW/GIN indexes

The first remember/recall downloads the ~130 MB bge-small model once. Upgrading from the legacy four-layer schema? Run python scripts/migrate_to_memory_db.py to port existing rows (re-embedded) into the memory table.

Run

uvicorn thinkingmemory.api.main:app --reload --port 8091

Use it

# remember (text is embedded server-side)
curl -X POST localhost:8091/v1/remember -H 'Content-Type: application/json' -d '{
  "agent_id": "agent-1",
  "content": {"text": "To reset the analytics pipeline: stop workers, flush Redis, replay the Kafka offset."},
  "mtype": "procedural"
}'

# recall: intent in, packed + cited context out
curl -X POST localhost:8091/v1/recall -H 'Content-Type: application/json' -d '{
  "agent_id": "agent-1",
  "intent": "how do I restart the analytics pipeline?",
  "token_budget": 2000
}'

recall returns:

{
  "context": "[1] To reset the analytics pipeline: stop workers, flush Redis, replay the Kafka offset.",
  "items": [{"citation": 1, "id": 42, "mtype": "procedural", "score": 0.83, "why": ["vector", "keyword"]}],
  "tokens_used": 22,
  "tokens_saved_vs_dump": 280
}

API (/v1)

MethodEndpointPurpose
POST/v1/rememberStore a memory (embedded server-side)
POST/v1/remember/batchStore many in one batched embed call
POST/v1/recallThe primitive - intent → packed, cited context
POST/v1/forgetForget a memory (soft by default; hard deletes)
GET/v1/memory/{id}Fetch one memory
GET/v1/trace/{id}Recursive provenance tree - why a memory is known
GET/v1/timeline/{agent}?as_of=…What the agent believed at a point in time
GET/v1/auditAppend-only log of memory operations
POST/v1/maintenance/runRun the lifecycle cycle for an agent
POST/v1/link · GET /v1/neighbors/{id}Entity graph: link memories / traverse
*/working/*Redis TTL scratchpad (short-term working memory)

recall accepts rerank (cross-encoder precision pass) and graph_hops (pull in memories connected via the entity graph) in addition to as_of.

Interactive docs at /docs. All endpoints accept an optional X-Tenant-ID header for per-tenant isolation (single-tenant when omitted).

MCP server

Any MCP-capable agent can use memory as native tools (no HTTP glue):

pip install -e ".[mcp]"
thinkingmemory-mcp          # stdio

Tools: remember, recall (intent → packed context), link_memories, remember_fact, remember_procedure, get_memory, forget, and working-memory tools. Set a default tenant with THINKINGMEMORY_TENANT_ID.

Lifecycle (recall that improves over time)

Background maintenance is what makes this a memory database, not a vector store:

  • decay - salience fades by e^(-decay_rate·Δt) (per-mtype defaults: episodic fades fast, semantic/procedural slowly); recall counteracts it, so useful memories stay high and stale ones sink.
  • extract - durable facts are pulled from recent episodic memories into semantic ones (offline heuristic, or an LLM when LLM_PROVIDER is set), linked to their source via provenance.
  • consolidate ("sleep") - clusters of similar episodic memories are summarized into one semantic memory, linked to its sources via provenance.
  • supersede - near-duplicate semantic memories collapse to the newest.
  • resolve contradictions - conflicting semantic memories (negation/NLI) are detected and the older one is closed, linked to the survivor.
  • forget / prune - low-salience, long-idle memories are soft-closed (recoverable), then hard-pruned after a grace period.

Run it on demand (POST /v1/maintenance/run), via cron (scripts/run_lifecycle.py --all), or with the built-in scheduler (SCHEDULER_ENABLED=true).

python scripts/run_lifecycle.py --agent agent-1 --interval-days 1

Bitemporal & audit (enterprise)

Every memory records when it was true (valid_from/valid_to) and when we learned/closed it (created_at/superseded_at), so you can ask what an agent believed in the past and prove how it knows things:

  • recall with as_of - retrieve against belief at a past moment.
  • GET /v1/timeline/{agent}?as_of=… - a snapshot of everything believed then.
  • GET /v1/trace/{id} - the recursive provenance tree (derived-from / superseded-by) behind a memory.
  • GET /v1/audit - append-only log of remember/recall/forget/maintenance (toggle with audit_enabled).
  • Row-Level Security - with RLS_ENABLED=true (run scripts/enable_rls.py once), Postgres itself restricts every query to the request's tenant via a per-session app.tenant_id GUC - defense-in-depth beneath app filtering. The policy allows access when unset, so single-tenant and admin/maintenance keep working.

Evaluation harness

Retrieval quality is the product, so it's measured:

python eval/run_eval.py

On the bundled synthetic corpus, hybrid recall returns the gold memory 100% of the time (recall@5) versus 0% for a naive recency dump, in ~68% fewer tokens than dumping the whole corpus. The harness is both the dev guardrail and the ROI demo.

Configuration

VariableDescriptionDefault
DATABASE_URLPostgreSQL URLpostgresql://memory_user:...@localhost:5432/thinkingmemory
REDIS_URLRedis URL (working memory)redis://localhost:6379/0
EMBEDDING_PROVIDERlocal or openailocal
EMBEDDING_MODELEmbedding model nameBAAI/bge-small-en-v1.5
EMBEDDING_DIMVector dimension (must match the model)384
OPENAI_API_KEY / ANTHROPIC_API_KEYKeys for OpenAI/Anthropic backends-
LOG_LEVELLogging levelINFO
AUDIT_ENABLEDAppend-only audit loggingtrue
RLS_ENABLEDPer-tenant Postgres Row-Level Securityfalse
RERANK_ENABLED / RERANK_MODELDefault-on cross-encoder rerank + modelfalse / ms-marco-MiniLM-L-6-v2
SCHEDULER_ENABLED / SCHEDULER_INTERVAL_MINUTESBackground lifecycle schedulerfalse / 1440
MEMORY_PARTITIONSHASH partitions for the memory table (fresh installs)8
LLM_PROVIDER / LLM_MODELLLM for extraction/summaries/NLI (none/openai/anthropic)none

Testing

pip install -e ".[dev]"
pytest          # runs against live Postgres + Redis; self-cleaning

Roadmap

Done: unified substrate + hybrid recall (Phase 1); the lifecycle engine - decay, consolidation, forgetting, supersession (Phase 2); bitemporal belief-over-time + provenance traces + audit log + per-tenant Row-Level Security (Phase 3); and the full follow-up set (Phase 4): cross-encoder reranking, a background lifecycle scheduler, relational graph-hop recall, pluggable LLM fact extraction + contradiction detection (offline default), and HASH tenant partitioning of the memory table. Future: cost/perf-driven scale-out (read replicas, a dedicated vector tier). See agent-db-plan.md.

License

Apache License 2.0 - see LICENSE and NOTICE. Copyright 2026 Mallesh Madapathi, ThinkingDBx Pvt. Ltd., Hyderabad, India.

↑↓ navigate openesc close