Skip to main content
Glama

shared-memory

CI License: MIT Python 3.10+

A portable, self-hosted semantic memory layer for LLM agents, exposed over the Model Context Protocol (MCP).

Point any number of agents — Claude Code, other MCP clients, or your own scripts — at one shared store and let them recall past decisions, notes, and conversations by meaning instead of grepping files.

  • One SQLite file. Text, a full-text index and vectors live together; no database service to run. ChromaDB is still available as an option.

  • Hybrid ranking. Vector similarity, plus exact matching of identifiers (order numbers, ABC-123 ids) that vectors alone miss, plus weights that put distilled notes above raw transcripts. See Search quality.

  • Local by default. Embeddings run on-device via fastembed; nothing leaves the host unless you opt into a remote API (OpenAI-compatible or Voyage AI).

  • One store, many agents. Every chunk carries an agent and source tag, so searches can be global or scoped to a single agent.

  • Five MCP tools. memory_search, memory_add, memory_update, memory_forget, memory_stats.

  • Bring your own content. Ships with a generic markdown indexer; writing an adapter for chat logs, tickets, or docs is a short script.

Why

Agents are stateless between sessions. The common fix is a short "always-loaded" memory file, but it can't scale — you can't paste everything you've ever decided into every prompt.

shared-memory is the second layer: an unbounded store you search on demand.

  • Layer A — a short, hand-curated memory file loaded into every prompt (you keep whatever you already use for this).

  • Layer B (this project) — unbounded, searched semantically. "What did we decide about X?" returns the relevant past chunk instead of a file dump.

Related MCP server: mnemo-mcp

Architecture

                 ┌────────────────┐
   agent A ─┐    │  MCP server    │   memory_search      ──▶  memory.sqlite
   agent B ─┼──▶ │  (FastMCP)     │   memory_add / update      chunks + FTS5 + vectors
   scripts ─┘    │                │   memory_forget / stats    (or ChromaDB, optional)
                 └────────────────┘
        ▲
        │ indexers / adapters
   your content (markdown, sessions, docs, …)
shared-memory/
├── shared_memory/
│   ├── server.py         # FastMCP server, the five memory_* tools  → shared-memory-mcp
│   ├── index_markdown.py # generic adapter: index a folder of *.md  → shared-memory-index
│   ├── migrate.py        # copy a Chroma store into SQLite          → shared-memory-migrate
│   ├── embedder.py       # embeddings: local fastembed | OpenAI-compatible | Voyage AI
│   ├── store.py          # backend selection, shared helpers
│   └── store_sqlite.py   # SQLite backend: storage, hybrid ranking, Chroma-style API
├── mcp-server/server.py  # compatibility shim for 0.1/0.2 configs (removed in 1.0)
├── embedding.env.example # copy to embedding.env to customize (optional)
└── requirements.txt

Install

Requires Python 3.10+.

git clone https://github.com/fazerluga-creator/shared-memory.git
cd shared-memory
python -m venv .venv && source .venv/bin/activate
pip install -e .              # SQLite backend (default)
pip install -e ".[chroma]"    # also the ChromaDB backend / migration

Or without cloning:

pip install "git+https://github.com/fazerluga-creator/shared-memory.git@v0.3.0"

This installs three commands: shared-memory-mcp (the server), shared-memory-index (markdown indexer) and shared-memory-migrate.

Where data lives. From a clone (pip install -e .) the config and data stay in the repo folder: embedding.env, memory.sqlite. From a regular install they live in ~/.local/share/shared-memory/ ($XDG_DATA_HOME/shared-memory). SHARED_MEMORY_HOME moves both; SHARED_MEMORY_ENV / SHARED_MEMORY_DB set single files.


Optionally copy the config and tweak it (defaults are fine to start):

