Skip to main content
Glama

loom-memory

Une mémoire locale pour Claude Desktop et mes agents LOOM. Serveur MCP + RAG hybride (BGE-M3, reranking, Qdrant). J'y mets ce que je veux, Claude vient y chercher. Tout tourne en local, sans cloud ni clé API.

Pourquoi

La mémoire automatique des assistants enregistre ce qu'elle croit avoir compris : des demi-vérités, des choses périmées, des inférences. Ici, rien n'entre sans que je l'aie décidé. J'ajoute un texte, une page web ou un fichier markdown ; Claude interroge cette base avant de répondre sur mes projets, mes décisions ou mes notes, et il n'écrit dedans que si je le lui demande explicitement dans le message courant.

Le contenu vit dans le dépôt, sur ma machine. Les modèles d'embedding et de reranking tournent en local. La seule sortie réseau du projet est le téléchargement d'une page quand je demande de la mémoriser.

Le même serveur sert Claude Desktop (chat et Cowork) et mes agents LOOM, qui consomment les mêmes tools MCP.

Related MCP server: Melchizedek

Ce que Claude peut faire

Neuf tools, séparés en deux familles.

Tool

Rôle

search(query, project?, tags?, k)

Recherche hybride puis reranking. Sans project, la recherche est globale et chaque résultat indique son projet. k est plafonné à 10.

get(doc_id)

Texte intégral d'un document et ses métadonnées.

list_docs(project?, n)

Derniers documents ajoutés, du plus récent au plus ancien, sans le texte.

projects()

Projets présents et nombre de documents pour chacun.

add_text(text, title, project, tags?)

Ajoute un texte brut.

add_url(url, project, tags?)

Télécharge une page, en extrait le contenu et l'ajoute. Le titre est celui de la page.

add_file(path, project, tags?)

Ajoute un fichier markdown local, découpé par titres. Soumis au contrôle d'accès décrit plus bas.

update(doc_id, text)

Remplace le texte d'un document. Même identifiant, titre, projet et tags conservés.

delete(doc_id)

Supprime définitivement un document et ses chunks.

Les quatre premiers sont déclarés en lecture seule au sens MCP. Les cinq autres portent, dans leur description et dans les instructions du serveur, la même consigne : n'appeler que sur demande explicite de l'utilisateur, jamais de sa propre initiative. C'est une consigne au modèle, pas un contrôle technique. Le seul contrôle technique porte sur add_file.

