Aller au contenu principal

Implémenter un client MCP

Mis à jour le 30 juillet 2026

Notre serveur MCP expose des outils ; il faut maintenant la seconde moitié de l’équation : le client, c’est-à-dire le morceau de code qui vit dans votre application, se connecte au serveur et lui relaie les demandes de Claude. C’est lui qui remplace, dans une architecture MCP, le run_tool local des leçons précédentes — la logique des outils a déménagé sur le serveur, le client n’en est plus que le point d’accès.

Architecture du client

Le client s’organise en deux couches :

  • MCP Client : classe personnalisée pour simplifier l’utilisation
  • Client Session : la connexion réelle au serveur (fournie par le SDK)

Pourquoi une classe d’enveloppe plutôt qu’un usage direct de la session ? Parce qu’une session MCP est une ressource vivante : elle ouvre une connexion vers le processus serveur, doit être initialisée avant usage et proprement refermée après — y compris quand une exception interrompt le programme. En encapsulant la session dans une classe qui gère ce cycle de vie, le reste de l’application manipule un objet simple, sans se soucier d’ouverture, de nettoyage ni d’état de connexion. C’est le même réflexe d’ingénierie que le gestionnaire de contexte d’un fichier : la ressource est fragile, on l’enferme dans une abstraction qui garantit sa discipline.

Fonction list_tools

Première capacité : demander au serveur ce qu’il sait faire.

async def list_tools(self) -> list[types.Tool]:
    result = await self.session().list_tools()
    return result.tools

L’implémentation est minuscule — un appel de session, une extraction — mais son rôle est structurant : c’est cette liste, obtenue dynamiquement, qui sera convertie en schémas d’outils et envoyée à Claude. Votre application ne connaît plus ses outils à l’écriture du code ; elle les découvre à l’exécution. Ajoutez un outil au serveur, et tous les clients connectés le proposent à Claude sans modification.

Fonction call_tool

Seconde capacité : exécuter un outil au nom de Claude.

async def call_tool(
    self, tool_name: str, tool_input: dict
) -> types.CallToolResult | None:
    return await self.session().call_tool(tool_name, tool_input)

La signature vous rappellera exactement le run_tool des leçons sur les outils : un nom, un dictionnaire d’arguments. Ce n’est pas un hasard — c’est la même opération, transportée à travers le protocole. Quand Claude émet un bloc tool_use, votre boucle de conversation appelle désormais call_tool au lieu d’une fonction locale ; le serveur exécute et renvoie un CallToolResult, dont le contenu repart vers Claude dans un bloc tool_result. La boucle multi-tours, elle, n’a pas changé d’un iota.

Test du client

Avant de brancher Claude, on vérifie la tuyauterie seule : exécuter uv run mcp_client.py doit afficher la liste des outils du serveur, avec leurs descriptions et leurs schémas. Si cette étape échoue, le problème est dans la connexion ou le serveur — le diagnostiquer maintenant évite de le chercher plus tard au milieu d’une conversation complète.

Test bout en bout

Reste à valider la chaîne entière. En demandant à Claude « Quel est le contenu du document report.pdf ? » :

  1. La liste des outils est envoyée à Claude
  2. Claude décide d’utiliser read_doc_contents
  3. Le client appelle l’outil sur le serveur
  4. Le serveur retourne le contenu du document
  5. Claude répond avec l’information

Chaque maillon de cette séquence a été construit dans une leçon différente — la boucle de conversation, le serveur, le client — et c’est leur assemblage qui fait le système. Le client y tient le rôle de pont : côté face, il parle le protocole MCP avec le serveur ; côté pile, il fournit à votre boucle de conversation exactement ce qu’elle attendait d’un run_tool local. Cette symétrie est voulue : migrer une application d’outils locaux vers MCP ne touche que la provenance des outils, jamais la logique de conversation.

Points clés à retenir

  • Le client MCP est le point d’accès du serveur dans votre application — il remplace le run_tool local
  • La session est une ressource à cycle de vie strict : on l’encapsule dans une classe qui garantit ouverture et nettoyage
  • list_tools découvre dynamiquement les outils ; call_tool les exécute — mêmes rôles qu’en local, à travers le protocole
  • Testez le client seul (uv run mcp_client.py) avant le test bout en bout avec Claude
  • La boucle de conversation ne change pas : seule la provenance des outils a migré vers le serveur