ha-mcp-dev 7.0.0.dev274__tar.gz → 7.0.0.dev276__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ha_mcp_dev-7.0.0.dev274/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.0.0.dev276}/PKG-INFO +1 -1
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/pyproject.toml +1 -1
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/auth/provider.py +1 -1
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/client/rest_client.py +20 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/errors.py +7 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/server.py +2 -1
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_filesystem.py +34 -86
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_hacs.py +38 -115
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_integrations.py +2 -19
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_mcp_component.py +2 -12
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/LICENSE +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/README.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/setup.cfg +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/card_types.json +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/dashboard_guide.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/smart_search.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_info.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/tests/test_env_manager.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-mcp-dev"
|
|
7
|
-
version = "7.0.0.
|
|
7
|
+
version = "7.0.0.dev276"
|
|
8
8
|
description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13,<3.14"
|
|
@@ -168,7 +168,7 @@ class HomeAssistantOAuthProvider(OAuthProvider):
|
|
|
168
168
|
decoded = urlsafe_b64decode(token.encode()).decode()
|
|
169
169
|
payload = json.loads(decoded)
|
|
170
170
|
|
|
171
|
-
ha_token = payload.get("ha_token")
|
|
171
|
+
ha_token: str | None = payload.get("ha_token")
|
|
172
172
|
|
|
173
173
|
if ha_token:
|
|
174
174
|
return str(ha_token)
|
|
@@ -769,6 +769,26 @@ class HomeAssistantClient:
|
|
|
769
769
|
)
|
|
770
770
|
return found
|
|
771
771
|
|
|
772
|
+
async def delete_config_entry(self, entry_id: str) -> dict[str, Any]:
|
|
773
|
+
"""Delete a config entry via REST API.
|
|
774
|
+
|
|
775
|
+
The WebSocket command ``config_entries/delete`` is not supported by
|
|
776
|
+
Home Assistant. The REST endpoint ``DELETE /api/config/config_entries/
|
|
777
|
+
entry/{entry_id}`` is the correct way to remove a config entry.
|
|
778
|
+
|
|
779
|
+
Args:
|
|
780
|
+
entry_id: Config entry ID to delete.
|
|
781
|
+
|
|
782
|
+
Returns:
|
|
783
|
+
Result dict with ``require_restart`` flag.
|
|
784
|
+
|
|
785
|
+
Raises:
|
|
786
|
+
HomeAssistantAPIError: If the entry is not found or the API
|
|
787
|
+
returns an error status.
|
|
788
|
+
"""
|
|
789
|
+
logger.debug(f"Deleting config entry: {entry_id}")
|
|
790
|
+
return await self._request("DELETE", f"/config/config_entries/entry/{entry_id}")
|
|
791
|
+
|
|
772
792
|
async def send_websocket_message(self, message: dict[str, Any]) -> dict[str, Any]:
|
|
773
793
|
"""Send message via WebSocket and wait for response.
|
|
774
794
|
|
|
@@ -78,6 +78,9 @@ class ErrorCode(StrEnum):
|
|
|
78
78
|
RESOURCE_ALREADY_EXISTS = "RESOURCE_ALREADY_EXISTS"
|
|
79
79
|
RESOURCE_LOCKED = "RESOURCE_LOCKED"
|
|
80
80
|
|
|
81
|
+
# Component errors
|
|
82
|
+
COMPONENT_NOT_INSTALLED = "COMPONENT_NOT_INSTALLED"
|
|
83
|
+
|
|
81
84
|
|
|
82
85
|
# Default suggestions for common error codes
|
|
83
86
|
DEFAULT_SUGGESTIONS: dict[ErrorCode, list[str]] = {
|
|
@@ -183,6 +186,10 @@ DEFAULT_SUGGESTIONS: dict[ErrorCode, list[str]] = {
|
|
|
183
186
|
"Check Home Assistant MCP server logs",
|
|
184
187
|
"Report this issue if it persists",
|
|
185
188
|
],
|
|
189
|
+
ErrorCode.COMPONENT_NOT_INSTALLED: [
|
|
190
|
+
"Install the required custom component via HACS",
|
|
191
|
+
"Restart Home Assistant after installation",
|
|
192
|
+
],
|
|
186
193
|
}
|
|
187
194
|
|
|
188
195
|
|
|
@@ -10,6 +10,7 @@ Implements lazy initialization pattern for improved startup time:
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import logging
|
|
13
|
+
from collections.abc import Callable, Coroutine
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import TYPE_CHECKING, Any, cast
|
|
15
16
|
|
|
@@ -388,7 +389,7 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
|
|
|
388
389
|
# Use factory to capture ref_files in closure
|
|
389
390
|
def _make_skill_handler(
|
|
390
391
|
s_name: str, s_uri: str, files: list[dict[str, str]],
|
|
391
|
-
):
|
|
392
|
+
) -> Callable[[], Coroutine[Any, Any, dict[str, Any]]]:
|
|
392
393
|
async def handler() -> dict[str, Any]:
|
|
393
394
|
return {
|
|
394
395
|
"skill": s_name,
|
|
@@ -20,6 +20,7 @@ from typing import Annotated, Any
|
|
|
20
20
|
from fastmcp.exceptions import ToolError
|
|
21
21
|
from pydantic import Field
|
|
22
22
|
|
|
23
|
+
from ..errors import ErrorCode, create_error_response
|
|
23
24
|
from .helpers import exception_to_structured_error, log_tool_usage, raise_tool_error
|
|
24
25
|
from .util_helpers import add_timezone_metadata, coerce_bool_param, coerce_int_param
|
|
25
26
|
|
|
@@ -55,23 +56,34 @@ def is_filesystem_tools_enabled() -> bool:
|
|
|
55
56
|
return value in ("true", "1", "yes", "on")
|
|
56
57
|
|
|
57
58
|
|
|
58
|
-
async def
|
|
59
|
-
"""
|
|
59
|
+
async def _is_mcp_tools_available(client: Any) -> bool:
|
|
60
|
+
"""Return True if the ha_mcp_tools custom component is registered in HA services.
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
Raises if the services API call fails — callers handle API errors via
|
|
63
|
+
their own exception_to_structured_error blocks.
|
|
63
64
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
# HA /api/services returns a list of {"domain": str, "services": {...}} objects.
|
|
66
|
+
# This format has been stable since before HA 0.7 (the first public release).
|
|
67
|
+
services = await client.get_services()
|
|
68
|
+
return any(
|
|
69
|
+
isinstance(s, dict) and s.get("domain") == MCP_TOOLS_DOMAIN
|
|
70
|
+
for s in services
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def _assert_mcp_tools_available(client: Any) -> None:
|
|
75
|
+
"""Raise ToolError if ha_mcp_tools is not available.
|
|
76
|
+
|
|
77
|
+
Must be called within a try block that handles API errors via
|
|
78
|
+
exception_to_structured_error, so connection failures are classified
|
|
79
|
+
correctly rather than masked as COMPONENT_NOT_INSTALLED.
|
|
80
|
+
"""
|
|
81
|
+
if not await _is_mcp_tools_available(client):
|
|
82
|
+
raise_tool_error(create_error_response(
|
|
83
|
+
ErrorCode.COMPONENT_NOT_INSTALLED,
|
|
70
84
|
f"The {MCP_TOOLS_DOMAIN} custom component is not installed. "
|
|
71
|
-
"Use ha_install_mcp_tools() to install it via HACS, then restart Home Assistant."
|
|
72
|
-
)
|
|
73
|
-
except Exception as e:
|
|
74
|
-
return False, f"Failed to check for {MCP_TOOLS_DOMAIN}: {str(e)}"
|
|
85
|
+
"Use ha_install_mcp_tools() to install it via HACS, then restart Home Assistant.",
|
|
86
|
+
))
|
|
75
87
|
|
|
76
88
|
|
|
77
89
|
def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
@@ -145,20 +157,7 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
145
157
|
"""
|
|
146
158
|
try:
|
|
147
159
|
# Check if custom component is available
|
|
148
|
-
|
|
149
|
-
if not is_available:
|
|
150
|
-
return await add_timezone_metadata(
|
|
151
|
-
client,
|
|
152
|
-
{
|
|
153
|
-
"success": False,
|
|
154
|
-
"error": error_msg,
|
|
155
|
-
"error_code": "MCP_TOOLS_NOT_INSTALLED",
|
|
156
|
-
"suggestions": [
|
|
157
|
-
"Run ha_install_mcp_tools() to install the custom component",
|
|
158
|
-
"Restart Home Assistant after installation",
|
|
159
|
-
],
|
|
160
|
-
},
|
|
161
|
-
)
|
|
160
|
+
await _assert_mcp_tools_available(client)
|
|
162
161
|
|
|
163
162
|
# Build service data
|
|
164
163
|
service_data: dict[str, Any] = {"path": path}
|
|
@@ -192,13 +191,10 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
192
191
|
except ToolError:
|
|
193
192
|
raise
|
|
194
193
|
except Exception as e:
|
|
195
|
-
|
|
194
|
+
exception_to_structured_error(
|
|
196
195
|
e,
|
|
197
196
|
context={"tool": "ha_list_files", "path": path, "pattern": pattern},
|
|
198
|
-
raise_error=False,
|
|
199
197
|
)
|
|
200
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
201
|
-
raise_tool_error(error_with_tz)
|
|
202
198
|
|
|
203
199
|
@mcp.tool(
|
|
204
200
|
annotations={
|
|
@@ -277,20 +273,7 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
277
273
|
)
|
|
278
274
|
|
|
279
275
|
# Check if custom component is available
|
|
280
|
-
|
|
281
|
-
if not is_available:
|
|
282
|
-
return await add_timezone_metadata(
|
|
283
|
-
client,
|
|
284
|
-
{
|
|
285
|
-
"success": False,
|
|
286
|
-
"error": error_msg,
|
|
287
|
-
"error_code": "MCP_TOOLS_NOT_INSTALLED",
|
|
288
|
-
"suggestions": [
|
|
289
|
-
"Run ha_install_mcp_tools() to install the custom component",
|
|
290
|
-
"Restart Home Assistant after installation",
|
|
291
|
-
],
|
|
292
|
-
},
|
|
293
|
-
)
|
|
276
|
+
await _assert_mcp_tools_available(client)
|
|
294
277
|
|
|
295
278
|
# Build service data
|
|
296
279
|
service_data: dict[str, Any] = {"path": path}
|
|
@@ -320,13 +303,10 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
320
303
|
except ToolError:
|
|
321
304
|
raise
|
|
322
305
|
except Exception as e:
|
|
323
|
-
|
|
306
|
+
exception_to_structured_error(
|
|
324
307
|
e,
|
|
325
308
|
context={"tool": "ha_read_file", "path": path},
|
|
326
|
-
raise_error=False,
|
|
327
309
|
)
|
|
328
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
329
|
-
raise_tool_error(error_with_tz)
|
|
330
310
|
|
|
331
311
|
@mcp.tool(
|
|
332
312
|
annotations={
|
|
@@ -420,20 +400,7 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
420
400
|
create_dirs_bool = coerce_bool_param(create_dirs, "create_dirs", default=True)
|
|
421
401
|
|
|
422
402
|
# Check if custom component is available
|
|
423
|
-
|
|
424
|
-
if not is_available:
|
|
425
|
-
return await add_timezone_metadata(
|
|
426
|
-
client,
|
|
427
|
-
{
|
|
428
|
-
"success": False,
|
|
429
|
-
"error": error_msg,
|
|
430
|
-
"error_code": "MCP_TOOLS_NOT_INSTALLED",
|
|
431
|
-
"suggestions": [
|
|
432
|
-
"Run ha_install_mcp_tools() to install the custom component",
|
|
433
|
-
"Restart Home Assistant after installation",
|
|
434
|
-
],
|
|
435
|
-
},
|
|
436
|
-
)
|
|
403
|
+
await _assert_mcp_tools_available(client)
|
|
437
404
|
|
|
438
405
|
# Build service data
|
|
439
406
|
service_data: dict[str, Any] = {
|
|
@@ -466,13 +433,10 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
466
433
|
except ToolError:
|
|
467
434
|
raise
|
|
468
435
|
except Exception as e:
|
|
469
|
-
|
|
436
|
+
exception_to_structured_error(
|
|
470
437
|
e,
|
|
471
438
|
context={"tool": "ha_write_file", "path": path},
|
|
472
|
-
raise_error=False,
|
|
473
439
|
)
|
|
474
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
475
|
-
raise_tool_error(error_with_tz)
|
|
476
440
|
|
|
477
441
|
@mcp.tool(
|
|
478
442
|
annotations={
|
|
@@ -556,20 +520,7 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
556
520
|
)
|
|
557
521
|
|
|
558
522
|
# Check if custom component is available
|
|
559
|
-
|
|
560
|
-
if not is_available:
|
|
561
|
-
return await add_timezone_metadata(
|
|
562
|
-
client,
|
|
563
|
-
{
|
|
564
|
-
"success": False,
|
|
565
|
-
"error": error_msg,
|
|
566
|
-
"error_code": "MCP_TOOLS_NOT_INSTALLED",
|
|
567
|
-
"suggestions": [
|
|
568
|
-
"Run ha_install_mcp_tools() to install the custom component",
|
|
569
|
-
"Restart Home Assistant after installation",
|
|
570
|
-
],
|
|
571
|
-
},
|
|
572
|
-
)
|
|
523
|
+
await _assert_mcp_tools_available(client)
|
|
573
524
|
|
|
574
525
|
# Build service data
|
|
575
526
|
service_data: dict[str, Any] = {"path": path}
|
|
@@ -597,10 +548,7 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
597
548
|
except ToolError:
|
|
598
549
|
raise
|
|
599
550
|
except Exception as e:
|
|
600
|
-
|
|
551
|
+
exception_to_structured_error(
|
|
601
552
|
e,
|
|
602
553
|
context={"tool": "ha_delete_file", "path": path},
|
|
603
|
-
raise_error=False,
|
|
604
554
|
)
|
|
605
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
606
|
-
raise_tool_error(error_with_tz)
|
|
@@ -11,6 +11,7 @@ from typing import Annotated, Any, Literal
|
|
|
11
11
|
from fastmcp.exceptions import ToolError
|
|
12
12
|
from pydantic import Field
|
|
13
13
|
|
|
14
|
+
from ..errors import ErrorCode, create_error_response
|
|
14
15
|
from .helpers import exception_to_structured_error, log_tool_usage, raise_tool_error
|
|
15
16
|
from .util_helpers import add_timezone_metadata, coerce_int_param
|
|
16
17
|
|
|
@@ -32,29 +33,35 @@ CATEGORY_DISPLAY = {v: k for k, v in CATEGORY_MAP.items()}
|
|
|
32
33
|
CATEGORY_DISPLAY["plugin"] = "lovelace" # Display as lovelace for users
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
async def
|
|
36
|
-
"""
|
|
37
|
-
Check if HACS is installed and available via WebSocket.
|
|
36
|
+
async def _is_hacs_available() -> bool:
|
|
37
|
+
"""Return True if HACS is installed and responding via WebSocket.
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
Raises if the WebSocket connection fails — callers handle API errors via
|
|
40
|
+
their own exception_to_structured_error blocks.
|
|
41
41
|
"""
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
from ..client.websocket_client import get_websocket_client
|
|
43
|
+
ws_client = await get_websocket_client()
|
|
44
|
+
response = await ws_client.send_command("hacs/info")
|
|
45
|
+
return bool(response.get("success"))
|
|
46
|
+
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
async def _assert_hacs_available() -> None:
|
|
49
|
+
"""Raise ToolError if HACS is not available.
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
Must be called within a try block that handles API errors via
|
|
52
|
+
exception_to_structured_error, so connection failures are classified
|
|
53
|
+
correctly rather than masked as COMPONENT_NOT_INSTALLED.
|
|
54
|
+
"""
|
|
55
|
+
if not await _is_hacs_available():
|
|
56
|
+
raise_tool_error(create_error_response(
|
|
57
|
+
ErrorCode.COMPONENT_NOT_INSTALLED,
|
|
58
|
+
"HACS is not installed or not loaded.",
|
|
59
|
+
suggestions=[
|
|
60
|
+
"Install HACS from https://hacs.xyz/",
|
|
61
|
+
"Restart Home Assistant after HACS installation",
|
|
62
|
+
"Check Home Assistant logs for HACS errors",
|
|
63
|
+
],
|
|
64
|
+
))
|
|
58
65
|
|
|
59
66
|
|
|
60
67
|
def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
@@ -83,18 +90,7 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
83
90
|
"""
|
|
84
91
|
try:
|
|
85
92
|
# Check if HACS is available
|
|
86
|
-
|
|
87
|
-
if not is_available:
|
|
88
|
-
return await add_timezone_metadata(client, {
|
|
89
|
-
"success": False,
|
|
90
|
-
"error": error_msg,
|
|
91
|
-
"error_code": "HACS_NOT_AVAILABLE",
|
|
92
|
-
"suggestions": [
|
|
93
|
-
"Install HACS from https://hacs.xyz/",
|
|
94
|
-
"Ensure Home Assistant has been restarted after HACS installation",
|
|
95
|
-
"Check Home Assistant logs for HACS errors",
|
|
96
|
-
],
|
|
97
|
-
})
|
|
93
|
+
await _assert_hacs_available()
|
|
98
94
|
|
|
99
95
|
# Get HACS info via WebSocket
|
|
100
96
|
from ..client.websocket_client import get_websocket_client
|
|
@@ -123,18 +119,15 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
123
119
|
except ToolError:
|
|
124
120
|
raise
|
|
125
121
|
except Exception as e:
|
|
126
|
-
|
|
122
|
+
exception_to_structured_error(
|
|
127
123
|
e,
|
|
128
124
|
context={"tool": "ha_hacs_info"},
|
|
129
|
-
raise_error=False,
|
|
130
125
|
suggestions=[
|
|
131
126
|
"Verify HACS is installed: https://hacs.xyz/",
|
|
132
127
|
"Check Home Assistant connection",
|
|
133
128
|
"Restart Home Assistant if HACS was recently installed",
|
|
134
129
|
],
|
|
135
130
|
)
|
|
136
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
137
|
-
raise_tool_error(error_with_tz)
|
|
138
131
|
|
|
139
132
|
@mcp.tool(annotations={"idempotentHint": True, "readOnlyHint": True, "tags": ["hacs", "search"], "title": "List HACS Installed"})
|
|
140
133
|
@log_tool_usage
|
|
@@ -179,18 +172,7 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
179
172
|
"""
|
|
180
173
|
try:
|
|
181
174
|
# Check if HACS is available
|
|
182
|
-
|
|
183
|
-
if not is_available:
|
|
184
|
-
return await add_timezone_metadata(client, {
|
|
185
|
-
"success": False,
|
|
186
|
-
"error": error_msg,
|
|
187
|
-
"error_code": "HACS_NOT_AVAILABLE",
|
|
188
|
-
"suggestions": [
|
|
189
|
-
"Install HACS from https://hacs.xyz/",
|
|
190
|
-
"Ensure Home Assistant has been restarted after HACS installation",
|
|
191
|
-
"Check Home Assistant logs for HACS errors",
|
|
192
|
-
],
|
|
193
|
-
})
|
|
175
|
+
await _assert_hacs_available()
|
|
194
176
|
|
|
195
177
|
# Get installed repositories via WebSocket
|
|
196
178
|
from ..client.websocket_client import get_websocket_client
|
|
@@ -244,18 +226,15 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
244
226
|
except ToolError:
|
|
245
227
|
raise
|
|
246
228
|
except Exception as e:
|
|
247
|
-
|
|
229
|
+
exception_to_structured_error(
|
|
248
230
|
e,
|
|
249
231
|
context={"tool": "ha_hacs_list_installed", "category": category},
|
|
250
|
-
raise_error=False,
|
|
251
232
|
suggestions=[
|
|
252
233
|
"Verify HACS is installed: https://hacs.xyz/",
|
|
253
234
|
"Check category name is valid: integration, lovelace, theme, appdaemon, python_script",
|
|
254
235
|
"Check Home Assistant connection",
|
|
255
236
|
],
|
|
256
237
|
)
|
|
257
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
258
|
-
raise_tool_error(error_with_tz)
|
|
259
238
|
|
|
260
239
|
@mcp.tool(annotations={"idempotentHint": True, "readOnlyHint": True, "tags": ["hacs", "search"], "title": "Search HACS Store"})
|
|
261
240
|
@log_tool_usage
|
|
@@ -329,18 +308,7 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
329
308
|
) or 0
|
|
330
309
|
|
|
331
310
|
# Check if HACS is available
|
|
332
|
-
|
|
333
|
-
if not is_available:
|
|
334
|
-
return await add_timezone_metadata(client, {
|
|
335
|
-
"success": False,
|
|
336
|
-
"error": error_msg,
|
|
337
|
-
"error_code": "HACS_NOT_AVAILABLE",
|
|
338
|
-
"suggestions": [
|
|
339
|
-
"Install HACS from https://hacs.xyz/",
|
|
340
|
-
"Ensure Home Assistant has been restarted after HACS installation",
|
|
341
|
-
"Check Home Assistant logs for HACS errors",
|
|
342
|
-
],
|
|
343
|
-
})
|
|
311
|
+
await _assert_hacs_available()
|
|
344
312
|
|
|
345
313
|
# Get all repositories via WebSocket
|
|
346
314
|
from ..client.websocket_client import get_websocket_client
|
|
@@ -426,18 +394,15 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
426
394
|
except ToolError:
|
|
427
395
|
raise
|
|
428
396
|
except Exception as e:
|
|
429
|
-
|
|
397
|
+
exception_to_structured_error(
|
|
430
398
|
e,
|
|
431
399
|
context={"tool": "ha_hacs_search", "query": query, "category": category},
|
|
432
|
-
raise_error=False,
|
|
433
400
|
suggestions=[
|
|
434
401
|
"Verify HACS is installed: https://hacs.xyz/",
|
|
435
402
|
"Try a simpler search query",
|
|
436
403
|
"Check category name is valid: integration, lovelace, theme, appdaemon, python_script",
|
|
437
404
|
],
|
|
438
405
|
)
|
|
439
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
440
|
-
raise_tool_error(error_with_tz)
|
|
441
406
|
|
|
442
407
|
@mcp.tool(annotations={"idempotentHint": True, "readOnlyHint": True, "tags": ["hacs", "info"], "title": "Get HACS Repository Info"})
|
|
443
408
|
@log_tool_usage
|
|
@@ -468,18 +433,7 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
468
433
|
"""
|
|
469
434
|
try:
|
|
470
435
|
# Check if HACS is available
|
|
471
|
-
|
|
472
|
-
if not is_available:
|
|
473
|
-
return await add_timezone_metadata(client, {
|
|
474
|
-
"success": False,
|
|
475
|
-
"error": error_msg,
|
|
476
|
-
"error_code": "HACS_NOT_AVAILABLE",
|
|
477
|
-
"suggestions": [
|
|
478
|
-
"Install HACS from https://hacs.xyz/",
|
|
479
|
-
"Ensure Home Assistant has been restarted after HACS installation",
|
|
480
|
-
"Check Home Assistant logs for HACS errors",
|
|
481
|
-
],
|
|
482
|
-
})
|
|
436
|
+
await _assert_hacs_available()
|
|
483
437
|
|
|
484
438
|
from ..client.websocket_client import get_websocket_client
|
|
485
439
|
ws_client = await get_websocket_client()
|
|
@@ -545,18 +499,15 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
545
499
|
except ToolError:
|
|
546
500
|
raise
|
|
547
501
|
except Exception as e:
|
|
548
|
-
|
|
502
|
+
exception_to_structured_error(
|
|
549
503
|
e,
|
|
550
504
|
context={"tool": "ha_hacs_repository_info", "repository_id": repository_id},
|
|
551
|
-
raise_error=False,
|
|
552
505
|
suggestions=[
|
|
553
506
|
"Verify HACS is installed: https://hacs.xyz/",
|
|
554
507
|
"Check repository ID format (e.g., 'hacs/integration' or 'owner/repo')",
|
|
555
508
|
"Use ha_hacs_search() to find the correct repository ID",
|
|
556
509
|
],
|
|
557
510
|
)
|
|
558
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
559
|
-
raise_tool_error(error_with_tz)
|
|
560
511
|
|
|
561
512
|
@mcp.tool(annotations={"destructiveHint": True, "tags": ["hacs", "management"], "title": "Add HACS Repository"})
|
|
562
513
|
@log_tool_usage
|
|
@@ -604,18 +555,7 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
604
555
|
"""
|
|
605
556
|
try:
|
|
606
557
|
# Check if HACS is available
|
|
607
|
-
|
|
608
|
-
if not is_available:
|
|
609
|
-
return await add_timezone_metadata(client, {
|
|
610
|
-
"success": False,
|
|
611
|
-
"error": error_msg,
|
|
612
|
-
"error_code": "HACS_NOT_AVAILABLE",
|
|
613
|
-
"suggestions": [
|
|
614
|
-
"Install HACS from https://hacs.xyz/",
|
|
615
|
-
"Ensure Home Assistant has been restarted after HACS installation",
|
|
616
|
-
"Check Home Assistant logs for HACS errors",
|
|
617
|
-
],
|
|
618
|
-
})
|
|
558
|
+
await _assert_hacs_available()
|
|
619
559
|
|
|
620
560
|
# Validate repository format
|
|
621
561
|
if "/" not in repository:
|
|
@@ -667,14 +607,13 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
667
607
|
except ToolError:
|
|
668
608
|
raise
|
|
669
609
|
except Exception as e:
|
|
670
|
-
|
|
610
|
+
exception_to_structured_error(
|
|
671
611
|
e,
|
|
672
612
|
context={
|
|
673
613
|
"tool": "ha_hacs_add_repository",
|
|
674
614
|
"repository": repository,
|
|
675
615
|
"category": category,
|
|
676
616
|
},
|
|
677
|
-
raise_error=False,
|
|
678
617
|
suggestions=[
|
|
679
618
|
"Verify HACS is installed: https://hacs.xyz/",
|
|
680
619
|
"Check repository format: 'owner/repo'",
|
|
@@ -683,8 +622,6 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
683
622
|
"Check repository follows HACS guidelines: https://hacs.xyz/docs/publish/start",
|
|
684
623
|
],
|
|
685
624
|
)
|
|
686
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
687
|
-
raise_tool_error(error_with_tz)
|
|
688
625
|
|
|
689
626
|
@mcp.tool(annotations={"destructiveHint": True, "tags": ["hacs", "management"], "title": "Download/Install HACS Repository"})
|
|
690
627
|
@log_tool_usage
|
|
@@ -731,18 +668,7 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
731
668
|
"""
|
|
732
669
|
try:
|
|
733
670
|
# Check if HACS is available
|
|
734
|
-
|
|
735
|
-
if not is_available:
|
|
736
|
-
return await add_timezone_metadata(client, {
|
|
737
|
-
"success": False,
|
|
738
|
-
"error": error_msg,
|
|
739
|
-
"error_code": "HACS_NOT_AVAILABLE",
|
|
740
|
-
"suggestions": [
|
|
741
|
-
"Install HACS from https://hacs.xyz/",
|
|
742
|
-
"Ensure Home Assistant has been restarted after HACS installation",
|
|
743
|
-
"Check Home Assistant logs for HACS errors",
|
|
744
|
-
],
|
|
745
|
-
})
|
|
671
|
+
await _assert_hacs_available()
|
|
746
672
|
|
|
747
673
|
from ..client.websocket_client import get_websocket_client
|
|
748
674
|
ws_client = await get_websocket_client()
|
|
@@ -806,14 +732,13 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
806
732
|
except ToolError:
|
|
807
733
|
raise
|
|
808
734
|
except Exception as e:
|
|
809
|
-
|
|
735
|
+
exception_to_structured_error(
|
|
810
736
|
e,
|
|
811
737
|
context={
|
|
812
738
|
"tool": "ha_hacs_download",
|
|
813
739
|
"repository_id": repository_id,
|
|
814
740
|
"version": version,
|
|
815
741
|
},
|
|
816
|
-
raise_error=False,
|
|
817
742
|
suggestions=[
|
|
818
743
|
"Verify HACS is installed: https://hacs.xyz/",
|
|
819
744
|
"Check repository ID is valid (use ha_hacs_search() to find it)",
|
|
@@ -821,5 +746,3 @@ def register_hacs_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
821
746
|
"Check version format (e.g., 'v1.2.3' or '1.2.3')",
|
|
822
747
|
],
|
|
823
748
|
)
|
|
824
|
-
error_with_tz = await add_timezone_metadata(client, error_response)
|
|
825
|
-
raise_tool_error(error_with_tz)
|
|
@@ -353,25 +353,8 @@ def register_integration_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
353
353
|
},
|
|
354
354
|
))
|
|
355
355
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
"entry_id": entry_id,
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
result = await client.send_websocket_message(message)
|
|
362
|
-
|
|
363
|
-
if not result.get("success"):
|
|
364
|
-
error_msg = result.get("error", {})
|
|
365
|
-
if isinstance(error_msg, dict):
|
|
366
|
-
error_msg = error_msg.get("message", str(error_msg))
|
|
367
|
-
raise_tool_error(create_error_response(
|
|
368
|
-
ErrorCode.SERVICE_CALL_FAILED,
|
|
369
|
-
f"Failed to delete config entry: {error_msg}",
|
|
370
|
-
context={"entry_id": entry_id},
|
|
371
|
-
))
|
|
372
|
-
|
|
373
|
-
# Get result info
|
|
374
|
-
require_restart = result.get("result", {}).get("require_restart", False)
|
|
356
|
+
result = await client.delete_config_entry(entry_id)
|
|
357
|
+
require_restart = result.get("require_restart", False)
|
|
375
358
|
|
|
376
359
|
return {
|
|
377
360
|
"success": True,
|
|
@@ -52,7 +52,7 @@ def register_mcp_component_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
52
52
|
logger.info("MCP tools installer enabled via feature flag")
|
|
53
53
|
|
|
54
54
|
# Import HACS helpers - we depend on HACS functionality
|
|
55
|
-
from .tools_hacs import CATEGORY_MAP,
|
|
55
|
+
from .tools_hacs import CATEGORY_MAP, _assert_hacs_available
|
|
56
56
|
|
|
57
57
|
@mcp.tool(
|
|
58
58
|
annotations={
|
|
@@ -98,17 +98,7 @@ def register_mcp_component_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
98
98
|
"""
|
|
99
99
|
try:
|
|
100
100
|
# Check if HACS is available
|
|
101
|
-
|
|
102
|
-
if not is_available:
|
|
103
|
-
raise_tool_error(create_error_response(
|
|
104
|
-
ErrorCode.SERVICE_CALL_FAILED,
|
|
105
|
-
error_msg or "HACS is not available",
|
|
106
|
-
suggestions=[
|
|
107
|
-
"Install HACS from https://hacs.xyz/",
|
|
108
|
-
"Ensure Home Assistant has been restarted after HACS installation",
|
|
109
|
-
"Check Home Assistant logs for HACS errors",
|
|
110
|
-
],
|
|
111
|
-
))
|
|
101
|
+
await _assert_hacs_available()
|
|
112
102
|
|
|
113
103
|
from ..client.websocket_client import get_websocket_client
|
|
114
104
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/resources/skills-vendor/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/best_practice_checker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.0.0.dev274 → ha_mcp_dev-7.0.0.dev276}/src/ha_mcp_dev.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|