Chaque tool d'écriture renvoie ce qu'il a réellement fait : identifiant, titre, projet, nombre de chunks écrits, et le cas échéant duplicate_of (contenu identique à un document existant, rien n'a été écrit) ou updated (un document existant a été remplacé).

search renvoie le chunk entier quand il fait moins de 1 000 caractères, ce qui couvre la plupart des notes et évite de couper un tableau au milieu. Au delà, un extrait de 300 caractères et truncated: true, signal pour appeler get.

Comment ça marche

Ingestion

Les trois entrées convergent vers le même pipeline. Le texte brut est pris tel quel. Une page web est convertie en markdown structuré : titres, listes, tableaux et blocs de code sont conservés, la navigation, le pied de page, les scripts et les formulaires retirés. Un fichier markdown est lu tel quel, son titre est le premier # H1 ou, à défaut, le nom du fichier.

Le texte est ensuite découpé par sections, en suivant les titres markdown. Une section trop longue est retaillée en paragraphes à environ 1 800 caractères avec un chevauchement de 180. Chaque chunk est préfixé par son chemin de titres (Titre du document > Section > Sous-section) avant d'être vectorisé : un paragraphe isolé perd son contexte, et c'est le détail qui fait le plus de différence en rappel sur des notes.

Une empreinte SHA-256 du texte normalisé (casse, espaces) sert de déduplication. Un contenu déjà présent n'est pas réécrit. Une source déjà connue, URL ou chemin de fichier, dont le contenu a changé remplace le document existant au lieu d'en créer un second.

BGE-M3 produit en un seul passage un vecteur dense (1 024 dimensions) et un vecteur sparse lexical par chunk.

Retrieval

La requête est vectorisée de la même façon. Qdrant exécute deux recherches, dense et sparse, avec les filtres de projet et de tags appliqués dans chacune, puis fusionne les deux listes par RRF. Les candidats passent ensuite dans le cross-encoder bge-reranker-v2-m3, qui note chaque couple question/chunk entre 0 et 1. Les résultats sont regroupés à deux chunks maximum par document, filtrés par un score minimal, puis tronqués à k.

Le score renvoyé est celui du reranker. Au dessus de 0,8, le passage répond directement à la question ; entre 0,3 et 0,8, il traite le sujet sans y répondre franchement ; en dessous de 0,1, c'est du bruit ramené par le vecteur faute de mieux. Le seuil par défaut est 0,1 ; la section Évaluation explique comment le régler.

Stockage

Deux collections Qdrant. documents contient le texte intégral et les métadonnées, sans vecteur : c'est la source de vérité. memory contient les chunks vectorisés et se reconstruit entièrement depuis documents avec loom-memory reindex. Les identifiants de chunk sont dérivés de l'identifiant du document et de l'index du chunk, donc une réécriture ne laisse pas d'orphelin.

Le nom du modèle d'embedding est enregistré dans data/meta.json au moment de l'indexation. Si la configuration demande un autre modèle, le serveur refuse de démarrer et demande un reindex : deux modèles ne se mélangent jamais en silence dans le même index.

Installation

Python 3.12, uv, et une machine capable de faire tourner deux modèles de 570 M de paramètres.

git clone git@github.com:denislamard/loom-memory.git
cd loom-memory
uv sync

uv sync installe aussi le groupe models (FlagEmbedding, torch). Les poids de BGE-M3 (environ 3 Go) et du reranker (environ 2,3 Go) sont téléchargés depuis Hugging Face au premier appel et mis en cache dans ~/.cache/huggingface. Il n'y a rien d'autre à télécharger ensuite.

Sur GPU, les deux modèles tiennent dans 3 Go de VRAM en fp16. Sur CPU, ça fonctionne avec LOOM_MEMORY_DEVICE=cpu (le fp16 est coupé automatiquement), mais le reranker devient le poste dominant : comptez une dizaine de secondes par recherche. Une carte Pascal ou plus ancienne (compute capability inférieure à 7.5) n'est pas prise en charge par les roues torch CUDA 13 publiées sur PyPI ; il faut alors soit le CPU, soit une roue CUDA 12.6.

Le pinning transformers<5 n'est pas un oubli : FlagEmbedding 1.4 casse avec transformers 5 (tokenizer.pad reçoit une liste au lieu d'un dictionnaire).

Qdrant

Le mode embarqué de Qdrant (QdrantClient(path=…)) n'accepte qu'un processus à la fois. Or Claude Desktop lance deux instances de chaque serveur MCP, une pour le chat et une pour les sessions Cowork et Code ; la seconde mourrait à l'ouverture. Le serveur est donc branché sur un Qdrant en conteneur, avec le stockage dans le dépôt.

docker run -d --name qdrant --restart unless-stopped \
  --user "$(id -u):$(id -g)" \
  -e QDRANT__STORAGE__SNAPSHOTS_PATH=/qdrant/storage/snapshots \
  -p 127.0.0.1:6333:6333 \
  -v /home/[user]/dev/loom-memory/data/qdrant:/qdrant/storage \
  qdrant/qdrant
curl -s localhost:6333/

--user fait que les fichiers de data/qdrant appartiennent à l'utilisateur et non à root ; la variable QDRANT__STORAGE__SNAPSHOTS_PATH est nécessaire dans ce cas, sinon Qdrant ne peut pas écrire son dossier de snapshots dans l'image. Le port n'est exposé que sur l'interface locale.

--restart unless-stopped relance le conteneur avec le service Docker au démarrage de la machine, à condition que ce service soit activé (systemctl is-enabled docker, sinon sudo systemctl enable docker). Un docker stop qdrant manuel le laisse arrêté jusqu'au prochain docker start qdrant.

Le mode embarqué reste disponible en l'absence de LOOM_MEMORY_QDRANT_URL ; les tests l'utilisent. Les deux modes n'ont pas le même format sur disque, on ne passe pas de l'un à l'autre sans réingérer (export puis import).

Branchement dans Claude Desktop

~/.config/Claude/claude_desktop_config.json :

