Files
javis/homey-mcp
2026-10-04 15:31:44 +02:00
..
2026-10-04 15:31:44 +02:00
2026-09-21 21:44:00 +02:00
2026-09-21 21:44:00 +02:00
2026-10-04 15:31:44 +02:00
2026-10-04 15:31:44 +02:00
2026-10-04 15:31:44 +02:00
2026-09-21 21:44:00 +02:00
2026-09-21 21:44:00 +02:00
2026-09-21 21:44:00 +02:00
2026-09-21 21:44:00 +02:00
2026-10-04 15:31:44 +02:00
2026-10-04 15:31:44 +02:00

homey-mcp

An MCP (Model Context Protocol) server that exposes a local Homey Pro instance to LLM clients (Claude Desktop, VS Code / GitHub Copilot Chat, MCP Inspector, …).

It talks to Homey's local HTTP Web API (/api/manager/...) using a Personal Access Token, and optionally subscribes to Socket.IO realtime events.

Features

Tools (read-only ones carry readOnlyHint; zone/home-wide and flow tools are marked destructiveHint):

  • Connectivity: ping
  • Devices: list_devices(zone_id?), find_devices(query, zone?), get_device(device_id)
  • Zones: list_zones
  • Status views: get_zone_status(zone), get_house_status(class_filter?), get_temperatures
  • Capabilities: get_capability, set_capability, set_light, set_capabilities_bulk
  • Zone / home control: control_zone(zone, capability, value), control_all(capability, value, device_class?)
  • Flows: list_flows, find_flows(query), trigger_flow(flow_id)
  • Logic variables: list_variables, set_variable
  • Notifications: send_notification(title, excerpt?)
  • Events: list_recent_events(limit, kind?) — realtime events from the in-memory buffer

Zone names resolve exact match → prefix → substring, so Wohnzimmer never picks Wohnzimmer Ecke. IDs are URL-encoded before they reach Homey's API.

Resources:

  • homey://devices
  • homey://zones
  • homey://events/recent

Transport: stdio (default, for local LLM hosts like Javis) or streamable-http / sse (for remote MCP clients over the LAN, e.g. the Odysseus AI helper) — see Run on the LAN below.

Prerequisites

  • Python 3.10+
  • A Homey Pro on the local network
  • A Personal Access Token for the Homey Web API — create one at the Homey Developer Tools: https://tools.developer.homey.app/ (My Homey → API Keys / Personal Access Token).

Install

From this folder:

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e .

uv works equivalently:

uv venv
uv pip install -e .

Configure

Copy .env.example to .env and fill in:

HOMEY_HOST=192.168.1.10
HOMEY_TOKEN=eyJhbGciOi...your PAT...
HOMEY_SCHEME=http
HOMEY_PORT=4859
HOMEY_TIMEOUT=10
HOMEY_EVENT_BUFFER=200
HOMEY_REALTIME=true

Run

homey-mcp
# or
python -m homey_mcp
# or
.\run.bat

Inspect with the MCP Inspector

uv run mcp dev src/homey_mcp/server.py
# or
npx -y @modelcontextprotocol/inspector homey-mcp

Run on the LAN (remote MCP clients)

To let a remote MCP client on the same network (e.g. Odysseus) connect over HTTP instead of stdio, set the transport env vars and run run-lan.bat (or set them yourself and run homey-mcp / python -m homey_mcp):

HOMEY_MCP_TRANSPORT=streamable-http   # or: sse
HOMEY_MCP_HOST=0.0.0.0                # bind address (or specific LAN IP)
HOMEY_MCP_ADVERTISED_HOST=10.10.10.50 # advertised host for MCP validation (required for 0.0.0.0 bind)
HOMEY_MCP_PORT=8080
HOMEY_MCP_AUTH_MODE=none              # compatible trusted-LAN default
HOMEY_MCP_AUTH_TOKEN=<a long random secret>
.\run-lan.bat

The remote client connects to http://<this-machine-ip>:8080/mcp (streamable-http) or /sse. Streamable HTTP clients that only accept a base URL may use http://<this-machine-ip>:8080/; MCP traffic at / is forwarded to /mcp, while an ordinary browser GET returns service discovery.

