MCP с нуля, часть 3: Подключение клиента к вашему серверу
Создайте клиент MCP stdio, который запускает hr_server.py, вызывает функцию search_employee и возвращает результаты — без необходимости вручную запускать сервер.
В предыдущей части был оставлен сервер MCP с единственной задачей — предоставлять возможность поиска сотрудников.
HR MCP Server
↓
search_employee
Этот процесс начинался с следующего:
python hr_server.py
Оставался один открытый вопрос: кто на самом деле общается с сервером? Эту роль выполняет клиент.
Client
↓
MCP
↓
HR MCP Server
Рассматривайте эти понятия просто: сервер предлагает функции, а клиент подключается к ним и использует их.
Создадим наш клиент
Рядом с файлом hr_server.py добавьте ещё один модуль:
hr_client.py
Структура проекта становится такой:
mcp-hr
│
├── hr_server.py
│
└── hr_client.py
Поскольку сервер уже написан, внимание сосредотачивается на клиенте.
Подключаемся к нашему серверу
В файл 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())
Сначала список элементов может показаться обширным. Решающим фрагментом является:
server = StdioServerParameters(
command="python",
args=["hr_server.py"]
)
Эти параметры указывают клиенту запустить и подключиться к hr_server.py. Клиент запускает процесс сервера и открывает сессию ввода-вывода с ним.
Теперь вызовем наш инструмент
В части 2 был определен инструмент с таким названием:
search_employee
Клиент может искать Джона следующим образом:
await session.initialize()
result = await session.call_tool(
"search_employee",
{"name": "John"}
)
print(result.content[0].text)
Простыми словами, клиент просит сессию выполнить функцию search_employee с именем Джон. Полный скрипт клиента выглядит так:
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())
Этот файл представляет собой полную клиентскую часть демо-программы.
Запустим его
В терминале выполните:
python hr_client.py
Полезная деталь: hr_server.py не требует отдельного ручного запуска. Клиент запускает его через:
command="python",
args=["hr_server.py"]
а затем подключается к работающему процессу.
Что происходит?
Клиент отправляет запрос к инструменту в следующем формате:
Tool:
search_employee
Name:
John
Сервер получает его и выполняет следующее:
search_employee("John")
Он решает задачу:
John → Finance
и возвращает полученный результат вызывающей стороне. В целом путь выглядит так:
hr_client.py
│
│ search_employee("John")
↓
hr_server.py
│
↓
Search employee list
│
↓
John → Finance
│
↓
hr_client.py
Теперь обе стороны протокола осуществляют настоящий полный цикл обмена данными.
Возвращаемся к примеру с USB
Сравнение с подключением клавиатуры к компьютеру может оказаться полезным.
Keyboard
↓
USB
↓
Computer
Обе стороны согласовывают использование общего протокола кабеля. В данном случае клиент и сервер используют протокол MCP. Именно этот общий стандарт позволяет им сотрудничать.
Но выбрал ли он инструмент?
Одно ограничение легко упустить из виду. Рассмотрим место вызова снова:
session.call_tool(
"search_employee",
{"name": "John"}
)
Кто выбрал search_employee? Это сделал автор приложения, встроив имя инструмента напрямую в код. Клиент никогда не читал вопрос на естественном языке вроде «Работает ли Джон в отделе финансов?», чтобы определить, какой функционал использовать. Следующим недостающим элементом является автоматический выбор инструмента.
Что дальше?
Представьте, что на сервере появляется несколько новых инструментов:
search_employee
create_employee
get_leave_balance
list_departments
Когда пользователь спрашивает, к какому отделу принадлежит Джон, необходимо определить, какой именно функционал подходит:
search_employee
а не какой-либо другой инструмент. Это решение зависит от названий инструментов, их описаний и каталога, предоставленного разработчиком. В следующей части рассматривается, как приложение выбирает среди инструментов MCP, когда их становится много.
На данный момент ключевой урок, извлекаемый из этого шага, сводится к механике работы: клиент stdio может запустить сервер, инициализировать сессию, вызвать определённый инструмент с аргументами и вывести структурированный ответ — без необходимости вручную запускать отдельный процесс сервера.