{
  "mcpServers": {
    "loom-memory": {
      "command": "/home/[user]/dev/loom-memory/.venv/bin/loom-memory-mcp",
      "args": [],
      "env": {
        "FASTMCP_SHOW_SERVER_BANNER": "false",
        "FASTMCP_CHECK_FOR_UPDATES": "off",
        "LOOM_MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
        "LOOM_MEMORY_ALLOWED_ROOTS": "/home/[user]/dev",
        "LOOM_MEMORY_DEVICE": "cpu"
      }
    }
  }
}

Pointer directement le binaire du venv plutôt que uv run : uv peut résoudre des dépendances, voire télécharger un interpréteur, entre l'exec() et le handshake MCP, sans rien écrire sur la sortie standard pendant ce temps, et le client attend. Fermer complètement l'application avant d'éditer ce fichier, elle le réécrit à la fermeture.

Au démarrage, le serveur répond au handshake tout de suite et charge les modèles en tâche de fond. La première recherche peut attendre la fin de ce chargement, les suivantes non.

Configuration

Toutes les options sont des variables d'environnement préfixées LOOM_MEMORY_, ou un fichier .env à la racine du dépôt (ignoré par git). Les valeurs ci-dessous sont les défauts.

Variable

Défaut

Rôle

DATA_DIR

data/ dans le dépôt

Répertoire des données : Qdrant embarqué, meta.json, export, jeu doré.

QDRANT_URL

vide

URL d'un serveur Qdrant. Vide : mode embarqué dans DATA_DIR/qdrant.

QDRANT_API_KEY

vide

Clé d'API si le serveur en exige une.

DEVICE

cuda

Périphérique torch. cpu coupe le fp16.

DENSE_MODEL

BAAI/bge-m3

Modèle d'embedding. En changer impose un reindex.

RERANKER_MODEL

BAAI/bge-reranker-v2-m3

Cross-encoder de reranking.

ALLOWED_ROOTS

vide

Racines lisibles par add_file, séparées par :. Vide : add_file refusé.

DENY_PATTERNS

vide

Motifs refusés en plus de la liste de base, séparés par :.

CHUNK_TARGET_CHARS

1800

Taille visée d'un chunk.

CHUNK_MAX_CHARS

2400

Taille maximale d'une section avant retaille.

CHUNK_OVERLAP_CHARS

180

Chevauchement entre chunks consécutifs d'une même section.

CHUNK_MIN_CHARS

200

En dessous, une section est fusionnée avec la précédente.

PREFETCH_LIMIT

15

Candidats par branche (dense, sparse) avant fusion.

RERANK_CANDIDATES

20

Candidats passés au reranker.

MAX_CHUNKS_PER_DOC

2

Chunks d'un même document dans les résultats.

FULL_CHUNK_CHARS

1000

En dessous, search renvoie le chunk entier.

SNIPPET_CHARS

300

Longueur de l'extrait au delà.

MIN_SCORE

0.1

Score reranker minimal d'un résultat.

WARMUP_ON_START

true

Préchargement des modèles au démarrage du serveur.

FAKE_MODELS

false

Modèles factices, pour les tests ou une démo sans GPU.

FETCH_TIMEOUT_S

20

Délai de téléchargement pour add_url.

Sécurité de add_file

Un serveur MCP tourne avec les droits de l'utilisateur qui lance Claude Desktop, et Claude Desktop ne lui applique aucune restriction de dossier. Sans garde-fou, add_file suivi de get est une lecture de fichier arbitraire, y compris depuis une session Cowork dont les dossiers autorisés sont bien plus étroits. Ce n'est pas théorique, c'est le test qui a motivé ce paragraphe.

Trois contrôles s'appliquent, dans cet ordre, et le message d'erreur nomme celui qui a mordu.

Le chemin résolu, symlinks compris, doit être inclus dans une des racines de ALLOWED_ROOTS, elles-mêmes résolues. Un lien qui sort d'une racine est refusé. Sans racine configurée, add_file est refusé d'office.

Aucun composant du chemin relatif à la racine ne doit correspondre à un motif refusé. La liste de base est dans le code et ne peut pas être retirée par configuration :

.env  .env.*  *.pem  *.key  *.p12  *.pfx
id_rsa*  id_ecdsa*  id_ed25519*
.ssh  .aws  .gnupg  .netrc  .npmrc  .pypirc
.git  .venv  node_modules  __pycache__

