Разбор≈ 14 мин с практикой

Первый собственный инструмент: normalize_currency

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

В результате

Отдельный сохранённый разбор функции и реального обмена stdio.

Перед началом

Основы Python и материалы первого сервера.

Граница версии

Python SDK v1 · mcp==1.30.0 · протокол 2025-11-25. Для SDK v2 требуется отдельная миграция.

1. Определите, что делает ваш инструмент

У normalize_currency один аргумент char_code и один результат — словарь с полем char_code. Для строки usd результатом служит {"char_code":"USD"}. Аннотация char_code: str описывает строковый вход, а dict[str, str] — словарь со строковыми значениями. Это простая учебная операция: она не запрашивает курс и не проверяет, существует ли код валюты в справочнике.

Проверка внутри функции требует ровно три буквы ASCII. Сначала strip() убирает пробелы по краям, затем upper() меняет регистр. Проверки длины, isascii() и isalpha() отсекают пустую строку, короткое или длинное значение, цифры и кириллицу. Неверный вход вызывает ValueError с сообщением Expected three ASCII letters.

2. Зарегистрируйте функцию в сервере

Импорт FastMCP берётся из mcp.server.fastmcp. Здесь используется компонент пакета mcp версии 1.30.0, а не отдельно установленный пакет fastmcp. Экземпляр FastMCP("workshop") хранит наш сервер, а @mcp.tool() регистрирует функцию normalize_currency как инструмент.

Имя функции и её описание попали в сохранённый ответ tools/list. Там же входная схема содержит обязательное строковое поле char_code. Проверенный выход описан объектом со строковыми значениями; конкретное поле char_code возвращает наша функция. Поэтому проверяйте одновременно описание инструмента и фактический ответ — одна аннотация не доказывает результат вызова.

3. Запустите пример с транспортом stdio

Сохраните приведённый код в first_tool.py. Запускайте его интерпретатором среды, где уже установлен проверенный mcp 1.30.0. В местной проверке использовался Python 3.11.4; установка зависимостей и проверка других версий в этот материал не входят.

При запуске файла блок if __name__ == "__main__" вызывает mcp.run(transport="stdio"). Транспорт читает сообщения клиента из стандартного ввода процесса и отправляет ответы в стандартный вывод. Сам запуск файла в терминале ещё не выполняет инструмент: для обмена нужен MCP-клиент. Не подменяйте такой обмен прямым вызовом функции Python.

В исходнике SDK stdio.py входная строка разбирается как JSONRPCMessage, а ответ сериализуется в JSON и записывается в stdout с переводом строки. Поэтому не добавляйте в stdout произвольный print() с отладочным текстом. В текущем примере такого вывода нет; отдельный случай сбоя из-за загрязнения stdout здесь не воспроизводился.

Команда в вашей тестовой средеpython first_tool.py

4. Проверьте обмен и правильный ответ

Сохранённая проверка использует ClientSession и запускает собственный файл через stdio. Сначала initialize согласовал версию протокола 2025-11-25. Затем tools/list вернул ровно один инструмент normalize_currency. Это результаты конкретного местного прогона, а не обещание работы любого клиента.

После tools/call с аргументом {"char_code":"usd"} сохранён ответ isError: false и structuredContent: {"char_code":"USD"}. Проверяйте это поле результата, а не только наличие текста в терминале. В ответе также сохранён текстовый блок с тем же объектом.

Воспроизводимый путь проверки уже есть в checks/stdio_smoke.py: initialize → list_tools() → call_tool(). Он вызывает ваш сервер как отдельный процесс. checks/check_example.py решает другую задачу: проверяет синтаксис и вызывает саму функцию для двух допустимых и пяти недопустимых строк. Его успех сам по себе не подтверждает обмен MCP.

5. Отличайте ожидаемый отказ от сбоя запуска

Для {"char_code":"123"} местный tools/call вернул isError: true и сообщение Error executing tool normalize_currency: Expected three ASCII letters. Этот отказ означает, что вход дошёл до инструмента и его правило проверки сработало. Не исправляйте его удалением проверки: передайте строку из трёх букв, например usd, и проверьте успешный ответ отдельно.

Здесь подтверждён только этот ожидаемый отказ собственного кода. Ошибка запуска стороннего клиента не воспроизведена; настройка такого клиента, HTTP-транспорт и работа реального сервиса не проверялись. Если измените функцию, повторите обе проверки — успешный ответ и отказ — уже для новой версии файла.

Что подтверждает этот материал

Материал относится к конкретному first_tool.py и сохранённым исходникам SDK. Его SHA-256 сверяется с синтаксической проверкой и записью обмена stdio; версия mcp, версия протокола и контрольные суммы первичных файлов тоже должны совпасть. Изменение этих исходников делает старые доказательства и редакционное принятие неприменимыми.

Сетевые обращения инструмента отсутствуют в его коде. В сохранённом прогоне внешние соединения были запрещены; исключение для внутреннего socketpair Windows обеспечивало работу asyncio. Этот результат не доказывает безопасность произвольного расширения сервера и не заменяет проверку нового кода.

Полный пример

Сохраните файлы с указанными именами в одной папке. Проверка функции и запуск MCP-сервера — разные действия.

first_tool.pyPython
Скачать
"""Local workshop specimen: no network or filesystem access in the tool."""
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("workshop")

@mcp.tool()
async def normalize_currency(char_code: str) -> dict[str, str]:
    """Normalize a three-letter currency code; reject other input."""
    code = char_code.strip().upper()
    if len(code) != 3 or not code.isascii() or not code.isalpha():
        raise ValueError("Expected three ASCII letters")
    return {"char_code": code}

if __name__ == "__main__":
    mcp.run(transport="stdio")
first_tool.pyPython
"""Local workshop specimen: no network or filesystem access in the tool."""
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("workshop")

@mcp.tool()
async def normalize_currency(char_code: str) -> dict[str, str]:
    """Normalize a three-letter currency code; reject other input."""
    code = char_code.strip().upper()
    if len(code) != 3 or not code.isascii() or not code.isalpha():
        raise ValueError("Expected three ASCII letters")
    return {"char_code": code}

if __name__ == "__main__":
    mcp.run(transport="stdio")

Проверьте себя

Сравните прямой вызов normalize_currency и tools/call. Что подтверждает каждый тест?

Источники, версия и границы проверки

У этого отдельного примера сохранены проверки функции и обмена stdio для SDK 1.30.0. Они относятся к исходному first_tool.py.

SDK v1 — ветка поддержки; SDK v2 имеет отдельную документацию.

Вопросы по материалу

Успешный ответ означает, что такой код валюты существует?

Нет. normalize_currency проверяет форму строки: после удаления крайних пробелов и перевода в верхний регистр должны остаться три буквы ASCII. Справочник валют и курсы функция не читает.

Продукты ATOMNO

Другие задачи для AI-помощника

Это отдельные продукты ATOMNO для задач с данными. Они не входят в учебные примеры.

Все продукты ATOMNO (в новой вкладке)