Инструкция по использованию протокола MCP для управления IoT

Инструкция по использованию протокола MCP для управления IoT

Этот документ представляет, как реализовать управление IoT устройствами ESP32 на основе протокола MCP. Для подробного потока протокола см. Документацию протокола MCP.

Введение

MCP (Model Context Protocol) - это протокол нового поколения, рекомендуемый для управления IoT, который обнаруживает и вызывает “инструменты” (Tool) между бэкендом и устройствами через стандартный формат JSON-RPC 2.0, реализуя гибкое управление устройствами.

Типичный поток использования

  1. После запуска устройство устанавливает соединение с бэкендом через базовый протокол (например, WebSocket/MQTT).
  2. Бэкенд инициализирует сессию через метод initialize протокола MCP.
  3. Бэкенд получает все инструменты (функции), поддерживаемые устройством, и описания параметров через tools/list.
  4. Бэкенд вызывает конкретные инструменты через tools/call, реализуя управление устройством.

Подробные форматы протокола и взаимодействие см. в Документации протокола MCP.

Описание метода регистрации инструментов на стороне устройства

Устройство регистрирует “инструменты”, которые могут быть вызваны бэкендом, через метод McpServer::AddTool. Его общая сигнатура функции:

void AddTool(
    const std::string& name,           // Имя инструмента, рекомендуется уникальное и иерархическое, например: self.dog.forward
    const std::string& description,    // Описание инструмента, краткое объяснение функции, удобное для понимания большой модели
    const PropertyList& properties,    // Список входных параметров (может быть пустым), поддерживаемые типы: булевый, целочисленный, строковый
    std::function<ReturnValue(const PropertyList&)> callback // Реализация обратного вызова при вызове инструмента
);

Описание параметров

  • name: Уникальный идентификатор инструмента, рекомендуется стиль именования “модуль.функция”.
  • description: Описание на естественном языке, удобное для понимания ИИ/пользователя.
  • properties: Список параметров, поддерживает булевый, целочисленный, строковый типы, может указать диапазон и значения по умолчанию.
  • callback: Фактическая логика выполнения при получении запроса вызова, возвращаемое значение может быть bool/int/string.

Типичный пример регистрации (на примере ESP-Hi)

void InitializeTools() {
    auto& mcp_server = McpServer::GetInstance();
    
    // Пример 1: Без параметров, управление движением робота вперед
    mcp_server.AddTool("self.dog.forward", "Робот движется вперед", PropertyList(), 
        [this](const PropertyList&) -> ReturnValue {
            servo_dog_ctrl_send(DOG_STATE_FORWARD, NULL);
            return true;
        });
    
    // Пример 2: С параметрами, установка RGB цвета света
    mcp_server.AddTool("self.light.set_rgb", "Установка RGB цвета", PropertyList({
        Property("r", kPropertyTypeInteger, 0, 255),
        Property("g", kPropertyTypeInteger, 0, 255),
        Property("b", kPropertyTypeInteger, 0, 255)
    }), [this](const PropertyList& properties) -> ReturnValue {
        int r = properties["r"].value<int>();
        int g = properties["g"].value<int>();
        int b = properties["b"].value<int>();
        led_on_ = true;
        SetLedColor(r, g, b);
        return true;
    });
}

Примеры общих вызовов инструментов JSON-RPC

1. Получение списка инструментов

{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "params": { "cursor": "" },
  "id": 1
}

2. Управление движением шасси вперед

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "self.chassis.go_forward",
    "arguments": {}
  },
  "id": 2
}

3. Переключение режима освещения

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "self.chassis.switch_light_mode",
    "arguments": { "light_mode": 3 }
  },
  "id": 3
}

4. Поворот камеры

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "self.camera.set_camera_flipped",
    "arguments": {}
  },
  "id": 4
}

Лучшие практики регистрации инструментов

1. Стандарты именования

  • Использовать иерархическое именование: self.модуль.функция
  • Сохранять имена краткими и описательными
  • Избегать специальных символов и пробелов
// Примеры хорошего именования
"self.light.set_brightness"
"self.motor.rotate_left"  
"self.sensor.get_temperature"
"self.display.show_text"
// Не рекомендуемое именование
"light-control"  // Недостаток иерархии
"setBrightness"  // Недостаток информации о модуле
"控制灯光"       // Неанглийское именование
"self.light.set brightness" // Содержит пробелы

2. Дизайн параметров

  • Использовать четкие имена параметров
  • Установить разумные диапазоны параметров и значения по умолчанию
  • Предоставить подробные описания параметров
// Пример дизайна параметров
PropertyList({
    Property("volume", kPropertyTypeInteger, 0, 100, "Размер громкости, диапазон 0-100"),
    Property("enabled", kPropertyTypeBool, true, "Включить ли функцию"),
    Property("message", kPropertyTypeString, "", "Содержимое текста для отображения")
})

3. Обработка ошибок

Выполнить соответствующую обработку ошибок в обратных вызовах инструментов:

mcp_server.AddTool("self.servo.set_angle", "Установка угла серво", PropertyList({
    Property("angle", kPropertyTypeInteger, 0, 180)
}), [this](const PropertyList& properties) -> ReturnValue {
    try {
        int angle = properties["angle"].value<int>();
        if (angle < 0 || angle > 180) {
            return "Угол вне диапазона: 0-180";
        }
        SetServoAngle(angle);
        return true;
    } catch (const std::exception& e) {
        return std::string("Ошибка установки угла: ") + e.what();
    }
});

Техники отладки

1. Ведение журнала

Добавить журналирование в реализацию инструментов:

mcp_server.AddTool("self.debug.test", "Тестовый инструмент", PropertyList(), 
    [this](const PropertyList& properties) -> ReturnValue {
        ESP_LOGI("MCP", "Тестовый инструмент был вызван");
        // Выполнить тестовую логику
        return "Тест завершен";
    });

2. Проверка параметров

Добавить подробную проверку параметров:

int volume = properties["volume"].value<int>();
if (volume < 0 || volume > 100) {
    ESP_LOGE("MCP", "Недействительный параметр громкости: %d", volume);
    return "Громкость должна быть между 0-100";
}

Примечания

  • Имена инструментов, параметры и возвращаемые значения должны основываться на регистрации AddTool на стороне устройства.
  • Рекомендуется единообразно использовать протокол MCP для управления IoT во всех новых проектах.
  • Для подробного протокола и продвинутого использования см. Документацию протокола MCP.

Связанная документация

Часто задаваемые вопросы

Q: Как обработать тайм-аут вызова инструмента?

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

Q: Можно ли динамически добавлять или удалять инструменты?

A: Текущая реализация обычно регистрирует инструменты при запуске устройства, динамическое управление требует переопределения механизма регистрации инструментов.

Q: Как обработать разрешения вызова инструментов?

A: Можно добавить логику проверки разрешений в обратные вызовы инструментов или выполнить контроль разрешений на уровне бэкенд API.