Главная / Статьи

Создайте свой первый сервер MCP на Python: инструменты, ресурсы, команды и инспектор

Создание сервера MCP на Python с FastMCP: удобный инструмент с функциями, ресурсами и шаблонами запросов, поддержкой ввода-вывода и HTTP, возможностями отладки через Inspector и одной конфигурацией для 3 клиентов.

Издатель: REACTAPP.TOP9 мин чтения2056 слов

Источники: What Is MCP? Build a Custom MCP Server in Python

Представьте себе три практически идентичных файла на Python с именами Claude, Cursor и ChatGPT, каждый из которых обращается к API Slack, парсит ответ и преобразует его для конкретного клиента ИИ. Теперь представьте, что все три файла заменяются одним файлом, из которого могут напрямую читать все эти клиенты. Когда в Slack меняется какое-либо поле, его нужно исправлять только один раз. Этот единственный файл — это сервер MCP, и его создание занимает примерно полдня.

MCP в нескольких предложениях

Протокол Model Context Protocol настолько прост, что его можно описать за три сообщения. Клиент подключается к серверу и спрашивает, что тот может делать. Сервер отвечает о инструментах, которые может использовать модель, о данных, которые может получить хост, и о промптах, которые может выбрать пользователь. Затем клиент вызывает инструмент с аргументами, сервер его запускает и отвечает. В сети это и есть вся суть.

То, что делает MCP достойным использования, — это не формат сообщений, а общее соглашение. Claude Code, Cursor, Copilot в VS Code и другие инструменты используют одинаковый формат взаимодействия, поэтому файл на Python, написанный сейчас, позже можно будет присоединить к любому из них.

Сервер может предлагать любой из трех типов возможностей:

  • Инструменты — это функции, которые можно вызвать, и модель выбирает их по имени; например, функция для поиска в базе данных, создания ветки или сжатия логов.
  • Ресурсы — это материалы только для чтения, такие как файл конфигурации, схема или последние коммиты, которые хост загружает при необходимости.
  • Шаблоны запросов — это повторно используемые шаблоны, которые выбирает пользователь; они отображаются как команда с слешом в Claude Code или как пункт меню в Claude Desktop.

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

Почему раньше умножались интеграции

До появления MCP подключение ИИ-инструмента к бэкенду означало необходимость написания адаптера, преобразующего структуру бэкенда в формат, требуемый клиентом для вызова инструмента. Один клиент ожидал схему инструментов Anthropic, другой использовал собственный формат, а третий требовал структуру вызова функций от OpenAI. Поиск в Slack оставался неизменным, в то время как вокруг него формировалось три слоя обёрток.

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

Дизайн намеренно асимметричен. Клиент решает всё: какие серверы загружать, когда вызывать инструменты и как отображать результаты. Обязанности сервера гораздо меньше — он лишь объявляет о своих инструментах и отвечает при их вызове. Такая асимметрия позволяет протоколу оставаться достаточно компактным, чтобы каждый крупный клиент мог его быстро реализовать.

Вспомогательный инструмент для ежедневных собраний в одном файле

Вспомогательный инструмент для стендапа — это классический внутренний инструмент, который инженеры постоянно переписывают. Он считывает локальный журнал git и подводит итоги того, что делал разработчик вчера. Здесь он реализован как инструмент, ресурс и подсказка в одном файле на Python.

Вам понадобится пакет FastMCP — высокоуровневый слой на Python, на котором работают многие серверы MCP.

pip install fastmcp

Создайте файл standup_server.py. Сначала в нем импортируется библиотека, а затем определяется инструмент.

# standup_server.py
import subprocess
from typing import TypedDict

from fastmcp import FastMCP

mcp = FastMCP("standup-helper")


class StandupSummary(TypedDict):
    branch: str
    since: str
    commit_count: int
    commits: list[str]


@mcp.tool()
def summarize_standup(
    branch: str = "main",
    since: str = "yesterday",
) -> StandupSummary:
    """Summarize recent git activity for a standup.

    Reads the local git log on the given branch since the
    given time window. Returns commit count and one-line
    subjects for each commit. Used by AI clients via MCP.
    """
    try:
        result = subprocess.run(
            [
                "git", "log",
                f"--since={since}",
                "--pretty=format:%h %s",
                branch,
            ],
            capture_output=True,
            text=True,
            timeout=5,
            check=True,
        )
    except (subprocess.CalledProcessError,
            subprocess.TimeoutExpired) as exc:
        return {
            "branch": branch,
            "since": since,
            "commit_count": 0,
            "commits": [f"git error: {exc}"],
        }

    lines = [
        line for line in result.stdout.splitlines() if line
    ]
    return {
        "branch": branch,
        "since": since,
        "commit_count": len(lines),
        "commits": lines,
    }


