Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## [3.2.1] - 2026-08-31

### Migration vers MCP Python SDK 2

- **SDK officiel `mcp==2.1.1`** : serveur `MCPServer`, version applicative annoncée à l'initialisation, endpoint `/mcp` et 40 outils métier conservés.
- **CLI Python** : transport `streamable_http_client` avec `httpx2`, callback public de progression et lecture des résultats SDK 2 ; timeout de lecture de 15 minutes et désactivation des proxies d'environnement conservés.
- **Console `/admin`** : appels via l'API publique `mcp.call_tool`, validation des arguments et suppression de l'accès au registre privé du SDK.
- **Gros documents** : limite HTTP explicitement alignée sur les 50 Mio applicatifs encodés en base64, pour éviter la nouvelle limite SDK de 4 Mio.
- **Dépendances figées** : `requirements.lock` inclut les dépendances transitives et l'outillage Python ; Docker l'installe sans `pip --upgrade` non borné et vérifie les imports SDK 2 au build.
- Migration interne : pas de changement des paramètres métier ni de migration des données. Les environnements exécutant la CLI Python doivent réinstaller les dépendances.
- **Validation** : 9 tests ciblés passent sous Python 3.11/Docker (authentification, admin, protocoles legacy/2026, notifications, uploads >4 Mio et limite HTTP), build sans cache réussi, schémas des 40 outils inchangés. Après renouvellement de la clé LLMaaS, 19 contrôles fonctionnels réels passent avec le CLI Go et le serveur SDK 2 : ingestion de 3 documents, extraction d'entités et relations, recherche graphe et vectorielle, déduplication locale/distante, remplacement explicite et cohérence S3/Neo4j/Qdrant.
- **Réserve de recette locale** : les accès TLS/S3 présentent des délais intermittents, reproduits également avec le serveur SDK 1. Le nettoyage a dépassé le délai client de 120 secondes ; la suppression effective des ressources de test a été vérifiée séparément (aucun résidu S3, Neo4j ou Qdrant). La suite de recette globale n'a pas été rejouée.

### 🧹 Cleanup des ontologies S3 orphelines

- **`storage_check`** ne considère plus automatiquement tous les objets contenant `_ontology_` comme légitimes : seules les ontologies encore référencées par une mémoire existante sont protégées.
- **`storage_cleanup`** peut désormais détecter et supprimer les ontologies laissées sur S3 après la suppression d'une mémoire, y compris les copies obsolètes d'une ontologie.
- **Compatibilité legacy** : les mémoires anciennes sans `ontology_uri` restent protégées par une correspondance stricte sur leur identifiant et leur nom d'ontologie.
- Correctif de l'issue [#31](https://github.com/Cloud-Temple/graph-memory/issues/31), validé sur Docker local : 5 ontologies orphelines détectées puis supprimées, second contrôle à 0 orphelin.

## [3.2.0] - 2026-06-04

### 🧭 `source_path` exposé dans la recherche Graph-first
Expand Down
2 changes: 1 addition & 1 deletion DESIGN/SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ Le canal de collaboration `graph_push` entre Live Memory et Graph Memory est un
| Composant | Technologie | Version |
| --------------- | ----------------------------- | --------------------- |
| Runtime | Python | 3.11+ |
| MCP SDK | `mcp` (FastMCP) | ≥ 1.8.0 |
| MCP SDK | `mcp` (MCPServer) | 2.1.1 (SDK 2) |
| Web Framework | FastAPI + Starlette | ≥ 0.100.0 |
| ASGI Server | Uvicorn | ≥ 0.20.0 |
| Graph Database | Neo4j Community | 5.x |
Expand Down
9 changes: 5 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ FROM python:3.11.12-slim
# Métadonnées
LABEL maintainer="Cloud Temple"
LABEL description="MCP Memory Service - Knowledge Graph Memory for AI Agents"
LABEL version="3.2.0"
LABEL version="3.2.1"

