Подключите к Claude Code собственный MCP-сервер с одной функцией: получить вымышленный товар по идентификатору. Проверим результат, отсутствие товара и недоступность записи. Так будет видно, что агент действительно вызвал программу и какие действия она ему предлагает.

Разберите, что будет запущено

MCP, или Model Context Protocol, описывает обмен между приложением с агентом и отдельной программой. Сервер сообщает, какие инструменты у него есть, а приложение может вызвать их с аргументами. В нашем упражнении Claude Code будет клиентом, Python-программа — сервером.

«Сервер» здесь не означает внешний сайт. Используем локальный транспорт stdio: Claude Code запускает дочерний процесс и общается с ним через стандартные ввод и вывод. Порт и публичный адрес не понадобятся. Такой запуск описан в документации официального Python SDK.

Понадобятся установленный Claude Code, рабочая сессия, Python 3.10 или новее и редактор. Для чтения примера достаточно понимать функцию, словарь и условие. Установку агента пройдите по официальному руководству. Если знакомство с агентами только начинается, сначала разберите учебный запуск Qwen Code.

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

Подготовьте отдельную папку и SDK

Создайте папку mcp-catalog-demo и откройте её в терминале. В macOS, Linux или WSL выполните:

python3 -m venv .venv
.venv/bin/python -m pip install "mcp==2.3.0"

В PowerShell Windows:

py -m venv .venv
.venv\Scripts\python.exe -m pip install "mcp==2.3.0"

Команда py подходит, если установлена программа запуска Python py. Если у вас несколько Python, сначала проверьте, что выбранный интерпретатор удовлетворяет требованию SDK.

Виртуальное окружение .venv отделяет зависимости примера от других проектов. Используем его Python напрямую: активировать окружение и менять политику выполнения PowerShell не требуется.

На 9 октября 2026 года текущая стабильная линия SDK — v2. Для воспроизводимости зафиксирована официальная версия 2.3.0, опубликованная 2 октября. Требование Python 3.10+ и установка описаны в руководстве SDK. Примеры v1 могут использовать другие импорты: не смешивайте их с этим кодом.

Создайте сервер с одной операцией чтения

Сохраните в server.py:

import json

from mcp.server import MCPServer

mcp = MCPServer("Demo catalog")

CATALOG = {
    "cup": {"name": "Учебная кружка", "price": 450},
    "lamp": {"name": "Учебная лампа", "price": 1200},
}


@mcp.tool()
def get_product(product_id: str) -> str:
    """Возвращает вымышленный товар. Не изменяет каталог."""
    product = CATALOG.get(product_id)
    if product is None:
        result = {"found": False, "id": product_id}
    else:
        result = {
            "found": True,
            "id": product_id,
            "name": product["name"],
            "price": product["price"],
        }
    return json.dumps(result, ensure_ascii=False)


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

Декоратор @mcp.tool() делает функцию доступной через MCP. Имя, описание и тип аргумента дают клиенту сведения для вызова. Функция получает строку product_id и возвращает строку с JSON. Клиент видит, какой аргумент нужно передать, ещё до вызова.

Каталог встроен в программу. Функция не читает файлы, не обращается к сети и не записывает цену. Если товара нет, она возвращает found: false. Так клиент получает определённый результат и не должен дополнять каталог догадкой.

Вызов mcp.run() без аргументов запускает stdio. Условие if __name__ == "__main__" позволяет импортировать объект сервера для проверки, не запуская бесконечное ожидание входных сообщений.

Не добавляйте отладочный print() в сервер: стандартный вывод занят протоколом. Для диагностики используйте журнал в стандартном потоке ошибок, как рекомендует документация SDK.

Проверьте программу отдельно от агента

Сохраните рядом check_server.py:

import asyncio
import json

from mcp import Client
from mcp.types import TextContent

from server import CATALOG, mcp


async def check() -> None:
    before = json.dumps(CATALOG, sort_keys=True)
    async with Client(mcp, raise_exceptions=True) as client:
        listed = await client.list_tools()
        assert {tool.name for tool in listed.tools} == {"get_product"}

        known = await client.call_tool(
            "get_product", {"product_id": "cup"}
        )
        assert not known.is_error
        assert isinstance(known.content[0], TextContent)
        data = json.loads(known.content[0].text)
        assert data == {
            "found": True, "id": "cup",
            "name": "Учебная кружка", "price": 450,
        }

        missing = await client.call_tool(
            "get_product", {"product_id": "unknown"}
        )
        assert not missing.is_error
        assert isinstance(missing.content[0], TextContent)
        assert json.loads(missing.content[0].text) == {
            "found": False, "id": "unknown",
        }

        absent = await client.call_tool(
            "set_price", {"product_id": "cup", "price": 1}
        )
        assert absent.is_error

    assert json.dumps(CATALOG, sort_keys=True) == before
    print("Проверки учебного сервера прошли")


