Aller au contenu

Cycle de vie MCP à l'exécution

Ce document décrit comment les serveurs MCP sont découverts, connectés, exposés en tant qu’outils, rafraîchis et arrêtés dans le runtime de l’agent de codage.

  1. Le démarrage du SDK appelle discoverAndLoadMCPTools() (sauf si MCP est désactivé).
  2. La découverte (loadAllMCPConfigs) résout les configurations de serveurs MCP à partir des sources de capacités, filtre les entrées désactivées/projet/Exa, et préserve les métadonnées de source.
  3. La phase de connexion du gestionnaire (MCPManager.connectServers) lance la connexion par serveur + tools/list en parallèle.
  4. La porte de démarrage rapide attend jusqu’à 250 ms, puis peut retourner :
    • des MCPTool entièrement chargés,
    • des échecs par serveur,
    • ou des DeferredMCPTool mis en cache pour les serveurs encore en attente.
  5. Le câblage du SDK fusionne les outils MCP dans le registre d’outils du runtime pour la session.
  6. La session active peut rafraîchir les outils MCP via les flux /mcp (disconnectAll + redécouverte + session.refreshMCPTools).
  7. L’arrêt se produit lorsque les appelants invoquent disconnectServer/disconnectAll ; le gestionnaire supprime également les enregistrements d’outils MCP pour les serveurs déconnectés.

createAgentSession() dans src/sdk.ts effectue le démarrage MCP lorsque enableMCP est vrai (par défaut) :

  • appelle discoverAndLoadMCPTools(cwd, { ... }),
  • transmet authStorage, le stockage de cache et le paramètre mcp.enableProjectConfig,
  • définit toujours filterExa: true,
  • journalise les erreurs de chargement/connexion par serveur,
  • stocke le gestionnaire retourné dans toolSession.mcpManager et le résultat de session.

Si enableMCP est faux, la découverte MCP est entièrement ignorée.

loadAllMCPConfigs() (src/mcp/config.ts) charge les éléments canoniques de serveurs MCP via la découverte de capacités, puis les convertit en MCPServerConfig legacy.

Comportement de filtrage :

  • enableProjectConfig: false supprime les entrées au niveau projet (_source.level === "project").
  • Les serveurs avec enabled: false sont ignorés avant les tentatives de connexion.
  • Les serveurs Exa sont filtrés par défaut et les clés API sont extraites pour l’intégration native de l’outil Exa.

Le résultat inclut à la fois configs et sources (métadonnées utilisées ultérieurement pour l’étiquetage des fournisseurs).

Comportement en cas d’échec au niveau de la découverte

Section intitulée « Comportement en cas d’échec au niveau de la découverte »

discoverAndLoadMCPTools() distingue deux classes d’échec :

  • Échec critique de la découverte (exception provenant de manager.discoverAndConnect, typiquement de la découverte de configuration) : retourne un ensemble d’outils vide et une erreur synthétique { path: ".mcp.json", error }.
  • Échec d’exécution/connexion par serveur : le gestionnaire retourne un succès partiel avec une map errors ; les autres serveurs continuent.

Ainsi, le démarrage ne fait pas échouer l’ensemble de la session de l’agent lorsque des serveurs MCP individuels échouent.

MCPManager suit le cycle de vie à l’exécution avec des registres séparés :

  • #connections: Map<string, MCPServerConnection> — serveurs entièrement connectés.
  • #pendingConnections: Map<string, Promise<MCPServerConnection>> — négociation en cours.
  • #pendingToolLoads: Map<string, Promise<{ connection, serverTools }>> — connectés mais outils encore en chargement.
  • #tools: CustomTool[] — vue actuelle des outils MCP exposée aux appelants.
  • #sources: Map<string, SourceMeta> — métadonnées fournisseur/source même avant que la connexion ne soit terminée.

getConnectionStatus(name) dérive le statut à partir de ces maps :

  • connected si présent dans #connections,
  • connecting si en attente de connexion ou de chargement d’outils,
  • disconnected sinon.

Établissement de la connexion et chronologie du démarrage

Section intitulée « Établissement de la connexion et chronologie du démarrage »

