Группы сессий
Машинный перевод
Эта страница переведена с английской документации автоматически, и основной версией остаётся английская страница. Если что-то читается неправильно, на странице Переводы объясняется, как об этом сообщить.
Client подключается к одному серверу. Настоящим приложениям часто нужно несколько (поисковый сервер, сервер базы данных, внутренний API), и в итоге приходится отдельно вести подключение и список инструментов для каждого.
ClientSessionGroup — это один объект, который держит много подключений и сводит всё, что они предоставляют, в единое представление.
Два сервера
Начнём с двух обычных серверов. Они никак не связаны друг с другом, поэтому оба, естественно, назвали свой инструмент 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}."
Одна группа
Создайте ClientSessionGroup и вызовите connect_to_server по одному разу на каждый сервер:
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_serverпринимает параметры транспорта, а не объект сервера:StdioServerParameters(изmcp), чтобы запустить подпроцесс, илиStreamableHttpParameters/SseServerParameters(изmcp.client.session_group) для сервера, который уже слушает по URL.group.tools— этоdict[str, Tool]с инструментами всех подключённых серверов.group.resourcesиgroup.promptsустроены так же.group.call_tool(name, arguments)ищет имя, находит сессию, которой оно принадлежит, и перенаправляет вызов. Какой именно сервер — указывать не нужно.
Check
Положите client.py рядом с двумя серверами и запустите его. Второй вызов connect_to_server завершится отказом:
mcp.shared.exceptions.MCPError: {'search'} already exist in group tools.
Это MCPError, выброшенное ещё до того, как что-либо от второго сервера было зарегистрировано. Имя должно
быть уникальным в пределах всей группы, а два сервера, которые вы не контролируете, рано или поздно столкнутся.
component_name_hook
Исправляется это на уровне группы, а не серверов. Передайте функцию от (name, server_info), и группа будет применять её к каждому регистрируемому имени:
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())
Запустите снова. Теперь print(sorted(group.tools)) показывает оба:
['Library.search', 'Web.search']
- Ключ — ваш.
by_serverсобрала его изserver_info.name— имени, с которым был создан каждыйMCPServer(...). Toolвнутри не тронут:group.tools["Web.search"].nameпо-прежнему"search", и именно это имяcall_toolпередаёт по сети. Префикс никогда не покидает ваш процесс.- Это касается не только инструментов. Ресурс
hoursбиблиотеки зарегистрирован какLibrary.hours.
Tip
Хук применяется к каждому имени от каждого сервера, а не только при конфликтах: режима «префикс при столкновении» нет. Выберите одну схему и пусть она действует везде.
Добавление и удаление серверов
connect_to_server возвращает открытую им ClientSession. Сохраните её, если когда-нибудь захотите убрать этот сервер: await group.disconnect_from_server(session) удаляет его инструменты, ресурсы и промпты из группы.
Если уже есть подключённая ClientSession (например, Client.session), передайте её в await group.connect_with_session(server_info, session) вместо того, чтобы открывать новый транспорт. Агрегирование работает так же. Группа никогда не закрывает сессию, которую открыла не она. server_info задаёт имя сервера для префиксов компонентов; на подключении поколения 2026 client.server_info может быть None (идентификация необязательна), так что в этом случае передайте собственный Implementation(name=..., version=...).
Классическое рукопожатие
ClientSessionGroup построен на ClientSession, а не на Client. Каждый вызов connect_to_server выполняет классическое рукопожатие initialize. Он никогда не отправляет пробный запрос server/discover, описанный на странице Версии протокола. Это рукопожатие понимает любой MCP-сервер, так что совместимостью вы не жертвуете ни с чем; это лишь значит, что к серверу, который умеет лучше, группа идёт более старым и медленным путём.
Итоги
ClientSessionGroupдержит много подключений к серверам и сводит их инструменты, ресурсы и промпты в одинdictдля каждого вида.connect_to_server(params)— по одному на сервер. Принимает параметры транспорта, а не объект сервера или URL, какClient.group.call_tool(name, arguments)сам направляет вызов на сервер-владелец.- Имена должны быть уникальны в пределах всей группы; два сервера с инструментом
searchсами по себе ужиться не могут. component_name_hook=переписывает каждое регистрируемое имя. Меняется ключ словаря, но не имя в передаваемых данных.connect_with_sessionдобавляет сессию, которая у вас уже есть;disconnect_from_serverудаляет сессию.
Рукопожатию, на котором говорит группа (и более быстрому, которое предпочитает Client), посвящена страница Версии протокола.