DENY_PATTERNS peut seulement y ajouter. Une garantie qu'un fichier de configuration peut désactiver n'en est plus une.

L'extension doit être .md, .markdown ou .txt.

Ce filtrage porte sur des noms. Un secret écrit en clair dans une note markdown d'une racine autorisée sera lu. La frontière, c'est le choix des racines ; le reste est de la défense en profondeur. add_url n'est pas concerné (réseau sortant, pas de lecture disque) et add_text ne lit rien.

Ligne de commande

loom-memory expose les mêmes opérations que le serveur, plus la maintenance. En mode serveur Qdrant, la CLI fonctionne pendant que Claude Desktop tourne.

loom-memory add-text PROJET TITRE [TEXTE]     texte brut ; lu sur stdin si absent
loom-memory add-url PROJET URL
loom-memory add-file PROJET CHEMIN
loom-memory search "question" [-p projet] [-t tag] [-k 5]
loom-memory get DOC_ID
loom-memory list [-p projet] [-n 20]
loom-memory projects
loom-memory delete DOC_ID
loom-memory export [FICHIER]                   sauvegarde JSONL, défaut data/export.jsonl
loom-memory import [FICHIER]                   réimport ; les doc_id déjà présents sont ignorés
loom-memory reindex                            reconstruit les chunks depuis les documents
loom-memory eval [FICHIER] [--json]            évalue le retrieval sur le jeu doré
loom-memory eval-add "question" DOC_ID [-p projet]

Les commandes d'ajout acceptent -t plusieurs fois pour les tags. Les options globales --fake (modèles factices) et --data-dir se placent avant la commande.

Évaluation

data/golden.jsonl est un jeu doré : une ligne par cas, avec la question telle que je la poserais et l'identifiant du document qui doit sortir.

{"query":"comment purger le journal d'audit sans arrêter le serveur","doc_id":"f3c9d186-…","title":"loom-fs — serveur MCP filesystem à rôles"}

loom-memory eval rejoue chaque question à seuil zéro et donne, par cas, le rang du document attendu et son score, puis recall@1, recall@5 et MRR, et enfin le plus haut seuil qui ne fait perdre aucun cas trouvé, avec la part des résultats hors document attendu qui tomberaient sous ce seuil. C'est la valeur à mettre dans MIN_SCORE.

Le jeu s'enrichit au fil de l'eau : deux questions par document ajouté, avec eval-add. Un cas qui sort avec un score faible alors que le document contient la réponse signale presque toujours un document mal rédigé, typiquement une commande sans la phrase qui dit quand et pourquoi l'utiliser. Sur les premiers cas, la même question passait de 0,91 à 0,11 selon que le document gardait ou non ses deux phrases de contexte.

Un changement de chunking, de modèle, de seuil ou de fusion se valide par un eval avant et après.

Sauvegarde et réindexation

loom-memory export écrit un document par ligne dans data/export.jsonl, texte intégral et métadonnées compris. Ce fichier est commité avec le dépôt : c'est la sauvegarde de référence, indépendante de Qdrant et du modèle d'embedding. data/qdrant est ignoré par git.

loom-memory import recharge un export dans une base vide ou partielle, en réindexant chaque document et en ignorant les identifiants déjà présents. Changer de modèle d'embedding ou de mode Qdrant revient à un export, un changement de configuration, puis un import ou un reindex.

Dépannage