Important for MCP transport over LAN: requests are only accepted when their Host header is HOMEY_MCP_ADVERTISED_HOST, localhost or 127.0.0.1 (otherwise 421 Misdirected Request). This blocks DNS-rebinding attacks, where a web page in any browser on your LAN rebinds its domain to this server. If you bind to 0.0.0.0, set HOMEY_MCP_ADVERTISED_HOST to the IP clients use; add further names (e.g. a DNS name) to HOMEY_MCP_ALLOWED_HOSTS (comma-separated, * disables the check). Without either, the check is off and a warning is logged.

HOMEY_MCP_AUTH_MODE=none is the compatible mode for OAuth-negotiating MCP clients such as Odysseus, because this server is not an OAuth authorization server. Use it only on a trusted, firewall-restricted LAN. Set the mode to bearer only when the MCP client can be configured to send Authorization: Bearer <HOMEY_MCP_AUTH_TOKEN> directly; OAuth discovery and dynamic registration are not provided. Generate a token with python -c "import secrets; print(secrets.token_urlsafe(32))".

When HOMEY_MCP_AUTH_TOKEN is set, the OpenAPI REST routes always require it, regardless of the MCP auth mode. In bearer mode every MCP route requires it too (/mcp, the root alias, and /sse + /messages/ for the SSE transport); only GET / (discovery) and /openapi.json stay public.

This is independent of Javis's own connection: Javis keeps talking to homey-mcp over stdio via mcp_servers.json as before — run the LAN instance as a second, separate process for other clients.

Odysseus API integration

If Odysseus exposes integrations through its app_api tool rather than a native MCP client, import this server's OpenAPI document:

  • Schema URL: http://<this-machine-ip>:8080/openapi.json
  • Authentication: Bearer token
  • Token: the value of HOMEY_MCP_AUTH_TOKEN

The API provides reads at /api/system, /api/zones, and /api/devices, plus writes at /api/devices/{device_id}/capabilities/{capability} and /api/zones/control.

Odysseus must invoke its registered app_api tool as a structured tool call. It must not print or execute Python such as api_call(...); api_call is not a Python function and is not the registered tool name. Use this integration instruction in Odysseus:

For Homey requests, always call the app_api tool with integration="Homey".
Never write api_call(...) or place tool calls in Python/code blocks.
Use GET /api/system to check connectivity, GET /api/zones and GET /api/devices
to discover IDs, PUT /api/devices/{device_id}/capabilities/{capability} with
{"value": ...} for one device, and POST /api/zones/control with
{"zone": "...", "capability": "onoff", "value": true|false} for a room.
After the tool returns, summarize the actual result. Never claim success before
receiving a successful tool response.

Use from Claude Desktop / VS Code

Add to your MCP client config (path varies per client):

{
  "mcpServers": {
    "homey": {
      "command": "homey-mcp",
      "env": {
        "HOMEY_HOST": "192.168.1.10",
        "HOMEY_TOKEN": "your-personal-access-token"
      }
    }
  }
}

If homey-mcp is not on PATH, use the full venv path, e.g. C:\\local\\Code\\PRIVAT\\homey-mcp\\.venv\\Scripts\\homey-mcp.exe.

Notes & limitations

  • Local API only (no cloud OAuth).
  • LAN MCP transport authentication is controlled by HOMEY_MCP_AUTH_MODE. Never expose none mode beyond a trusted, firewall-restricted LAN.
  • Realtime events are best-effort — if the Socket.IO connection fails the REST tools still work. Set HOMEY_REALTIME=false to disable. The server performs Homey's handshakeClient and subscribes to homey:manager:devices plus each homey:device:<id> (capability changes).
  • No admin operations (zone/user/app management) are exposed.
  • Capability values are typed as bool | int | float | str; the model picks based on the capability (onoff → bool, dim → 0..1 float, etc.).

Project layout

homey-mcp/
  pyproject.toml
  README.md
  .env.example
  run.bat
  src/homey_mcp/
    __init__.py
    __main__.py
    config.py
    homey_client.py
    server.py