Все уроки · 44
Создайте свой первый MCP-сервер
Пошаговый MCP-сервер на Python с официальным SDK: инструменты, ресурсы, промпты, транспорт stdio и подключение к клиенту.
Сначала решите, что предоставлять
MCP-сервер может предлагать три вида возможностей, и этот выбор важнее кода:
- Инструменты (tools) — действия, которые модель сама решает вызвать: искать, записывать, отправлять, создавать. Используйте их для всего, что имеет побочные эффекты или требует вычислений.
- Ресурсы (resources) — данные, которые приложение читает и подставляет в контекст: конфигурации, схемы, документы. Используйте их для справочных материалов.
- Промпты (prompts) — готовые шаблоны, которые поставляет сервер; полезны, когда у вашей команды есть стандартный способ что-то делать (ревью кода, разбор инцидента).
Хороший первый сервер делает одну небольшую вещь хорошо. Мы построим сервер заметок с двумя инструментами: сохранить заметку и показать список заметок.
«Сервер» здесь — не стойка в дата-центре. Это небольшая программа, которая тихо сидит, отвечает на вопросы вроде «какие у вас есть инструменты?» и «запустите этот инструмент с такими аргументами» и отдаёт ответ. Вот и всё.
Чтобы повторять за уроком, нужны три вещи: Python 3.10 или новее, терминал и одна команда установки.
Сколько усилий это потребует. Код ниже занимает около сорока строк, и
запустить его можно за десять минут. Но на первый сервер всё равно заложите час.
Вот на что он уходит: настроить конфигурационный файл клиента так,
чтобы он указывал на правильный абсолютный путь; перезапустить приложение, чтобы
оно заметило изменение; научиться отлаживать программу, вывод которой вы не видите.
Причина в том, что print stdio-сервера попадает прямо в поток протокола и ломает
его. Код пишется легко, а трудности начинаются при подключении. Если ваш первый сервер не появляется
в клиенте, это обычное дело.
Минимальный сервер
Официальный Python SDK (pip install "mcp[cli]") даёт вам FastMCP, где
инструмент — это просто функция Python с декоратором. Аннотации типов становятся
JSON Schema, а докстринг — описанием, которое читает модель:
1# Разовая настройка, выполните в терминале:2# pip install "mcp[cli]"3# Это устанавливает официальный Python SDK для MCP.4 5# FastMCP — простой способ написать сервер: вы пишете обычные функции6# Python, а он превращает их в инструменты MCP.7from mcp.server.fastmcp import FastMCP8 9# У каждого сервера есть имя. Клиент показывает его пользователю.10mcp = FastMCP(#98c379">"notes")11 12# Наша «база данных» — просто словарь Python: заголовок заметки -> текст заметки.13# Он живёт в памяти, поэтому заметки пропадают, когда сервер останавливается.14_notes: dict[str, str] = {}15 16 17# @mcp.tool() регистрирует эту функцию как инструмент, который может вызвать модель.18# Докстринг ниже читает МОДЕЛЬ — пишите его для модели,19# а не для ревьюера-человека.20# Аннотации типов (title: str, body: str) становятся JSON Schema, по которой21# модель строит правильный вызов.22@mcp.tool()23def save_note(title: str, body: str) -> str:24 #98c379">"""Save a note under the given title. Overwrites if the title exists."""25 _notes[title] = body26 # Всё, что вы вернёте, — единственное, что получит модель.27 # Понятное сообщение лучше «сырой» выгрузки данных.28 return #98c379">"Saved: " + title29 30 31@mcp.tool()32def list_notes() -> str:33 #98c379">"""List all saved note titles."""34 if not _notes:35 # Полезное сообщение об ошибке позволяет модели исправиться самой.36 return #98c379">"No notes yet."37 return "\n".join(sorted(_notes))38 39 40# Ресурс — это справочный материал, который ПРИЛОЖЕНИЕ может прочитать в контекст:41# вызывать его решает не модель, а приложение.42@mcp.resource(#98c379">"notes://help")43def help_text() -> str:44 #98c379">"""Short guide to the notes server."""45 return #98c379">"Ask the model to save or list notes by title."46 47 48# Транспорт stdio: клиент сам запускает этот скрипт и общается с ним49# через стандартный ввод и вывод. Сетевые порты нигде не открываются.50if __name__ == #98c379">"__main__":51 mcp.run(transport=#98c379">"stdio")Три вещи, на которые стоит обратить внимание:
- Докстринг — это интерфейс. Модель никогда не видит ваш код — только имя функции, аннотации типов и докстринг. Пишите докстринг для модели.
- Аннотации типов задают схему вызова.
title: str, body: strстановится JSON Schema, по которой модель строит правильный вызов инструмента. - Транспорт stdio означает, что клиент запускает ваш скрипт и общается с ним через стандартный ввод и вывод. Ничто не слушает сетевой порт.
Проверьте, прежде чем трогать клиент
Не пропускайте этот шаг. Он отделяет «сервер сломан» от «клиент не находит мой файл» — а без него эти две ситуации выглядят одинаково. В SDK есть отладчик, который запускает ваш сервер и позволяет вызывать инструменты вручную в браузере:
# установите официальный SDK (один раз)
pip install "mcp[cli]"
# запустите сервер под Inspector, в браузере
mcp dev server.py
# ...или вызовите его прямо из терминала, без всякого клиента
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python server.py
# Если вы видите здесь список из двух своих инструментов, сервер работает.
# Если нет, ошибка в коде, а не в конфигурации клиента.Две вещи, которые ломают каждый первый сервер, и ни одна не очевидна:
- Отладка через print всё ломает. При транспорте stdio
print()пишет в тот самый поток, который использует протокол, поэтому случайный print портит разговор. Вместо этого пишите логи в файл (loggingсFileHandler) или в stderr. - Относительные пути не работают. Клиент запускает ваш скрипт из произвольного
рабочего каталога. Используйте в конфигурации абсолютный путь, а пути к файлам
стройте относительно
__file__, а не текущего каталога.
Зарегистрируйте его в клиенте
Конфигурационный файл выше — для Claude Desktop (Settings, затем Developer, затем
Edit Config). command вместе с args говорят: запусти этот скрипт как локальный
процесс и подключись к нему по stdio. После перезапуска приложения оба инструмента
автоматически появляются в контексте модели — попросите её «сохранить заметку с
названием groceries: молоко и хлеб» и посмотрите, как происходит вызов инструмента.
Cursor, VS Code (агентный режим Copilot), Zed и Claude Code используют похожую
JSON-структуру или эквивалентную команду claude mcp add. В этом и смысл
протокола: один и тот же сервер работает везде. Но гладко бывает не всегда:
клиенты различаются тем, где лежит конфигурационный файл, нужен ли полный
перезапуск и насколько громко они сообщают, что сервер не запустился (часто —
вообще никак). Сначала загляните в собственную документацию клиента по MCP, а затем
возвращайтесь сюда за разбором серверной части.
Локально или удалённо
Пока что сервер работает на вашей машине и запускается клиентом (stdio). Для команды его размещают удалённо, по HTTP:
{
"mcpServers": {
"notes": {
"url": "https://notes.example.com/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
Если в конфигурации указан URL, клиент подключается по Streamable HTTP, а не запускает процесс. Объём работы при этом вырастает скачком. Удалённый сервер — это публичная точка доступа, а значит, вам понадобятся полноценная аутентификация (в спецификации для этого предусмотрен OAuth), TLS, ограничение частоты запросов, логирование, управление секретами и кто-то, кто заметит, что сервер лёг. Этот список не короче, чем у любого API в реальной эксплуатации. Практическое правило: пока разрабатываете, начинайте со stdio, переходите на HTTP, когда сервер нужен более чем одному человеку или сервису, и считайте этот переход отдельным проектом.
Самые важные практики
- Чёткие описания инструментов. Скажите, что делает инструмент, когда его использовать и когда не использовать. «Сохраняет заметку. Используйте для текста, созданного пользователем; не используйте для фрагментов кода» лучше, чем «сохраняет данные».
- Небольшие наборы инструментов. Схема каждого инструмента занимает место в контексте, а слишком много похожих инструментов сбивает модель с толку. Десять целенаправленных инструментов лучше сорока пересекающихся.
- Полезные сообщения об ошибках. Когда вызов не удался, верните сообщение, по которому модель поймёт, что делать дальше: «Заметка ‘groceries’ не найдена. Доступны: milk-list, ideas», а не «сырую» трассировку стека. С понятным сообщением об ошибке модель справляется без помощи человека.
- Возвращайте структурированные, компактные результаты. Обрежьте данные до того, что нужно модели; огромные ответы сжигают окно контекста.
- Делайте инструменты безопасными по умолчанию. Подтверждайте разрушительные операции, ограничивайте права учётных данных и никогда не позволяйте инструменту делать больше, чем обещано в его описании.
Проверьте себя
Что делает докстринг функции-инструмента в MCP-сервере?
Что на практике означает транспорт stdio?
Вы хотите предоставить справочный материал, который приложение читает в контекст, а не то, что модель решает вызвать. Какую возможность использовать?
Куда идти дальше
Теперь вы умеете создавать, подключать и доводить до надёжного состояния MCP-сервер — востребованный навык, ведь команды встраивают MCP во всё подряд. Если вы пропустили теорию, урок MCP: USB-порт для ИИ объясняет три роли и путь запроса, а Инструменты и вызов функций раскрывает механизм, на котором построен MCP.