Групи сесій
Машинний переклад
Цю сторінку перекладено автоматично з англомовної документації, і основною версією є англомовна сторінка. Якщо щось читається неправильно, на сторінці Переклади пояснено, як про це повідомити.
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), присвячено сторінку Версії протоколу.