Groupes de sessions
Traduction automatique
Cette page a été traduite automatiquement à partir de la documentation en anglais, et la page en anglais fait foi. Si quelque chose vous semble incorrect, la page Traductions explique comment le signaler.
Un Client se connecte à un seul serveur. Les applications réelles en veulent souvent plusieurs (un serveur de recherche, un serveur de base de données, une API interne) et finissent par jongler avec une connexion et une liste d’outils pour chacun.
ClientSessionGroup est un objet unique qui détient de nombreuses connexions et fusionne tout ce qu’elles exposent en une seule vue.
Deux serveurs
Commencez par deux serveurs ordinaires. Ils n’ont rien à voir l’un avec l’autre, si bien que tous deux ont naturellement appelé leur outil search :
from mcp.server import MCPServer
mcp = MCPServer("Library")
@mcp.tool()
def search(query: str) -> str:
"""Search the library catalog."""
return f"3 books match {query!r}."
@mcp.resource("library://hours")
def hours() -> str:
"""When the library is open."""
return "Mon-Fri 09:00-17:00"
from mcp.server import MCPServer
mcp = MCPServer("Web")
@mcp.tool()
def search(query: str) -> str:
"""Search the web."""
return f"12 pages match {query!r}."
Un groupe
Créez un ClientSessionGroup et appelez connect_to_server une fois par serveur :
import asyncio
from mcp import ClientSessionGroup, StdioServerParameters
async def main() -> None:
library = StdioServerParameters(command="uv", args=["run", "mcp", "run", "library_server.py"])
web = StdioServerParameters(command="uv", args=["run", "mcp", "run", "web_server.py"])
async with ClientSessionGroup() as group:
await group.connect_to_server(library)
await group.connect_to_server(web)
result = await group.call_tool("search", {"query": "model context protocol"})
print(result.structured_content)
if __name__ == "__main__":
asyncio.run(main())
connect_to_serverprend des paramètres de transport, pas un objet serveur :StdioServerParameters(depuismcp) pour lancer un sous-processus, ouStreamableHttpParameters/SseServerParameters(depuismcp.client.session_group) pour un serveur qui écoute déjà sur une URL.group.toolsest undict[str, Tool]regroupant les outils de tous les serveurs connectés.group.resourcesetgroup.promptsont la même forme.group.call_tool(name, arguments)recherche le nom, trouve la session qui le possède et lui transmet l’appel. Vous n’indiquez jamais quel serveur.
Check
Placez client.py à côté des deux serveurs et exécutez-le. Le second connect_to_server refuse :
mcp.shared.exceptions.MCPError: {'search'} already exist in group tools.
C’est une MCPError, levée avant que quoi que ce soit du second serveur ne soit enregistré. Un nom doit
être unique dans tout le groupe, et deux serveurs que vous ne contrôlez pas finiront tôt ou tard par entrer en collision.
component_name_hook
Vous corrigez cela au niveau du groupe, pas des serveurs. Passez une fonction de (name, server_info) et le groupe l’exécute sur chaque nom qu’il enregistre :
import asyncio
from mcp import ClientSessionGroup, StdioServerParameters
from mcp.types import Implementation
def by_server(name: str, server_info: Implementation) -> str:
return f"{server_info.name}.{name}"
async def main() -> None:
library = StdioServerParameters(command="uv", args=["run", "mcp", "run", "library_server.py"])
web = StdioServerParameters(command="uv", args=["run", "mcp", "run", "web_server.py"])
async with ClientSessionGroup(component_name_hook=by_server) as group:
await group.connect_to_server(library)
await group.connect_to_server(web)
print(sorted(group.tools))
result = await group.call_tool("Web.search", {"query": "model context protocol"})
print(result.structured_content)
if __name__ == "__main__":
asyncio.run(main())
Relancez-le. print(sorted(group.tools)) affiche maintenant les deux :
['Library.search', 'Web.search']
- La clé est à vous.
by_serverl’a construite à partir deserver_info.name, le nom avec lequel chaqueMCPServer(...)a été construit. - Le
Toolà l’intérieur est intact :group.tools["Web.search"].namevaut toujours"search", et c’est ce nom quecall_toolenvoie sur la liaison. Le préfixe ne quitte jamais votre processus. - Cela ne concerne pas que les outils. La ressource
hoursde la bibliothèque est enregistrée sous le nomLibrary.hours.
Tip
Le hook s’exécute sur chaque nom de chaque serveur, pas seulement en cas de conflit : il n’existe pas de mode « préfixe en cas de collision ». Choisissez un schéma et laissez-le s’appliquer partout.
Ajouter et retirer des serveurs
connect_to_server renvoie la ClientSession qu’il a ouverte. Conservez-la si vous voulez un jour vous séparer de ce serveur : await group.disconnect_from_server(session) retire ses outils, ressources et prompts du groupe.
Si vous détenez déjà une ClientSession connectée (Client.session en est une), passez-la à await group.connect_with_session(server_info, session) au lieu d’ouvrir un nouveau transport. Elle est agrégée de la même façon. Le groupe ne ferme jamais une session qu’il n’a pas ouverte. server_info nomme le serveur pour les préfixes de composants ; sur une connexion de génération 2026, client.server_info peut valoir None (l’identité est facultative), passez donc votre propre Implementation(name=..., version=...) dans ce cas.
La poignée de main classique
ClientSessionGroup est construit sur ClientSession, pas sur Client. Chaque connect_to_server exécute la poignée de main (handshake) initialize classique. Il n’envoie jamais la sonde server/discover décrite dans Versions du protocole. Tous les serveurs MCP comprennent cette poignée de main, donc cela ne vous coûte aucune compatibilité ; cela signifie seulement qu’un groupe emprunte le chemin plus ancien et plus lent vers un serveur qui pourrait faire mieux.
Récapitulatif
ClientSessionGroupdétient de nombreuses connexions serveur et fusionne leurs outils, ressources et prompts en undictchacun.connect_to_server(params)par serveur. Il prend des paramètres de transport, jamais l’objet serveur ni l’URL que prend unClient.group.call_tool(name, arguments)achemine l’appel vers le serveur propriétaire à votre place.- Les noms doivent être uniques dans tout le groupe ; deux serveurs dotés d’un outil
searchne peuvent pas coexister tels quels. component_name_hook=réécrit chaque nom enregistré. La clé du dict change, pas le nom sur la liaison.connect_with_sessionajoute une session que vous détenez déjà ;disconnect_from_serveren retire une.
La poignée de main que parle un groupe (et celle, plus rapide, que préfère un Client) fait l’objet de Versions du protocole.