Skip to main content
Glama
FernanMoreno

domoai-mcp

by FernanMoreno

DomoAI

Universal agentic domotics runtime with a semantic device model, multi-adapter composition and one general MCP interface.

Development environment

This project uses uv and Python 3.12.

uv sync
uv run pytest
uv run ruff check .
uv run mypy src

The runtime dependencies include the MCP Python SDK, Pydantic, Home Assistant HTTP/WebSocket clients, aiomqtt for the optional Zigbee2MQTT adapter, JSON Schema validation and OR-Tools. Local SQLite persistence uses Python's standard library. Development tools are installed through uv's default dev dependency group.

To add or update a dependency, edit pyproject.toml and regenerate the lockfile:

uv lock
uv sync

Local MCP server

The semantic MCP server can be launched over stdio. Without Home Assistant settings it uses the deterministic fixture:

uv run domoai-mcp

Example host configuration:

{
  "mcpServers": {
    "domoai": {
      "command": "uv",
      "args": ["run", "domoai-mcp"],
      "cwd": "/path/to/DomoAI"
    }
  }
}

The same command can be registered in Claude Code, Codex or another compatible MCP client.

Unified MCP surface

The single domoai-mcp server exposes discovery, state, energy context, policy-aware plan validation/execution and the proposal-only OR-Tools tools validate_scenario, optimize_scenario and explain_solution through the same MCP session. Register exactly one server in Claude Code, Codex or any other compatible MCP client that supports local stdio:

{
  "mcpServers": {
    "domoai": {
      "command": "uv",
      "args": ["run", "domoai-mcp"],
      "cwd": "/path/to/DomoAI"
    }
  }
}

OR-Tools remains an internal proposal/validation/explanation layer. It cannot execute a device, approve a plan or call an adapter, and there is no second public OR-Tools MCP endpoint.

The portable optimize-home-energy skill routes every DomoAI operation through one mcp role. Its reference workflow is validated locally with deterministic in-process fixtures:

uv run pytest -q tests/contract/test_skill_contract.py
uv run pytest -q tests/integration/test_energy_skill_workflow.py

The workflow uses the same connection for semantic reads, proposals, explanations and plan validation, and never executes outside execute_plan. Sensitive plans pause for explicit operator approval.

For energy-aware scenarios, the portable v2 procedure reads a complete typed context through mcp.get_energy_context before calling the proposal-only optimizer. The context aligns tariffs and solar forecasts to a fixed horizon and may include one battery profile. CP-SAT returns cost, peak-import and solar-self-consumption evidence plus per-slot energy balance; it never calls a physical adapter. Context failure, revision mismatch, infeasibility or solver timeout stops before validation and execution. The deterministic provider and focused acceptance commands are covered by the repository contract and integration tests.

One-time solar profile for live energy data

OMIE tariffs and Open-Meteo forecasts are collected automatically whenever the energy context is requested. Only the physical installation metadata needs to be supplied once. Copy the example, replace its placeholder values with the inverter or installer data, and point the runtime at it:

cp config/solar-profile.example.json config/solar-profile.json
export DOMOAI_ENERGY_LIVE=1
export DOMOAI_TARIFF_PROVIDER=omie
export DOMOAI_SOLAR_PROVIDER=open_meteo
export DOMOAI_SOLAR_PROFILE_PATH=config/solar-profile.json
uv run domoai-mcp

The profile is strict, versioned and credential-free. It must contain real installation values before using the result for optimization; the example's Madrid values only document the shape. The older individual DOMOAI_SOLAR_* variables remain available as a mutually exclusive compatibility fallback.

Universal Provider SDK

Future Home Assistant, inverter and MQTT integrations must translate their source-specific identities and payloads into the Provider SDK v1 boundary before reaching the semantic runtime. The SDK reuses DomoAI's canonical DeviceType, Capability and SourceRef models and separates providers into telemetry and command roles:

external provider
      ↓
ProviderManifest + DeviceDescriptor + Measurement
      ↓
ProviderRegistry (stable order, safe diagnostics)
      ↓
canonical runtime / StateStore / MCP / OR-Tools

Provider commands carry only bounded semantic parameters and an idempotency key. They do not bypass PlanService, policy validation or AdapterPort. The first concrete implementation is HomeAssistantProvider. It reuses the authenticated REST/WebSocket client, groups entities by Home Assistant device_id when registry metadata is available, and exposes only explicit entity/capability metric mappings. It remains additive to the classic HomeAssistantAdapter; the runtime factory selects it only when DOMOAI_HOME_ASSISTANT_PROVIDER=1 is explicitly enabled. The same provider object is registered in ProviderRegistry and wrapped by the existing AdapterPort, so DeviceRegistry, StateStore, plan execution and MCP keep one semantic path and one Home Assistant client. See docs/adapter-sdk.md and docs/contracts.md for the public boundary.

