| Developer: | VTMikel's Plugins Like this plugin? Show your appreciation! |
| Category: | Hubs and Protocol Bridges |
| Github: | Github Repo |
| Assistance: | Get help! |
| Plugin ID: | com.vtmikel.mcp_server |
| Latest release: | v2026.7.0 released on July 18, 2026 |
| Release downloaded: | 17 times |
| Requires: | Indigo v2024.2.0 or higher |
| (Check the Releases tab below for older releases that may have different requirements) | |
| Download latest release |
A Model Context Protocol (MCP) server plugin that lets AI assistants like Claude search, understand, and control your Indigo home automation system through natural language.
npx mcp-remote): brew install nodeOptional integrations, all off by default:
analyze_historical_data tool. Works with InfluxDB 1.x and
InfluxDB 3 (via its v1-compatibility API): use an https:// URL to enable TLS, and for v3 enter a
database token as the password (the username is ignored by v3).Every connection authenticates with an Indigo API key sent as Authorization: Bearer <key>. Two kinds:
/Library/Application Support/Perceptive Automation/Indigo <VERSION>/Preferences/secrets.json
(format),
then restart the Indigo Web Server.The endpoint path is always /message/com.vtmikel.mcp_server/mcp/. Choose the base by where you connect
from (default Web Server port is 8176):
| Access | Endpoint URL | Key |
|---|---|---|
| Same machine as Indigo | http://localhost:8176/message/com.vtmikel.mcp_server/mcp/ |
Local secret |
| Another machine on your LAN | http://<indigo-ip>:8176/message/com.vtmikel.mcp_server/mcp/ |
Local secret |
| Remote (outside your network) | https://<your-reflector>.indigodomo.net/message/com.vtmikel.mcp_server/mcp/ |
Reflector key |
For HTTPS on the LAN with a self-signed certificate, use the https://<indigo-host>:8176/... URL and see
the self-signed note in the client examples below.
VS Code, Cursor, Claude Code support direct HTTP transport — simpler and more reliable. Add to your MCP
settings (.vscode/mcp.json, Cursor MCP settings, or ~/.claude.json / project .mcp.json):
{
"mcpServers": {
"indigo": {
"type": "http",
"url": "http://localhost:8176/message/com.vtmikel.mcp_server/mcp/",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
Swap the url for the LAN or Reflector variant from the table above.
Claude Desktop does not support direct HTTP, so it proxies through mcp-remote. Add to
~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"indigo": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://your-reflector.indigodomo.net/message/com.vtmikel.mcp_server/mcp/",
"--header", "Authorization:Bearer YOUR_API_KEY"
]
}
}
}
"--allow-http" to args."env": { "NODE_TLS_REJECT_UNAUTHORIZED": "0" } to the server block.Why the difference?
mcp-remoterequests OAuth endpoints that Indigo doesn't implement; direct HTTP transport avoids that entirely, so prefer it whenever your client supports it.
The endpoint uses the MCP streamable-HTTP transport over the Indigo Web Server: POST carries all
messages, GET returns 405 (no server→client SSE stream), and sessions expire after 2 hours idle
(current Indigo Web Server versions reject the DELETE teardown before it reaches the plugin).
What the plugin can and can't do with each kind of Indigo entity.
| Entity | Read | Create | Edit definition | Control / run | Delete |
|---|---|---|---|---|---|
| Devices | ✓ full — state, properties, model, type, address | ✗ | ✗ | ✓ on/off, brightness, RGB/RGBW color, thermostat setpoints & modes | ✗ |
| Variables | ✓ full — value, folder | ✓ variable_create |
~ value only | — | ✗ |
| Action groups | ✓ full — every action step (scripts, plugin configs) | ✗ | ~ name / description | ✓ execute, duplicate, move | ✓ gated |
| Triggers | ✓ full — event, condition tree, action steps | ✗ | ~ name / description, event settings | ✓ enable/disable (timed auto-revert), execute, duplicate, move, remove delayed actions | ✓ gated |
| Schedules | ✓ full — timing, next run, condition tree, action steps | ✗ | ~ name / description | ✓ enable/disable (timed auto-revert), execute, duplicate, move, remove delayed actions | ✓ gated |
Legend: ✓ supported · ~ partial (see the cell) · ✗ not possible · — not applicable.
control_trigger / control_schedule /
control_action_group, action: "duplicate"), then edit the copy.confirm=true.Most list and search tools paginate with limit (default 50, max 500) and offset, and return
total_count / has_more for navigation.
Inspect triggers, schedules, and action groups in full — including the action steps and condition trees that Indigo's scripting API does not expose (read from the server's database file, refreshed within minutes of a change).
query/regex/types/start_time/end_time to scan the full historical daily
log files instead. Each entry is {timestamp, type, message}.enable/disable
(with a duration_seconds auto-revert — "disable this trigger for 2 hours"), execute, duplicate,
move_to_folder, remove_delayed_actions, and delete. (Action groups support execute/duplicate/move/delete
only.) Delete requires confirm=true and the Allow AI to delete automations preference (off by
default); every other action is always available.Action steps, conditions, and schedule timing are read-only in Indigo's scripting API — change those in the
Indigo UI. Since there's no API to author actions from scratch, duplicate (via control_trigger) followed by
update_trigger is the supported way to make a trigger variant.
See Event Subscriptions & Webhooks for the full guide.
Added in v2026.1.0. Event subscriptions let an MCP client ask Indigo to notify it the next time something happens — "the next time the front door opens", "if the temperature goes above 80°F", "if the garage door stays open for 10 minutes".
⚠️ This is an outbound webhook — you must run your own server
When a subscription's conditions match, the plugin sends an HTTP POST to a URL you provide. It is a sender only — there is no built-in receiver. This will not work with stock Claude Desktop or most off-the-shelf MCP clients, which have no way to receive a proactive notification. It's meant for custom agents / automation systems that own a persistent HTTP endpoint (for example, OpenClaw).
Enable it under Plugins → MCP Server → Configure → Enable Event Webhooks (the three tools are hidden until then).
create_event_subscription accepts:
| Parameter | Type | Required | Description |
|---|---|---|---|
webhook_url |
string | yes | HTTP(S) endpoint you run that events are POSTed to. |
entity_type |
"device" | "variable" |
yes | What kind of entity to watch. |
conditions |
object | yes | State conditions that trigger the webhook (see operators below). |
entity_id |
integer | no | A specific device/variable ID, or omit to watch all entities of that type. |
auth |
object | no | { "mode": "none"\|"bearer"\|"hmac", "token": "…", "verify_ssl": true } (see Authentication). |
duration_seconds |
integer (≥1) | no | Dwell time — the condition must stay matched this long before firing. If it reverts first, nothing is sent. |
max_fires |
integer (≥1) | no | Auto-delete the subscription after this many successful deliveries. Use 1 for a one-shot notification. Omit for unlimited. |
description |
string | no | Human-readable label for the subscription. |
A webhook fires on the transition into a matching state (not repeatedly while it stays matched). Multiple conditions are combined with AND.
# Notify me once, the next time the front door opens
create_event_subscription(
webhook_url="https://my-server.example.com/indigo-hook",
entity_type="device", entity_id=12345,
conditions={"onState": True}, max_fires=1,
description="Front door opened",
)
# Alert me if the garage door stays open for 10 minutes
create_event_subscription(
webhook_url="https://my-server.example.com/indigo-hook",
entity_type="device", entity_id=67890,
conditions={"onState": True}, duration_seconds=600,
description="Garage left open",
)
Conditions match against device/variable state keys (including third-party plugin states). Use simple equality, or an operator object per key:
{ "onState": true } // equality
{ "brightness": { "gt": 50 } } // single operator
{ "temperatureInput1": { "gt": 80 }, "onState": true } // AND of multiple keys
| Operator | Meaning |
|---|---|
eq / ne |
equal to / not equal to |
gt / gte |
greater than / greater than or equal |
lt / lte |
less than / less than or equal |
contains |
substring is contained in the value |
regex |
value matches the regular expression |
Variables match on their value key. Indigo stores every value as a string, but booleans and
numbers in your conditions are coerced automatically, so { "value": true }, { "value": { "eq": "open" } },
and { "value": { "gt": 50 } } all work. To fire on every change regardless of the new value, use
{ "any_change": true } — variables only, and not combinable with duration_seconds.
Set via the auth parameter; your receiver should validate it so only your Indigo server can post to your
endpoint.
none (default) — no auth headers.bearer — adds Authorization: Bearer <token>.hmac — adds X-Webhook-Signature: sha256=<hexdigest> (HMAC-SHA256(token, raw_body_bytes)) and
X-Webhook-Timestamp: <unix-seconds>. Verify on the receiver:
python
import hmac, hashlib
expected = "sha256=" + hmac.new(SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers["X-Webhook-Signature"])
Set "verify_ssl": false only if your receiver uses a self-signed certificate.
Each delivery is a POST with Content-Type: application/json, the headers X-Event-Id, X-Event-Type
(device.state_changed | variable.value_changed), and X-Subscription-Id (plus any auth headers), and a
body like:
{
"event_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
"schema_version": "1.0",
"dedupe_key": "indigo:device:12345:state:onState:True",
"source": { "system": "indigo", "plugin": "com.vtmikel.mcp_server", "host": "my-indigo-mac" },
"timestamp": "2026-06-01T15:30:45.123456+00:00",
"event_type": "device.state_changed",
"entity": { "kind": "device", "id": 12345, "name": "Front Door", "device_type": "…" },
"state": { "changed_keys": ["onState"], "old": { "onState": false }, "new": { "onState": true } },
"trigger": { "subscription_id": "…", "conditions_matched": { "onState": true } },
"human": { "title": "Front Door state changed", "summary": "Front Door: onState=true" }
}
Variable changes use event_type: "variable.value_changed", entity.kind: "variable", and a state of
{ "changed_keys": ["value"], "old": { "value": "…" }, "new": { "value": "…" } }.
event_id (or dedupe_key).5xx
and network errors. A 4xx is a permanent rejection and is not retried. Success is any 2xx — return
200 promptly.0600) to
…/Preferences/Plugins/com.vtmikel.mcp_server/subscriptions.json and reloaded on startup, so they survive
restarts and upgrades. The file contains your webhook auth tokens (required so authenticated webhooks
can re-authenticate). Pending dwell timers are not persisted — a held condition re-arms on its next
matching transition.When webhooks are enabled, the plugin serves a page that lists active subscriptions and lets you remove them (create/edit stays with the MCP tools; auth tokens are never shown).

