| 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.6.0 released on July 3, 2026 |
| Release downloaded: | 4 times |
| Requires: | Indigo v2024.2.0 or higher |
| (Check the Releases tab below for older releases that may have different requirements) | |
| Download release v2026.3.0 |
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.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).
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 3, 2026 |
| Requires: | Indigo v2024.2.0+ |
| Downloaded: | 4 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: | 24 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: | 32 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: | 30 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: | 34 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: | 31 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: | 33 times |
| Download this release | |
_restart_mcp_server_from_device() method callsFixes https://github.com/mlamoure/indigo-mcp-server/issues/4