Live Home Assistant runtime

Para desarrollo local sin hardware, el laboratorio virtual reproducible está en dev/lab/README.md y su arranque mínimo cubre Mosquitto/fake Zigbee2MQTT y PyModbus. Home Assistant, Matter Server y KNX Virtual/ETS permanecen como perfiles manuales opt-in.

La ruta recomendada para operar ese laboratorio es el runner explícito:

uv run domoai-lab up
uv run domoai-lab status
uv run domoai-lab smoke

El smoke usa únicamente fixtures locales de Home Assistant, MQTT/Zigbee2MQTT, Modbus, Matter y KNX; no inventa gateways, tokens ni commissioning. Los smoke tests live siguen separados y requieren sus servicios y variables DOMOAI_* reales.

The composition root selects the deterministic fixture when no live source is configured, a direct adapter for one source, or a composite runtime for two or more complete source configurations. Configure Home Assistant with:

export DOMOAI_HOME_ASSISTANT_URL="http://home-assistant.local:8123"
export DOMOAI_HOME_ASSISTANT_TOKEN="<long-lived-access-token>"
export DOMOAI_HOME_ASSISTANT_PROVIDER="1"
export DOMOAI_HOME_ASSISTANT_MAPPING_PATH="config/home-assistant-mappings.json"
export DOMOAI_DATABASE_PATH="data/domoai.sqlite3"
uv run domoai-mcp

Provider mode is opt-in. Without it, the classic HomeAssistantAdapter is selected for compatibility. If it is enabled, the URL/token pair is required and an optional strict v1 mapping document can make energy roles explicit:

{
  "schema_version": "v1",
  "metric_mappings": {
    "sensor.pv_power": {"power": "energy.pv.power"},
    "sensor.grid_power": {"power": "energy.grid.power"}
  }
}

The runtime authenticates REST service calls, persists plans, outcomes and redacted audit events in SQLite, and runs the adapter event consumer in the background. Supported write mappings currently include light/switch power and toggle operations, light brightness, cover position/open/close/stop and climate target temperature. An incomplete URL/token pair is rejected before startup. Tokens are read as secret configuration and are never included in device, command, outcome or audit payloads.

The Provider SDK path can be exercised independently of the runtime factory:

provider = HomeAssistantProvider(
    HomeAssistantClient(base_url, token),
    metric_mappings={
        "sensor.pv_power": {"power": "energy.pv.power"},
        "sensor.battery_soc": {"battery": "battery.soc"},
    },
)

Only mapped sensor capabilities become canonical energy metrics. The client also reads Home Assistant's enabled entity registry over WebSocket when state payloads do not include device_id; registry identity is preserved when provided, never inferred from names or areas.

Removing DOMOAI_HOME_ASSISTANT_PROVIDER rolls back to the classic adapter without changing the agent-facing MCP surface. The provider path is covered by deterministic fixtures. The opt-in live provider-runtime smoke validates the same route against a real Home Assistant instance without executing commands:

uv run pytest -q tests/integration/test_home_assistant_provider_smoke.py

It requires a real URL/token pair and keeps the token outside the repository.

Live Zigbee2MQTT runtime

The native Zigbee2MQTT adapter is opt-in and supports the bounded v1 profile: light/switch power, light brightness, temperature, humidity and occupancy. Configure it alongside Home Assistant or another source:

export DOMOAI_ZIGBEE2MQTT_URL="mqtt://mqtt-broker.local:1883"
export DOMOAI_ZIGBEE2MQTT_BASE_TOPIC="zigbee2mqtt"
export DOMOAI_MQTT_TIMEOUT_SECONDS="5"
export DOMOAI_MQTT_USERNAME="domoai"
export DOMOAI_MQTT_PASSWORD="<mqtt-password>"
uv run domoai-mcp

Zigbee2MQTT may run alongside Home Assistant or another configured source. The adapter consumes Zigbee2MQTT bridge/device topics and publishes only mapped device /set commands through the existing plan, policy and executor boundary. Pairing, removal, OTA, groups, bridge administration and arbitrary MQTT publishing are not exposed.

Live Matter Server runtime

The native Matter adapter uses Matter Server as the controller boundary and connects to its compatible WebSocket endpoint. Configure it alongside Home Assistant, Zigbee2MQTT or another source:

export DOMOAI_MATTER_SERVER_URL="ws://matter-server.local:5580/ws"
export DOMOAI_MATTER_TIMEOUT_SECONDS="5"
uv run domoai-mcp

The adapter validates the server schema range before discovery, preserves node:<node_id>/endpoint:<endpoint_id> source references and exposes only the bounded v1 light/switch power and brightness profile plus read-only temperature, humidity and occupancy state. Commissioning, fabric management, OTA, groups, vendor clusters and arbitrary attribute operations remain outside the agent-facing boundary. Live Matter smoke tests are opt-in; fixture tests need no Matter server or hardware.

