ha-mcp-dev 7.6.0.dev604__tar.gz → 7.6.0.dev606__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.6.0.dev604/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev606}/PKG-INFO +1 -1
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/pyproject.toml +1 -1
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_filesystem.py +72 -19
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_mcp_component.py +41 -7
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_yaml_config.py +19 -9
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/LICENSE +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/README.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/setup.cfg +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/smart_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/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.6.0.
|
|
7
|
+
version = "7.6.0.dev606"
|
|
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"
|
|
@@ -17,7 +17,7 @@ import asyncio
|
|
|
17
17
|
import json
|
|
18
18
|
import logging
|
|
19
19
|
import weakref
|
|
20
|
-
from typing import Annotated, Any
|
|
20
|
+
from typing import Annotated, Any, NoReturn
|
|
21
21
|
|
|
22
22
|
from fastmcp.exceptions import ToolError
|
|
23
23
|
from fastmcp.tools import tool
|
|
@@ -47,6 +47,28 @@ MCP_TOOLS_DOMAIN = "ha_mcp_tools"
|
|
|
47
47
|
# subsequent call comes back unauthorized (covers token rotation).
|
|
48
48
|
CALLER_TOKEN_FIELD = "_ha_mcp_token"
|
|
49
49
|
CALLER_TOKEN_BOOTSTRAP_SERVICE = "get_caller_token"
|
|
50
|
+
|
|
51
|
+
# Minimum version of the ha_mcp_tools custom component that this ha-mcp
|
|
52
|
+
# release expects. Bumps in lockstep with ``manifest.json`` whenever a
|
|
53
|
+
# server-side behavior change requires it. Older components (no
|
|
54
|
+
# ``version`` in the get_caller_token response, or a version below this)
|
|
55
|
+
# get an actionable "update via HACS" error.
|
|
56
|
+
MIN_COMPONENT_VERSION = "0.5.1"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _version_tuple(version: str) -> tuple[int, ...]:
|
|
60
|
+
"""Parse ``'0.5.1'`` → ``(0, 5, 1)`` for tuple-comparison.
|
|
61
|
+
|
|
62
|
+
Raises ``ValueError`` on any non-numeric segment. Coercing a bad
|
|
63
|
+
segment to ``0`` would not actually achieve the "fail closed"
|
|
64
|
+
intent: a malformed high-order segment like ``"1.x.0"`` would
|
|
65
|
+
still parse to ``(1, 0, 0)`` and pass a ``>= (0, 5, 1)`` gate.
|
|
66
|
+
Caller routes the ValueError through ``_raise_component_too_old``
|
|
67
|
+
so the actionable update prompt fires.
|
|
68
|
+
"""
|
|
69
|
+
return tuple(int(segment) for segment in version.split("."))
|
|
70
|
+
|
|
71
|
+
|
|
50
72
|
# Weak-keyed by client object to support multi-client setups and self-evict
|
|
51
73
|
# when a client is garbage-collected (avoids id() reuse if a freed client's
|
|
52
74
|
# address gets recycled before the unauthorized-retry fires).
|
|
@@ -81,29 +103,47 @@ async def _is_bootstrap_service_registered(client: Any) -> bool:
|
|
|
81
103
|
return False
|
|
82
104
|
|
|
83
105
|
|
|
106
|
+
def _raise_component_too_old(detail: str) -> NoReturn:
|
|
107
|
+
"""Single actionable 'update via HACS' error path."""
|
|
108
|
+
raise_tool_error(
|
|
109
|
+
create_error_response(
|
|
110
|
+
ErrorCode.COMPONENT_NOT_INSTALLED,
|
|
111
|
+
f"The installed ha_mcp_tools custom component is too old: {detail}. "
|
|
112
|
+
f"This ha-mcp release requires >= {MIN_COMPONENT_VERSION}. "
|
|
113
|
+
"Update via HACS and restart Home Assistant.",
|
|
114
|
+
suggestions=[
|
|
115
|
+
"HACS → Integrations → HA MCP Tools → Update",
|
|
116
|
+
"Restart Home Assistant after update completes",
|
|
117
|
+
"Then retry the operation",
|
|
118
|
+
],
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
84
123
|
async def _fetch_caller_token(client: Any) -> str:
|
|
85
124
|
"""Call the bootstrap service and cache the returned token.
|
|
86
125
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
126
|
+
Two version gates:
|
|
127
|
+
|
|
128
|
+
1. ``_is_bootstrap_service_registered`` — pre-0.5.0 components don't
|
|
129
|
+
ship ``get_caller_token`` at all. Surface an actionable "update"
|
|
130
|
+
error instead of letting HA return an opaque 400 to the caller.
|
|
131
|
+
2. ``MIN_COMPONENT_VERSION`` — even when the bootstrap service is
|
|
132
|
+
present, the response now carries the component's manifest
|
|
133
|
+
version. ha-mcp releases that depend on newer custom-component
|
|
134
|
+
behavior (e.g. a new accepted yaml_path key, a new schema field)
|
|
135
|
+
bump ``MIN_COMPONENT_VERSION`` together with the manifest, and
|
|
136
|
+
this check rejects 0.5.0+ components that are still behind that
|
|
137
|
+
bar with the same actionable update prompt.
|
|
138
|
+
|
|
139
|
+
Components that pre-date the version-reporting field (returned no
|
|
140
|
+
``version`` in the response) are treated as "too old" for the same
|
|
141
|
+
reason: the absence of the field IS the signal that the component
|
|
142
|
+
doesn't yet know how to report its capabilities to ha-mcp.
|
|
92
143
|
"""
|
|
93
144
|
if not await _is_bootstrap_service_registered(client):
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
ErrorCode.COMPONENT_NOT_INSTALLED,
|
|
97
|
-
"The installed ha_mcp_tools custom component is too old "
|
|
98
|
-
"(pre-0.5.0) — it does not register the get_caller_token "
|
|
99
|
-
"bootstrap service that this ha-mcp version requires. "
|
|
100
|
-
"Update via HACS and restart Home Assistant.",
|
|
101
|
-
suggestions=[
|
|
102
|
-
"HACS → Integrations → HA MCP Tools → Update",
|
|
103
|
-
"Restart Home Assistant after update completes",
|
|
104
|
-
"Then retry the operation",
|
|
105
|
-
],
|
|
106
|
-
)
|
|
145
|
+
_raise_component_too_old(
|
|
146
|
+
"the get_caller_token bootstrap service is not registered (pre-0.5.0)"
|
|
107
147
|
)
|
|
108
148
|
result = await client.call_service(
|
|
109
149
|
MCP_TOOLS_DOMAIN,
|
|
@@ -125,6 +165,19 @@ async def _fetch_caller_token(client: Any) -> str:
|
|
|
125
165
|
],
|
|
126
166
|
)
|
|
127
167
|
)
|
|
168
|
+
raw_version: Any = unwrapped.get("version") if isinstance(unwrapped, dict) else None
|
|
169
|
+
if not isinstance(raw_version, str) or not raw_version:
|
|
170
|
+
_raise_component_too_old(
|
|
171
|
+
"the get_caller_token response did not include a version "
|
|
172
|
+
f"field (pre-{MIN_COMPONENT_VERSION})"
|
|
173
|
+
)
|
|
174
|
+
version: str = raw_version
|
|
175
|
+
try:
|
|
176
|
+
parsed = _version_tuple(version)
|
|
177
|
+
except ValueError:
|
|
178
|
+
_raise_component_too_old(f"malformed version: {version!r}")
|
|
179
|
+
if parsed < _version_tuple(MIN_COMPONENT_VERSION):
|
|
180
|
+
_raise_component_too_old(f"reported version is {version}")
|
|
128
181
|
_CALLER_TOKEN_CACHE[client] = token
|
|
129
182
|
return token
|
|
130
183
|
|
|
@@ -8,6 +8,7 @@ that are not available through standard Home Assistant APIs.
|
|
|
8
8
|
Feature Flag: Set HAMCP_ENABLE_CUSTOM_COMPONENT_INTEGRATION=true to enable this tool.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
+
import asyncio
|
|
11
12
|
import logging
|
|
12
13
|
from typing import Annotated, Any
|
|
13
14
|
|
|
@@ -15,6 +16,7 @@ from fastmcp.exceptions import ToolError
|
|
|
15
16
|
from fastmcp.tools import tool
|
|
16
17
|
from pydantic import Field
|
|
17
18
|
|
|
19
|
+
from ..client.rest_client import HomeAssistantCommandError
|
|
18
20
|
from ..errors import ErrorCode, create_error_response
|
|
19
21
|
from .helpers import (
|
|
20
22
|
exception_to_structured_error,
|
|
@@ -246,22 +248,54 @@ class McpComponentTools:
|
|
|
246
248
|
repo_id = await self._resolve_repo_id(ws_client, existing_repo)
|
|
247
249
|
|
|
248
250
|
logger.info(f"Installing {MCP_TOOLS_REPO} (ID: {repo_id})")
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
# HACS' download often returns a generic "Command failed:
|
|
252
|
+
# Unknown error" on transient GitHub hiccups (rate-limit,
|
|
253
|
+
# tarball stream interruption). Retry the download with
|
|
254
|
+
# exponential backoff so a one-shot transient doesn't
|
|
255
|
+
# surface as an installation failure to the caller.
|
|
256
|
+
# ``send_command`` raises ``HomeAssistantCommandError`` on
|
|
257
|
+
# ``success: False`` responses, so the retry catches that
|
|
258
|
+
# specific class — programming bugs / connection errors
|
|
259
|
+
# propagate normally.
|
|
260
|
+
max_attempts = 3
|
|
261
|
+
backoff_seconds = 2.0
|
|
262
|
+
last_error: HomeAssistantCommandError | None = None
|
|
263
|
+
download_response: dict[str, Any] | None = None
|
|
264
|
+
for attempt in range(1, max_attempts + 1):
|
|
265
|
+
try:
|
|
266
|
+
download_response = await ws_client.send_command(
|
|
267
|
+
"hacs/repository/download",
|
|
268
|
+
repository=repo_id,
|
|
269
|
+
)
|
|
270
|
+
last_error = None
|
|
271
|
+
break
|
|
272
|
+
except HomeAssistantCommandError as e:
|
|
273
|
+
last_error = e
|
|
274
|
+
if attempt < max_attempts:
|
|
275
|
+
wait_for = backoff_seconds * (2 ** (attempt - 1))
|
|
276
|
+
logger.warning(
|
|
277
|
+
"hacs/repository/download attempt %d/%d failed (%s); "
|
|
278
|
+
"retrying in %.1fs",
|
|
279
|
+
attempt,
|
|
280
|
+
max_attempts,
|
|
281
|
+
e,
|
|
282
|
+
wait_for,
|
|
283
|
+
)
|
|
284
|
+
await asyncio.sleep(wait_for)
|
|
285
|
+
if last_error is not None:
|
|
255
286
|
raise_tool_error(
|
|
256
287
|
create_error_response(
|
|
257
288
|
ErrorCode.SERVICE_CALL_FAILED,
|
|
258
|
-
f"Failed to download repository
|
|
289
|
+
f"Failed to download repository after {max_attempts} "
|
|
290
|
+
f"attempts: {last_error}",
|
|
259
291
|
suggestions=[
|
|
260
292
|
"Check HACS logs for errors",
|
|
261
293
|
"Verify GitHub is accessible",
|
|
294
|
+
"HACS may be rate-limited; wait a minute and retry",
|
|
262
295
|
],
|
|
263
296
|
)
|
|
264
297
|
)
|
|
298
|
+
assert download_response is not None # narrowing for type checker
|
|
265
299
|
|
|
266
300
|
result: dict[str, Any] = {
|
|
267
301
|
"success": True,
|
|
@@ -108,9 +108,13 @@ class YamlConfigTools:
|
|
|
108
108
|
"For YAML-mode dashboards, "
|
|
109
109
|
"use the dotted form 'lovelace.dashboards.<url_path>' where "
|
|
110
110
|
"<url_path> is lowercase, hyphenated, and not a reserved HA "
|
|
111
|
-
"route. No other
|
|
112
|
-
"
|
|
113
|
-
"
|
|
111
|
+
"route. No other multi-segment paths are supported. "
|
|
112
|
+
"'automation', 'script', and 'scene' are accepted only when "
|
|
113
|
+
"file is under packages/*.yaml; in configuration.yaml use "
|
|
114
|
+
"the dedicated storage-mode tools "
|
|
115
|
+
"(ha_config_set_automation, ha_config_set_script, "
|
|
116
|
+
"ha_config_set_scene). Not for template sensors or "
|
|
117
|
+
"input_* helpers — those have dedicated tools."
|
|
114
118
|
),
|
|
115
119
|
),
|
|
116
120
|
],
|
|
@@ -162,9 +166,9 @@ class YamlConfigTools:
|
|
|
162
166
|
|
|
163
167
|
- Template sensors (state-based or trigger-based) ->
|
|
164
168
|
ha_config_set_helper(helper_type='template')
|
|
165
|
-
- Automations -> ha_config_set_automation
|
|
166
|
-
- Scripts -> ha_config_set_script
|
|
167
|
-
- Scenes -> ha_config_set_scene
|
|
169
|
+
- Automations (storage-mode) -> ha_config_set_automation
|
|
170
|
+
- Scripts (storage-mode) -> ha_config_set_script
|
|
171
|
+
- Scenes (storage-mode) -> ha_config_set_scene
|
|
168
172
|
- All 27 helper types (input_*, counter, timer, schedule, zone, person,
|
|
169
173
|
tag, group, min_max, threshold, derivative, statistics, utility_meter,
|
|
170
174
|
trend, filter, switch_as_x, etc.) -> ha_config_set_helper
|
|
@@ -174,10 +178,16 @@ class YamlConfigTools:
|
|
|
174
178
|
for integrations with significant YAML-only configuration (knx
|
|
175
179
|
entities in package files), and for registering YAML-mode dashboards via
|
|
176
180
|
``lovelace.dashboards.<url_path>`` (no other ``lovelace.*`` keys).
|
|
181
|
+
Also accepts ``automation``, ``script``, and ``scene`` keys when
|
|
182
|
+
``file`` is a ``packages/*.yaml`` — for git-managed YAML configs
|
|
183
|
+
that track these alongside templates and other YAML items. Writes
|
|
184
|
+
to ``configuration.yaml`` for those three keys remain rejected so
|
|
185
|
+
storage-mode and YAML-mode collections don't collide; use the
|
|
186
|
+
dedicated storage-mode tools instead.
|
|
177
187
|
Check ``post_action`` in the response: most keys need a full HA
|
|
178
|
-
restart; template, mqtt,
|
|
179
|
-
comments and HA tags (``!include``,
|
|
180
|
-
``replace`` swaps the subtree as-is.
|
|
188
|
+
restart; template, mqtt, group, automation, script, and scene
|
|
189
|
+
support reload. Preserves YAML comments and HA tags (``!include``,
|
|
190
|
+
``!secret``) on round-trip; ``replace`` swaps the subtree as-is.
|
|
181
191
|
|
|
182
192
|
For detailed routing guidance, use ha_get_skill_guide.
|
|
183
193
|
"""
|
|
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
|
|
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.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/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
|
|
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.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/transforms/lite_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp/utils/kill_signal_diagnostics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev604 → ha_mcp_dev-7.6.0.dev606}/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
|