if __name__ == "__main__":
    asyncio.run(check())

SDK позволяет подключить Client к объекту сервера в памяти. Проверка обращается к серверу в той же программе. Так мы сначала проверяем данные и список функций, а затем отдельно разбираемся с запуском сервера из Claude Code.

Вызов неизвестного инструмента возвращает результат с is_error, что описано в справочнике клиента. Проверка ожидает это поле, а не случайное исключение.

Выполните .venv/bin/python check_server.py; в Windows — .venv\Scripts\python.exe check_server.py. При успехе появится строка о пройденных проверках. Если возникло исключение, прочитайте его до подключения агента. Неверный импорт часто означает, что установилась другая линия SDK или запускался Python вне .venv.

Зарегистрируйте локальное подключение

Всё ещё находясь в mcp-catalog-demo, выполните в Bash:

demoRoot="$(pwd)"
claude mcp add --transport stdio --scope local demo-catalog -- "$demoRoot/.venv/bin/python" "$demoRoot/server.py"

В PowerShell:

$demoRoot = (Get-Location).Path
claude mcp add --transport stdio --scope local demo-catalog -- "$demoRoot\.venv\Scripts\python.exe" "$demoRoot\server.py"

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

Разделитель -- отделяет параметры Claude Code от команды сервера. --scope local делает подключение личным для текущего проекта; запись хранится в ~/.claude.json под путём проекта. Это отличается от общей конфигурации в .mcp.json. Синтаксис и область действия приведены в документации Claude Code.

Команда добавления сохраняет настройки. Затем выполните:

claude mcp get demo-catalog
claude mcp list

Запустите claude из той же папки и откройте /mcp. Найдите demo-catalog, проверьте состояние соединения и инструмент get_product. Если возникнет запрос доверия или разрешения, прочитайте точную команду и путь. Для упражнения достаточно этого сервера.

Сверьте реальный вызов и результат

Отправьте агенту:

Через MCP-сервер demo-catalog вызовите get_product
с product_id="cup".
Назовите id, название и цену из результата инструмента.
Не читайте server.py вместо вызова и не меняйте файлы.

Ожидаемые данные заданы исходным кодом: cup, «Учебная кружка», 450. Смотрите журнал инструментов. Верный текст ответа сам по себе недостаточен: агент мог прочитать программу или вспомнить предыдущий запрос.

Затем запросите product_id="unknown". Агент должен сообщить, что товар не найден. Если он придумал название, верните его к полю found из реального ответа.

Наконец, предложите изменить цену через этот MCP-сервер. Операции set_price у него нет. Агент должен назвать ограничение, сохранив данные. Если вместо этого предложена правка server.py, остановите такое действие: оно меняет программу через другой инструмент и выходит за условия проверки подключения.

ПроверкаЧто должно быть видно
Чтение cupВызов get_product и совпадение трёх полей
Чтение unknownfound=false без выдуманного товара
Запись ценыНет доступной операции записи; каталог прежний

Подключение к Claude Code в этом материале не выполнялось. Приведены код и ожидаемые свойства; фактические вызовы и ответы получите при прохождении упражнения.

Уточните границы доступа

«Только чтение» относится к интерфейсу нашей функции. Это не изоляция всего Python-процесса. Локальный сервер запускается как программа на вашей машине; другой код в нём мог бы читать файлы или отправлять запросы.

Разрешение MCP-инструмента тоже не делает встроенную песочницу Bash защитой этого процесса. Песочница Claude Code ограничивает соответствующие команды оболочки; MCP и другие инструменты имеют отдельные границы.

Перед чужим сервером изучайте код, команду запуска и предлагаемые операции. В рабочем подключении права должны ограничиваться и на стороне источника данных: например, отдельной учётной записью с доступом только к чтению. Поле в промпте «работайте осторожно» таких прав не создаёт.

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

Завершите упражнение

Чтобы убрать подключение, выйдите из сессии и выполните из учебной папки:

claude mcp remove demo-catalog --scope local

Проверьте список серверов. Удаление настройки не удаляет server.py или виртуальное окружение.

Когда чтение, неизвестный товар и отсутствие записи проверены, откройте бесплатный раздел курса «Разработка с AI-агентами: инженерный подход». Сохраните этот маленький сервер как пример, в котором можете объяснить каждую доступную операцию.


«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.

ТелеграмПодкастБесплатные учебники