diff --git a/BUGFIX_MCP_ERROR_HANDLING.md b/BUGFIX_MCP_ERROR_HANDLING.md new file mode 100644 index 0000000..0f22d06 --- /dev/null +++ b/BUGFIX_MCP_ERROR_HANDLING.md @@ -0,0 +1,127 @@ +# Correction du bug de gestion d'erreur MCP + +## Problème identifié + +L'agent Nabu entrait dans une boucle infinie en essayant d'accéder à une URL Wikipedia inexistante avec l'outil `web-search-web_url_read`. Le problème était dans le fichier `core/agent/mcp.go` à la ligne 68. + +### Symptômes observés dans les logs : +``` +mcphub-1 | [2025-06-14T13:51:11.649Z] [INFO] [web-search] [child] Error: Failed to fetch the URL: Not Found +localagi-1 | time=2025-06-14T13:51:11.649Z level=DEBUG msg="MCP response" response="&{Result:{Meta:map[]} Content:[{Annotated:{Annotations:} Type:text Text:Error: Failed to fetch the URL: Not Found}] IsError:true}" +``` + +### Code problématique : +```go +// Traiter le résultat +textResult := "" +if result.IsError { + return types.ActionResult{}, err // ❌ err est nil ici ! +} +``` + +Quand `result.IsError` était `true`, le code retournait `err` qui était `nil`, ce qui faisait que l'agent ne recevait aucune information sur l'erreur et continuait à essayer la même action. + +## Solution implémentée + +### 1. Correction de la gestion d'erreur +- Extraction du contenu de l'erreur avant de vérifier `result.IsError` +- Retour du message d'erreur comme résultat au lieu de faire échouer l'action +- Ajout de suggestions spécifiques selon le type d'erreur + +### 2. Code corrigé : +```go +// Traiter le résultat +textResult := "" + +// Extraire le texte du résultat selon le format de mark3labs/mcp-go +for _, content := range result.Content { + if textContent, ok := content.(*mcp.TextContent); ok { + textResult += textContent.Text + "\n" + } else { + xlog.Error("Unsupported content type", "type", content) + } +} + +// Si c'est une erreur, retourner le contenu de l'erreur comme résultat +// plutôt que de faire échouer complètement l'action +if result.IsError { + xlog.Error("MCP tool returned error", "tool", m.toolName, "error", textResult) + + // Fournir des suggestions spécifiques selon le type d'erreur + errorMessage := textResult + if strings.Contains(strings.ToLower(textResult), "not found") { + if m.toolName == "web-search-web_url_read" { + errorMessage = "L'URL spécifiée n'a pas pu être trouvée. Essayez plutôt d'utiliser l'outil de recherche web 'web-search-searxng_web_search' pour chercher des informations sur ce sujet." + } else { + errorMessage = "Ressource non trouvée: " + textResult + } + } + + // Retourner le message d'erreur comme résultat pour que l'agent puisse réagir + return types.ActionResult{ + Result: "Erreur: " + errorMessage, + }, nil +} +``` + +### 3. Améliorations apportées : +- ✅ L'agent reçoit maintenant le message d'erreur complet +- ✅ Suggestions intelligentes pour utiliser des outils alternatifs +- ✅ Logging approprié des erreurs MCP +- ✅ Prévention des boucles infinies sur les erreurs + +## Impact attendu + +Avec cette correction, quand l'agent Nabu rencontre une erreur "Not Found" avec `web-search-web_url_read`, il : + +1. Recevra le message d'erreur complet +2. Sera guidé vers l'utilisation de `web-search-searxng_web_search` comme alternative +3. Pourra adapter sa stratégie au lieu de répéter la même action échouée + +## Corrections supplémentaires + +### Problème de parsing des réponses MCP +Après le premier fix, un nouveau problème est apparu : LocalAGI n'arrivait pas à parser correctement les réponses MCP qui utilisent la structure : +```json +{ + "content": [ + { + "type": "text", + "text": "Title: Meteo Montjaux... [contenu complet]" + } + ], + "isError": false +} +``` + +### Solution : Utilisation de `mcp.AsTextContent()` +Le problème était dans la conversion de type à la ligne 71. Au lieu d'utiliser : +```go +if textContent, ok := content.(*mcp.TextContent); ok { +``` + +Il fallait utiliser la fonction appropriée de la bibliothèque `mark3labs/mcp-go` : +```go +if textContent, ok := mcp.AsTextContent(content); ok { +``` + +## Test de la correction + +La correction a été testée avec : +```bash +go build ./core/agent # ✅ Compilation réussie +docker build -f Dockerfile.webui -t localagi-fixed:v2 . # ✅ Image Docker construite +``` + +## Fichiers modifiés + +- `core/agent/mcp.go` : + - Correction de la gestion d'erreur MCP (ligne 68) + - Correction du parsing des réponses MCP (ligne 71) + - Ajout de l'import `strings` pour la détection de type d'erreur + +## Prochaines étapes recommandées + +1. Tester avec un déploiement Docker complet +2. Vérifier que l'agent utilise bien `web-search-searxng_web_search` après cette erreur +3. Surveiller les logs pour confirmer que les boucles infinies sont évitées \ No newline at end of file diff --git a/__env b/__env deleted file mode 100644 index d9f016f..0000000 --- a/__env +++ /dev/null @@ -1,58 +0,0 @@ -LOG_LEVEL=debug -# Modèles à utiliser -MODEL_NAME=gpt-alex -#MULTIMODAL_MODEL=minicpm-v-2_6 -#IMAGE_MODEL=sd-1.5-ggml -#EMBEDDING_MODEL=granite-embedding-107m-multilingual - -STT_ENGINE=STT_ENGINE=whisper - -# For Fast Whisper (GPU recommended) -STT_ENGINE=whisper-alex-fast - -CUDA_VISIBLE_DEVICES=0 -GGML_CUDA_FORCE_MMQ=0 -GGML_CUDA_FORCE_CUBLAS=1 - -# Home Assistant Configuration -HASS_HOST=https://jarvis.carriere.cloud -HASS_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjYjYzMTQwZjc4Njk0ZTdhODFiYTY2OGI4YzM1NWQzMSIsImlhdCI6MTc0OTM4ODkzMCwiZXhwIjoyMDY0NzQ4OTMwfQ.y6zC6fOk_d7COngm4QG-WatC8lQCYfltuvrJSDbZtk8 -HASS_SOCKET_URL=ws://jarvis.carriere.cloud/api/websocket - -# Server Configuration -PORT=3000 -NODE_ENV=production -DEBUG=false -# URLs des services -LOCALAGI_LLM_API_URL=http://localai:8080 -LOCALAGI_LOCALRAG_URL=http://localrecall:8080 - -# Configuration générale -LOCALAGI_TIMEOUT=5m -LOCALAGI_MCP_TIMEOUT=5m -LOCALAGI_STATE_DIR=/pool -LOCALAGI_ENABLE_CONVERSATIONS_LOGGING=false - -# Configuration LocalAI (basée sur votre instance Unraid) -DEBUG=true -MODELS_PATH=/models -THREADS=4 -COQUI_TOS_AGREED=1 -GALLERIES=[{"name":"localai","url":"github:mudler/LocalAI/gallery/index.yaml@master"}] -SINGLE_ACTIVE_BACKEND=false -LOCALAI_SINGLE_ACTIVE_BACKEND=false -PYTHON_GRPC_MAX_WORKERS=12 -LLAMACPP_PARALLEL=6 -PARALLEL_REQUESTS=true -WATCHDOG_IDLE=true -WATCHDOG_BUSY=true -WATCHDOG_IDLE_TIMEOUT=60m -WATCHDOG_BUSY_TIMEOUT=5m -LOCALAI_UPLOAD_LIMIT=256 -DISABLE_AUTODETECT=true -LOW_VRAM=true -MMAP=true -CONTEXT_SIZE=32768 -LOCALAI_P2P=true -LOCALAI_FEDERATED=true -LOCALAI_P2P_LOGLEVEL=info diff --git a/plan_migration_mcp.md b/plan_migration_mcp.md deleted file mode 100644 index 0815cbc..0000000 --- a/plan_migration_mcp.md +++ /dev/null @@ -1,474 +0,0 @@ -# Plan de migration détaillé : metoro-io/mcp-golang vers mark3labs/mcp-go - -Ce document présente un plan de migration détaillé pour remplacer la bibliothèque metoro-io/mcp-golang par mark3labs/mcp-go dans le projet LocalAGI. Le plan est divisé en phases avec des exemples de code concrets pour chaque étape critique. - -## Phase 1 : Préparation et analyse - -### Étape 1.1 : Audit du code existant - -```bash -# Identifier tous les fichiers qui utilisent la bibliothèque metoro-io/mcp-golang -grep -r "github.com/metoro-io/mcp-golang" --include="*.go" . - -# Identifier les fonctionnalités spécifiques utilisées -grep -r "mcp\.Client" --include="*.go" . -grep -r "transport/http" --include="*.go" . -grep -r "transport/stdio" --include="*.go" . -``` - -### Étape 1.2 : Création d'un environnement de test - -```bash -# Créer une branche pour la migration -git checkout -b mcp-migration - -# Installer la nouvelle bibliothèque -go get github.com/mark3labs/mcp-go -``` - -## Phase 2 : Modification des importations et structures - -### Étape 2.1 : Mise à jour des importations - -```go -// Avant -import ( - mcp "github.com/metoro-io/mcp-golang" - "github.com/metoro-io/mcp-golang/transport/http" - stdioTransport "github.com/metoro-io/mcp-golang/transport/stdio" -) - -// Après -import ( - "github.com/mark3labs/mcp-go/mcp" - "github.com/mark3labs/mcp-go/server" -) -``` - -### Étape 2.2 : Adaptation des structures - -```go -// Avant -type mcpAction struct { - mcpClient *mcp.Client - inputSchema ToolInputSchema - toolName string - toolDescription string -} - -// Après -type mcpAction struct { - // Nous devrons déterminer la structure équivalente dans mark3labs/mcp-go - // Basé sur la documentation, cela pourrait ressembler à: - mcpClient *mcp.Client // ou une structure équivalente - toolDefinition *mcp.Tool - toolName string - toolDescription string -} - -// Avant -type ToolInputSchema struct { - Type string `json:"type"` - Properties map[string]interface{} `json:"properties,omitempty"` - Required []string `json:"required,omitempty"` -} - -// Après -// Nous utiliserons directement les structures de schéma fournies par mark3labs/mcp-go -``` - -## Phase 3 : Migration des clients et transports - -### Étape 3.1 : Clients HTTP - -```go -// Avant -transport := http.NewHTTPClientTransport("/mcp") -transport.WithBaseURL(mcpServer.URL) -if mcpServer.Token != "" { - transport.WithHeader("Authorization", "Bearer "+mcpServer.Token) -} -client := mcp.NewClient(transport) - -// Après -// Basé sur les exemples disponibles, nous pourrions avoir besoin de créer un client HTTP personnalisé -// Voici une approche possible: -httpClient := &http.Client{} -// Configurer un client HTTP pour se connecter au serveur MCP -// Note: L'API exacte dépendra de la façon dont mark3labs/mcp-go implémente les clients -``` - -### Étape 3.2 : Clients STDIO - -```go -// Avant -client := stdio.NewClient(a.options.mcpBoxURL) -p, err := client.CreateProcess(a.context, - mcpStdioServer.Cmd, - mcpStdioServer.Args, - mcpStdioServer.Env, - a.Character.Name) -read, writer, err := client.GetProcessIO(p.ID) -transport := stdioTransport.NewStdioServerTransportWithIO(read, writer) -mcpClient := mcp.NewClient(transport) - -// Après -// Basé sur les exemples, mark3labs/mcp-go utilise une approche différente pour STDIO -// Nous devrons adapter notre code pour utiliser l'API de mark3labs/mcp-go -// Exemple possible: -cmd := exec.Command(mcpStdioServer.Cmd, mcpStdioServer.Args...) -cmd.Env = append(os.Environ(), mcpStdioServer.Env...) -// Configurer les pipes stdin/stdout -// Créer un client qui utilise ces pipes -``` - -## Phase 4 : Migration des outils et actions - -### Étape 4.1 : Définition des outils - -```go -// Avant -// Les outils sont définis implicitement via les réponses du serveur MCP -var inputSchema ToolInputSchema -err = json.Unmarshal(dat, &inputSchema) -generatedActions = append(generatedActions, &mcpAction{ - mcpClient: client, - toolName: t.Name, - inputSchema: inputSchema, - toolDescription: desc, -}) - -// Après -// Avec mark3labs/mcp-go, nous pouvons définir les outils plus explicitement -// Exemple basé sur la documentation: -calculatorTool := mcp.NewTool("calculate", - mcp.WithDescription("Perform basic arithmetic operations"), - mcp.WithString("operation", - mcp.Required(), - mcp.Description("The operation to perform"), - mcp.Enum("add", "subtract", "multiply", "divide"), - ), - mcp.WithNumber("x", - mcp.Required(), - mcp.Description("First number"), - ), - mcp.WithNumber("y", - mcp.Required(), - mcp.Description("Second number"), - ), -) -``` - -### Étape 4.2 : Appel des outils - -```go -// Avant -func (m *mcpAction) Run(ctx context.Context, sharedState *types.AgentSharedState, params types.ActionParams) (types.ActionResult, error) { - resp, err := m.mcpClient.CallTool(ctx, m.toolName, params) - if err != nil { - return types.ActionResult{}, err - } - - textResult := "" - for _, c := range resp.Content { - switch c.Type { - case mcp.ContentTypeText: - textResult += c.TextContent.Text + "\n" - // ... - } - } - - return types.ActionResult{ - Result: textResult, - }, nil -} - -// Après -// Basé sur la documentation, l'appel d'outil pourrait ressembler à: -func (m *mcpAction) Run(ctx context.Context, sharedState *types.AgentSharedState, params types.ActionParams) (types.ActionResult, error) { - // Convertir params en format attendu par mark3labs/mcp-go - args := make(map[string]interface{}) - if err := params.Unmarshal(&args); err != nil { - return types.ActionResult{}, err - } - - // Créer une requête d'appel d'outil - request := mcp.CallToolRequest{ - Params: mcp.CallToolParams{ - Name: m.toolName, - Arguments: args, - }, - } - - // Appeler l'outil - // Note: L'API exacte dépendra de la façon dont mark3labs/mcp-go implémente les appels d'outils - result, err := m.mcpClient.CallTool(ctx, request) - if err != nil { - return types.ActionResult{}, err - } - - // Traiter le résultat - textResult := "" - // Extraire le texte du résultat selon le format de mark3labs/mcp-go - - return types.ActionResult{ - Result: textResult, - }, nil -} -``` - -## Phase 5 : Gestion des résultats - -```go -// Avant -textResult := "" -for _, c := range resp.Content { - switch c.Type { - case mcp.ContentTypeText: - textResult += c.TextContent.Text + "\n" - case mcp.ContentTypeImage: - xlog.Error("Image content not supported yet") - case mcp.ContentTypeEmbeddedResource: - xlog.Error("Embedded resource content not supported yet") - } -} - -// Après -// Basé sur la documentation, le traitement des résultats pourrait ressembler à: -textResult := "" -// Supposons que result est de type *mcp.CallToolResult -if result.Error != nil { - return types.ActionResult{}, fmt.Errorf("tool error: %s", result.Error) -} - -// Traiter les différents types de contenu -for _, content := range result.Content { - switch content.Type { - case "text": - textContent, ok := content.Content.(mcp.TextContent) - if ok { - textResult += textContent.Text + "\n" - } - case "image": - xlog.Error("Image content not supported yet") - case "embedded_resource": - xlog.Error("Embedded resource content not supported yet") - } -} -``` - -## Phase 6 : Gestion des sessions - -```go -// Avant -// La gestion des sessions est limitée dans la bibliothèque actuelle - -// Après -// Implémentation d'une gestion de session plus avancée avec mark3labs/mcp-go -type MCPSession struct { - id string - notifChannel chan mcp.JSONRPCNotification - isInitialized bool -} - -// Implémenter l'interface ClientSession -func (s *MCPSession) SessionID() string { - return s.id -} - -func (s *MCPSession) NotificationChannel() chan<- mcp.JSONRPCNotification { - return s.notifChannel -} - -func (s *MCPSession) Initialize() { - s.isInitialized = true -} - -func (s *MCPSession) Initialized() bool { - return s.isInitialized -} - -// Utilisation dans le code -session := &MCPSession{ - id: mcpServer.SessionId, - notifChannel: make(chan mcp.JSONRPCNotification, 10), -} -if err := mcpServer.RegisterSession(context.Background(), session); err != nil { - xlog.Error("Failed to register session", "error", err.Error()) -} -``` - -## Phase 7 : Adaptation de la fonction initMCPActions - -```go -// Avant -func (a *Agent) initMCPActions() error { - a.mcpActions = nil - var err error - generatedActions := types.Actions{} - - // MCP HTTP Servers - for _, mcpServer := range a.options.mcpServers { - transport := http.NewHTTPClientTransport("/mcp") - transport.WithBaseURL(mcpServer.URL) - if mcpServer.Token != "" { - transport.WithHeader("Authorization", "Bearer "+mcpServer.Token) - } - - client := mcp.NewClient(transport) - actions, err := a.addTools(client) - if err != nil { - xlog.Error("Failed to add tools for MCP server", "error", err.Error()) - } - generatedActions = append(generatedActions, actions...) - } - - // MCP STDIO Servers - // ... - - a.mcpActions = generatedActions - return err -} - -// Après -func (a *Agent) initMCPActions() error { - a.mcpActions = nil - var err error - generatedActions := types.Actions{} - - // MCP HTTP Servers - for _, mcpServer := range a.options.mcpServers { - // Créer un client HTTP pour se connecter au serveur MCP - // Note: L'implémentation exacte dépendra de l'API de mark3labs/mcp-go - httpClient := &http.Client{} - // Configurer les headers, l'URL, etc. - - // Initialiser le client - // ... - - // Lister et ajouter les outils - actions, err := a.addTools(client, &mcpServer) - if err != nil { - xlog.Error("Failed to add tools for MCP server", "error", err.Error()) - } - generatedActions = append(generatedActions, actions...) - } - - // MCP STDIO Servers - // Adapter pour utiliser l'API STDIO de mark3labs/mcp-go - // ... - - a.mcpActions = generatedActions - return err -} -``` - -## Phase 8 : Tests et validation - -### Étape 8.1 : Tests unitaires - -```go -// Créer des tests unitaires pour chaque composant migré -func TestMCPClientInitialization(t *testing.T) { - // Tester l'initialisation du client avec mark3labs/mcp-go -} - -func TestToolDefinition(t *testing.T) { - // Tester la définition des outils avec mark3labs/mcp-go -} - -func TestToolExecution(t *testing.T) { - // Tester l'exécution des outils avec mark3labs/mcp-go -} -``` - -### Étape 8.2 : Tests d'intégration - -```go -// Créer des tests d'intégration pour vérifier que tout fonctionne ensemble -func TestMCPServerIntegration(t *testing.T) { - // Tester l'intégration complète avec un serveur MCP -} -``` - -## Phase 9 : Déploiement progressif - -### Étape 9.1 : Déploiement en environnement de test - -```bash -# Déployer la version migrée en environnement de test -docker build -t localagi-mcp-migration:test . -docker run -d --name localagi-test localagi-mcp-migration:test -``` - -### Étape 9.2 : Surveillance et correction - -```bash -# Surveiller les logs pour détecter d'éventuels problèmes -docker logs -f localagi-test - -# Corriger les problèmes identifiés -# ... - -# Redéployer -docker build -t localagi-mcp-migration:test-v2 . -docker run -d --name localagi-test-v2 localagi-mcp-migration:test-v2 -``` - -### Étape 9.3 : Déploiement en production - -```bash -# Une fois les tests validés, déployer en production -docker build -t localagi:latest . -# Déployer selon votre processus habituel -``` - -## Considérations supplémentaires - -### Compatibilité avec les serveurs MCP existants - -Il est important de vérifier que mark3labs/mcp-go est compatible avec les serveurs MCP existants auxquels votre application se connecte. Si des différences de protocole existent, des adaptations supplémentaires pourraient être nécessaires. - -### Documentation - -Documenter tous les changements effectués et les différences de comportement entre les deux bibliothèques. Cela facilitera la maintenance future et aidera les développeurs à comprendre les choix de migration. - -### Formation - -Prévoir une session de formation pour les développeurs afin de les familiariser avec la nouvelle bibliothèque et ses particularités. - -## Diagramme de séquence de la migration - -```mermaid -sequenceDiagram - participant Dev as Développeur - participant Git as Système de contrôle de version - participant Test as Environnement de test - participant Prod as Production - - Dev->>Git: Créer branche de migration - Dev->>Git: Modifier importations - Dev->>Git: Adapter structures - Dev->>Git: Migrer clients HTTP - Dev->>Git: Migrer clients STDIO - Dev->>Git: Migrer définition des outils - Dev->>Git: Migrer appel des outils - Dev->>Git: Migrer gestion des résultats - Dev->>Git: Migrer gestion des sessions - Dev->>Git: Écrire tests unitaires - Dev->>Git: Écrire tests d'intégration - Git->>Test: Déployer en test - Test->>Dev: Retour d'erreurs - Dev->>Git: Corrections - Git->>Test: Redéployer en test - Test->>Dev: Validation - Git->>Prod: Déployer en production -``` - -## Conclusion - -Ce plan de migration détaillé devrait vous guider efficacement à travers le processus de remplacement de metoro-io/mcp-golang par mark3labs/mcp-go. La migration nécessitera des modifications significatives du code existant, mais les avantages potentiels en termes de maintenance, de fonctionnalités et de robustesse justifient cet effort. - -Les principales difficultés résident dans les différences d'API et la façon dont les transports et les outils sont configurés et utilisés. Une approche progressive, avec des tests approfondis à chaque étape, est recommandée pour minimiser les risques. - -N'hésitez pas à adapter ce plan en fonction des spécificités de votre projet et des découvertes faites pendant la migration. \ No newline at end of file