--- title: IA Router, Free Text et MCP Confirmation url: https://blog.guigpap.com/fr/workflows/ia-router-mcp/ url_md: https://blog.guigpap.com/fr/workflows/ia-router-mcp.md category: automation date: '2026-05-19' maturite: production techno: - n8n - telegram - claude application: - automation - ai --- # IA Router, Free Text et MCP Confirmation > Detection d'intent multi-providers, dispatch des reponses libres et confirmation Telegram des outils MCP critiques ## 1. Quoi ? — Definition et contexte Trois sub-workflows N8N forment la couche d'aiguillage intelligent du bot Telegram. L'**IA Router** detecte l'intent d'un message texte libre et choisit le service cible. Le **Free Text Handler** dispatche les reponses textuelles a des questions interactives ouvertes par l'API cli-ollama. Le **MCP Confirmation Handler** declenche un workflow d'approbation Telegram avant l'execution d'un outil MCP critique (creation, mise a jour, suppression de workflow N8N). Ces trois workflows partagent une meme philosophie : ils ne portent pas de logique metier propre, ils orientent le flux principal vers le bon handler en fonction du contexte. > **Note - MCP, Model Context Protocol** > > Le **Model Context Protocol** est un standard introduit par Anthropic qui permet a un LLM d'appeler des outils externes via une interface normalisee. Dans cette infrastructure, le serveur MCP `n8n-local` expose 20 outils de gestion N8N (list, get, create, update, delete workflows) que l'agent Codex peut invoquer pendant une conversation. ### Place dans le pipeline Telegram | Workflow | ID | Type | Declencheur | |----------|-----|------|-------------| | **IA Router - Intent Detection** | `HWryf7aJkZa5FuUA` | wait-for-result | Execute Workflow depuis Orchestrateur | | **Telegram Free Text Handler** | `xwGcEOpzHkvx0HTR` | fire-and-forget | Execute Workflow depuis Orchestrateur | | **MCP Confirmation Handler** | `lyDYsxzTrDzZrfJk` | webhook | POST interne `/webhook/mcp-confirmation` | Aucun de ces workflows n'est appele directement par Telegram : ils sont des points d'entree internes que l'Orchestrateur ou la gateway cli-ollama declenchent au moment opportun. --- ## 2. Pourquoi ? — Enjeux et motivations Sans cette couche d'aiguillage, l'Orchestrateur devrait porter toute la logique de detection d'intent et de validation des actions sensibles. La taille du workflow exploserait, et un changement de strategie (ajout d'un provider AI, modification de la politique de confirmation) impliquerait de toucher au cœur du systeme central. ### Problemes resolus | Probleme | Sans cette couche | Avec cette couche | |----------|-------------------|-------------------| | **Detection d'intent rigide** | Pattern matching sur mots-cles uniquement | LLM avec fallback chain et regex en derniere ligne | | **Surcharge des providers** | Appel direct sans verification de quota | `/api/usage/summary` pre-calcule le provider disponible | | **Reponses libres orphelines** | Pas de lien entre question et reponse free-text | Mapping `question_mappings` lie chaque message attendu | | **MCP critique non controle** | L'agent execute en silence des outils destructifs | Confirmation Telegram obligatoire avec timeout 90s | | **Callback Telegram > 64 bytes** | Erreur API silencieuse | `mcp_approve_{id}` / `mcp_reject_{id}` (39 chars) sous la limite | ### Choix d'architecture Pourquoi separer ces trois workflows plutot que les inliner dans l'Orchestrateur ? | Approche | Avantage | Inconvenient | |----------|----------|--------------| | **Inline** | Pas d'overhead Execute Workflow | Orchestrateur > 200 nodes, edition fragile | | **Sub-workflows extraits** | Refactoring isole, evolution independante | Surface d'API entre parent et sub a maintenir | Le refactoring #273 a explicitement extrait le Free Text Handler comme SW-8, et le #295 a livre le MCP Confirmation Handler comme webhook independant pour decoupler la gateway cli-ollama du graph principal. --- ## 3. Comment ? — Mise en oeuvre technique ### Composants | Workflow | ID | Nodes | Role | |----------|-----|-------|------| | IA Router - Intent Detection | `HWryf7aJkZa5FuUA` | 13 | Detection d'intent multi-providers avec fallback | | Telegram Free Text Handler | `xwGcEOpzHkvx0HTR` | 10 | Dispatch reponse libre vers `/api/questions/{id}/answer` | | MCP Confirmation Handler | `lyDYsxzTrDzZrfJk` | 3 | Webhook + format + Telegram send avec inline keyboard | ### Cycle de vie d'un message ```mermaid flowchart TD Msg["Message Telegram"] Orch["Orchestrateur · 124 nodes"] CheckConv{"Conversation active ?"} CheckAwait{"En attente reponse texte ?"} subgraph IA["IA Router · 13 nodes"] direction TB Usage["GET /api/usage/summary"] Route["Switch fallback_chain[0]"] Codex["Codex · codex-yolo"] Gemini["Gemini · gemini-flash-yolo"] Keyword["Keyword regex routing"] Parse["Parse JSON response"] end subgraph FT["Free Text Handler · 10 nodes"] direction TB Lookup["DT question_mappings"] Post["POST /api/questions/{id}/answer"] Cleanup["Edit message + delete DT row"] end subgraph Conv["Conversation Agent"] direction TB Agent["Detection blocs tool/plan/mcp"] MCPCall["Appel outil MCP"] end subgraph MCP["MCP Confirmation Handler · 3 nodes"] direction TB Webhook["POST /webhook/mcp-confirmation"] Format["Format message + callbacks"] Send["Send Telegram + [Approuver] [Rejeter]"] end Cb{"Click bouton"} CB11["SW-11 Conv Callback Handler"] Gateway["mcp-gateway:3001/confirm/{id}/respond"] Tool["Tool execute ou rejet"] Msg --> Orch --> CheckAwait CheckAwait -->|oui| FT --> Post --> Cleanup CheckAwait -->|non| CheckConv CheckConv -->|oui| Conv --> Agent --> MCPCall CheckConv -->|non| IA --> Usage --> Route Route --> Codex --> Parse Route --> Gemini --> Parse Route --> Keyword --> Parse Parse --> Orch MCPCall -. outil critique .-> MCP --> Webhook --> Format --> Send Send --> Cb --> CB11 --> Gateway --> Tool ``` ### IA Router : fallback chain Le workflow commence par interroger `GET http://cli-ollama:11434/api/usage/summary` qui retourne un `fallback_chain` pre-calcule en fonction des quotas et de la disponibilite de chaque provider : ```json { "can_use": true, "fallback_chain": ["codex", "gemini"], "providers": { "codex": { "available": true, "can_use": true }, "gemini": { "available": true, "can_use": true } } } ``` Le node Switch `Route by Availability` lit `fallback_chain[0]` et bascule vers la branche correspondante : | Branche | Provider | Modele | Confidence typique | |---------|----------|--------|--------------------| | `codex` | OpenAI Codex via cli-ollama | `codex-yolo` | 0.85 - 0.95 | | `gemini` | Google Gemini via cli-ollama | `gemini-flash-yolo` | 0.80 - 0.90 | | `keyword` | Regex fallback (pas de LLM) | — | 0.6 - 0.7 | Chaque branche LLM appelle `POST /api/generate` avec un prompt qui force une reponse JSON stricte de la forme `{"intent": ..., "service": ..., "action": ..., "params": {...}, "confidence": ...}`. Le node Parse Response extrait le JSON via regex, applique des valeurs par defaut, et retourne le tout au parent. > **Tip - Le suffixe -yolo** > > Le suffixe `-yolo` sur le nom du modele est obligatoire dans cette infrastructure. Sans lui, le CLI sous-jacent est lance en mode interactif (approval mode) et le HTTP Request timeout au bout de 120 secondes. Le suffixe court-circuite l'approval mode pour les calls automatises. ### Free Text Handler : du message a l'API Quand une question interactive est posee par cli-ollama (par exemple : "quel est le titre du nouveau workflow ?"), une ligne est inseree dans la Data Table `question_awaiting_text` avec le `qid_short`. Au message suivant, l'Orchestrateur detecte cet etat et delegue au SW-8 : ``` Execute Workflow Trigger -> Handle Free Text Answer (extrait qid_short, chat_id, free_text) -> Load Question for Answer (DT question_mappings by qid_short) -> POST Free Text Answer (cli-ollama /api/questions/{id}/answer) -> Cleanup Messages (prepare message_ids pour edit) -> Edit Header Free Text (edit message original avec reponse) -> Loop Over Items [0] -> Send Confirmation "Votre reponse a ete transmise" [1] -> Delete Awaiting (DT question_awaiting_text) -> Delete Question Mapping (DT question_mappings) ``` Deux Data Tables sont impliquees : `question_awaiting_text` (`YYEsLEk9s3XDbo89`) stocke les inputs en attente, `question_mappings` (`mXrPedR5PKYCUSpo`) lie un `qid_short` au `message_id` Telegram pour permettre l'edition retroactive du message contenant la question. ### MCP Confirmation Handler : approbation des outils critiques La gateway MCP (`mcp-gateway:3001`) maintient une liste de tools consideres comme critiques (toute operation d'ecriture sur N8N : create, update, delete, activate, deactivate). Quand Codex CLI invoque un de ces tools pendant une conversation, la gateway suspend l'execution, genere un `confirmation_id` (12 chars hex via `secrets.token_hex(6)`), et POST le payload suivant sur `/webhook/mcp-confirmation` : ```json { "type": "mcp_confirmation", "confirmation_id": "mcp_a1b2c3d4e5f6", "session_id": "tg_5883063462_1234567890", "tool_name": "n8n_create_workflow", "arguments_summary": "{\"name\": \"Test Workflow\", ...}", "chatId": "5883063462", "callback_url": "http://mcp-gateway:3001/confirm/mcp_a1b2c3d4e5f6/respond", "timeout_seconds": 90 } ``` Le workflow comporte trois nodes : 1. **Webhook Trigger** — Recoit le POST en `responseMode: immediately` (200 OK direct) 2. **Format Confirmation Message** (Code) — Construit le texte HTML et les callbacks `mcp_approve_{id}` / `mcp_reject_{id}` 3. **Send Confirmation** (Telegram sendMessage) — Envoie le message avec inline keyboard 2 boutons ### Convention de callback MCP | Pattern | Sens | |---------|------| | `mcp_approve_{confirmation_id}` | Approuve l'execution de l'outil critique | | `mcp_reject_{confirmation_id}` | Rejette l'execution | La longueur totale d'un callback `mcp_approve_mcp_a1b2c3d4e5f6` est de 39 caracteres, bien sous la limite Telegram de 64 octets. Le `confirmation_id` issu de `secrets.token_hex(6)` reste non-devinable, ce qui empeche un attaquant qui aurait acces a la conversation de forger un callback pour un autre `confirmation_id`. Les callbacks transitent par le chemin Telegram normal : Orchestrateur -> Callback Router -> SW-11 (Conversation Callback Handler, `e0bLff6av97daYvi`, 87 nodes). Le SW-11 detecte le prefixe `mcp_`, route vers `Parse MCP Action` qui distingue `confirm` (les approve/reject) des autres actions MCP. Pour `confirm`, deux nodes sont ajoutes : - **Call MCP Confirm** (HTTP Request) — POST sur `http://mcp-gateway:3001/confirm/{confirmation_id}/respond` avec body `{"approved": true|false}`, header `Authorization: Bearer {{ $env.N8N_MCP_AUTH_TOKEN }}` - **Edit Confirm Result** (Telegram editMessageText) — Remplace le message original par "Outil MCP approuve." ou "Outil MCP rejete." > **Danger - Timeout 90 secondes** > > La gateway MCP detient un `asyncio.Event` cote Python qui attend la reponse N8N. Si personne ne clique dans les 90 secondes, l'event timeout, la gateway rejette automatiquement l'execution, et Codex CLI recoit une erreur `-32001` (Action denied by user). Aucune action critique ne peut s'executer sans validation humaine explicite. ### MCP per-conversation (#294) Chaque conversation porte une config MCP individuelle stockee dans la Data Table `conversations` (colonne `mcp_config` JSON string). La commande `/mcp` ouvre un menu paginé (SW-11) qui permet d'activer/desactiver chaque outil. Le flag derive `mcp_enabled` est calcule a chaque tour par l'Orchestrateur : - Si aucun outil n'est active : `mcp_enabled = false` -> Codex CLI est lance avec `-c 'mcp_servers={}'` (aucun serveur MCP charge) - Si au moins un outil est active : `mcp_enabled = true` + `allowed_tools` liste blanche -> Codex peut appeler les outils, mais cli-ollama filtre les appels non autorises (403 si tool hors liste) Cette configuration par conversation permet d'avoir une conversation "lecture seule" sur N8N (list_workflows uniquement) et une autre conversation "admin" sur la meme infrastructure, sans melanger les permissions. --- ## 4. Et si ? — Perspectives et limites ### Limites actuelles | Limite | Impact | Mitigation | |--------|--------|------------| | **Latence IA Router 2-5s** | Premier appel du chemin pour chaque message libre | Acceptable, masquee par latence Telegram | | **Keyword fallback approximatif** | Confidence 0.6 - 0.7 si aucun LLM dispo | Patterns regex larges, route vers `general` en cas de doute | | **MCP timeout fixe 90s** | Pas de configuration par tool | Suffisant pour confirmation humaine en mobilite | | **Free Text non-contextuel** | Pas de validation cote N8N du format attendu | cli-ollama valide en aval, erreur visible si malforme | ### Scenarios d'evolution **Si besoin de plus de providers LLM** : - Ajouter un case dans le Switch `Route by Availability` - Ajouter un node Prepare Prompt + Call Provider + Parse Response - Mettre a jour `/api/usage/summary` pour exposer le nouveau provider dans `fallback_chain` **Si besoin de pre-approbation MCP** : - Ajouter un mode "whitelisted user" dans la gateway qui bypass la confirmation pour des outils prevalides - Ou ajouter une politique par conversation (auto-approuver si `mcp_auto_approve = true`) **Si besoin de mesurer la precision de l'IA Router** : - Logger les decisions dans une Data Table `intent_decisions` (text, predicted_service, actual_service apres feedback) - Calculer la precision hebdomadaire via un workflow scheduled --- ## Pages liees ### Workflows - [Telegram Orchestrator](/fr/workflows/telegram-orchestrator/) — Hub central qui appelle l'IA Router et le Free Text Handler - [Systeme Conversationnel](/fr/workflows/systeme-conversationnel/) — Conversations multi-tours qui declenchent les confirmations MCP - [Service Handlers](/fr/workflows/service-handlers/) — Handlers Docker, Odoo, N8N, General appeles apres l'IA Router - [Question Hub](/fr/workflows/question-hub/) — Questions interactives dont les reponses transitent par le Free Text Handler ### Infrastructure - [AI Stack](/fr/infrastructure/ai-stack/) — cli-ollama, MCP gateway, Codex CLI ### Reference - [Glossaire](/fr/reference/glossary/) — MCP, Intent, Callback, Sub-workflow ## Metadonnees agent - Cet article est issu du blog GuiGPaP Lab. - Contexte global du blog: https://blog.guigpap.com/llms.txt - Contact auteur: https://odoo.guigpap.com/mon-cv - Licence: CC-BY-SA 4.0