http://<your-indigo-host>:8176/message/com.vtmikel.mcp_server/events_ui/ — served by the Indigo
Web Server under the same authentication as the rest of IWS (open it from a browser logged into Indigo).Any HTTPS endpoint reachable from your Indigo host works. A dependency-free Python receiver to test with:
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
# (Optional) verify X-Webhook-Signature here if using HMAC auth.
event = json.loads(body)
# Dedupe by event_id — at-least-once delivery means retries can repeat.
print(f"{event['event_type']} {event['event_id']}: {event['human']['summary']}")
self.send_response(200) # any 2xx = success
self.end_headers()
HTTPServer(("0.0.0.0", 8888), Handler).serve_forever()
list_devices({"onState": true}) for state queries, search_entities("lights")
for discovery.Sent to OpenAI (only to generate search embeddings, stored locally on your Indigo server): device name/description/model/type/address, variable name/description, and action-group name/description — sent on install and when entities are added or changed. Never sent: device states or values, credentials, URLs, IP/network configuration, or historical/usage data.
Network — every MCP connection requires Bearer-token authentication. Local HTTP stays on your LAN; use the
Indigo Reflector for encrypted remote access. For self-signed HTTPS on the LAN, set
NODE_TLS_REJECT_UNAUTHORIZED=0 (Claude Desktop) as shown above.
| Released on: | July 18, 2026 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 17 times |
| Download this release | |
The analyze_historical_data tool now works with InfluxDB 3 (Core/Enterprise) via its v1-compatibility API, while InfluxDB 1.8 remains fully supported — same configuration, same code path, no new dependencies.
To use InfluxDB 3: enter an https:// (or http://) URL, your v3 database token as the password (username can be anything), and the database name. That's it — no version selector needed.
https:// InfluxDB URL now actually enables TLS. Previously the scheme was stripped and the client sent plaintext HTTP to the TLS port, failing with errors like 404 page not found. (#49)SHOW DATABASES (admin-only on InfluxDB 3) or the 1.x ping() (incompatible with v3's /ping response). It now uses a database-scoped SHOW MEASUREMENTS LIMIT 1, which also validates auth and database access on both versions. (#49, #50)'list' object has no attribute 'get'). (#51)| Released on: | July 3, 2026 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 15 times |
| Download this release | |
Major release: triggers, schedules, and action groups are now first-class citizens of the MCP server. Eight new tools close the gap Indigo's scripting API leaves around understanding and managing automations.
query/regex/types/start_time/end_time and it scans the full historical daily log files instead. One tool, filters optional, consistent {timestamp, type, message} output.duration_seconds auto-revert: "disable this trigger for 2 hours"), execute, duplicate, move to folder, remove delayed actions, delete. Deletion requires confirm=true and the Allow AI to delete automations preference (off by default); every other action is always available.control_trigger, get_schedule_details) rather than one tool with an entity_type parameter — clearer, self-documenting names.confirm=true). Reading, control, and editing are always available.replaceOnServer() preserves action steps and conditions exactly.PluginVersion instead of CFBundleVersion.query_event_log covers both the recent live tail and filtered historical search behind one interface.| Released on: | June 2, 2026 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 32 times |
| Download this release | |
Event subscriptions get a management web UI, smarter variable matching, an "any change" trigger, and — most importantly — persistence across restarts.
/message/com.vtmikel.mcp_server/events_ui/ (behind the same auth as the rest of IWS). A new plugin menu item, Print Event Subscriptions Web UI URL, prints the link.conditions={"any_change": true} fires the webhook on every change to a variable's value. Variable-only.{"value": true} never matched (bool ≠ string) and numeric operators like {"value": {"gt": 50}} silently failed. Conditions now coerce booleans and numbers, so these work as expected. The fix is in the shared filter, so it also hardens list_devices / search / get_devices_by_state.…/Preferences/Plugins/com.vtmikel.mcp_server/subscriptions.json) contains your webhook auth tokens — required so authenticated webhooks can re-authenticate after a restart. It is written 0600 inside Indigo's protected app-support directory. Pending dwell timers are not persisted (they re-arm on the next matching transition).Download MCP Server.indigoPlugin.zip below, unzip, and double-click the .indigoPlugin to install (or update in place).
| Released on: | May 1, 2026 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 43 times |
| Download this release | |
Dependency bump to fix Indigo 2025.2 (Python 3.13) startup crash.
Impact: - Now requires Indigo 2025.2 or later (Python 3.13) - Apple Silicon only — Intel Mac (x86) is no longer supported (lancedb 0.27+ dropped x86 wheels)
What changed: - lancedb: 0.25.x → 0.30.2 - pyarrow: 23.0.x → 23.0.x (range widened)
| Released on: | March 21, 2026 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 39 times |
| Download this release | |
*/* Accept header — Fixes internal server error when MCP clients send Accept: */* instead of application/json.headers key, causing Indigo Web Server to return generic errors instead of proper error details.| Released on: | Jan. 17, 2026 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 45 times |
| Download this release | |
langgraph, langgraph-checkpoint, langgraph-prebuilt, langgraph-sdk (never imported)influxdb-client (code uses legacy 1.x influxdb only)pydantic-settings, python-dotenv, dicttoxmlopenai >=2.15.0 (was >=2.3.0)tiktoken >=0.12.0 (was >=0.11.0)pydantic >=2.12.5 (was >=2.12.2)jinja2 >=3.1.5 (was >=3.1.0)lancedb <0.27.0 (0.26+ deprecates Intel Mac support)langsmith <0.6.0 (0.6+ requires Python 3.10+)matplotlib <4.0.0Full Changelog: https://github.com/mlamoure/indigo-mcp-server/compare/v2025.1.5...v2025.1.6
| Released on: | Dec. 22, 2025 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 42 times |
| Download this release | |
Fixes internal server error when MCP clients send Accept: */* header.
*/* (wildcard) as valid Accept header in addition to application/json and text/event-stream"headers" key to 406 response in mcp_handler.py"headers" key to 500 error response in plugin.pyThe missing "headers" key in error responses caused Indigo Web Server to return a generic "internal server error" instead of the proper error response. Clients like mcp-remote send Accept: */* which was incorrectly rejected.
Related to #5 (investigation ongoing)
| Released on: | Nov. 6, 2025 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 41 times |
| Download this release | |
_restart_mcp_server_from_device() method callsFixes https://github.com/mlamoure/indigo-mcp-server/issues/4