# resources and prompts come next

На фоне происходит выполнение четырех задач декоратором. Он считывает указания типов для создания схемы JSON, использует документацию в качестве описания, отображаемого модели, указывает инструмент в списке возможностей сервера во время установления соединения и направляет входящие запросы JSON-RPC к соответствующей функции.

Обратите внимание на вызов подпроцесса с строгим таймаутом в пять секунд. Если команда git застревает, приложение-хост, ожидающее ответа от инструмента, также застревает вместе с ним, поэтому каждый внешний вызов должен иметь таймаут. Ветка с ошибками также возвращает структуру в виде словаря, аналогичную ветке с успешным выполнением, так что модель всегда получает предсказуемую структуру. Более сложные инструменты могут также вызывать GitHub CLI для открытия pull-запросов, добавляя еще один вызов подпроцесса и включая его результат в словарь.

Далее идут ресурс и промпт, которые завершают формирование файла.

# standup_server.py (continued)

@mcp.resource("recent_commits://main")
def recent_commits_main() -> str:
    """Last 10 commits on the main branch, plain text.

    Resources are pulled by the host opportunistically.
    They are not invoked by the model the way tools are.
    """
    result = subprocess.run(
        [
            "git", "log",
            "-n", "10",
            "--pretty=format:%h %ad %s",
            "--date=short",
            "main",
        ],
        capture_output=True,
        text=True,
        timeout=5,
    )
    return result.stdout or "(no commits found)"


@mcp.prompt("standup_template")
def standup_template(focus: str = "shipping work") -> str:
    """Reusable standup question exposed as a prompt
    template. Surfaces as a slash command in clients that
    expose prompts (e.g. /standup_template in Claude Code).
    """
    return (
        f"Summarize what I worked on yesterday, focusing on "
        f"{focus}. Use the summarize_standup tool to get the "
        f"git log, then write a one-paragraph standup note."
    )


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

URI ресурса — это просто строка. Его форма не регулируется строгой схемой, хотя обычно она состоит из схемы, за которой следует путь; кроме того, FastMCP поддерживает шаблоны URI при необходимости использования параметров. Пrompt представляет собой функцию, возвращающую форматированный текст. Клиенты представляют его в виде шаблона, который вызывает пользователь, а Claude Code обеспечивает его выполнение через команду с слешом. Пromptы никогда не вставляются автоматически, поэтому пользователь всегда выбирает их сам.

Последняя строка запускает сервер. Не требуется порт, веб-фреймворк или механизм обработки событий, поскольку FastMCP самостоятельно обрабатывает ввод-вывод.

Транспорты и аутентификация

Транспорт определяет способ передачи сообщений JSON-RPC между клиентом и сервером.

Для локальных серверов используйте stdio. Хост запускает ваш скрипт в качестве дочернего процесса. Stdio обходит весь сетевой стек: нет необходимости в портах, TLS или аутентификации, поскольку дочерний процесс наследует границы доверия хоста.

Используйте Streamable HTTP, когда серверу необходимо сохранять состояние между запросами или к нему должны иметь доступ несколько устройств. У удаленного сервера есть выход в сеть, поэтому требуется строгая аутентификация.

# bottom of standup_server.py

if __name__ == "__main__":
    # Default transport is stdio. The host (Claude Code,
    # Cursor, Claude Desktop, etc.) launches this script
    # as a subprocess and talks to it over stdin/stdout.
    # No port, no TLS, no auth. The trust boundary is
    # whoever launched the host.
    mcp.run()

    # To expose the same server over the network instead,
    # use Streamable HTTP. SSE was deprecated in the
    # March 2025 spec update. Do not use it for new code.
    #
    # Production HTTP also needs an auth layer in front.
    # OAuth 2.1 with Dynamic Client Registration is the
    # current pattern. See Week 22 for the full flow.
    #
    # mcp.run(
    #     transport="streamable-http",
    #     host="0.0.0.0",
    #     port=8000,
    # )