# Variables d'environnement Python
ENV PYTHONDONTWRITEBYTECODE=1
Expand All @@ -29,9 +29,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/*

# Copie et installation des dépendances Python
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -r requirements.txt
COPY requirements.txt requirements.lock ./
RUN pip install --no-cache-dir -r requirements.txt -r requirements.lock \
&& pip check \
&& python -c "from mcp.server.mcpserver import MCPServer, Context; from mcp.client.streamable_http import streamable_http_client"

# Créer un utilisateur non-root pour la sécurité
RUN groupadd -r mcp && useradd -r -g mcp -d /app -s /sbin/nologin mcp
Expand Down
22 changes: 15 additions & 7 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Built by **[Cloud Temple](https://www.cloud-temple.com)**.

## 📋 Changelog

See **[CHANGELOG.md](CHANGELOG.md)** for the full version history (v0.5.0 → v3.2.0).
See **[CHANGELOG.md](CHANGELOG.md)** for the full version history (v0.5.0 → v3.2.1).

**Latest**: v3.2.0 (June 4, 2026) — `source_path` exposed in Graph-first search: `memory_search` and `memory_query` now return the canonical source path (`source_path`) and a derived `repo_path` for every document/chunk, letting an agent open the Git file immediately without a full `document_list`. Enriched via a retroactive graph join (no re-ingestion). `document_get`/`document_list` tools aligned. Previously: v3.1.1 (`/admin` "⚡ Ingest Jobs" console).
**Latest**: v3.2.1 (August 31, 2026) — `storage_check` and `storage_cleanup` now detect S3 ontology objects orphaned after memory deletion, while preserving ontologies still referenced by existing memories and legacy memories without an `ontology_uri`. Fixes [#31](https://github.com/Cloud-Temple/graph-memory/issues/31). Previously: v3.2.0 (`source_path` exposed in Graph-first search).

---

Expand Down Expand Up @@ -144,17 +144,24 @@ open http://localhost:8070/admin

### With Python (MCP SDK)

Version 3.2.1 uses the official MCP SDK **2.1.1**. Reinstall CLI dependencies with
`pip install -r requirements.txt -r requirements.lock`. Existing MCP clients,
tool names and arguments remain supported. Docker uses the same Python
dependency lock.

```python
from mcp.client.streamable_http import streamablehttp_client
import httpx2
from mcp.client.streamable_http import streamable_http_client
from mcp import ClientSession
import base64

async def example():
headers = {"Authorization": "Bearer your_token"}

async with streamablehttp_client(
"http://localhost:8070/mcp", headers=headers
) as (read, write, _):
async with (
httpx2.AsyncClient(headers=headers, timeout=httpx2.Timeout(30, read=900), trust_env=False) as http,
streamable_http_client("http://localhost:8070/mcp", http_client=http) as (read, write),
):
async with ClientSession(read, write) as session:
await session.initialize()

Expand Down Expand Up @@ -225,7 +232,8 @@ Custom ontologies can be added as YAML files in `ONTOLOGIES/`.

```bash
# Install CLI dependencies
pip install httpx click rich prompt_toolkit mcp
pip install -r requirements.txt -r requirements.lock
pip install prompt_toolkit

# Scriptable mode
python scripts/mcp_cli.py health
Expand Down
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ Développé par **[Cloud Temple](https://www.cloud-temple.com)**.

## 📋 Changelog

Voir **[CHANGELOG.md](CHANGELOG.md)** pour l'historique complet des versions (v0.5.0 → v3.2.0).
Voir **[CHANGELOG.md](CHANGELOG.md)** pour l'historique complet des versions (v0.5.0 → v3.2.1).

**Dernière version** : v3.2.0 (4 juin 2026) — `source_path` exposé dans la recherche Graph-first : `memory_search` et `memory_query` renvoient désormais le chemin source canonique (`source_path`) et un `repo_path` dérivé pour chaque document/chunk, permettant à un agent d'ouvrir immédiatement le fichier Git sans `document_list` complet. Enrichissement par jointure graphe rétroactive (aucune ré-ingestion). Outils `document_get`/`document_list` alignés. Précédemment : v3.1.1 (console `/admin` « ⚡ Ingest Jobs »).
**Dernière version** : v3.2.1 (31 août 2026) — `storage_check` et `storage_cleanup` détectent désormais les ontologies S3 devenues orphelines après la suppression d'une mémoire, tout en protégeant les ontologies encore référencées et les mémoires legacy sans `ontology_uri`. Correctif de l'issue [#31](https://github.com/Cloud-Temple/graph-memory/issues/31). Précédemment : v3.2.0 (`source_path` exposé dans la recherche Graph-first).

---

Expand Down Expand Up @@ -353,7 +353,8 @@ Interfaces disponibles :
### Installation des dépendances CLI

```bash
pip install httpx click rich prompt_toolkit mcp
pip install -r requirements.txt -r requirements.lock
pip install prompt_toolkit
```

### Mode Click (scriptable)
Expand Down Expand Up @@ -666,15 +667,24 @@ Ajoutez dans votre configuration MCP :

### Via Python (client MCP)

La v3.2.1 utilise le SDK officiel MCP **2.1.1**. Réinstaller les dépendances de la
CLI avec `pip install -r requirements.txt -r requirements.lock`. Le serveur reste
compatible avec les clients MCP existants ; les noms et arguments des 40 outils
ne changent pas. Docker utilise le même verrouillage des dépendances Python.

```python
from mcp.client.streamable_http import streamablehttp_client
import httpx2
from mcp.client.streamable_http import streamable_http_client
from mcp import ClientSession
import base64

async def exemple():
headers = {"Authorization": "Bearer votre_token"}

async with streamablehttp_client("http://localhost:8070/mcp", headers=headers) as (read, write, _):
async with (
httpx2.AsyncClient(headers=headers, timeout=httpx2.Timeout(30, read=900), trust_env=False) as http,
streamable_http_client("http://localhost:8070/mcp", http_client=http) as (read, write),
):
async with ClientSession(read, write) as session:
await session.initialize()

Expand Down Expand Up @@ -932,4 +942,4 @@ Développé par **[Cloud Temple](https://www.cloud-temple.com)**.

---

*Graph Memory v3.2.0Juin 2026*
*Graph Memory v3.2.1Août 2026*
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.0
3.2.1
87 changes: 87 additions & 0 deletions requirements.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Python 3.11 / Linux. Base : image locale 3.2.1, puis migration mcp==2.1.1.
# Capture avec python -m pip freeze --all : runtime et outils de build figés.
# Docker installe ce fichier ET requirements.txt pour détecter toute divergence.
aiofiles==25.1.0
aiohappyeyeballs==2.6.2
aiohttp==3.14.0
aiosignal==1.4.0
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.13.0
attrs==26.1.0
beautifulsoup4==4.14.3
boto3==1.43.21
botocore==1.43.21
certifi==2026.5.20
cffi==2.0.0
click==8.4.1
cryptography==48.0.0
distro==1.9.0
fastapi==0.136.3
frozenlist==1.8.0
grpcio==1.81.0
h11==0.16.0
h2==4.3.0
hpack==4.1.0
httpcore==1.0.9
httpcore2==2.12.0
httptools==0.8.0
httpx==0.28.1
httpx-sse==0.4.3
httpx2==2.12.0
hyperframe==6.1.0
idna==3.18
jiter==0.15.0
jmespath==1.1.0
jsonschema==4.26.0
jsonschema-specifications==2025.9.1
lxml==6.1.1
markdown-it-py==4.2.0
mcp==2.1.1
mcp-types==2.1.1
mdurl==0.1.2
multidict==6.7.1
neo4j==6.2.0
numpy==2.4.6
openai==2.40.0
opentelemetry-api==1.44.0
pip==26.1.2
portalocker==3.2.0
propcache==0.5.2
protobuf==7.35.0
pycparser==3.0
pydantic==2.13.4
pydantic-settings==2.14.1
pydantic_core==2.46.4
Pygments==2.20.0
PyJWT==2.13.0
pypdf==6.12.2
python-dateutil==2.9.0.post0
python-docx==1.2.0
python-dotenv==1.2.2
python-multipart==0.0.30
pytz==2026.2
PyYAML==6.0.3
qdrant-client==1.18.0
referencing==0.37.0
rich==15.0.0
rpds-py==2026.5.1
s3transfer==0.18.0
setuptools==65.5.1
six==1.17.0
sniffio==1.3.1
soupsieve==2.8.4
sse-starlette==3.4.4
starlette==1.2.1
tenacity==9.1.4
tqdm==4.67.3
truststore==0.10.4
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.7.0
uvicorn==0.48.0
uvloop==0.22.1
watchfiles==1.2.0
websockets==16.0
wheel==0.45.1
yarl==1.24.2
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
# =============================================================================

# === MCP SDK (Streamable HTTP transport) ===
mcp>=1.8.0
mcp==2.1.1

# === Web Framework (pour FastMCP Streamable HTTP) ===
# === Web Framework (pour MCPServer Streamable HTTP) ===
fastapi>=0.100.0
uvicorn[standard]>=0.20.0
starlette>=0.27.0

# === HTTP Client (async) ===
httpx>=0.27.0
httpx2>=2.5.0,<3 # Transport du SDK MCP 2 ; httpx reste utilisé par les autres clients

# === Neo4j Driver ===
neo4j>=5.0.0
Expand Down
63 changes: 22 additions & 41 deletions scripts/cli/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,65 +92,46 @@ async def on_progress(message: str) -> None
"""
import asyncio
import sys
import httpx
import httpx2
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
from mcp.client.streamable_http import streamable_http_client

headers = {"Authorization": f"Bearer {self.token}"}

def _httpx_client_factory(headers=None, timeout=None, auth=None):
return httpx.AsyncClient(
follow_redirects=True,
headers=headers,
timeout=timeout,
auth=auth,
trust_env=False,
)
async def _on_log(params):
if on_progress and params.data:
try:
await on_progress(str(params.data))
except Exception:
pass # Une erreur d'affichage ne doit pas interrompre l'opération.

last_error = None
for attempt in range(1, max_retries + 1):
try:
async with streamablehttp_client(
f"{self.base_url}/mcp",
async with httpx2.AsyncClient(
headers=headers,
timeout=30, # connexion initiale : 30s
sse_read_timeout=900, # attente réponse : 15 min (extraction LLM de gros docs)
httpx_client_factory=_httpx_client_factory,
) as (read, write, _):
async with ClientSession(read, write) as session:
timeout=httpx2.Timeout(30, read=900),
follow_redirects=True,
trust_env=False,
) as http_client, streamable_http_client(
f"{self.base_url}/mcp", http_client=http_client,
) as (read, write):
async with ClientSession(
read, write, logging_callback=_on_log,
log_level="info" if on_progress else None,
) as session:
await session.initialize()

# Capturer les notifications de progression (ctx.info())
# Le SDK MCP expose _received_notification() comme hook surchargeable
if on_progress:
_original_received = session._received_notification

async def _patched_received_notification(notification):
try:
# Le SDK wrappe dans un type union : notification.root
# est le vrai objet (ex: LoggingMessageNotification)
root = getattr(notification, 'root', notification)
params = getattr(root, 'params', None)
if params:
# ctx.info() → LoggingMessageNotification.params.data
msg = getattr(params, 'data', None)
if msg:
await on_progress(str(msg))
except Exception:
pass
# Appeler le handler original
await _original_received(notification)

session._received_notification = _patched_received_notification

result = await session.call_tool(tool_name, args)
# --- Parsing robuste de la réponse MCP ---
# Vérifier si le serveur a renvoyé une erreur
if getattr(result, 'isError', False):
if result.is_error:
error_msg = "Erreur serveur MCP"
if result.content:
error_msg = getattr(result.content[0], 'text', '') or error_msg
return {"status": "error", "message": error_msg}
if result.structured_content is not None:
return result.structured_content
# Extraire le texte du premier bloc de contenu
text = ""
if result.content:
Expand Down
2 changes: 1 addition & 1 deletion scripts/cli/ingest_progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def create_progress_callback(state: dict):
et met à jour l'état de progression.

Les messages proviennent de ctx.info() côté serveur MCP et sont
interceptés via le hook _received_notification du SDK MCP.
reçus via le callback public logging_callback du SDK MCP.

Args:
state: dict créé par create_progress_state()
Expand Down
2 changes: 1 addition & 1 deletion src/mcp_memory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
python -m src.mcp_memory.server --port 8002
"""

__version__ = "3.2.0"
__version__ = "3.2.1"
__author__ = "Cloud Temple"
Loading