Инструкция по использованию протокола MCP для управления IoT
Введение
MCP (Model Context Protocol) - это протокол нового поколения, рекомендуемый для управления IoT, который обнаруживает и вызывает “инструменты” (Tool) между бэкендом и устройствами через стандартный формат JSON-RPC 2.0, реализуя гибкое управление устройствами.
Типичный поток использования
- После запуска устройство устанавливает соединение с бэкендом через базовый протокол (например, WebSocket/MQTT).
- Бэкенд инициализирует сессию через метод
initialize
протокола MCP. - Бэкенд получает все инструменты (функции), поддерживаемые устройством, и описания параметров через
tools/list
. - Бэкенд вызывает конкретные инструменты через
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.
Связанная документация
- Документация протокола MCP - Подробный поток взаимодействия протокола
- Протокол связи WebSocket - Базовый протокол связи WebSocket
- Смешанный протокол MQTT + UDP - Смешанный протокол связи MQTT + UDP
Часто задаваемые вопросы
Q: Как обработать тайм-аут вызова инструмента?
A: Избегать длительных блокирующих операций в обратных вызовах инструментов, для трудоемких операций можно обрабатывать асинхронно и немедленно возвращать статус.
Q: Можно ли динамически добавлять или удалять инструменты?
A: Текущая реализация обычно регистрирует инструменты при запуске устройства, динамическое управление требует переопределения механизма регистрации инструментов.
Q: Как обработать разрешения вызова инструментов?
A: Можно добавить логику проверки разрешений в обратные вызовы инструментов или выполнить контроль разрешений на уровне бэкенд API.