```bash
cp embedding.env.example embedding.env

The first run downloads the local embedding model (a few hundred MB) once. With a remote provider (voyage, openai_compatible) nothing is downloaded.

Docker

docker build -t shared-memory .
docker run -i --rm -v shared-memory-data:/data shared-memory

The container speaks MCP over stdio and needs no credentials: the SQLite store lives in /data and embeddings run locally (the model is downloaded on the first search or add). Mount /data to keep the memory between runs.

fazerluga-creator/shared-memory MCP server

Quick start

Index some markdown and search it:

# 1. ingest a folder of notes
shared-memory-index ./notes --agent notes

# 2. run the MCP server (stdio transport)
shared-memory-mcp
# or over HTTP on 127.0.0.1:8000 (read the Security section first)
shared-memory-mcp --transport http

Or use the library directly:

from shared_memory import Embedder, get_client, get_collection

col = get_collection(get_client())
emb = Embedder()

col.add(
    ids=["note::1"],
    embeddings=[emb.embed_document_one("Ship the beta on Friday, feature-flag the new UI.")],
    documents=["Ship the beta on Friday, feature-flag the new UI."],
    metadatas=[{"agent": "notes", "source": "manual"}],
)

res = col.query(query_embeddings=[emb.embed_one("when is the beta?")], n_results=3)
print(res["documents"])

MCP tools

Tool

Signature

Returns

memory_search

(query, top_k=5, agent_filter=None, source_filter=None, since=None)

[{id, text, metadata, score}, ...] sorted by relevance

memory_add

(text, metadata=None)

id of the added chunk

memory_update

(id, text=None, metadata=None, replace_metadata=False)

{id, changed, metadata}; the id is kept, unknown ids raise

memory_forget

(ids=None, agent_filter=None, source_filter=None, dry_run=True, expect_count=None)

{matched, deleted, dry_run, sample}

memory_stats

()

{total, by_agent, by_source, collection, backend}

memory_forget is a dry run unless you pass dry_run=False; deleting by filter also requires expect_count equal to the number of matches, and a call with no ids and no filters is refused.

Example memory_search result:

[
  {
    "id": "notes::markdown::decisions.md::003",
    "text": "decisions.md › Release: ship the beta on Friday, feature-flag the new UI.",
    "metadata": {"agent": "notes", "source": "markdown", "kind": "note",
                 "timestamp": "2026-10-01T09:12:00+00:00"},
    "score": 0.7134
  }
]

Filters:

  • agent_filter — restrict to a single metadata.agent value.

  • source_filter — restrict to a metadata.source value (e.g. markdown, sessions).

  • since — ISO timestamp lower bound on metadata.timestamp. On the SQLite backend it is applied before ranking, so a narrow window on a large store still returns the matches inside it.

Connect to Claude Code / any MCP client

Add the server to your client config (paths are examples):

{
  "mcpServers": {
    "shared-memory": {
      "command": "/path/to/shared-memory/.venv/bin/shared-memory-mcp"
    }
  }
}

Security

The server has no authentication. Anyone who can reach it can read every memory, add to it, rewrite it and delete it. Run it over stdio, or bind the HTTP transport to 127.0.0.1 (FastMCP's default), and never expose it to the internet or to a network you do not fully control. shared-memory-mcp --transport http refuses a non-loopback --host unless you also pass --allow-unauthenticated-remote. If several machines need it, put it behind a VPN or an authenticating reverse proxy.

Memories tend to collect secrets that were pasted into chats. Treat the database file like a password store: restrictive file permissions (chmod 600) and encrypted backups. With a remote embedding provider every chunk is sent to that provider; filter secrets out in your indexer before that if it matters.

Configuration

All settings are read from embedding.env (see embedding.env.example). Paths are overridable by environment variable:

Variable

Default

Purpose

SHARED_MEMORY_ENV

./embedding.env

location of the env file

SHARED_MEMORY_BACKEND

sqlite

sqlite or chroma (see below)

SHARED_MEMORY_DB

./memory.sqlite

SQLite database file

SHARED_MEMORY_DATA_DIR

./chroma_data

ChromaDB persistence directory

EMBEDDING_PROVIDER

local

local (fastembed), openai_compatible or voyage

EMBEDDING_MODEL

MiniLM multilingual (voyage-4 for voyage)

embedding model id

EMBEDDING_COLLECTION

shared_memory

collection name (display only on SQLite)

To route embeddings through a remote OpenAI-compatible /v1/embeddings endpoint, set EMBEDDING_PROVIDER=openai_compatible and fill in EMBEDDING_BASE_URL / EMBEDDING_API_KEY. For Voyage AI set EMBEDDING_PROVIDER=voyage and EMBEDDING_API_KEY (or VOYAGE_API_KEY).

Vectors are tagged with the model that produced them and search ignores vectors of any other model, so changing the provider or model means re-indexing (shared-memory-migrate --reembed does this for a Chroma store).

Upgrading from 0.1.x (Chroma)

If an existing chroma_data directory is found and no memory.sqlite, 0.2.0 keeps using Chroma and logs a warning, so nothing disappears after an upgrade. To move to SQLite:

pip install -e ".[chroma]"
shared-memory-migrate   # reuses stored vectors, no API calls
# check memory_stats / a few searches, then set SHARED_MEMORY_BACKEND=sqlite

Search quality

On the SQLite backend a chunk's score is its cosine similarity times a weight for metadata.kind — note 1.0, doc 0.97, transcript 0.90, anything else 1.0 — plus 0.3 when the query contains an exact token (a number of 4+ digits, a word with a digit, ABC-123) that the chunk contains verbatim. Chunks with metadata.superseded: true are multiplied by 0.9: still findable, just below the current version. Without a query vector (the embedding API is down) search falls back to BM25 full-text ranking instead of failing.

Measured on a private 32-question benchmark over a real 25k-chunk multi-agent memory (right file among the top 3 results):

setup

top-3

ChromaDB + local MiniLM (0.1.x)

16 / 32

Voyage voyage-4, vectors only

26 / 32

Voyage + kind weights + exact tokens + superseded (0.2.0)

30 / 32

The numbers come from one corpus; treat them as a direction, not a guarantee.

Upgrading from 0.2.x

The lib package is now shared_memory: change from lib.store import ... to from shared_memory.store import ... (or from shared_memory import ...). Client configs that run mcp-server/server.py keep working through a shim; switch them to the shared-memory-mcp command when convenient. Data files in a clone stay where they were.

Agents without access to the server: a file bus

The MCP server is meant to stay on a private machine (see Security). Agents that cannot reach it, such as a chat assistant on a phone or a hosted agent with only a Google Drive or Dropbox connector, can still share work through a plain shared folder used as a message bus. One setup that works in practice:

Agent Bus/
├── 00 Welcome          # what this is, which files to read first
├── 01 Rules            # task and answer formats, what never goes here (secrets, personal data)
├── 01_Inbox            # one file per task: task_id, from, to, priority, goal, expected result
├── 02_Outbox           # one file per answer: task_id, agent, status done|blocked|needs_review
├── 03_Shared Memory    # confirmed facts and decisions only, one file per decision
└── 05_Logs             # traces, daily snapshots

Rules that keep it sane: every task and answer carries a task_id; silence from another agent is never treated as agreement; a record is a proposal, not permission to act on anything irreversible; secrets stay on the server and the bus carries only references. An agent that does have server access (for example Claude Code on the same machine) bridges the two: it reads the inbox, does the work with memory_search / memory_add, and writes confirmed facts back as files. shared-memory does not ship a bus; the folder layout above is a convention, not code.

Writing your own indexer

shared_memory/index_markdown.py is the template. An adapter needs to:

  1. discover source items,

  2. turn each into (text, metadata) chunks — always set agent and source, an ISO timestamp if you want since filtering, and kind (note / doc / transcript) if you want the ranking weights,

  3. call add_chunks(collection, embedder, ids, texts, metadatas, upsert=True).

Use a stable, deterministic id scheme so re-runs upsert instead of duplicating.

Design notes

  • Embeddings are computed by us, not by the store. Vectors are passed explicitly on add/query, so the store stays decoupled from embedder availability.

  • No vector index on SQLite. Search is a brute-force cosine over an in-memory numpy matrix: 25k chunks x 1024 dims is ~100 MB and a few milliseconds per query, with nothing to corrupt or rebuild. The matrix is reloaded when another process writes to the file.

  • Chunking (markdown): split by ## headings; fall back to paragraphs; soft-cap 8000 chars at word/newline boundaries. Paths in ids/metadata use POSIX form for cross-OS stability.

  • Timestamps are ISO-UTC strings throughout; the SQLite backend normalizes them on write, so Z, +02:00, naive and unix-epoch inputs compare correctly.

  • Search-time hygiene: results are de-duplicated by content hash and short known-error strings are dropped. Scores are clamped to [0, 1].

  • Stats: memory_stats pages through metadatas to bound peak memory.

Roadmap

  • TTL / forgetting policy for high-volume sources.

  • Optional PII filter at index time.

  • Session-end hooks to auto-index new content.

  • More example adapters (chat logs, issue trackers).

Contributing

Issues and PRs welcome — see CONTRIBUTING.md. New indexer adapters (chat logs, issue trackers, docs) are a great place to start.

License

MIT.

Available Tools

5 tools
memory_addMemory AddC

Add an ad-hoc memory chunk. Returns the generated id.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes
metadataNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It notes that an id is returned but says nothing about persistence lifetime, duplicate handling, whether metadata is indexed/searchable, or any permissions or rate limits for a write operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with the core action front-loaded and no filler. It is efficient, though the return-value sentence is partly redundant given an output schema exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The output schema covers the return value, but with no annotations, 0% parameter coverage, and a mutation that stores user data permanently, the definition omits the ingestion semantics and sibling routing an agent needs to call this safely and correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so nothing documents 'text' or 'metadata'. The description only implies a free-form string ('memory chunk') and gives no hint about what metadata is for or how it affects later retrieval, leaving the schema gap unclosed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a concrete verb and resource ('Add ... memory chunk'), so an agent can tell it is the write path in a memory_* family. However, it does not differentiate itself from siblings (memory_update, memory_search) or explain what 'ad-hoc' means and how it differs from other insertion paths.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance and no mention of alternatives. An agent must infer on its own that this is for creating a new free-form memory rather than updating an existing one via memory_update, which is a real ambiguity given the sibling names.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_forgetMemory ForgetA

Delete memory chunks by explicit ids or by agent/source filter.

Deletion is irreversible, so there are two safety catches: dry_run is on by default, and deleting by filter requires expect_count equal to the number of matches, so an over-broad filter cannot wipe the collection by mistake. A call with no ids and no filters is refused.

ParametersJSON Schema
NameRequiredDescriptionDefault
idsNospecific chunks (memory_search returns ids)
dry_runNoTrue (default) only reports what would be deleted
agent_filterNoeverything from this agent
expect_countNorequired for filter deletes with dry_run=False
source_filterNoeverything from this source

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the entire behavioral burden and does so well: it discloses irreversibility, that dry_run defaults to true, that filter deletes require expect_count to match, and that empty calls are refused. This is exactly the kind of safety-critical context an agent needs before invoking a destructive tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short paragraphs, zero filler, with the core action front-loaded and the safety mechanics following. Every sentence adds distinct information; nothing is restated from the schema verbatim without added context.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, 5-parameter tool with an output schema present, the description covers targeting modes, irreversibility, defaults, the filter guard, and the refusal case. Return-value explanation is unnecessary given the output schema, and no critical gap remains.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the baseline is 3; the description adds meaning beyond it by framing dry_run and expect_count as a coordinated pair of "safety catches" and explaining the rationale (preventing an over-broad filter from wiping the collection). It stops short of describing the ids format or filter matching semantics, so it is not a full 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ("Delete memory chunks") and immediately names the two supported targeting modes (explicit ids vs. agent/source filter). This clearly separates it from siblings like memory_add, memory_search, and memory_update, which are non-destructive or retrieval-oriented.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear usage contexts: delete by ids, or delete by filter, plus an explicit when-not ("A call with no ids and no filters is refused"). It does not explicitly route the agent to an alternative tool (e.g., memory_update for modifying rather than removing), so it falls short of the full when/when-not/alternatives bar.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_statsMemory StatsA

Return collection statistics: total count, breakdown by agent/source.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the disclosure burden. It reveals the shape of what comes back, which implies a safe read with no side effects, but it never states that it is non-mutating, requires no auth, or has any cost/limit. Adequate but thin for an annotation-free tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single short sentence that front-loads the return content with no filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be re-explained, and a zero-parameter read tool has few unknowns. Nothing essential is missing, though the absence of any read-only/safety statement leaves a small gap given no annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. The description correctly implies no input is needed by describing only output content.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Return) and resource (collection statistics) and enumerates what is returned (total count, breakdown by agent/source). It is distinguishable from the mutating siblings (add/update/forget) and the query sibling (search), but it never explicitly contrasts itself with them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no statement of when to use this tool versus the search or add siblings, nor any prerequisite context. The usage is only faintly implied by the read-oriented nature of the name.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_updateMemory UpdateA

Update an existing memory chunk in place; the id is kept.

Keeping the id means references to it stay valid. An unknown id raises, so a typo never silently turns into a new chunk.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYeschunk id (memory_search returns it in "id")
textNonew text; the embedding is recomputed when given
metadataNometadata fields to change (merged unless replace_metadata)
replace_metadataNoreplace metadata wholesale instead of merging

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden and does well: it discloses that the id is preserved, references remain valid, and an unknown id raises rather than silently creating a new chunk. It does not cover permission requirements or all side effects, but it provides meaningful error and identity behavior beyond the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three short sentences with zero waste, front-loads the core update behavior, and then explains the two most important behavioral consequences: id stability and unknown-id errors.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 100% schema coverage and an output schema, the description does not need to explain parameters or return values. It complements those structured fields with the key mutation semantics: in-place update, id preservation, and error on unknown id. It could be slightly more complete by clarifying when to use this versus memory_add, but it is otherwise sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all four parameters thoroughly, including embedding recomputation and metadata merge/replace behavior. The description adds no additional parameter semantics beyond the schema, so the baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: update an existing memory chunk in place. The phrase 'existing' and 'id is kept' helps distinguish it from memory_add, though it does not explicitly name sibling tools or contrast against memory_search/memory_forget.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'update an existing memory chunk' and the failure behavior for unknown ids, but the description does not explicitly say when to use this versus memory_add or other siblings. It gives enough context to infer the tool's role but leaves alternative selection unstated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.3.0
    • First observedmemory_add
    • First observedmemory_forget
    • First observedmemory_search
    • First observedmemory_stats
    • First observedmemory_update

TDQS

A3.7/5.0

Scored across 5 tools

Disambiguation5/5

Each tool maps to a distinct lifecycle operation on a single resource: add, search, update, forget, stats. There is no meaningful overlap — search retrieves, stats aggregates, and the write operations are cleanly separated.

Naming Consistency5/5

All five tools follow a strict memory_<verb> snake_case pattern with clear, conventional verbs (add, search, update, forget, stats). No mixed conventions or vague names.

Tool Count5/5

Five tools is a tight, well-scoped surface for a memory store: write, read, mutate, delete, and observe. Every tool earns its place with no redundancy or filler.

Completeness4/5

The set covers the full CRUD lifecycle plus stats, and search likely substitutes for a direct get-by-id. Minor gaps remain around bulk operations and explicit id-based retrieval, but core workflows are covered without dead ends.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Self-hosted semantic memory for AI agents. Save worklogs, decisions, and notes via MCP, then recall them across sessions by meaning rather than keyword. Backed by Postgres + pgvector with local embeddings (multilingual-e5-base).
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A portable self-hosted memory layer for AI tools, storing context, memories, and handoffs for access from any MCP-compatible client.
    22 npm
    MIT