feat(mcp): add auto-configuration for Home Assistant and enhance documentation

This commit is contained in:
techartdev
2026-03-10 21:12:21 +02:00
parent e6cad868ab
commit c6232b6f5d
12 changed files with 271 additions and 37 deletions
+100 -3
View File
@@ -58,6 +58,7 @@ CONTROLUI_DISABLE_DEVICE_AUTH=$(jq -r '.controlui_disable_device_auth // true' "
FORCE_IPV4_DNS=$(jq -r '.force_ipv4_dns // true' "$OPTIONS_FILE")
ACCESS_MODE=$(jq -r '.access_mode // "custom"' "$OPTIONS_FILE")
NGINX_LOG_LEVEL=$(jq -r '.nginx_log_level // "minimal"' "$OPTIONS_FILE")
AUTO_CONFIGURE_MCP=$(jq -r '.auto_configure_mcp // false' "$OPTIONS_FILE")
GW_ENV_VARS_TYPE=$(jq -r 'if .gateway_env_vars == null then "null" else (.gateway_env_vars | type) end' "$OPTIONS_FILE")
GW_ENV_VARS_RAW=$(jq -r '.gateway_env_vars // empty' "$OPTIONS_FILE")
GW_ENV_VARS_JSON=$(jq -c '.gateway_env_vars // []' "$OPTIONS_FILE")
@@ -453,6 +454,7 @@ EOF
# Graceful shutdown handling (PID 1 trap) to reduce stale locks
# ------------------------------------------------------------------------------
GW_PID=""
GW_RELAY_PID=""
NGINX_PID=""
TTYD_PID=""
SHUTTING_DOWN="false"
@@ -476,6 +478,11 @@ shutdown() {
wait "${GW_PID}" || true
fi
if [ -n "${GW_RELAY_PID}" ] && kill -0 "${GW_RELAY_PID}" >/dev/null 2>&1; then
kill -TERM "${GW_RELAY_PID}" >/dev/null 2>&1 || true
wait "${GW_RELAY_PID}" || true
fi
if [ "$CLEAN_LOCKS_ON_EXIT" = "true" ]; then
cleanup_session_locks || true
fi
@@ -700,15 +707,61 @@ if [ -f /usr/local/lib/openclaw-proxy-shim.cjs ]; then
export OPENCLAW_GLOBAL_NODE_MODULES
fi
# ------------------------------------------------------------------------------
# Auto-configure MCP (Model Context Protocol) for Home Assistant
# Registers HA as an MCP server so OpenClaw can control HA entities/services.
# Requires: homeassistant_token set in add-on options + mcporter CLI available.
# Runs once; re-runs when the token changes.
# Auto-detects HA API URL: supervisor proxy if available, else localhost:8123.
# ------------------------------------------------------------------------------
if [ "$AUTO_CONFIGURE_MCP" = "true" ] && [ -n "$HA_TOKEN" ]; then
if command -v mcporter >/dev/null 2>&1; then
# Detect HA API URL: prefer supervisor proxy (works in all add-on network modes),
# fall back to localhost:8123 (works with host_network: true).
if [ -n "${SUPERVISOR_TOKEN:-}" ]; then
MCP_HA_URL="http://supervisor/core/api/mcp"
else
MCP_HA_URL="http://localhost:8123/api/mcp"
fi
MCP_FLAG="/config/.openclaw/.mcp_ha_configured"
MCP_TOKEN_HASH=$(printf '%s' "$HA_TOKEN" | sha256sum | cut -d' ' -f1)
if [ -f "$MCP_FLAG" ] && [ "$(cat "$MCP_FLAG" 2>/dev/null)" = "$MCP_TOKEN_HASH" ]; then
echo "INFO: MCP Home Assistant server already configured (token unchanged)"
else
echo "INFO: Configuring MCP for Home Assistant at $MCP_HA_URL ..."
# Remove stale entry if present (token may have changed)
mcporter config remove HA 2>/dev/null || true
if mcporter config add HA "$MCP_HA_URL" \
--header "Authorization=Bearer $HA_TOKEN" \
--scope home 2>&1; then
printf '%s' "$MCP_TOKEN_HASH" > "$MCP_FLAG"
echo "INFO: MCP server 'HA' registered — OpenClaw can now control Home Assistant"
else
echo "WARN: MCP auto-configuration failed. Configure manually in the terminal:"
echo "WARN: mcporter config add HA \"$MCP_HA_URL\" --header \"Authorization=Bearer YOUR_TOKEN\" --scope home"
fi
fi
else
echo "INFO: mcporter not available; skipping MCP auto-configuration (run 'openclaw onboard' first)"
fi
elif [ "$AUTO_CONFIGURE_MCP" = "true" ] && [ -z "$HA_TOKEN" ]; then
echo "INFO: MCP auto-configure enabled but homeassistant_token not set — skipping"
echo "INFO: To auto-configure, set homeassistant_token in add-on Configuration, then restart"
fi
start_openclaw_runtime() {
echo "Starting OpenClaw Assistant runtime (openclaw)..."
if [ "$GATEWAY_MODE" = "remote" ]; then
# Remote mode: do NOT start a local gateway service.
# Start a node/client host that connects to the configured remote gateway URL.
REMOTE_URL="$(timeout 2s openclaw config get gateway.remote.url 2>/dev/null | tr -d '\n' || true)"
# Use $GATEWAY_REMOTE_URL directly from add-on options — do NOT read back via
# 'openclaw config get' which can time out at startup or return redacted values.
REMOTE_URL="$GATEWAY_REMOTE_URL"
if [ -z "$REMOTE_URL" ]; then
echo "ERROR: gateway_mode=remote but gateway.remote.url is empty in openclaw config"
echo "ERROR: Configure gateway.remote.url first, then restart the add-on"
echo "ERROR: gateway_mode=remote but gateway_remote_url is not set in add-on options"
echo "ERROR: Set gateway_remote_url in add-on Configuration (e.g. ws://192.168.1.10:18789), then restart"
return 1
fi
@@ -748,6 +801,37 @@ if ! start_openclaw_runtime; then
exit 1
fi
# --- Loopback relay for tailnet bind mode (issue #90) ---
# When gateway.bind=tailnet the gateway only listens on the Tailscale IP.
# The local CLI always tries ws://127.0.0.1:PORT and fails with
# "Gateway not running" even though the gateway is healthy.
# A lightweight Node.js relay (loopback-only) forwards those connections
# to the Tailscale IP so terminal CLI commands work normally.
# Token auth is still enforced end-to-end by the gateway.
if [ "$GATEWAY_BIND_MODE" = "tailnet" ]; then
TAILSCALE_IP=$(ip -4 addr show tailscale0 2>/dev/null \
| awk '/inet /{gsub(/\/.*/,"",$2); print $2; exit}' || true)
if [[ "${TAILSCALE_IP:-}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "INFO: Starting loopback relay for tailnet gateway (127.0.0.1:${GATEWAY_PORT} -> ${TAILSCALE_IP}:${GATEWAY_PORT})"
node -e "
const net = require('net');
const TARGET_HOST = '${TAILSCALE_IP}';
const TARGET_PORT = ${GATEWAY_PORT};
const server = net.createServer(function(c) {
const t = net.createConnection(TARGET_PORT, TARGET_HOST);
c.pipe(t); t.pipe(c);
c.on('error', function() { t.destroy(); });
t.on('error', function() { c.destroy(); });
});
server.listen(TARGET_PORT, '127.0.0.1');" &
GW_RELAY_PID=$!
echo "INFO: Loopback relay started (PID ${GW_RELAY_PID})"
else
echo "WARN: tailnet bind mode active but Tailscale IP not found on tailscale0 interface."
echo "WARN: Terminal CLI may show gateway as unreachable. Ensure Tailscale is running and restart."
fi
fi
# Start web terminal (optional)
TTYD_PID_FILE="/var/run/openclaw-ttyd.pid"
@@ -868,6 +952,19 @@ while true; do
break
fi
# Detect agent/user-initiated self-restart (e.g. 'openclaw gateway restart').
# When the gateway restarts itself, the old PID exits but a new process immediately
# binds the same port. Without this check the supervisor would spawn a second
# instance, which fails with "already listening" and loops forever.
# Give the new process a moment to start, then re-track it instead of spawning a duplicate.
sleep 1
RESTARTED_PID=$(pgrep -f "openclaw.*(gateway|node).*run" 2>/dev/null | head -1 || true)
if [ -n "$RESTARTED_PID" ] && [ "$RESTARTED_PID" != "$GW_PID" ]; then
echo "INFO: OpenClaw runtime restarted itself (new PID $RESTARTED_PID); re-tracking."
GW_PID="$RESTARTED_PID"
continue
fi
echo "WARN: OpenClaw runtime exited with code ${GW_EXIT_CODE}. Restarting in 2s..."
sleep 2