Вызов функции run без аргументов означает использование stdio, а комментированный блок показывает, как выглядит процесс обновления через HTTP. В данной версии сохраняется использование stdio, что является правильным выбором для большинства внутренних инструментов разработчиков. Инструмент, который читает только локальные файлы или локальный репозиторий, вообще не должен находиться за сетевым интерфейсом.

Цикл локальной разработки

Не тестируйте новый сервер, подключая его напрямую к клиенту ИИ. Клиенты создаются с учетом устойчивости: они молча игнорируют несоответствия схемы и отсутствующие функции, чтобы обеспечить работу чата, а при сбое инструмента модель просто извиняется.

Вместо этого используйте официальный MCP Inspector — интерфейс разработки в браузере, который взаимодействует с вашим сервером через stdio.

npx @modelcontextprotocol/inspector python standup_server.py

Эта команда запускает ваш скрипт в качестве подпроцесса и открывает локальную панель управления. Вы можете прочитать необработанные данные обмена, увидеть, какие именно возможности объявил ваш сервер, а также запускать инструменты и ресурсы в реальном времени.

В этом цикле обычно возникают три проблемы:

  1. Несоответствие схемы. Значение, возвращаемое функцией, не совпадает с указанным типом, и инспектор сразу же на это указывает.
  2. Отсутствующая возможность. Вы вызываете функцию, которую сервер никогда не объявлял, и панель управления отображает то, что на самом деле было зарегистрировано во время переговоров.
  3. Загрязнение вывода. Любой ненужный вызов print() портит поток JSON-RPC, поскольку через стандартный ввод-вывод передаются сообщения. Отправляйте логи в стандартную ошибку с помощью модуля logging в Python — этот канал предназначен для читаемой диагностики.

FastMCP также предоставляет режим разработки, при котором сервер перезагружается при изменении файла, что удобно во время работы с инспектором.

Один сервер, несколько клиентов

Теперь преимущества такого подхода становятся очевидными. Существует единый файл на Python, проверяемый в инспекторе, который можно использовать с разными клиентами ИИ без необходимости написания кода, специфичного для каждого клиента. Каждый клиент читает конфигурационный файл с указанием локальных серверов для запуска, причем структура JSON остается одинаковой в Claude Code, Cursor и Claude Desktop: объект mcpServers, содержащий команду для выполнения и её аргументы.

{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}
{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}
{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}

Ключ, команда и аргументы одинаковы во всех трех случаях. Различается только местоположение файла конфигурации в зависимости от клиента.

Откройте любой из них и запросите проведение встречи. Клиент запускает процесс Python, договаривается о возможностях через стандартные ввод-вывод, показывает модели инструмент для подведения итогов и направляет запрос модели обратно в ваш скрипт. Затем запускается Git, возвращается словарь, и модель записывает результаты встречи, всегда следуя тому же пути обработки кода.

ChatGPT отсутствует в этом списке, поскольку его интерфейсы взаимодействия работают с удаленными серверами через Streamable HTTP. Для интеграции этого вспомогательного инструмента потребуются публичный URL и слой аутентификации, что сильно отличается от принципов работы этой строго локальной версии.

Когда не стоит использовать MCP

Этот протокол не является универсальным решением. В некоторых ситуациях он не подходит:

  • Пути, чувствительные к задержкам. Каждый вызов инструмента влечёт за собой два обмена данными по протоколу JSON-RPC, поэтому инструменты, находящиеся в тесных внутренних циклах, должны использовать прямую интеграцию с SDK.
  • Персонализированный интерактивный вывод. Если вам нужна определённая диаграмма или специальная форма, приходится создавать интеграцию с хост-системой, поскольку протокол разделяет данные и их отображение.
  • Настройки, полностью находящиеся под вашим контролем. Если одна команда разрабатывает как клиент ИИ, так и бэкенд, их прямая связь может быть проще в обслуживании, чем использование MCP в качестве посредника.

Небольшой протокол, большие изменения

Весь протокол состоит из трех примитивов, двух механизмов передачи данных и одного шага обмена сообщениями по протоколу JSON-RPC. Именно такая простота является его сильной стороной: она разделяет тех, кто создаёт интеллектуальные алгоритмы, и тех, кто обеспечивает контекст. Написание первого сервера занимает всего несколько часов, и это оправдывается каждый раз, когда появляется новый инструмент искусственного интеллекта.