diff --git a/docs/FLOWDECK_MCP_SERVER_GUIDE.md b/docs/FLOWDECK_MCP_SERVER_GUIDE.md index 659d9d9..0b9b90d 100644 --- a/docs/FLOWDECK_MCP_SERVER_GUIDE.md +++ b/docs/FLOWDECK_MCP_SERVER_GUIDE.md @@ -13,6 +13,9 @@ 1. [Verdict : est-ce une bonne idée ?](#1-verdict) 2. [Ce que FlowDeck a déjà (l'essentiel est fait)](#2-ce-que-flowdeck-a-déjà) 3. [Positionnement : deux rôles complémentaires](#3-positionnement) + - [3.1 Vue générale des interactions](#31-vue-générale-des-interactions) + - [3.2 Workflow d'un échange avec un agent externe](#32-workflow-dun-échange-avec-un-agent-externe) + - [3.3 Les agents internes passent-ils par le MCP ?](#33-les-agents-internes-passent-ils-par-le-mcp-) 4. [Architecture cible](#4-architecture-cible) 5. [Services offerts par le serveur MCP](#5-services-offerts) 6. [Plan d'implémentation pas à pas](#6-plan-dimplémentation) @@ -116,6 +119,109 @@ Deux surfaces agentiques, complémentaires — **ne pas confondre** : Le serveur MCP **ne contient pas de LLM** : il exécute les outils demandés par le client. C'est le client (claude, cursor, openclaw…) qui décide quoi appeler. Le code de raisonnement n'est pas dupliqué. +### 3.1 Vue générale des interactions {#31-vue-générale-des-interactions} + +Deux portes d'entrée distinctes, **un seul socle commun** : + +``` + CHEMIN A — AGENTS EXTERNES CHEMIN B — AGENT INTERNE + (serveur MCP, NOUVEAU) (UI existante, INCHANGÉE) + + Claude Desktop ─┐ Utilisateur (navigateur) + Cursor ├─ MCP stdio ───┐ │ + OpenClaw/Hermes ├─ MCP HTTP ────┼──┐ │ chat 🤖 + @mentions + npx mcp-remote ─┘ │ │ ▼ + ┌───────────▼──▼──────────────┐ ┌─────────────────────────┐ + │ Serveur MCP │ │ AgentEngine (ReAct) │ + │ app/mcp_server.py │ │ boucle raisonnement↔act │ + │ auth token `fd_*` │ │ streaming SSE vers l'UI │ + └───────────┬─────────────────┘ └────────────┬────────────┘ + │ calls d'outils │ function calls + ┌───────────▼──────────────────────────────────▼─────────────┐ + │ ToolRegistry — 22 outils │ + │ search · read · create · update · delete · gitea · … │ + └───────────┬────────────────────────────────────────────────┘ + │ + ┌───────────▼───────────────┐ + │ PermissionManager │ ← mêmes ACL pour les deux chemins + │ agent_actions (audit+undo)│ + └───────────┬───────────────┘ + │ + ┌───────────▼───────────────┐ + │ SQLite · API Gitea/GitHub │ + └───────────────────────────┘ +``` + +**À retenir :** les agents externes (chemin A) et l'agent interne (chemin B) ne se rencontrent +jamais « en vol » — ils **convergent** sur le même `ToolRegistry`, passent par les mêmes +`PermissionManager` et le même journal `agent_actions`. Une action faite par Claude Desktop arrive +au même endroit et avec les mêmes règles qu'une action faite par l'agent intégré au navigateur. + +### 3.2 Workflow d'un échange avec un agent externe {#32-workflow-dun-échange-avec-un-agent-externe} + +Exemple réel : un utilisateur demande à Claude Desktop *« crée une collection Sprint 27 avec les +issues ouvertes du dépôt bruno/flowdeck »*. + +``` + Claude Desktop Serveur MCP ToolRegistry / Perms / SQLite + │ │ │ + │ 1. initialize (handshake) │ │ + │─────────────────────────────▶│ │ + │ 2. tools/list │ │ + │─────────────────────────────▶│ │ + │ ◀── 22 outils + schémas │ │ + │ (JSON Schema exacts) │ │ + │ │ │ + │ 3. tools/call │ │ + │ "sync_gitea" │ │ + │ {owner:"bruno", │ │ + │ repo:"flowdeck"} │ │ + │─────────────────────────────▶│ 4. résolution user_id │ + │ │ (Bearer fd_* → users.id) │ + │ │ 5. PermissionManager │ + │ │ assert_can(...) │ + │ │ 6. execute() → GET issues │ + │ │ Gitea + INSERT collection│ + │ │ 7. journal agent_actions │ + │ │ (payload + undo snapshot)│ + │ ◀── résultat JSON │ │ + │ {status:"success", │ │ + │ data:{collection_id, │ │ + │ issues:N}} │ │ + │ │ │ + │ 8. tools/call "create_page" │ (même séquence 4→7 : │ + │ … contenu du CR… │ perms → execute → audit) │ +``` + +Le client MCP orchestre : il décide seul d'enchaîner `sync_gitea` puis `create_page` (ou +`create_document`, `create_gitea_issue`…). Le serveur ne fait que **vérifier, exécuter, auditer** — +l'équivalent de ce que fait `AgentEngine` côté interne, mais sans LLM et sans streaming UI. + +### 3.3 Les agents internes passent-ils par le MCP ? {#33-les-agents-internes-passent-ils-par-le-mcp-} + +**Non. L'agent interne continue d'appeler directement le `ToolRegistry`** — et c'est une décision +d'architecture, pas un oubli. Le serveur MCP est la porte d'entrée des agents **externes uniquement**. + +| Critère | Agent interne → ToolRegistry (direct) | Agent interne → serveur MCP (contournement) | +|---|---|---| +| Streaming SSE (raisonnement + actions live) | événements natifs | **perdu** — le MCP est atomique requête/réponse | +| Contexte (user, workspace, @mentions, page ouverte) | déjà en mémoire dans `AgentEngine` | à re-transmettre à chaque appel | +| Mode approval interactif (`confirm`) | fonctionne (dialogue dans l'UI) | impossible — pas d'humain pour confirmer | +| Surcharge | aucune | sérialisation JSON-RPC + re-auth à chaque outil | +| Bénéfice | — | **aucun** : mêmes outils, mêmes permissions, même base | + +Un mauvais aiguillage (agent interne → MCP → ToolRegistry) créerait une boucle hermétique : +l'agent interne n'est pas un « agent externe », il vit **dans** FlowDeck et possède déjà tout ce +dont le MCP aurait besoin d'être re-sécurisé. Réutiliser le serveur MCP en interne reviendrait à +faire transiter des appels locaux par un protocole réseau sans protection supplémentaire. + +**Ce que ça ne change pas :** les deux chemins respectent exactement les mêmes règles +(permissions, audit, rollback), donc un agent externe ne peut ni voir ni modifier plus de choses +que l'agent interne ou l'utilisateur lui-même. Et si un jour l'agent interne doit *consommer* +des serveurs MCP externes (rôle client, façon « External Agents » de Notion), c'est une +fonctionnalité séparée — documentée dans `Guide_Complet_Notion_AI.md` — qui n'entre pas en conflit +avec le serveur décrit ici. + --- ## 4. Architecture cible {#4-architecture-cible}