Почему ваш умный дом тупее, чем кажется
Вы собрали ферму датчиков. Температура, влажность, CO₂, вибрация — всё летит в MQTT-брокер. Данные аккуратно складываются в TimescaleDB. Красота. Но когда нужно узнать «какой тренд температуры за последние сутки и не пора ли включать кондиционер?» — вы лезете в Grafana, строите дашборд, пишете SQL-запрос.
А что если просто спросить: «Эй, LLM, что там с датчиками?» — и получить ответ на нормальном русском языке? Причём без копирования данных в облако, без сложных API и без посредников. Model Context Protocol (MCP) от Anthropic превращает эту фантазию в рабочую архитектуру. Сегодня я покажу, как это собрать.
MCP — это не очередной «протокол для ИИ». Это, как я писал в обзоре протокола, USB-порт для нейросетей. LLM через MCP получает доступ к инструментам — базам данных, брокерам сообщений, API. Без всяких костылей.
На июнь 2026 года MCP уже не экспериментальная фича. Его поддержка встроена в llama.cpp (прочтите как это развивалось), OpenWebUI и даже LM Studio (хотя там пока отстаёт).
Архитектура: что куда воткнуть
Схема предельно простая. Датчики шлют данные в MQTT (Eclipse Mosquitto 2.0.19). Подписчик-коннектор сохраняет их в TimescaleDB 2.17 (реляционная база, заточенная под временные ряды). Два MCP-сервера — один для чтения из TimescaleDB, другой для подписки на MQTT топики — открывают LLM каналы к данным. Клиент (OpenWebUI или ваш скрипт) подключается к LLM с поддержкой MCP.
| Компонент | Что делает | Версия (2026) |
|---|---|---|
| Mosquitto | Принимает MQTT-сообщения от датчиков | 2.0.19 |
| TimescaleDB | Хранит временные ряды, поддерживает continuous aggregates | 2.17 |
| MCP-сервер TimescaleDB | Превращает SQL-запросы в инструменты MCP | — |
| MCP-сервер MQTT | Позволяет LLM подписываться или публиковать в топики | — |
| LLM (локальная) | Понимает запросы и вызывает инструменты | GPT-OSS 20B, Llama 4.5 |
Фаза 1: Поднимаем инфраструктуру
1 MQTT + TimescaleDB
Деплою через docker-compose. Никаких хитростей — только production-ready настройки.
version: '3.8'
services:
mosquitto:
image: eclipse-mosquitto:2.0.19
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf
ports:
- "1883:1883"
timescaledb:
image: timescale/timescaledb:2.17.0-pg16
environment:
POSTGRES_PASSWORD: iot_secret
volumes:
- ./data:/var/lib/postgresql/data
ports:
- "5432:5432"
connector:
build: ./connector
depends_on:
- mosquitto
- timescaledbКоннектор — простой Python-скрипт на paho-mqtt и asyncpg. Он подписывается на sensors/# и вставляет данные в TimescaleDB. БД инициализирую с hypertable:
CREATE TABLE sensor_data (
time TIMESTAMPTZ NOT NULL,
device_id TEXT NOT NULL,
temperature FLOAT,
humidity FLOAT
);
SELECT create_hypertable('sensor_data', 'time');2 MCP-сервер для TimescaleDB
Беру пример self-hosted MCP-сервера для финансов и адаптирую. Инструменты: query_sensor_data и get_latest_reading.
from mcp.server import FastMCP
import asyncpg
mcp = FastMCP("timescaledb")
@mcp.tool()
async def query_sensor_data(device_id: str, hours: int = 1):
conn = await asyncpg.connect("postgresql://postgres:iot_secret@localhost:5432/iot")
rows = await conn.fetch(
"SELECT time, temperature, humidity FROM sensor_data "
"WHERE device_id=$1 AND time > NOW() - $2::interval",
device_id, f"{hours} hours")
await conn.close()
return [dict(r) for r in rows]
@mcp.tool()
async def get_latest_reading(device_id: str):
# аналогично, последняя запись
pass
if __name__ == "__main__":
mcp.run(transport="sse")Запускаю на порту 8001. Важно: MCP-серверы должны быть доступны LLM-клиенту. Использую transport="sse" — Server-Sent Events. Это стандартный способ для MCP (его использует MCP Chat Studio).
3 MCP-сервер для MQTT
Он должен дать LLM возможность подписываться на топики и публиковать команды. Например, «включи реле на устройстве kitchen_lamp». Реализую через библиотеку paho-mqtt с asyncio.
@mcp.tool()
async def publish_to_topic(topic: str, payload: str):
client = mqtt.Client()
client.connect("mosquitto", 1883, 60)
client.publish(topic, payload)
return f"Published to {topic}: {payload}"
@mcp.tool()
async def subscribe_and_get_latest(topic: str, timeout: int = 5):
client = mqtt.Client()
messages = []
# подписка, ожидание, возврат
...Безопасность: не давайте LLM публиковать в системные топики. В production используйте ACL Mosquitto и проверку прав в MCP-сервере.
4 LLM клиент с MCP
На июнь 2026 проще всего взять OpenWebUI — у него рождённая поддержка MCP (заходите в Admin Settings > Connections > MCP Servers). Добавляете URL серверов (http://localhost:8001/sse и http://localhost:8002/sse) — и LLM получает инструменты. Либо используете llama.cpp с флагом --mcp-server-timescaledb (подробнее тут).
Живой диалог с инфраструктурой
Запускаю всё. Говорят нейросеть: «Какая средняя влажность в теплице за последние 2 часа?» LLM вызывает query_sensor_data(device_id="greenhouse", hours=2), получает массив точек, вычисляет среднее и отвечает: «Средняя влажность — 72%, что на 5% выше нормы. Рекомендую включить осушитель.»
Прошу: «Включи осушитель в теплице». LLM вызывает publish_to_topic("actuators/greenhouse/dehumidifier", "ON").
Всё работает. Никакого кода на стороне пользователя — только естественный язык.
Грабли, на которые я наступил
- Тайм-ауты LLM: если запрос к TimescaleDB занимает >30 секунд (агрегаты по огромным таблицам), LLM может сбросить инструмент. Решение — использовать continuous aggregates в TimescaleDB, чтобы данные были предвычисленными.
- Спам MQTT: LLM может вызвать subscribe без фильтра и утонуть в сообщениях. В MCP-сервере MQTT я ограничил количество возвращаемых сообщений (до 100) и ввёл обязательный timeout.
- Контекстный токсикоз: MCP-сервер возвращает много сырых данных. Если датчиков 1000, ответ может забить контекст. Используйте лимиты в инструментах — например,
limit=50. Полезно прочитать как mcp-context-proxy режет болтовню — может пригодиться. - Аутентификация: MCP-серверы висят на localhost, но если их выставить наружу — без HTTPS и токенов не обойтись. Схема с обходом аутентификации для локальных моделей тут не катит.
Финальный трюк: голосовой интерфейс
Самая крутая фича, которую вы упустите, если не сделаете сразу — подключить голосовой ввод/вывод через whisper и TTS. LLM с MCP + голос = вы говорите «сколько электроэнергии нагорело за сегодня?» — и слышите ответ. Без экранов, без терминала.
Я это сделал через OpenWebUI с плагином voice. MCP-серверы как были, так и есть. Просто добавил микрофон. Рекомендую.