feat(ai): serveur MCP Streamable HTTP + confirmations two-step (#79 phase E)
CI / lint (push) Successful in 1m3s
CI / security (push) Successful in 41s
CI / test (push) Successful in 1m20s
CI / build (push) Successful in 1m19s
CI / e2e (push) Successful in 10m56s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s

This commit is contained in:
2026-09-11 21:28:05 -04:00
parent 83c412b33d
commit 88eecd7671
9 changed files with 984 additions and 17 deletions
+17 -6
View File
@@ -159,14 +159,23 @@ boucle (max N itérations):
(les appels d'outils exigent la réponse complète avant exécution). Confirmations UI en deux temps
(`confirmation` → `confirm`/`confirm_messages`).
### 3.4 Couche 4 — Serveur MCP
### 3.4 Couche 4 — Serveur MCP (livré, phase E)
Nouveau `backend/mcp/server.py` basé sur le SDK MCP Python :
`backend/mcp/server.py` s'appuie sur le SDK MCP Python (`mcp==1.9.4`) :
- **Tools** : enregistrés depuis le registry (mutations + recherche).
- **Resources** : vaults/fichiers en lecture (`vault://name/path.md`).
- **Prompts** : templates (résumer un dossier, générer une note…).
- Transports : **stdio** (local) et **HTTP/SSE** (distant, auth JWT).
- **Tools** : enregistrés depuis le registry (scope `mcp`). Les outils `read` sont exposés
directement ; les outils `write`/`dangerous` le sont via la paire `propose_<tool>` /
`apply_<tool>` (jeton signé, usage unique, TTL).
- **Resources** : `vault://<name>` (vaults accessibles) et `vault://<name>/<path>` (fichiers,
lecture seule, secrets redactés).
- **Prompts** : `summarize-directory`, `generate-note`, `find-related`.
- Transport : **Streamable HTTP** (`/mcp`, SDK `StreamableHTTPSessionManager` en
`json_response=True`), auth `Authorization: Bearer <JWT>` → `get_current_user`. `stdio`
optionnel plus tard.
- Confirmations (E4) : `backend/mcp/confirmations.py` — jeton JWT (`type=mcp_confirmation`)
contenant outil + arguments + utilisateur ; blacklist de JTI persistée pour l'anti-rejeu.
- Le manager de session est démarré **paresseusement** à la première requête, pour fonctionner
aussi bien sous uvicorn que sous le client de test (sans lifespan ASGI).
---
@@ -319,6 +328,8 @@ Voir `docs/ROADMAP.md` (item dédié) pour le détail des activités.
- `backend/services/` — logique métier partagée (vaults, files, search, backups, graph, recent) consommée par les routes et les outils
- `backend/ai_chat.py` — chat completion provider-agnostique avec tool calling et streaming (OpenAI-compat + Gemini)
- `backend/agent/loop.py` — agent loop in-app (multi-étapes, LLM injectable)
- `backend/mcp/server.py` — serveur MCP (Streamable HTTP `/mcp`, tools/resources/prompts)
- `backend/mcp/confirmations.py` — jetons de confirmation signés (two-step, anti-rejeu)
- `backend/bookslm.py`, `backend/bookslm_routes.py` — assistant contextuel (+ endpoint `/agent`)
- `frontend/js/ai.js`, `frontend/js/bookslm.js` — UI IA
- `backend/auth/middleware.py` — permissions
+3 -3
View File
@@ -43,12 +43,12 @@
### 79. Assistant IA — Outils (function calling) & serveur MCP
- **Effort :** 10-15 jours | **Impact :** 🟡
- **Statut :** 🔵 Phase 0 + A2 + B1/B2/B3/B4/B5/B6/B7 + C + D + G livrés (2026-09-11). Détail complet : [features/ai-tools-mcp.md](./features/ai-tools-mcp.md)
- **Statut :** 🔵 Phase 0 + A2 + B1/B2/B3/B4/B5/B6/B7 + C + D + E + G livrés (2026-09-11). Détail complet : [features/ai-tools-mcp.md](./features/ai-tools-mcp.md)
- **Description :** Transformer l'assistant BooksLM en agent (lire, chercher, lister, ouvrir, modifier) via **function calling natif**, puis exposer ObsiGate à des **clients MCP externes** (Claude Desktop, Cursor…). Les deux fronts consomment une **couche d'outils partagée**.
- **Reste à faire :**
- [x] **C.** Catalogue lecture & recherche (vaults, read, backlinks, backups, search, tags)
- [x] **D.** Catalogue mutations (create/edit/append/rename/move/delete) + confirmations two-step
- [ ] **E.** Serveur MCP (Streamable HTTP `/mcp`, auth Bearer JWT, `propose`/`apply`, toggle par vault)
- [x] **E.** Serveur MCP (Streamable HTTP `/mcp`, auth Bearer JWT, `propose`/`apply`, toggle par vault)
- [ ] **F.** Durcissement (rate limiting, redaction secrets, OpenAPI + guide MCP, E2E)
- **Documentation :** [AI_ARCHITECTURE_GUIDE.md](./AI_ARCHITECTURE_GUIDE.md) (architecture, catalogue d'outils, sécurité, phases).
@@ -180,7 +180,7 @@
|---|---|---|
| ✅ Complété | #1 → #59, #61, #63–68, #71, #72, #74–76, #78 | ~82 jours réalisés |
| 🔵 P2 restant | #77 Desktop : signature code (optionnel), wizard 1er lancement (optionnel), 6 tests E2E **manuels** | ~1-2 jours |
| ⚪ P3 restant | #62 Collaboration Yjs (5-7j) · #79 Assistant IA outils + MCP (10-15j, Phase 0 + A2 + B + C + D + G livrés) · #80 Assistant IA UX (livré, en attente vérif) | ~15-22 jours |
| ⚪ P3 restant | #62 Collaboration Yjs (5-7j) · #79 Assistant IA outils + MCP (10-15j, Phase 0 + A2 + B + C + D + E + G livrés) · #80 Assistant IA UX (livré, en attente vérif) | ~15-22 jours |
| ⚪ P4 restant | #69 Mobile éditeur (2-3j) · #70 Sémantique (4-5j) · #73 Sync (6-8j) | 11-16 jours |
| **Total restant** | **6 items + finitions** | **~27-39 jours** |
+12 -8
View File
@@ -1,6 +1,6 @@
# #79 — Assistant IA — Outils (function calling) & serveur MCP
> **Statut :** 🔵 En cours — Phase 0 + A2 + B1/B2/B3/B4/B5/B6/B7 + C + D + G livrés (2026-09-11)
> **Statut :** 🔵 En cours — Phase 0 + A2 + B1/B2/B3/B4/B5/B6/B7 + C + D + E + G livrés (2026-09-11)
> **Effort :** 10-15 jours | **Impact :** 🟡
> **Références :** [Roadmap](../ROADMAP.md) · [Guide d'architecture IA](../AI_ARCHITECTURE_GUIDE.md)
@@ -41,13 +41,17 @@
Toggle par vault `aiDestructiveTools` (défaut activé) pour désactiver les outils `DANGEROUS`.
Tests : `tests/test_tools_mutations.py` (40 tests).
## E. Serveur MCP (2-3 jours)
- [ ] **E1.** `backend/mcp/server.py` (SDK MCP Python) enregistrant les outils depuis le registry
- [ ] **E2.** Mapping des primitives : Tools (mutations + recherche), Resources (vaults/fichiers en lecture `vault://`), Prompts (templates)
- [ ] **E3.** Transport **Streamable HTTP** : endpoint `/mcp` dans FastAPI, auth `Authorization: Bearer <JWT>` → `get_current_user` (décision 2026-09-11). `stdio` optionnel plus tard
- [ ] **E4.** Confirmations : **two-step `propose_*`/`apply_*`** avec token signé (usage unique, TTL) ; élicitation optionnelle si le client l'annonce (décision 2026-09-11)
- [ ] **E5.** Toggle par vault pour désactiver les outils destructifs (défaut : activés)
- [ ] **E6.** Tests : handshake MCP, permissions par vault, mapping des ressources, anti-rejeu du token
## E. Serveur MCP (2-3 jours) — ✅ livré (2026-09-11)
- [x] **E1.** `backend/mcp/server.py` (SDK MCP Python `mcp==1.9.4`) enregistrant les outils depuis le registry
- [x] **E2.** Mapping des primitives : Tools (lecture + `propose_*`/`apply_*`), Resources (`vault://<name>`, `vault://<name>/<path>`), Prompts (`summarize-directory`, `generate-note`, `find-related`)
- [x] **E3.** Transport **Streamable HTTP** : route `/mcp` (matching exact + sous-chemins), auth `Authorization: Bearer <JWT>` → `get_current_user`. `stdio` optionnel plus tard
- [x] **E4.** Confirmations **two-step `propose_*`/`apply_*`** avec token JWT signé (usage unique, TTL `OBSIGATE_MCP_CONFIRMATION_TTL`, défaut 300 s) + blacklist de JTI persistée (`data/mcp_used_tokens.json`, anti-rejeu)
- [x] **E5.** Toggle par vault `aiDestructiveTools` appliqué à la proposition et à l'application
- [x] **E6.** Tests : `tests/test_mcp.py` (13 tests) — handshake `initialize`, `tools/list`/`tools/call`, resources, prompts, permissions par vault, anti-rejeu du token
- **Détail :** `backend/mcp/confirmations.py` (jetons signés + anti-rejeu) ; `backend/mcp/server.py`
(`Server` lowlevel + `StreamableHTTPSessionManager` en `json_response=True`, `McpMount` pour
matcher `/mcp` exact, démarrage paresseux du manager pour les tests). Dépendances ajoutées :
`mcp==1.9.4`, `sse-starlette==2.1.3` (compatibles avec FastAPI 0.110 / starlette 0.37).
## F. Durcissement & documentation (1 jour)
- [ ] **F1.** Rate limiting par token/outil + quotas (`BOOKSLM_MAX_*`)