Pour chaque serveur découvert dans connectServers() :

  1. stocker/mettre à jour les métadonnées de source,
  2. ignorer si déjà connecté/en attente,
  3. valider les champs de transport (validateServerConfig),
  4. résoudre les substitutions d’authentification/shell (#resolveAuthConfig),
  5. appeler connectToServer(name, resolvedConfig),
  6. appeler listTools(connection),
  7. mettre en cache les définitions d’outils (MCPToolCache.set) au mieux.

Comportement de connectToServer() (src/mcp/client.ts) :

  • crée un transport stdio ou HTTP/SSE,
  • effectue initialize MCP + notifications/initialized,
  • utilise un délai d’expiration (config.timeout ou 30 s par défaut),
  • ferme le transport en cas d’échec de l’initialisation.

Porte de démarrage rapide + solution de repli différée

Section intitulée « Porte de démarrage rapide + solution de repli différée »

connectServers() attend une course entre :

  • la résolution de toutes les tâches de connexion/chargement d’outils, et
  • STARTUP_TIMEOUT_MS = 250.

Après 250 ms :

  • les tâches réussies deviennent des MCPTool actifs,
  • les tâches rejetées produisent des erreurs par serveur,
  • les tâches encore en attente :
    • utilisent les définitions d’outils mises en cache si disponibles (MCPToolCache.get) pour créer des DeferredMCPTool,
    • sinon bloquent jusqu’à ce que ces tâches en attente soient résolues.

Il s’agit d’un modèle de démarrage hybride : retour rapide lorsque le cache est disponible, attente de correction lorsque le cache ne l’est pas.

Chaque toolsPromise en attente a également une continuation en arrière-plan qui finit par :

  • remplacer la tranche d’outils de ce serveur dans l’état du gestionnaire via #replaceServerTools,
  • écrire le cache,
  • journaliser les échecs tardifs uniquement après le démarrage (allowBackgroundLogging).

Exposition des outils et disponibilité en session active

Section intitulée « Exposition des outils et disponibilité en session active »

discoverAndLoadMCPTools() convertit les outils du gestionnaire en LoadedCustomTool[] et décore les chemins (mcp:<server> via <providerName> lorsque connu).

createAgentSession() pousse ensuite ces outils dans customTools, qui sont encapsulés et ajoutés au registre d’outils du runtime avec des noms comme mcp_<server>_<tool>.

  • MCPTool appelle les outils via une MCPServerConnection déjà connectée.
  • DeferredMCPTool attend waitForConnection(server) avant d’appeler ; cela permet aux outils mis en cache d’exister avant que la connexion ne soit prête.

Les deux retournent une sortie d’outil structurée et convertissent les erreurs de transport/outil en contenu d’outil MCP error: ... (l’abandon reste un abandon).

Chemins de rafraîchissement/rechargement (démarrage vs rechargement en direct)

Section intitulée « Chemins de rafraîchissement/rechargement (démarrage vs rechargement en direct) »
  • découverte/chargement unique dans sdk.ts,
  • les outils sont enregistrés dans le registre d’outils de la session initiale.

Le chemin /mcp reload (src/modes/controllers/mcp-command-controller.ts) effectue :

  1. mcpManager.disconnectAll(),
  2. mcpManager.discoverAndConnect(),
  3. session.refreshMCPTools(mcpManager.getTools()).

session.refreshMCPTools() (src/session/agent-session.ts) supprime tous les outils mcp_, ré-encapsule les derniers outils MCP et réactive l’ensemble d’outils afin que les modifications MCP s’appliquent sans redémarrer la session.

Il existe également un chemin de suivi pour les connexions tardives : après avoir attendu un serveur spécifique, si le statut devient connected, il ré-exécute session.refreshMCPTools(...) afin que les outils nouvellement disponibles soient reliés dans la session.

Santé, reconnexion et comportement en cas d’échec partiel

Section intitulée « Santé, reconnexion et comportement en cas d’échec partiel »

Le comportement actuel du runtime est intentionnellement minimal :

  • Pas de moniteur de santé autonome dans le gestionnaire/client.
  • Pas de boucle de reconnexion automatique lorsqu’un transport se déconnecte.
  • Le gestionnaire ne s’abonne pas aux événements onClose/onError du transport ; le statut est piloté par le registre.
  • La reconnexion est explicite : flux de rechargement ou invocation directe de connectServers().

En pratique :

  • l’échec d’un serveur ne supprime pas les outils des serveurs sains,
  • les échecs de connexion/listing sont isolés par serveur,
  • le cache d’outils et les mises à jour en arrière-plan fonctionnent au mieux (avertissements/erreurs journalisés, pas d’arrêt brutal).

disconnectServer(name) :

  • supprime les entrées en attente/métadonnées de source,
  • ferme le transport si connecté,
  • supprime les outils mcp_ de ce serveur de l’état du gestionnaire.

disconnectAll() :

  • ferme tous les transports actifs avec Promise.allSettled,
  • vide les maps en attente, les sources, les connexions et la liste d’outils du gestionnaire.

Dans le câblage actuel, l’arrêt explicite est utilisé dans les flux de commandes MCP (pour rechargement/suppression/désactivation). Il n’y a pas de hook de disposition automatique du gestionnaire séparé dans le chemin de démarrage lui-même ; les appelants sont responsables d’invoquer les méthodes de déconnexion du gestionnaire lorsqu’ils ont besoin d’un arrêt MCP déterministe.

ScénarioComportementÉchec critique vs au mieux
La découverte lève une exception (chemin de chargement capacité/config)Le chargeur retourne des outils vides + erreur synthétique .mcp.jsonDémarrage de session au mieux
Configuration de serveur invalideServeur ignoré avec entrée d’erreur de validationAu mieux par serveur
Délai de connexion dépassé/échec d’initialisationErreur du serveur enregistrée ; les autres continuentAu mieux par serveur
tools/list encore en attente au démarrage avec cache disponibleOutils différés retournés immédiatementDémarrage rapide au mieux
tools/list encore en attente au démarrage sans cacheLe démarrage attend la résolution des tâches en attenteAttente stricte pour la correction
Échec tardif du chargement d’outils en arrière-planJournalisé après la porte de démarrageJournalisation au mieux
Transport interrompu à l’exécutionPas de reconnexion automatique ; les appels futurs échouent jusqu’à reconnexion/rechargementRécupération au mieux via action manuelle

src/mcp/index.ts ré-exporte les API du chargeur/gestionnaire/client pour les appelants externes. src/sdk.ts expose discoverMCPServers() comme un wrapper de commodité retournant la même forme de résultat du chargeur.