Live KNX/IP runtime

The native KNX adapter uses an explicit mapping file rather than inferring devices from arbitrary group traffic. Its bounded v1 profile supports light and switch power, light brightness, and read-only temperature, humidity and occupancy. Configure it alongside the other physical sources:

export DOMOAI_KNX_GATEWAY_HOST="knx-gateway.local"
export DOMOAI_KNX_CONFIG_PATH="config/knx.json"
export DOMOAI_KNX_TIMEOUT_SECONDS="5"
uv run domoai-mcp

The mapping file declares each entity, semantic capability, state group address, command group address and DPT. Unknown fields, malformed addresses, unsupported DPTs and writable sensor mappings are rejected at startup. KNX/IP tunnelling is optional and can coexist with the other configured adapters; fixture tests use an in-memory transport and require no gateway or hardware. ETS import, commissioning, routing, secure credentials, arbitrary group-value operations, scenes and additional xknx device profiles are not included in v1.

Live Modbus TCP runtime

The native Modbus adapter uses an explicit v1 mapping of unit IDs, register areas, zero-based PDU offsets and scalar encodings. It supports light/switch power, light brightness, and read-only temperature, humidity and occupancy. Configure it alongside the other physical sources:

export DOMOAI_MODBUS_HOST="modbus-controller.local"
export DOMOAI_MODBUS_PORT="502"
export DOMOAI_MODBUS_CONFIG_PATH="config/modbus.json"
export DOMOAI_MODBUS_TIMEOUT_SECONDS="5"
export DOMOAI_MODBUS_POLL_INTERVAL_SECONDS="5"
uv run domoai-mcp

The mapping is strict and does not scan or infer devices. Unknown fields, ambiguous 40001-style addresses, unsupported encodings, writable sensors and unsafe commands are rejected. Modbus TCP is opt-in and can coexist with Home Assistant, Zigbee2MQTT, Matter Server and KNX. RTU/ASCII, TLS, scanning, vendor function codes and arbitrary register reads/writes are outside v1. Fixture tests use an in-memory transport and require no controller or hardware.

Multi-adapter identity and routing

The runtime follows the Home Assistant device/entity distinction: one physical source device may expose multiple source entities, while DomoAI presents one canonical device with capability-level routes. Stable source identifiers and connections preserve identity across name or area changes; an explicit canonical_id is required to link contributions from different adapters. Commands are resolved to one exact source entity before execution. Ambiguous, unknown or unavailable routes fail closed, so the runtime never silently sends a command to another protocol or entity.

No live gateway, broker or controller is required for this behavior. The deterministic multi-adapter fixture covers composition, partial failure, topology, exact routing and zero-write safety:

uv run pytest -q tests/contract/test_multi_adapter_runtime.py \
  tests/integration/test_multi_adapter_runtime.py \
  tests/performance/test_multi_adapter_targets.py

Verified local validation

On 2026-08-17 the repository passed the unit, adapter, discovery, plan, MCP-contract, optimization, performance, Home Assistant execution, KNX and Modbus fixture, runtime composition, OMIE and Open-Meteo provider scenarios covered by the repository test suite. The Home Assistant classic-adapter smoke passed against the local Docker lab; the local Zigbee2MQTT and Modbus smokes passed; and the read-only OMIE and Open-Meteo public-network smokes passed with opt-in configuration. Matter discovery and KNX/IP remain optional because they require a commissioned Matter node or a reachable KNX gateway and mapping.

The local launch command is:

uv run domoai-mcp

The quality gates are:

uv run pytest -q
uv run ruff check .
uv run mypy src
uv lock --check

The latest full-suite result without live credentials is 318 passed, 8 skipped, with no warnings. The skips are opt-in Matter Server, KNX/IP and other live cases without their external node, gateway or service configuration; deterministic fixture coverage remains enabled. The separate live results are: Zigbee2MQTT/Modbus 2 passed, OMIE/ Open-Meteo 2 passed, Home Assistant classic adapter 1 passed and the Home Assistant Provider runtime bridge 1 passed. The FastMCP compatibility seam keeps the known pydantic_settings incomplete-field warning out of the MCP contracts without globally suppressing warnings.

Adapter and public contract guidance lives in docs/adapter-sdk.md and docs/contracts.md.

-
license - not tested
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • MCP Hub: AI service discovery, per-user OAuth, and multi-service workflow orchestration

  • Cross-vendor AI memory over MCP. One semantic store, readable and writeable from every MCP client.

  • Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/FernanMoreno/DomoAI'

If you have feedback or need assistance with the MCP directory API, please join our Discord server