Le serveur affiche Échec dans Claude Desktop avec « Connection closed ». Lire les journaux depuis les paramètres de l'application. La première ligne d'erreur du serveur dit ce qui manque. Les deux causes habituelles : Qdrant injoignable (« Qdrant injoignable sur http://127.0.0.1:6333docker start qdrant »), ou un .venv d'avant l'ajout du script loom-memory-mcp (« No executable file », il suffit d'un uv sync).

Le serveur refuse de démarrer en parlant de modèle. data/meta.json enregistre le modèle avec lequel l'index a été construit et il diffère de DENSE_MODEL. loom-memory reindex reconstruit l'index avec le modèle courant.

add_file est refusé. Le message nomme la règle : hors des racines autorisées, motif refusé, extension. Sans ALLOWED_ROOTS, tout est refusé.

Une recherche prend dix secondes. Le reranker tourne sur CPU. Vérifier LOOM_MEMORY_DEVICE et que torch voit bien la carte (python -c "import torch; print(torch.cuda.is_available())" dans le venv).

IndexError: list index out of range dans FlagEmbedding au premier appel. Le GPU est vu par torch mais sans kernels compatibles (carte trop ancienne pour la roue CUDA installée). FlagEmbedding attrape l'erreur CUDA, réduit son batch jusqu'à zéro et plante sur une liste vide. Passer en cpu ou installer une roue torch adaptée à la carte.

Limites connues

Le contrôle « écriture sur demande explicite » est une consigne au modèle. Une page ajoutée par add_url peut contenir une injection ; elle n'aura aucun pouvoir sur le disque grâce au confinement de add_file, mais elle pourrait pousser le modèle à écrire dans la mémoire. Les tools d'écriture renvoient toujours ce qu'ils ont fait, c'est à l'utilisateur de le lire.

Les tailles de chunk sont en caractères, avec l'approximation de quatre caractères par token en français, pas en tokens du modèle.

list_docs et projects parcourent toute la collection documents. Linéaire, invisible jusqu'à quelques milliers de documents.

Le mode serveur Qdrant ne chiffre rien et n'authentifie personne par défaut. Le port est lié à l'interface locale ; ne pas l'exposer sans clé d'API.

Il n'y a pas de quota. Un agent en boucle peut lancer autant de recherches qu'il veut ; chacune coûte du temps de reranking, pas d'argent.

Tests et qualité

uv run ruff check src tests
uv run pyright
uv run pytest

Les tests tournent sans GPU et sans téléchargement : les modèles sont remplacés par des factices déterministes (dense par sac de mots haché, sparse par comptage) suffisants pour vérifier le filtrage, la déduplication, le remplacement par source, l'export et l'import, le confinement de add_file et le comportement des tools à travers un client MCP en mémoire. Pyright est en mode strict.

Licence

Apache 2.0.

Available Tools

9 tools
add_fileAdd FileA

Ajoute un fichier markdown local à la mémoire, découpé par titres. Le titre est le premier H1, sinon le nom du fichier. N'appeler QUE si Denis le demande explicitement dans son message courant ; jamais de ta propre initiative.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesChemin absolu du fichier .md, dans une des racines autorisées.
tagsNoTags optionnels.
projectYesProjet de rattachement.

Output Schema

ParametersJSON Schema
NameRequiredDescription
titleYes
chunksYes
doc_idYes
projectYes
updatedNoTrue si un document existant a été remplacé.
duplicate_ofNodoc_id d'un document au contenu identique ; rien n'a été écrit.

TDQS

A4.2/5.0
Behavior4/5

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

No annotations, so the description carries the full burden, and it delivers real behavioral detail: chunking by headings and the exact title-derivation rule (first H1, else filename). It omits permission/auth requirements and what happens on conflicts with existing memory, which are notable for a write 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?

Three sentences, each doing distinct work: purpose, chunking/title rule, invocation gate. Front-loaded with the action and free of filler.

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 explained, and the description covers the key ingestion behavior and the calling gate. Minor gap: consequences of adding a duplicate or invalid file are unstated.

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 'path', 'project', and 'tags' are already documented in the schema. The description only indirectly reinforces that the path is a local markdown file; it adds no syntax or constraint detail beyond the schema, matching the baseline 3.

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 ('Ajoute un fichier markdown local à la mémoire') and adds the chunking semantic ('découpé par titres'). The local-markdown resource implicitly distinguishes it from add_text/add_url, but no sibling is named explicitly, so it stops short of a 5.

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

Usage Guidelines5/5

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

Gives an unambiguous gating rule: call ONLY when Denis explicitly requests it in the current message, never on the agent's own initiative. This is explicit when/when-not guidance, the strongest form of routing instruction.

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

add_textAdd TextA

Ajoute un texte brut à la mémoire de Denis. N'appeler QUE si Denis le demande explicitement dans son message courant ; jamais de ta propre initiative.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoTags optionnels.
textYesContenu à mémoriser, tel quel.
titleYesTitre court et explicite (apparaît dans list_docs).
projectYesProjet de rattachement, ex. 'loom'.

Output Schema

ParametersJSON Schema
NameRequiredDescription
titleYes
chunksYes
doc_idYes
projectYes
updatedNoTrue si un document existant a été remplacé.
duplicate_ofNodoc_id d'un document au contenu identique ; rien n'a été écrit.

TDQS

A3.8/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 full behavioral burden. It discloses the important trigger constraint (must be explicitly requested), which is real added value, but says nothing about persistence scope, duplicate handling, required permissions, or whether writes are reversible for a mutation 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 sentences with no filler; the purpose comes first and the invocation constraint follows immediately. Every clause earns its place.

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 described, and all four parameters are fully documented in the schema. What remains thin is context about the target memory store and whether a valid 'project' must already exist, but the definition is otherwise sufficient to invoke correctly.

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 tags, text, title, and project. The description adds no parameter-level meaning beyond what the schema provides, which is the baseline 3 case.

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 and resource ('Ajoute un texte brut à la mémoire'), and the qualifier 'texte brut' implicitly separates it from add_url and add_file. It stops short of naming those siblings, so differentiation is inferable rather than explicit.

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 a clear, strong usage condition: call only when Denis explicitly requests it in the current message, never on the agent's own initiative. It does not name an alternative tool or the reverse condition (e.g. when to prefer add_url/add_file), so it stays at a solid 4 rather than a full when/when-not/alternatives statement.

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

add_urlAdd UrlA

Télécharge une page web, en extrait le contenu principal et l'ajoute à la mémoire. Le titre est celui de la page. N'appeler QUE si Denis le demande explicitement dans son message courant ; jamais de ta propre initiative.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesURL http(s) de la page.
tagsNoTags optionnels.
projectYesProjet de rattachement.

Output Schema

ParametersJSON Schema
NameRequiredDescription
titleYes
chunksYes
doc_idYes
projectYes
updatedNoTrue si un document existant a été remplacé.
duplicate_ofNodoc_id d'un document au contenu identique ; rien n'a été écrit.

TDQS

A3.8/5.0
Behavior3/5

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

With no annotations, the description carries the full behavioral burden and does disclose useful traits: it downloads, extracts main content, and derives the title from the page. However, it says nothing about failure modes (unreachable/blocked URLs), permissions, deduplication, or rate limits, leaving meaningful gaps for a mutation 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?

Three short sentences, front-loaded with the purpose before the usage restriction. No filler; every sentence carries either behavior or a constraint.

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 described, and the description covers purpose, internal behavior, and usage conditions. It is nearly complete, with only edge-case handling left unstated.

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 url, tags, and project are already documented in the schema. The description adds no parameter-level detail such as URL format expectations beyond http(s) or how tags/project affect storage, so the baseline 3 applies.

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 names a specific multi-step operation: download a web page, extract its main content, and add it to memory, with the title taken from the page. This clearly separates it from add_text and add_file siblings, though it doesn't explicitly name those alternatives by name.

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?

It gives an explicit and unusually strong usage constraint: call ONLY if Denis explicitly asks in his current message, never on your own initiative. This is a clear when/when-not rule, but it does not point to a concrete alternative tool for the cases where you shouldn't use this one.

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

deleteDeleteA
Destructive

Supprime définitivement un document et ses chunks. Renvoie le résumé de ce qui a été supprimé. N'appeler QUE si Denis le demande explicitement dans son message courant ; jamais de ta propre initiative.

ParametersJSON Schema
NameRequiredDescriptionDefault
doc_idYesIdentifiant du document.

Output Schema

ParametersJSON Schema
NameRequiredDescription
tagsYes
charsYes
titleYes
doc_idYes
sourceYes
projectYes
added_atYes
updated_atYes
source_kindYes

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true, but the description adds real value beyond them: it discloses that the deletion is permanent ('définitivement'), that it cascades to chunks, and that a summary of what was removed is returned. It does not mention permission or auth requirements, so not a full 5.

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?

Three short sentences, each load-bearing: what is deleted, what is returned, and the invocation constraint. The restriction is front-loaded after the core purpose with no filler.

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?

An output schema exists so return detail is covered, annotations cover the destructive profile, and the description adds cascade behavior plus the consent constraint. An agent has everything needed to decide and call correctly.

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 coverage is 100% for the single doc_id parameter, and the description adds no meaning beyond it (no format hints, no alternative identification schemes). Baseline 3 is appropriate when the schema fully documents the only parameter.

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?

States a specific verb (supprime) and resource (document), plus the cascade scope (et ses chunks), which distinguishes it from siblings like update or add_text. The 'définitivement' qualifier makes the permanence unmistakable.

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

Usage Guidelines5/5

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

Gives an explicit, unusually strict invocation condition: only when the user explicitly requests it in the current message, never on the agent's own initiative. This is exactly the when/when-not guidance that prevents accidental destructive calls.

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

getGetA
Read-only

Texte intégral d'un document de la mémoire, avec ses métadonnées.

ParametersJSON Schema
NameRequiredDescriptionDefault
doc_idYesIdentifiant renvoyé par search.

Output Schema

ParametersJSON Schema
NameRequiredDescription
tagsNo
textYes
titleYes
doc_idYes
sourceNo
projectYes
added_atNo
updated_atNo
source_kindYes
content_hashYes

TDQS

A3.5/5.0
Behavior3/5

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

readOnlyHint=true already tells the agent this is a safe read operation, and the description's mention of returned full text plus metadata is largely restated by the existing output schema. It adds little beyond the structured fields, e.g. no note on missing-document behavior or permissions.

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?

A single short sentence with no filler, front-loading the resource. It is efficient, though extremely terse for a tool whose name is as generic as 'get'.

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?

With an output schema covering return values and annotations covering the safety profile, the description only needs to state scope, which it does. Minor gap: nothing about failures for unknown doc_id or the relationship to the sibling search/list tools.

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% for the single parameter, and its schema text ('Identifiant renvoyé par search') already explains provenance. The description contributes no additional parameter meaning, so baseline 3 applies.

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 retrieval action and resource ('texte intégral d'un document de la mémoire, avec ses métadonnées'), which is far clearer than the bare name 'get'. It implicitly contrasts with list/search by emphasizing full-text retrieval of a single document, though it never names those siblings explicitly.

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?

No explicit when-to-use statement or exclusion versus search, list_docs, or the add_* tools. The only usage signal is indirect, via the param note 'Identifiant renvoyé par search', which implies a search-then-fetch flow but leaves the agent to infer it.

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

list_docsList DocsB
Read-only

Derniers documents ajoutés, du plus récent au plus ancien (métadonnées, sans texte).

ParametersJSON Schema
NameRequiredDescriptionDefault
nNo
projectNoFiltrer sur un projet.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, so safety is covered. The description adds useful context that results are metadata-only without document text and that ordering is newest-first, which goes beyond the annotations, but no pagination or result-count behavior is described.

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?

A single, tight sentence with the ordering constraint front-loaded and zero filler. Appropriately sized, though it is the bare minimum for a tool with an undocumented parameter.

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

Completeness3/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 needn't be explained, and the metadata/text distinction is a helpful addition. However, for only two parameters the description omits the 'n' limit entirely, leaving a genuine gap.

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 coverage is only 50%: 'n' has no description in either schema or description, and the description never mentions the result-count limit (max 50, default 20). Only 'project' is documented in the schema, so the description fails to compensate for the coverage gap.

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+resource ('list documents') plus sort order (most recent first) and content scope (metadata only, no text). Clear purpose, but there is no differentiation from the sibling 'search' tool or explanation of how it differs.

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?

No guidance on when to use this versus 'search' or 'projects'. The listing intent is implied by the name, but no conditions, exclusions, or alternatives are given.

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

projectsProjectsA
Read-only

Projets présents dans la mémoire et nombre de documents pour chacun. Utile pour choisir le filtre project de search.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior3/5

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

The readOnlyHint annotation already tells the agent this is a safe non-mutating call, so the description need not restate that. It does add the useful fact that each entry carries a document count, implying a small summary payload rather than full content. It does not disclose ordering or whether projects with zero documents are included.

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 no filler; the resource and its distinguishing content come first and the selection guidance second. Only quibble is the slight terseness of the first sentence, which omits a verb.

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 the description is not obliged to describe return values, and it only needs to convey scope and intent. For a zero-parameter enumeration tool this is close to sufficient; only ordering/pagination behavior is unstated.

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 no parameters, so per the rubric the baseline is 4. The description's mention of the `project` filter is about a sibling tool (search), not this one, so it adds no parameter meaning here and cannot exceed the baseline.

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 resource (projects held in memory) plus the payload (document count per project), which is more than a restatement of the name 'Projects'. It is distinguishable from the generic siblings (get, delete, search, list_docs) by virtue of being a zero-argument enumeration with counts. It lacks an explicit verb, but the intent is unambiguous.

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?

Explicitly gives the use case: consult this to pick the `project` filter value for search. That routes the agent to the right sibling at the right moment. It stops short of the full 5 because it gives no when-not guidance (e.g. that it should not be used to retrieve document content).

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

updateUpdateA
Idempotent

Remplace le texte d'un document existant (même doc_id, titre, projet et tags conservés ; chunks réindexés). N'appeler QUE si Denis le demande explicitement dans son message courant ; jamais de ta propre initiative.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYesNouveau contenu intégral.
doc_idYesIdentifiant du document.

Output Schema

ParametersJSON Schema
NameRequiredDescription
titleYes
chunksYes
doc_idYes
projectYes
updatedNoTrue si un document existant a été remplacé.
duplicate_ofNodoc_id d'un document au contenu identique ; rien n'a été écrit.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations only carry idempotentHint=true, so the description adds meaningful disclosure beyond them: what is preserved (doc_id, title, project, tags), that the text is fully replaced, and the side effect that chunks are reindexed. It stops short of noting permissions or failure modes, but the behavioral picture is substantially richer than the annotation.

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 tightly packed sentences with zero waste: the first states scope and preservation/reindexing behavior, the second states the calling precondition. Front-loaded and efficient.

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 two-parameter mutation tool that ships an output schema, everything an agent needs is present: replacement semantics, preserved fields, the reindexing side effect, and a strict invocation rule. Nothing material is missing.

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% with only two parameters, so the schema already documents both text and doc_id. The description adds only a light constraint ('même doc_id', existing document) without new format or syntax detail, so the baseline 3 applies.

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?

States a specific verb and resource ('Remplace le texte d'un document existant') and clarifies that it acts on an existing doc_id while preserving title, project and tags, which distinguishes it from the add_* siblings that add new content. An agent can tell it replaces the whole text rather than appending.

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

Usage Guidelines5/5

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

Explicit and strict: 'N'appeler QUE si Denis le demande explicitement dans son message courant ; jamais de ta propre initiative.' This gives a clear when-to-use condition and an explicit prohibition, leaving nothing to inference.

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. 9 tool updatesv1.1.0
    • First observedadd_file
    • First observedadd_text
    • First observedadd_url
    • First observeddelete
    • First observedget
    • First observedlist_docs
    • First observedprojects
    • First observedsearch
    • First observedupdate

TDQS

A3.8/5.0

Scored across 9 tools

Disambiguation5/5

Each tool targets a distinct operation: get (single doc read), search (hybrid retrieval), list_docs (recent listing), projects (aggregate counts), update (replace text), delete (remove), and three add variants differentiated by source (raw text, URL, local file). The add_* trio could superficially overlap but their input types are clearly separated, so an agent won't misselect.

Naming Consistency3/5

The set mixes bare verbs (get, delete, search, update) with verb_noun forms (list_docs, add_text, add_url, add_file) and a bare noun (projects). It's readable and snake_case throughout, but there's no single predictable convention, with 'projects' being the most notable outlier.

Tool Count5/5

Nine tools is well-scoped for a personal memory store covering read, search, list, aggregate, create-by-source, update, and delete. Each tool earns its place with no redundant members.

Completeness5/5

Full lifecycle is covered: creation (add_text/add_url/add_file), reading (get), retrieval (search), browsing (list_docs, projects), update (update), and removal (delete). No obvious dead ends for a memory system of this scope.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Persistent memory for Claude Code. Automatically indexes every conversation and provides production-grade hybrid search (BM25 + vectors + reranker) via MCP tools. 100% local, zero config, zero API keys, zero invoice.
    16
    33
    7
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides local-first, cross-session memory for Claude Code, enabling semantic search across past sessions to retrieve procedures, decisions, or answers without exposing secrets.
    Apache 2.0