Связка MQTT, TimescaleDB и LLM через MCP: гайд для IoT-аналитики | AiManual
AiManual Logo Ai / Manual.
29 Июн 2026 Гайд

MQTT + TimescaleDB + LLM через MCP: как заставить датчики говорить с нейросетью

Пошаговый гайд по интеграции IoT-датчиков с LLM через Model Context Protocol. Настройка MCP-серверов, запросы на естественном языке и реальная аналитика без API

Реклама
partv1

Почему ваш умный дом тупее, чем кажется

Вы собрали ферму датчиков. Температура, влажность, 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 aggregates2.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-серверы как были, так и есть. Просто добавил микрофон. Рекомендую.

💡
Не ждите, пока Grafana или Home Assistant реализуют подобное. MCP — это framework, который вы контролируете. Через интеграцию с LangGraph можно строить сложные агенты, которые не только отвечают на вопросы, но и автоматически корректируют режимы работы оборудования.

Подписаться на канал