Le Menu MCP : Comment l’IA Découvre et Utilise vos Outils
Vous avez créé votre premier outil readFile dans l’article précédent. Félicitations ! Mais imaginez que vous arriviez dans un restaurant sans menu. Comment sauriez-vous ce qui est disponible ? C’est exactement le problème que résout le système de découverte du MCP. Aujourd’hui, nous allons implémenter le “menu” complet qui permet à une IA de découvrir automatiquement tous vos outils et de les utiliser intelligemment.
Introduction
Dans mes 15 ans de développement d’API, j’ai vu beaucoup de systèmes d’intégration. Mais le MCP a quelque chose d’élégant : l’auto-découverte. Plutôt que de coder en dur chaque intégration, l’IA interroge votre serveur pour découvrir ce qu’il peut faire. C’est comme si votre API pouvait se présenter elle-même.
Cette approche change tout. Au lieu d’avoir des connecteurs spécifiques pour chaque IA, vous créez un standard que toutes les IA compatibles MCP peuvent comprendre. Une fois que vous maîtriserez ce système, vous pourrez exposer des dizaines d’outils sans jamais modifier le code de l’IA.
Rappel : Le Parcours Complet d’une Requête
Avant de coder, visualisons le parcours complet d’une interaction entre une IA et votre serveur MCP. C’est crucial pour comprendre où se situe le système de découverte.
Phase 1 : Découverte (ce que nous allons coder aujourd’hui)
Vous : “Claude, liste les fichiers de mon dossier projets”
Claude : “Je ne connais pas encore les outils de ce serveur. Laisse-moi les découvrir…”
Claude → Serveur : GET /mcp/tools
Serveur → Claude : Voici tous mes outils disponibles avec leurs descriptions
Claude : “Ah ! Il y a un outil listFiles. C’est exactement ce dont j’ai besoin.”
Phase 2 : Validation (gérée par l’application Claude)
Application Claude → Vous : “Autorisez-vous l’utilisation de l’outil listFiles sur le dossier /projets ?”
Vous : “Oui, autorisé”
Phase 3 : Exécution
Claude → Serveur : POST /mcp/execute avec {"tool": "listFiles", "params": {"chemin": "/projets"}}
Serveur → Claude : Résultat de l’exécution
Claude → Vous : “Voici les fichiers dans votre dossier projets : …”
Aujourd’hui, nous nous concentrons sur la Phase 1 : la découverte.
Le Format Standard de Découverte MCP
Le protocole MCP définit un format standard pour décrire vos outils. Voici la structure JSON que l’IA attend :
<span class="p">{</span><span class="w">
</span><span class="nl">"protocol_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"server_info"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Mon Serveur MCP"</span><span class="p">,</span><span class="w">
</span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Serveur MCP pour accès aux fichiers locaux"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"tools"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"readFile"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Lit le contenu d'un fichier texte"</span><span class="p">,</span><span class="w">
</span><span class="nl">"input_schema"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"object"</span><span class="p">,</span><span class="w">
</span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"chemin_du_fichier"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Chemin vers le fichier à lire"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"required"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"chemin_du_fichier"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>
Cette structure contient trois informations clés :
protocol_version : Quelle version du MCP vous implémentez
server_info : Métadonnées sur votre serveur
tools : La liste complète de vos outils avec leur schéma d’entrée
Le input_schema utilise le standard JSON Schema. C’est comme une documentation auto-générée que l’IA peut lire et comprendre.
Créer le Gestionnaire de Protocole
Commençons par structurer notre implémentation du protocole MCP. Créez le fichier src/mcp/protocol.ts :
<span class="c1">// src/mcp/protocol.ts</span>
<span class="cm">/**
* Version du protocole MCP implémenté
*/</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">MCP_PROTOCOL_VERSION</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">1.0</span><span class="dl">"</span><span class="p">;</span>
<span class="cm">/**
* Informations sur le serveur
*/</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ServerInfo</span> <span class="p">{</span>
<span class="nl">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">version</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">author</span><span class="p">?:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">capabilities</span><span class="p">?:</span> <span class="kr">string</span><span class="p">[];</span>
<span class="p">}</span>
<span class="cm">/**
* Schéma d'entrée pour un outil (JSON Schema)
*/</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">InputSchema</span> <span class="p">{</span>
<span class="nl">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">;</span>
<span class="nl">properties</span><span class="p">:</span> <span class="p">{</span>
<span class="p">[</span><span class="na">paramName</span><span class="p">:</span> <span class="kr">string</span><span class="p">]:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">enum</span><span class="p">?:</span> <span class="kr">string</span><span class="p">[];</span>
<span class="nl">default</span><span class="p">?:</span> <span class="kr">any</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">};</span>
<span class="nl">required</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>
<span class="p">}</span>
<span class="cm">/**
* Description complète d'un outil MCP
*/</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ToolDescription</span> <span class="p">{</span>
<span class="nl">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">input_schema</span><span class="p">:</span> <span class="nx">InputSchema</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/**
* Réponse de découverte complète
*/</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">DiscoveryResponse</span> <span class="p">{</span>
<span class="nl">protocol_version</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">server_info</span><span class="p">:</span> <span class="nx">ServerInfo</span><span class="p">;</span>
<span class="nl">tools</span><span class="p">:</span> <span class="nx">ToolDescription</span><span class="p">[];</span>
<span class="p">}</span>
<span class="cm">/**
* Informations sur notre serveur
*/</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">SERVER_INFO</span><span class="p">:</span> <span class="nx">ServerInfo</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MCP File Server</span><span class="dl">"</span><span class="p">,</span>
<span class="na">version</span><span class="p">:</span> <span class="dl">"</span><span class="s2">1.0.0</span><span class="dl">"</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Serveur MCP pour la gestion de fichiers locaux</span><span class="dl">"</span><span class="p">,</span>
<span class="na">author</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Nicolas Dabène</span><span class="dl">"</span><span class="p">,</span>
<span class="na">capabilities</span><span class="p">:</span> <span class="p">[</span>
<span class="dl">"</span><span class="s2">file_reading</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">directory_listing</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">file_search</span><span class="dl">"</span>
<span class="p">]</span>
<span class="p">};</span>
Ces types TypeScript nous donnent une structure solide. Chaque outil devra fournir une ToolDescription conforme à ce format.
Convertir nos Outils au Format MCP
Actuellement, nos outils ont une définition simple. Transformons-les au format MCP complet. Modifions src/tools/readFile.ts :
<span class="c1">// src/tools/readFile.ts</span>
<span class="k">import</span> <span class="nx">fs</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">fs/promises</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">path</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../types/mcp</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolDescription</span><span class="p">,</span> <span class="nx">InputSchema</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../mcp/protocol</span><span class="dl">'</span><span class="p">;</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ReadFileParams</span> <span class="p">{</span>
<span class="nl">chemin_du_fichier</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">encoding</span><span class="p">?:</span> <span class="dl">'</span><span class="s1">utf-8</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">ascii</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">base64</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">readFile</span><span class="p">(</span><span class="nx">params</span><span class="p">:</span> <span class="nx">ReadFileParams</span><span class="p">):</span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolResponse</span><span class="o">></span> <span class="p">{</span>
<span class="k">try</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">params</span><span class="p">.</span><span class="nx">chemin_du_fichier</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Le paramètre 'chemin_du_fichier' est requis</span><span class="dl">"</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">absolutePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">chemin_du_fichier</span><span class="p">);</span>
<span class="k">try</span> <span class="p">{</span>
<span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">access</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">);</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="s2">`Fichier introuvable : </span><span class="p">${</span><span class="nx">params</span><span class="p">.</span><span class="nx">chemin_du_fichier</span><span class="p">}</span><span class="s2">`</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">stats</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">stat</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">stats</span><span class="p">.</span><span class="nx">isFile</span><span class="p">())</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Le chemin spécifié n'est pas un fichier</span><span class="dl">"</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">MAX_FILE_SIZE</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">size</span> <span class="o">></span> <span class="nx">MAX_FILE_SIZE</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="s2">`Fichier trop volumineux (max </span><span class="p">${</span><span class="nx">MAX_FILE_SIZE</span> <span class="o">/</span> <span class="mi">1024</span> <span class="o">/</span> <span class="mi">1024</span><span class="p">}</span><span class="s2"> MB)`</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">encoding</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="nx">encoding</span> <span class="o">||</span> <span class="dl">'</span><span class="s1">utf-8</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">content</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">,</span> <span class="nx">encoding</span><span class="p">);</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">content</span><span class="p">:</span> <span class="nx">content</span><span class="p">,</span>
<span class="na">metadata</span><span class="p">:</span> <span class="p">{</span>
<span class="na">path</span><span class="p">:</span> <span class="nx">absolutePath</span><span class="p">,</span>
<span class="na">size</span><span class="p">:</span> <span class="nx">stats</span><span class="p">.</span><span class="nx">size</span><span class="p">,</span>
<span class="na">encoding</span><span class="p">:</span> <span class="nx">encoding</span><span class="p">,</span>
<span class="na">lastModified</span><span class="p">:</span> <span class="nx">stats</span><span class="p">.</span><span class="nx">mtime</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="na">error</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="s2">`Erreur lors de la lecture : </span><span class="p">${</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="s2">`</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/**
* Schéma d'entrée au format JSON Schema
*/</span>
<span class="kd">const</span> <span class="nx">readFileInputSchema</span><span class="p">:</span> <span class="nx">InputSchema</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">,</span>
<span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
<span class="na">chemin_du_fichier</span><span class="p">:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Chemin absolu ou relatif vers le fichier à lire</span><span class="dl">"</span>
<span class="p">},</span>
<span class="na">encoding</span><span class="p">:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Encodage du fichier</span><span class="dl">"</span><span class="p">,</span>
<span class="na">enum</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">utf-8</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ascii</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">base64</span><span class="dl">"</span><span class="p">],</span>
<span class="na">default</span><span class="p">:</span> <span class="dl">"</span><span class="s2">utf-8</span><span class="dl">"</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">chemin_du_fichier</span><span class="dl">"</span><span class="p">]</span>
<span class="p">};</span>
<span class="cm">/**
* Description MCP complète de l'outil
*/</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">readFileDescription</span><span class="p">:</span> <span class="nx">ToolDescription</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">readFile</span><span class="dl">"</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Lit le contenu d'un fichier texte depuis le système de fichiers local. Supporte différents encodages (UTF-8, ASCII, Base64).</span><span class="dl">"</span><span class="p">,</span>
<span class="na">input_schema</span><span class="p">:</span> <span class="nx">readFileInputSchema</span>
<span class="p">};</span>
Vous voyez la différence ? Nous avons maintenant :
Un schéma JSON formel qui décrit exactement les paramètres attendus
Des métadonnées enrichies comme les valeurs par défaut et les énumérations
Une description détaillée qui aide l’IA à comprendre quand utiliser cet outil
Faisons de même pour listFiles. Modifiez src/tools/listFiles.ts :
<span class="c1">// src/tools/listFiles.ts</span>
<span class="k">import</span> <span class="nx">fs</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">fs/promises</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">path</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../types/mcp</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolDescription</span><span class="p">,</span> <span class="nx">InputSchema</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../mcp/protocol</span><span class="dl">'</span><span class="p">;</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ListFilesParams</span> <span class="p">{</span>
<span class="nl">chemin_du_dossier</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">include_hidden</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">;</span>
<span class="nl">recursive</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">listFiles</span><span class="p">(</span><span class="nx">params</span><span class="p">:</span> <span class="nx">ListFilesParams</span><span class="p">):</span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolResponse</span><span class="o">></span> <span class="p">{</span>
<span class="k">try</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">params</span><span class="p">.</span><span class="nx">chemin_du_dossier</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Le paramètre 'chemin_du_dossier' est requis</span><span class="dl">"</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">absolutePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">chemin_du_dossier</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">stats</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">stat</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">stats</span><span class="p">.</span><span class="nx">isDirectory</span><span class="p">())</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Le chemin spécifié n'est pas un dossier</span><span class="dl">"</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="c1">// Lire le contenu du dossier</span>
<span class="kd">let</span> <span class="nx">files</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readdir</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">);</span>
<span class="c1">// Filtrer les fichiers cachés si nécessaire</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">params</span><span class="p">.</span><span class="nx">include_hidden</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">files</span> <span class="o">=</span> <span class="nx">files</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">file</span> <span class="o">=></span> <span class="o">!</span><span class="nx">file</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="dl">'</span><span class="s1">.</span><span class="dl">'</span><span class="p">));</span>
<span class="p">}</span>
<span class="c1">// Obtenir les détails de chaque fichier</span>
<span class="kd">const</span> <span class="nx">filesWithDetails</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span>
<span class="nx">files</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="nx">file</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">filePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">fileStats</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">stat</span><span class="p">(</span><span class="nx">filePath</span><span class="p">);</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="nx">file</span><span class="p">,</span>
<span class="na">type</span><span class="p">:</span> <span class="nx">fileStats</span><span class="p">.</span><span class="nx">isDirectory</span><span class="p">()</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">directory</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">file</span><span class="dl">'</span><span class="p">,</span>
<span class="na">size</span><span class="p">:</span> <span class="nx">fileStats</span><span class="p">.</span><span class="nx">size</span><span class="p">,</span>
<span class="na">lastModified</span><span class="p">:</span> <span class="nx">fileStats</span><span class="p">.</span><span class="nx">mtime</span><span class="p">,</span>
<span class="na">permissions</span><span class="p">:</span> <span class="nx">fileStats</span><span class="p">.</span><span class="nx">mode</span>
<span class="p">};</span>
<span class="p">})</span>
<span class="p">);</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">content</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">filesWithDetails</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
<span class="na">metadata</span><span class="p">:</span> <span class="p">{</span>
<span class="na">path</span><span class="p">:</span> <span class="nx">absolutePath</span><span class="p">,</span>
<span class="na">count</span><span class="p">:</span> <span class="nx">filesWithDetails</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span>
<span class="na">include_hidden</span><span class="p">:</span> <span class="nx">params</span><span class="p">.</span><span class="nx">include_hidden</span> <span class="o">||</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="na">error</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="s2">`Erreur lors de la lecture du dossier : </span><span class="p">${</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="s2">`</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">listFilesInputSchema</span><span class="p">:</span> <span class="nx">InputSchema</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">,</span>
<span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
<span class="na">chemin_du_dossier</span><span class="p">:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Chemin absolu ou relatif vers le dossier à lister</span><span class="dl">"</span>
<span class="p">},</span>
<span class="na">include_hidden</span><span class="p">:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">boolean</span><span class="dl">"</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Inclure les fichiers cachés (commençant par .)</span><span class="dl">"</span><span class="p">,</span>
<span class="na">default</span><span class="p">:</span> <span class="kc">false</span>
<span class="p">},</span>
<span class="na">recursive</span><span class="p">:</span> <span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">boolean</span><span class="dl">"</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Lister récursivement les sous-dossiers</span><span class="dl">"</span><span class="p">,</span>
<span class="na">default</span><span class="p">:</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">chemin_du_dossier</span><span class="dl">"</span><span class="p">]</span>
<span class="p">};</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">listFilesDescription</span><span class="p">:</span> <span class="nx">ToolDescription</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">listFiles</span><span class="dl">"</span><span class="p">,</span>
<span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Liste les fichiers et dossiers dans un répertoire donné. Peut inclure les fichiers cachés et supporter la récursivité.</span><span class="dl">"</span><span class="p">,</span>
<span class="na">input_schema</span><span class="p">:</span> <span class="nx">listFilesInputSchema</span>
<span class="p">};</span>
Créer le Registre d’Outils Centralisé
Maintenant, créons un registre qui rassemble tous nos outils. Créez src/mcp/registry.ts :
<span class="c1">// src/mcp/registry.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolDescription</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./protocol</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../types/mcp</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">readFile</span><span class="p">,</span> <span class="nx">readFileDescription</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../tools/readFile</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">listFiles</span><span class="p">,</span> <span class="nx">listFilesDescription</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../tools/listFiles</span><span class="dl">'</span><span class="p">;</span>
<span class="cm">/**
* Type pour une fonction outil
*/</span>
<span class="kd">type</span> <span class="nx">ToolFunction</span> <span class="o">=</span> <span class="p">(</span><span class="nx">params</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolResponse</span><span class="o">></span><span class="p">;</span>
<span class="cm">/**
* Registre central de tous les outils disponibles
*/</span>
<span class="kd">class</span> <span class="nx">ToolRegistry</span> <span class="p">{</span>
<span class="k">private</span> <span class="nx">tools</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nx">ToolFunction</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>
<span class="k">private</span> <span class="nx">descriptions</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nx">ToolDescription</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>
<span class="cm">/**
* Enregistrer un nouvel outil
*/</span>
<span class="nx">register</span><span class="p">(</span><span class="nx">description</span><span class="p">:</span> <span class="nx">ToolDescription</span><span class="p">,</span> <span class="nx">implementation</span><span class="p">:</span> <span class="nx">ToolFunction</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">description</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">implementation</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">descriptions</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">description</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">description</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`✅ Outil enregistré : </span><span class="p">${</span><span class="nx">description</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/**
* Obtenir tous les outils disponibles
*/</span>
<span class="nx">getAllDescriptions</span><span class="p">():</span> <span class="nx">ToolDescription</span><span class="p">[]</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">descriptions</span><span class="p">.</span><span class="nx">values</span><span class="p">());</span>
<span class="p">}</span>
<span class="cm">/**
* Obtenir la description d'un outil spécifique
*/</span>
<span class="nx">getDescription</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">ToolDescription</span> <span class="o">|</span> <span class="kc">undefined</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">descriptions</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/**
* Exécuter un outil
*/</span>
<span class="k">async</span> <span class="nx">execute</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">:</span> <span class="kr">any</span><span class="p">):</span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolResponse</span><span class="o">></span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">tool</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">tool</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="s2">`Outil '</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">' introuvable. Outils disponibles : </span><span class="p">${</span><span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">keys</span><span class="p">()).</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span><span class="p">)}</span><span class="s2">`</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="k">try</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">await</span> <span class="nx">tool</span><span class="p">(</span><span class="nx">params</span><span class="p">);</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="na">error</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="s2">`Erreur lors de l'exécution de '</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">' : </span><span class="p">${</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="s2">`</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/**
* Vérifier si un outil existe
*/</span>
<span class="nx">has</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">boolean</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/**
* Obtenir le nombre d'outils enregistrés
*/</span>
<span class="nx">count</span><span class="p">():</span> <span class="kr">number</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">size</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Instance singleton du registre</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">toolRegistry</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ToolRegistry</span><span class="p">();</span>
<span class="c1">// Enregistrement de tous nos outils au démarrage</span>
<span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="nx">readFileDescription</span><span class="p">,</span> <span class="nx">readFile</span><span class="p">);</span>
<span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="nx">listFilesDescription</span><span class="p">,</span> <span class="nx">listFiles</span><span class="p">);</span>
Ce registre est le cœur de notre système. C’est lui qui :
Maintient la liste de tous les outils disponibles
Gère l’exécution des outils de manière unifiée
Fournit les descriptions pour la découverte
Gère les erreurs de manière centralisée
Implémenter les Endpoints MCP
Modifions maintenant notre serveur Express pour implémenter les endpoints MCP standards. Remplaçons src/index.ts :
<span class="c1">// src/index.ts</span>
<span class="k">import</span> <span class="nx">express</span><span class="p">,</span> <span class="p">{</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">Response</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">express</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span> <span class="nx">SERVER_INFO</span><span class="p">,</span> <span class="nx">DiscoveryResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./mcp/protocol</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">toolRegistry</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./mcp/registry</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">PORT</span> <span class="o">=</span> <span class="mi">3000</span><span class="p">;</span>
<span class="c1">// Middleware</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">express</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span>
<span class="c1">// Logging middleware</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`[</span><span class="p">${</span><span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">()}</span><span class="s2">] </span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">method</span><span class="p">}</span><span class="s2"> </span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">path</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">next</span><span class="p">();</span>
<span class="p">});</span>
<span class="c1">// ============================================</span>
<span class="c1">// ROUTES MCP STANDARD</span>
<span class="c1">// ============================================</span>
<span class="cm">/**
* Endpoint racine - Informations sur le serveur
*/</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
<span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Serveur MCP File Server</span><span class="dl">'</span><span class="p">,</span>
<span class="na">version</span><span class="p">:</span> <span class="nx">SERVER_INFO</span><span class="p">.</span><span class="nx">version</span><span class="p">,</span>
<span class="na">protocol_version</span><span class="p">:</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span>
<span class="na">status</span><span class="p">:</span> <span class="dl">'</span><span class="s1">operational</span><span class="dl">'</span><span class="p">,</span>
<span class="na">endpoints</span><span class="p">:</span> <span class="p">{</span>
<span class="na">discovery</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/mcp/tools</span><span class="dl">'</span><span class="p">,</span>
<span class="na">execute</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/mcp/execute</span><span class="dl">'</span><span class="p">,</span>
<span class="na">health</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/health</span><span class="dl">'</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="cm">/**
* Endpoint de découverte - Le "menu" complet
* C'est ici que l'IA découvre tous vos outils
*/</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/tools</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="na">response</span><span class="p">:</span> <span class="nx">DiscoveryResponse</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">protocol_version</span><span class="p">:</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span>
<span class="na">server_info</span><span class="p">:</span> <span class="nx">SERVER_INFO</span><span class="p">,</span>
<span class="na">tools</span><span class="p">:</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">getAllDescriptions</span><span class="p">()</span>
<span class="p">};</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📋 Découverte demandée - </span><span class="p">${</span><span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">count</span><span class="p">()}</span><span class="s2"> outils disponibles`</span><span class="p">);</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span>
<span class="p">});</span>
<span class="cm">/**
* Endpoint d'exécution unifié
* Format : POST /mcp/execute
* Body : { "tool": "nomOutil", "params": {...} }
*/</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/execute</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">tool</span><span class="p">,</span> <span class="nx">params</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>
<span class="c1">// Validation</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">tool</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">400</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Le paramètre 'tool' est requis</span><span class="dl">"</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`⚙️ Exécution demandée : </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="c1">// Exécution via le registre</span>
<span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="nx">tool</span><span class="p">,</span> <span class="nx">params</span> <span class="o">||</span> <span class="p">{});</span>
<span class="c1">// Log du résultat</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">success</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`✅ Exécution réussie : </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`❌ Échec d'exécution : </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2"> - </span><span class="p">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">error</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
<span class="p">});</span>
<span class="cm">/**
* Endpoint de description d'un outil spécifique
*/</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/tools/:toolName</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">toolName</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">description</span> <span class="o">=</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">getDescription</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">description</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">404</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="s2">`Outil '</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">' introuvable`</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">description</span><span class="p">);</span>
<span class="p">});</span>
<span class="cm">/**
* Health check
*/</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/health</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
<span class="na">status</span><span class="p">:</span> <span class="dl">'</span><span class="s1">healthy</span><span class="dl">'</span><span class="p">,</span>
<span class="na">uptime</span><span class="p">:</span> <span class="nx">process</span><span class="p">.</span><span class="nx">uptime</span><span class="p">(),</span>
<span class="na">tools_count</span><span class="p">:</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">count</span><span class="p">(),</span>
<span class="na">timestamp</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">()</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="c1">// ============================================</span>
<span class="c1">// RÉTROCOMPATIBILITÉ (optionnel)</span>
<span class="c1">// ============================================</span>
<span class="cm">/**
* Ancien endpoint direct (pour tests rapides)
* @deprecated Utilisez /mcp/execute à la place
*/</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/tools/:toolName</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">toolName</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">params</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`⚠️ Utilisation de l'ancien endpoint /tools/</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2"> (déprécié)`</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="nx">toolName</span><span class="p">,</span> <span class="nx">params</span><span class="p">);</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// ============================================</span>
<span class="c1">// DÉMARRAGE DU SERVEUR</span>
<span class="c1">// ============================================</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="nx">PORT</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">🚀 MCP File Server</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📍 URL: http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📋 Découverte: http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">/mcp/tools`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`⚙️ Exécution: POST http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">/mcp/execute`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`🔧 Outils disponibles: </span><span class="p">${</span><span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">count</span><span class="p">()}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
Notre serveur implémente maintenant le protocole MCP complet avec trois endpoints principaux :
GET /mcp/tools : Découverte de tous les outils
POST /mcp/execute : Exécution unifiée de n’importe quel outil
GET /mcp/tools/:toolName : Détails d’un outil spécifique
Tester le Système de Découverte
Relançons notre serveur et testons le système complet :
npm run dev
Vous devriez voir :
═══════════════════════════════════════
🚀 MCP File Server
═══════════════════════════════════════
📍 URL: http://localhost:3000
📋 Découverte: http://localhost:3000/mcp/tools
⚙️ Exécution: POST http://localhost:3000/mcp/execute
🔧 Outils disponibles: 2
═══════════════════════════════════════
✅ Outil enregistré : readFile
✅ Outil enregistré : listFiles
Test 1 : Découverte Complète
curl http://localhost:3000/mcp/tools | json_pp
Réponse (formatée) :
<span class="p">{</span><span class="w">
</span><span class="nl">"protocol_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"server_info"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MCP File Server"</span><span class="p">,</span><span class="w">
</span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Serveur MCP pour la gestion de fichiers locaux"</span><span class="p">,</span><span class="w">
</span><span class="nl">"author"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Nicolas Dabène"</span><span class="p">,</span><span class="w">
</span><span class="nl">"capabilities"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"file_reading"</span><span class="p">,</span><span class="w">
</span><span class="s2">"directory_listing"</span><span class="p">,</span><span class="w">
</span><span class="s2">"file_search"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"tools"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"readFile"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Lit le contenu d'un fichier texte depuis le système de fichiers local. Supporte différents encodages (UTF-8, ASCII, Base64)."</span><span class="p">,</span><span class="w">
</span><span class="nl">"input_schema"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"object"</span><span class="p">,</span><span class="w">
</span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"chemin_du_fichier"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Chemin absolu ou relatif vers le fichier à lire"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"encoding"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Encodage du fichier"</span><span class="p">,</span><span class="w">
</span><span class="nl">"enum"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"utf-8"</span><span class="p">,</span><span class="w"> </span><span class="s2">"ascii"</span><span class="p">,</span><span class="w"> </span><span class="s2">"base64"</span><span class="p">],</span><span class="w">
</span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"utf-8"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"required"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"chemin_du_fichier"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"listFiles"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Liste les fichiers et dossiers dans un répertoire donné. Peut inclure les fichiers cachés et supporter la récursivité."</span><span class="p">,</span><span class="w">
</span><span class="nl">"input_schema"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"object"</span><span class="p">,</span><span class="w">
</span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"chemin_du_dossier"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Chemin absolu ou relatif vers le dossier à lister"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"include_hidden"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"boolean"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Inclure les fichiers cachés (commençant par .)"</span><span class="p">,</span><span class="w">
</span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"recursive"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"boolean"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Lister récursivement les sous-dossiers"</span><span class="p">,</span><span class="w">
</span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"required"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"chemin_du_dossier"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>
Parfait ! L’IA peut maintenant découvrir tous vos outils avec leurs descriptions complètes.
Test 2 : Exécution via l’Endpoint Unifié
curl <span class="nt">-X</span> POST http://localhost:3000/mcp/execute <span class="se">\</span>
<span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
<span class="nt">-d</span> <span class="s1">'{
"tool": "readFile",
"params": {
"chemin_du_fichier": "test.txt"
}
}'</span>
Réponse :
<span class="p">{</span><span class="w">
</span><span class="nl">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"content"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ceci est un fichier de test pour le MCP !</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span><span class="w">
</span><span class="nl">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/chemin/absolu/vers/test.txt"</span><span class="p">,</span><span class="w">
</span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span><span class="w">
</span><span class="nl">"encoding"</span><span class="p">:</span><span class="w"> </span><span class="s2">"utf-8"</span><span class="p">,</span><span class="w">
</span><span class="nl">"lastModified"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-11-12T10:30:00.000Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>
Test 3 : Détails d’un Outil Spécifique
curl http://localhost:3000/mcp/tools/listFiles | json_pp
Cela retourne uniquement les informations sur listFiles, utile pour une IA qui veut en savoir plus sur un outil spécifique.
Créer un Client de Test Simple
Pour mieux visualiser le fonctionnement, créons un petit script client. Créez src/test-client.ts :
<span class="c1">// src/test-client.ts</span>
<span class="cm">/**
* Client de test simple pour démontrer l'interaction MCP
*/</span>
<span class="kd">const</span> <span class="nx">SERVER_URL</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">http://localhost:3000</span><span class="dl">'</span><span class="p">;</span>
<span class="kr">interface</span> <span class="nx">ToolDescription</span> <span class="p">{</span>
<span class="nl">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">input_schema</span><span class="p">:</span> <span class="kr">any</span><span class="p">;</span>
<span class="p">}</span>
<span class="kr">interface</span> <span class="nx">DiscoveryResponse</span> <span class="p">{</span>
<span class="nl">protocol_version</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="nl">server_info</span><span class="p">:</span> <span class="kr">any</span><span class="p">;</span>
<span class="nl">tools</span><span class="p">:</span> <span class="nx">ToolDescription</span><span class="p">[];</span>
<span class="p">}</span>
<span class="cm">/**
* Étape 1 : Découvrir les outils disponibles
*/</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">discoverTools</span><span class="p">():</span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolDescription</span><span class="p">[]</span><span class="o">></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">📋 Étape 1 : Découverte des outils...</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">SERVER_URL</span><span class="p">}</span><span class="s2">/mcp/tools`</span><span class="p">);</span>
<span class="kd">const</span> <span class="na">data</span><span class="p">:</span> <span class="nx">DiscoveryResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Serveur : </span><span class="p">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">server_info</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="s2"> v</span><span class="p">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">server_info</span><span class="p">.</span><span class="nx">version</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Protocole MCP : </span><span class="p">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">protocol_version</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`\nOutils disponibles : </span><span class="p">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">length</span><span class="p">}</span><span class="s2">\n`</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">tool</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">}</span><span class="s2">. </span><span class="p">${</span><span class="nx">tool</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">` </span><span class="p">${</span><span class="nx">tool</span><span class="p">.</span><span class="nx">description</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">` Paramètres requis : </span><span class="p">${</span><span class="nx">tool</span><span class="p">.</span><span class="nx">input_schema</span><span class="p">.</span><span class="nx">required</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">return</span> <span class="nx">data</span><span class="p">.</span><span class="nx">tools</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/**
* Étape 2 : Utiliser un outil
*/</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">useTool</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`\n⚙️ Étape 2 : Utilisation de l'outil "</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">"...\n`</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Paramètres : </span><span class="p">${</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">params</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">SERVER_URL</span><span class="p">}</span><span class="s2">/mcp/execute`</span><span class="p">,</span> <span class="p">{</span>
<span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
<span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span>
<span class="p">},</span>
<span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span>
<span class="na">tool</span><span class="p">:</span> <span class="nx">toolName</span><span class="p">,</span>
<span class="na">params</span><span class="p">:</span> <span class="nx">params</span>
<span class="p">})</span>
<span class="p">});</span>
<span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">success</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">✅ Succès !</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Résultat : </span><span class="p">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">content</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">200</span><span class="p">)}</span><span class="s2">...`</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">metadata</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Métadonnées : </span><span class="p">${</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">metadata</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">❌ Échec !</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Erreur : </span><span class="p">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">error</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/**
* Simulation complète d'interaction IA
*/</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">simulateAIInteraction</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">🤖 Simulation d</span><span class="se">\'</span><span class="s1">interaction IA avec MCP</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="k">try</span> <span class="p">{</span>
<span class="c1">// Découverte</span>
<span class="kd">const</span> <span class="nx">tools</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">discoverTools</span><span class="p">();</span>
<span class="c1">// L'IA "choisit" un outil basé sur une tâche</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">💭 L</span><span class="se">\'</span><span class="s1">IA analyse la demande : "Liste les fichiers du dossier courant"</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">💭 L</span><span class="se">\'</span><span class="s1">IA identifie que l</span><span class="se">\'</span><span class="s1">outil "listFiles" correspond à la demande</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// Exécution</span>
<span class="k">await</span> <span class="nx">useTool</span><span class="p">(</span><span class="dl">'</span><span class="s1">listFiles</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
<span class="na">chemin_du_dossier</span><span class="p">:</span> <span class="dl">'</span><span class="s1">.</span><span class="dl">'</span><span class="p">,</span>
<span class="na">include_hidden</span><span class="p">:</span> <span class="kc">false</span>
<span class="p">});</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Erreur:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Exécution</span>
<span class="nx">simulateAIInteraction</span><span class="p">();</span>
Ajoutez un script dans votre package.json :
<span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ts-node src/index.ts"</span><span class="p">,</span><span class="w">
</span><span class="nl">"test-client"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ts-node src/test-client.ts"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>
Exécutez le client de test :
npm run test-client
Vous verrez une simulation complète de l’interaction IA :
═══════════════════════════════════════
🤖 Simulation d'interaction IA avec MCP
═══════════════════════════════════════
📋 Étape 1 : Découverte des outils...
Serveur : MCP File Server v1.0.0
Protocole MCP : 1.0
Outils disponibles : 2
1. readFile
Lit le contenu d'un fichier texte depuis le système de fichiers local. Supporte différents encodages (UTF-8, ASCII, Base64).
Paramètres requis : chemin_du_fichier
2. listFiles
Liste les fichiers et dossiers dans un répertoire donné. Peut inclure les fichiers cachés et supporter la récursivité.
Paramètres requis : chemin_du_dossier
💭 L'IA analyse la demande : "Liste les fichiers du dossier courant"
💭 L'IA identifie que l'outil "listFiles" correspond à la demande
⚙️ Étape 2 : Utilisation de l'outil "listFiles"...
Paramètres : {
"chemin_du_dossier": ".",
"include_hidden": false
}
✅ Succès !
Résultat : [
{
"name": "node_modules",
"type": "directory",
"size": 4096,
"lastModified": "2025-12-03T08:15:23.000Z",
"permissions": 16877
},
...
]
Métadonnées : {
"path": "/chemin/absolu/vers/projet",
"count": 8,
"include_hidden": false
}
═══════════════════════════════════════
Parfait ! Vous venez de simuler exactement comment une IA utilise votre serveur MCP.
Comprendre l’Impact Architecturale
Ce système de découverte change fondamentalement l’architecture des intégrations IA :
Avant MCP : Intégrations Rigides
Chaque IA devait être programmée spécifiquement pour chaque outil :
<span class="c1">// Code dans l'IA pour intégrer un outil spécifique</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">userWantsToReadFile</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">callReadFileAPI</span><span class="p">(</span><span class="nx">userParams</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">userWantsToListFiles</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">callListFilesAPI</span><span class="p">(</span><span class="nx">userParams</span><span class="p">);</span>
<span class="p">}</span>
Après MCP : Auto-Découverte
L’IA découvre dynamiquement les capacités :
<span class="c1">// L'IA peut maintenant découvrir et utiliser n'importe quel outil</span>
<span class="kd">const</span> <span class="nx">tools</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">discoverTools</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">tool</span> <span class="o">=</span> <span class="nx">tools</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">t</span> <span class="o">=></span> <span class="nx">matchesUserRequest</span><span class="p">(</span><span class="nx">t</span><span class="p">));</span>
<span class="k">await</span> <span class="nx">executeTool</span><span class="p">(</span><span class="nx">tool</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">userParams</span><span class="p">);</span>
Avantages pour les Développeurs
Évolutivité
Vous pouvez ajouter de nouveaux outils sans toucher au code de l’IA. Il suffit de les enregistrer dans le registre.
Standardisation
Toutes les IA compatibles MCP peuvent utiliser vos outils automatiquement.
Maintenance Simplifiée
Un seul endroit pour gérer tous vos outils : le registre centralisé.
Sécurité et Bonnes Pratiques
Validation des Requêtes
Toujours valider que l’outil demandé existe avant de l’exécuter :
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">toolName</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">404</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
<span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">error</span><span class="p">:</span> <span class="s2">`Outil '</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">' non trouvé`</span>
<span class="p">});</span>
<span class="p">}</span>
Logging et Monitoring
Loggez toutes les interactions pour le debugging et la sécurité :
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`[</span><span class="p">${</span><span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">()}</span><span class="s2">] </span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">ip</span><span class="p">}</span><span class="s2"> - </span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2"> - </span><span class="p">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">success</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">SUCCESS</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">ERROR</span><span class="dl">'</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
Rate Limiting
Implémentez des limites de fréquence pour éviter les abus :
<span class="c1">// Exemple simple de rate limiting</span>
<span class="kd">const</span> <span class="nx">requests</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="kr">number</span><span class="p">[]</span><span class="o">></span><span class="p">();</span>
<span class="kd">function</span> <span class="nx">isRateLimited</span><span class="p">(</span><span class="nx">ip</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">boolean</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">now</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
<span class="kd">const</span> <span class="nb">window</span> <span class="o">=</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span> <span class="c1">// 1 minute</span>
<span class="kd">const</span> <span class="nx">maxRequests</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="c1">// 100 requêtes par minute</span>
<span class="kd">const</span> <span class="nx">userRequests</span> <span class="o">=</span> <span class="nx">requests</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">ip</span><span class="p">)</span> <span class="o">||</span> <span class="p">[];</span>
<span class="kd">const</span> <span class="nx">recentRequests</span> <span class="o">=</span> <span class="nx">userRequests</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">time</span> <span class="o">=></span> <span class="nx">now</span> <span class="o">-</span> <span class="nx">time</span> <span class="o"><</span> <span class="nb">window</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">recentRequests</span><span class="p">.</span><span class="nx">length</span> <span class="o">>=</span> <span class="nx">maxRequests</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">recentRequests</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">now</span><span class="p">);</span>
<span class="nx">requests</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">ip</span><span class="p">,</span> <span class="nx">recentRequests</span><span class="p">);</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
Conclusion : Un Nouveau Paradigme
Le système de découverte MCP représente un changement de paradigme dans l’intégration des IA. Au lieu de créer des connecteurs spécifiques pour chaque cas d’usage, vous créez des “blocs de construction” standardisés que toutes les IA compatibles peuvent assembler dynamiquement.
Dans cet article, nous avons vu comment :
- Implémenter le protocole MCP complet avec découverte et exécution
- Créer un registre centralisé d’outils
- Structurer les outils avec des schémas JSON Schema
- Tester le système avec des clients HTTP
- Simuler l’interaction complète IA-serveur
La prochaine étape ? Connecter votre serveur MCP à une vraie IA comme Claude Desktop. Vous verrez alors la magie opérer : votre IA pourra réellement lire vos fichiers, lister vos dossiers, et utiliser tous vos outils personnalisés.
Le MCP n’est pas qu’un protocole technique : c’est une nouvelle façon de penser l’intégration homme-IA. Vos outils deviennent des extensions naturelles des capacités de l’IA, sans friction technique.
Article publié le 12 novembre 2025 par Nicolas Dabène - Expert PHP & PrestaShop avec 15+ ans d’expérience dans l’architecture logicielle et l’intégration d’IA
À lire aussi :
- Comprendre le Model Context Protocol (MCP) : Une Conversation Simple
- Créer son Premier Serveur MCP : Setup du Projet TypeScript
- Créer votre Premier Outil MCP : L’Outil readFile Expliqué
- Sécuriser votre Serveur MCP : Permissions, Validation et Protection
- Connecter votre Serveur MCP à Claude Desktop : L’Intégration Complète