БэкендData и ИИPython

Первый MCP-сервер на Python за 30 строк: учим ИИ вызывать нашу функцию

Создаём первый MCP-сервер на Python, добавляем типизированный инструмент, запускаем Inspector и проверяем, как ИИ вызывает нашу функцию с аргументами.

Кодик

Автор

7 мин чтения

MCP-сервер превращает Python-функцию в инструмент, который ИИ может обнаружить и вызвать сам. Для первого рабочего примера не нужен веб-сервер, база данных или ручная JSON-схема. Мы опишем функцию с типами, откроем её в MCP Inspector и увидим структурированный ответ.

Представьте, что вы просите ИИ оценить прогресс по курсу. Без инструмента модель рассуждает по тексту и может ошибиться в арифметике. С инструментом она передаёт два числа функции, получает точный процент и формулирует ответ. Модель выбирает момент вызова, но расчёт выполняет Python. Так MCP подключает к ассистентам файлы, API и внутренние сервисы по общему протоколу.

1Описываем

Создаём обычную функцию с понятным именем, docstring и типами аргументов.

2Открываем

Декоратор tool сообщает MCP-клиенту, что функцию разрешено вызывать.

3Проверяем

Inspector показывает схему аргументов, отправляет вызов и выводит ответ.

Что происходит между ИИ и Python-функцией

В цепочке есть три роли. Host является приложением, в котором пользователь общается с моделью. Внутри host работает MCP-клиент. Он подключается к вашему серверу, запрашивает список возможностей и передаёт вызовы. Сам сервер не разговаривает с моделью и не решает, когда нужен инструмент. Он принимает уже сформированные аргументы, запускает функцию и возвращает результат. В примере сервер называется Study Progress, а инструмент estimate_progress принимает solved и total. Такой проект легко проверить, потому что расчёт детерминированный: одинаковые числа всегда дают одинаковый процент.

РольЧто делаетЧего не делает
HostПоказывает чат и запускает клиентовНе хранит логику вашей функции
MCP-клиентУзнаёт инструменты и передаёт вызовыНе вычисляет процент сам
MCP-серверПроверяет аргументы и запускает PythonНе выбирает цель пользователя
ИнструментВозвращает точный структурированный результатНе получает лишний доступ автоматически

MCP не делает функцию умнее и не прячет её поведение. Он описывает безопасный договор: имя, назначение, типы входных данных и формат ответа. Чем уже этот договор, тем проще тестировать сервер и понимать действия модели.

Путь вызова MCP-инструмента
Запрос проходит четыре понятных слоя. Результат возвращается по той же цепочке

Начните с функции без побочных эффектов. Расчёт прогресса ничего не удаляет, не отправляет и не покупает. Для первого знакомства это лучше инструмента, который меняет файлы или вызывает внешний сервис. Сначала убедитесь, что понимаете путь вызова, затем добавляйте действия.

Создаём MCP-сервер и запускаем Inspector

Установите uv, создайте каталог командой uv init mcp-study, перейдите в него и добавьте SDK: uv add "mcp[cli]". Затем замените содержимое server.py кодом ниже. В актуальном Python SDK v2 сервер импортируется как MCPServer из mcp.server. Типы int и возвращаемый словарь нужны не только редактору: по ним SDK строит описание инструмента для клиента.

from mcp.server import MCPServer

mcp = MCPServer("Study Progress")


@mcp.tool()
def estimate_progress(solved: int, total: int) -> dict[str, int | str]:
    """Calculate completed practice as a percentage."""
    if total <= 0:
        raise ValueError("total must be greater than zero")
    if solved < 0 or solved > total:
        raise ValueError("solved must be between 0 and total")

    percent = round(solved / total * 100)
    if percent == 100:
        status = "готово"
    elif percent >= 70:
        status = "осталось немного"
    else:
        status = "продолжайте практику"

    return {"percent": percent, "status": status}
Что должно произойти после запуска
  • Команда uv run mcp dev server.py печатает адрес MCP Inspector.
  • Во вкладке Tools виден estimate_progress с двумя обязательными целыми полями.
  • Вызов с solved=7 и total=10 возвращает percent=70 и статус «осталось немного».
  • Вызов с total=0 завершается понятной ошибкой, а не делением на ноль.

Промпт и инструмент
У каждого слоя своя работа. Инструмент не заменяет модель, а даёт ей проверяемое действие

Откуда берётся схема инструмента

Когда клиент подключается, сервер объявляет capability tools. Затем клиент запрашивает tools/list и получает имя estimate_progress, текст из docstring и JSON Schema, собранную из аннотаций типов. Если модель решает, что функция поможет ответить, host показывает или выполняет вызов по своим правилам. Запрос tools/call содержит имя и аргументы. SDK превращает значения в параметры Python, запускает функцию и упаковывает словарь в структурированный ответ. Мы не писали JSON-RPC, обработчик маршрута и схему вручную, но все эти слои остаются проверяемыми через Inspector.

ФрагментЧто узнаёт клиентЗачем это модели
estimate_progressСтабильное имя инструментаМожно выбрать нужное действие
DocstringНазначение функцииПонятно, когда её вызывать
solved: intПоле solved должно быть целымМеньше неверных аргументов
dict[str, int | str]Ответ имеет структуруПроще использовать результат
ValueErrorГраница допустимых данныхОшибка объясняет, что исправить

Inspector запускает сервер по stdio: протокольные сообщения идут через стандартные потоки, поэтому не печатайте отладочный текст в stdout. Для логов используйте механизм SDK или stderr. Локальный stdio удобен для первого инструмента и не открывает порт наружу. Не передавайте функции больше прав, чем ей нужно. Для чтения заметок ограничьте каталог, для отправки сообщения требуйте явный текст и адресат. MCP стандартизирует соединение, но разрешения остаются вашей задачей.

