MCP de zéro à l’emploi, partie 3 : Connecter un client à votre serveur
Créez un client MCP stdio qui lance hr_server.py, appelle search_employee et renvoie les résultats, sans avoir à démarrer manuellement le serveur.
L’édition précédente a laissé en place un serveur MCP ayant une seule responsabilité : fournir une fonction de recherche RH.
HR MCP Server
↓
search_employee
Ce processus a commencé par :
python hr_server.py
Une question restait en suspens : qui communique réellement avec le serveur ? Ce rôle revient au client.
Client
↓
MCP
↓
HR MCP Server
Considérez les termes simplement. Le serveur propose des fonctionnalités. Le client se connecte et les utilise.
Créons notre client
À côté de hr_server.py, ajoutez un autre module :
hr_client.py
La structure du projet devient :
mcp-hr
│
├── hr_server.py
│
└── hr_client.py
Avec le serveur déjà écrit, l’attention se porte désormais sur le client.
Se connecter à notre serveur
Collez ce texte dans hr_client.py :
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
server = StdioServerParameters(
command="python",
args=["hr_server.py"]
)
async with stdio_client(server) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
asyncio.run(main())
La liste semble dense au premier abord. Le fragment décisif est :
server = StdioServerParameters(
command="python",
args=["hr_server.py"]
)
Ces paramètres indiquent au client de lancer et de se connecter à hr_server.py. Le client démarre le processus du serveur et ouvre une session stdio avec celui-ci.
Appelons maintenant notre outil
La deuxième partie a défini un outil nommé :
search_employee
Le client peut rechercher John de cette manière :
await session.initialize()
result = await session.call_tool(
"search_employee",
{"name": "John"}
)
print(result.content[0].text)
En termes simples, le client demande à la session d’exécuter search_employee avec le nom John. Le script client complet est :
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
server = StdioServerParameters(
command="python3",
args=["hr_server.py"]
)
async with stdio_client(server) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool(
"search_employee",
{"name": "John"}
)
print(result.content[0].text)
asyncio.run(main())
Ce fichier représente toute la partie client de la démonstration.
Exécutons-le
Dans un terminal, exécutez :
python hr_client.py
Détail utile : hr_server.py n’a pas besoin d’un démarrage manuel distinct. Le client le lance via :
command="python",
args=["hr_server.py"]
puis se connecte au processus en cours d’exécution.
Que se passe-t-il ?
Le client envoie une demande d’outil ayant cette forme :
Tool:
search_employee
Name:
John
Le serveur le reçoit et l’exécute :
search_employee("John")
Il résout :
John → Finance
et renvoie ce résultat à l’appelant. Du début à la fin, le chemin ressemble à ceci :
hr_client.py
│
│ search_employee("John")
↓
hr_server.py
│
↓
Search employee list
│
↓
John → Finance
│
↓
hr_client.py
Les deux parties du protocole effectuent désormais une véritable liaison aller-retour.
Retour à notre exemple USB
Relier un clavier à un ordinateur constitue une comparaison utile.
Keyboard
↓
USB
↓
Computer
Les deux extrémités s’accordent sur un protocole de câble commun. Ici, le client utilise MCP et le serveur utilise également MCP. Cet accord commun leur permet de coopérer.
Mais a-t-il choisi l’outil ?
Une limitation est facile à négliger. Examinons à nouveau le site d’appel :
session.call_tool(
"search_employee",
{"name": "John"}
)
Qui a sélectionné search_employee ? C’est l’auteur de l’application, en codant manuellement le nom de l’outil. Le client n’a jamais lu une question en langue naturelle telle que « John travaille-t-il dans le département Finances ? » pour décider quelle fonctionnalité invoquer. L’élément manquant suivant est la sélection automatique des outils.
Que vient-il après ?
Imaginons que le serveur intègre plusieurs outils supplémentaires :
search_employee
create_employee
get_leave_balance
list_departments
Lorsqu’un utilisateur demande à quel département appartient John, quelque chose doit déterminer que la fonctionnalité appropriée est :
search_employee
plutôt qu’un outil similaire. Cette décision dépend des noms des outils, de leurs descriptions et du catalogue affiché. La prochaine partie explique comment une application choisit parmi les outils MCP lorsqu’ils sont nombreux.
Jusqu’alors, la leçon importante tirée de cette étape est purement mécanique : un client stdio peut démarrer le serveur, initialiser une session, appeler un outil nommé avec des arguments et afficher la réponse structurée — sans avoir à lancer manuellement de processus serveur distinct.