Skip to content

Commit c5ecdde

Browse files
kingpanther13claudegemini-code-assist[bot]
authored
feat: Add generic add-on API proxy tool (ha_call_addon_api) (#641)
* feat: add ha_call_addon_api tool for add-on Ingress proxy Add a generic tool that proxies HTTP requests to any add-on's web API through Home Assistant's Ingress system. Works with Node-RED, Frigate, or any add-on that exposes a web API. Changes: - New tool: ha_call_addon_api (slug, path, method, body) - Extend ha_get_addon with optional slug param for detailed add-on info - Refactor duplicate WS boilerplate into _supervisor_api_call() helper Closes #634 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address Gemini code review feedback - Add path traversal sanitization (reject '..' components) - Use structured error helpers (create_error_response, create_validation_error, create_timeout_error, create_connection_error, exception_to_structured_error) instead of raw error dicts, per repo style guidelines - Change destructiveHint to False (default method is GET) - Remove unused import Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve ruff I001 and missing readOnlyHint annotation - Wrap .helpers import to multiline (91 chars exceeded 88-char line limit) - Add readOnlyHint: False to ha_call_addon_api annotations (required by test_tool_annotations) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add ha_call_addon_api to README tool list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: set destructiveHint to True for ha_call_addon_api The test requires every tool to have at least one of readOnlyHint: True or destructiveHint: True. Since ha_call_addon_api can make POST/PUT/DELETE requests to add-on APIs, destructiveHint: True is correct. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: raise ToolError on failure + add unit tests for _call_addon_api Address PR review feedback from julienld: - Tool functions (ha_get_addon, ha_call_addon_api) now raise ToolError via raise_tool_error() on failure instead of returning error dicts, setting isError: true in the MCP protocol response. - Add 9 unit tests covering _call_addon_api error paths: path traversal rejection, addon not found, no Ingress support, addon not running, missing ingress_entry, HTTP timeout, and connection errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: create Ingress session before proxying add-on API requests The Supervisor's Ingress proxy validates requests using a session cookie, not Bearer tokens. Without a valid session, all requests fail with "No valid ingress session" and HTTP 500. Fix: call POST /ingress/session via Supervisor WebSocket API to obtain a session token, then pass it as the `ingress_session` cookie on the HTTP request to the add-on's Ingress URL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use direct add-on communication instead of Ingress proxy The Ingress proxy requires session cookies that are difficult to obtain programmatically from within an add-on context. Instead, communicate directly with the target add-on via its internal Docker network IP and ingress_port (e.g., http://172.30.33.10:5000/...). All add-ons on HA OS share the hassio Docker network, so direct container-to-container communication works without authentication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add Ingress headers for direct add-on communication Add X-Ingress-Path and X-Hass-Source headers when making direct requests to add-on containers. Add-ons like Frigate and Node-RED check for these headers to bypass their own auth layer, since the Supervisor Ingress proxy normally sets them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * debug: add opt-in diagnostic info to addon API responses Add debug=true parameter to ha_call_addon_api that includes request URL, sent headers, and response headers in the response. Off by default to avoid wasting tokens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add Supervisor Ingress proxy fallback for IP-restricted add-ons When direct Docker network access returns 403 (community add-ons with IP-restricted Nginx), automatically retry through the Supervisor Ingress proxy using SUPERVISOR_TOKEN. Also fixes ruff C416 lint error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * debug: add fallback diagnostics to debug output Return status message from Supervisor Ingress fallback so debug=true shows why the fallback succeeded or failed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use WS-based Ingress session + HA Core proxy for fallback The direct Supervisor API call was rejected (403) due to hassio_role restrictions. Instead, create the Ingress session via the WebSocket proxy (which already works for other Supervisor calls) and route through HA Core's /api/hassio_ingress/ endpoint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use hassio/ingress_session WS command for session creation The supervisor/api WS command rejects addon in data. Use the dedicated hassio/ingress_session WS command instead, which is what HA Core's frontend uses to create Ingress sessions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use HA Core REST API for Ingress session creation hassio/ingress_session WS command doesn't exist. The HA frontend uses POST /api/hassio/ingress/session REST endpoint instead. Simpler too — no WS connection needed, just an HTTP call with the existing Bearer token. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: try both Supervisor and HA Core fallback paths with diagnostics When direct Docker access returns 403, try creating an Ingress session via SUPERVISOR_TOKEN first, then fall back to HA Core REST API. Both attempt results are reported in debug output for troubleshooting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove dead Ingress session fallback code The Ingress session approach doesn't work for community add-ons due to IP-based Nginx restrictions + Supervisor auth limitations. Remove the entire _try_ingress_proxy function, fallback logic, and os import. The tool works for add-ons without IP restrictions (Frigate, EVCC) via direct Docker network + X-Ingress-Path headers. Add-ons with IP- restricted Nginx (Node-RED, Glances) need per-addon workarounds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update ha_call_addon_api description with troubleshooting guidance Explain that community add-ons may need config changes (IP restrictions, direct port access) and recommend debug=True for diagnosing issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Include addon config in 401/403 errors for LLM troubleshooting When an add-on returns 401/403, the response now includes the add-on's options, port mappings, and a suggestion to review settings. This helps the LLM identify configuration fixes (e.g., leave_front_door_open) without a separate ha_get_addon call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address review feedback: path traversal and JSON truncation - URL-decode path before traversal check to catch %2e%2e%2f sequences - Return structured error instead of broken truncated JSON for oversized responses - Keep destructiveHint: True (tool can POST/PUT/DELETE to any add-on API) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add port parameter to ha_call_addon_api for direct port access Community add-ons (Node-RED, ESPHome, etc.) use Nginx IP restrictions on their ingress port that block direct container connections. The new `port` parameter lets callers connect to the add-on's direct access port instead (e.g., 1880 for Node-RED, 6052 for ESPHome), bypassing the IP-restricted ingress port entirely. Requires `leave_front_door_open` or equivalent to be enabled on the add-on. When `port` is specified, Ingress headers are omitted since the direct port doesn't use them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add offset/limit pagination for large addon API responses Array responses (e.g., Node-RED /flows, Frigate /api/events) can now be paginated with offset and limit parameters, avoiding the 50KB response size cap. For large dict responses (e.g., evcc /api/state), the too-large error now includes top-level keys with their types and sizes, helping callers make more targeted API calls. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add WebSocket support to ha_call_addon_api Add `websocket` and `wait_for_close` parameters to ha_call_addon_api for connecting to addon WebSocket APIs (e.g., ESPHome compile/validate, Node-RED comms, Z-Wave JS commands). When websocket=true: - Upgrades to WebSocket connection at ws://{addon_ip}:{port}/{path} - Sends `body` as initial WebSocket message - Collects response messages until server closes, timeout, or 50KB limit - Strips ANSI escape codes from addon output - Returns collected messages with metadata (count, close reason, duration) When websocket=false (default): existing HTTP behavior unchanged. `wait_for_close` controls collection strategy: - true (default): wait for server to close or timeout (120s) — for operations that stream until done (compile, validate, install) - false: return after first batch of messages (2s silence) — for quick commands or checking status Security: mandatory timeout, 50KB response cap, 1000 message limit, ANSI stripping, no persistent connections. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address idiot-check findings for WebSocket and port features - Add missing websockets.exceptions.ConnectionClosed handler to outer exception chain in _call_addon_ws (ws.send() could raise it before reaching the inner recv loop's handler) - Add missing Ingress support check to _call_addon_ws (matching the HTTP path's validation) - Fix ingress_port UnboundLocalError in HTTP 401/403 handler when using port parameter (latent bug made reachable by port feature) - Add unit tests for _call_addon_ws covering: path traversal, addon not found, no Ingress support, port override bypass, addon not running, handshake failure, connection closed during send, connection error, message collection, ANSI stripping, binary frame skipping, wait_for_close=false early return, missing network info Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: cap WS serialized output size, trim tool description, update README Address idiot-check findings: 1. Cap serialized WS result size: raw byte collection undercounts due to JSON + MCP protocol overhead (50KB raw -> 62KB serialized). Now checks serialized size and returns RESPONSE_TOO_LARGE summary if it exceeds the 50KB limit. 2. Fix duplicate step number comments in _call_addon_ws (5,5,6,7 -> 5,6,7,8). 3. Mark offset/limit param descriptions as "HTTP only" so LLMs don't try to use them with websocket=true. 4. Trim tool docstring per progressive disclosure pattern — removed verbose explanations, kept summary + examples. Details are discoverable via ha_get_addon and error-driven guidance. 5. Shorten port, websocket, wait_for_close param descriptions. 6. Update README Add-ons row to mention WebSocket and direct port access capabilities. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: kingpanther13 <kingpanther13@users.noreply.github.com>
1 parent aa350c8 commit c5ecdde

3 files changed

Lines changed: 1423 additions & 190 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Spend less time configuring, more time enjoying your smart home.
129129
| **💾 System** | Backup/restore, updates, add-ons, device registry |
130130

131131
<details>
132-
<summary><b>🛠️ Complete Tool List (96 tools)</b></summary>
132+
<summary><b>🛠️ Complete Tool List (97 tools)</b></summary>
133133

134134
| Category | Tools |
135135
|----------|-------|
@@ -148,7 +148,7 @@ Spend less time configuring, more time enjoying your smart home.
148148
| **Blueprints** | `ha_list_blueprints`, `ha_get_blueprint`, `ha_import_blueprint` |
149149
| **Device Registry** | `ha_get_device`, `ha_update_device`, `ha_remove_device`, `ha_rename_entity` |
150150
| **ZHA & Integrations** | `ha_get_zha_devices`, `ha_get_entity_integration_source` |
151-
| **Add-ons** | `ha_get_addon` |
151+
| **Add-ons** | `ha_get_addon`, `ha_call_addon_api` (HTTP & WebSocket, direct port access) |
152152
| **Camera** | `ha_get_camera_image` |
153153
| **History & Statistics** | `ha_get_history`, `ha_get_statistics` |
154154
| **Automation Traces** | `ha_get_automation_traces` |

0 commit comments

Comments
 (0)