Инструмент является кодом, а не магическим промптом. Модель может выбрать не тот tool или предложить неверные аргументы. Сервер обязан проверять диапазоны, права и формат данных. Для действий с последствиями добавляйте подтверждение на стороне host и журналируйте результат.

Проверяем правильные и ошибочные вызовы

Откройте Inspector и сначала вызывайте функцию вручную. Так вы отделите ошибку сервера от поведения модели. Проверьте нормальный прогресс, ноль решённых задач, полное завершение и три неверных набора. Затем переименуйте solved в completed и посмотрите, как изменилась форма. После этого подключите сервер к поддерживаемому MCP-host и сформулируйте запрос без имени функции: «Я решил 7 задач из 10, сколько процентов готово?». В журнале должен появиться именно один вызов estimate_progress.

Проверка своими руками
  1. Запустите uv run mcp dev server.py и откройте напечатанный адрес.
  2. Вызовите инструмент с 0 из 10, 7 из 10 и 10 из 10. Сверьте статус и процент.
  3. Передайте total=0 и solved=11 при total=10. Запишите тексты обеих ошибок.
  4. Измените границу статуса с 70 на 80 и убедитесь, что ответ 7 из 10 поменялся.
  5. Добавьте поле remaining со значением total - solved и проверьте структуру ответа.
  6. Только после ручной проверки подключите MCP-server к host и посмотрите журнал вызова.
Готово, если выполняются все пункты
  • Inspector видит инструмент и два целых аргумента.
  • Правильные значения дают предсказуемый словарь.
  • Неверные диапазоны отклоняются внутри Python-функции.
  • Вы можете объяснить разницу между host, client, server и tool.

Четыре проверки MCP-сервера
До подключения к реальному host. Сначала ручной вызов, потом решение модели

Расширяем сервер без потери контроля

Следующим шагом добавьте resource с учебным планом или второй tool для выполненной задачи. Не объединяйте чтение и запись в manage_everything. Отдельные инструменты проще описывать и тестировать. Для каждого действия определите минимальный вход, изменение после вызова и видимый результат. Для записи ограничьте тестовый каталог, для API храните ключ в переменной окружения и задайте таймаут. Так пример вырастет в сервер, которому можно доверять.

Как изучить тему в Кодике

В Кодике эту тему лучше проходить не с протокола, а снизу вверх. Сначала закрепите функции, словари, исключения и типы Python. Затем разберите, как программа получает внешние данные. Только после этого возвращайтесь к MCP и подключайте один безопасный инструмент. Такой порядок позволяет видеть в декораторах знакомый код, а не набор непонятных символов.

ШагЧто изучитьМини-проверка
1Функции и параметры в курсе PythonНаписать estimate_progress без MCP
2Словари и ValueErrorВернуть структуру и отклонить total=0
3API и границы доступаОбъяснить вход, выход и побочный эффект
4MCP tools и InspectorУвидеть schema и три ручных вызова
5Подключение к hostПолучить вызов из естественного запроса

Не измеряйте прогресс числом MCP-серверов. Один инструмент с тестами, понятными правами и обработкой ошибок полезнее пяти копий из видео. В тренажёре Кодика отдельно перепишите функцию расчёта, а затем восстановите декоратор и команду запуска по памяти.

Используется пример для старого SDK

В актуальном v2 берите MCPServer из mcp.server и сверяйте команды с текущей документацией.

Функция доверяет любым числам

Аннотация int не проверяет бизнес-правило. solved всё равно может оказаться больше total.

В stdout печатаются отладочные строки

При stdio они могут смешаться с протокольным обменом. Используйте корректное логирование.

Первый tool сразу удаляет файлы

Начните с чтения или расчёта, затем добавляйте подтверждения и узкие разрешения.

Сверьтесь с первичным источником. Команды, API и ограничения примера проверяйте по актуальной документации MCP Python SDK v2. Если интерфейс или версия изменились, первичная документация важнее скриншота из старой инструкции.

Что получится в итоге

Готовый результат: Первый MCP-сервер на Python за 30 строк: учим ИИ вызывать нашу функцию
Inspector показывает типизированный инструмент, принимает два числа и возвращает точный структурированный результат без ручного протокольного кода.

Короткие ответы
MCP-сервер сам обращается к языковой модели?

Нет. Он публикует возможности для клиента и выполняет вызовы. Модель находится на стороне host.

Кто решает, когда вызвать tool?

Модель внутри host предлагает вызов, а host применяет свои правила разрешения и подтверждения.

Зачем нужны аннотации типов?

SDK строит по ним схему аргументов, а редактор и тесты раньше замечают ошибки.

Как понять, что проект действительно работает?

Проверьте основной сценарий, ошибочный ввод, повторный запуск и один граничный случай. Затем объясните вслух, где появляются данные, кто принимает решение и где хранится состояние.

Можно ли начать с готового кода из статьи?

Да. После первого запуска измените один параметр, добавьте одну проверку и повторите проект без подсказки. Так пример превращается в собственный навык.

Закрепите Python, затем дайте ему интерфейс для ИИ

Функции, словари и исключения разберите в курсе Python. Общую картину протокола даёт статья что такое MCP.

После Inspector подключите сервер по инструкции про MCP-host и сравните с практическими сценариями MCP. В Кодике сначала восстановите чистую функцию без декоратора, затем соберите весь файл по памяти.