ha-mcp-dev 7.5.0.dev588__tar.gz → 7.5.0.dev589__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.5.0.dev588/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.5.0.dev589}/PKG-INFO +5 -5
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/README.md +4 -4
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/pyproject.toml +1 -1
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_groups.py +3 -3
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_integrations.py +309 -109
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589/src/ha_mcp_dev.egg-info}/PKG-INFO +5 -5
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/LICENSE +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/setup.cfg +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/smart_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/tests/test_env_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ha-mcp-dev
|
|
3
|
-
Version: 7.5.0.
|
|
3
|
+
Version: 7.5.0.dev589
|
|
4
4
|
Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
|
|
5
5
|
Author-email: Julien <github@qc-h.net>
|
|
6
6
|
License: MIT
|
|
@@ -38,7 +38,7 @@ Dynamic: license-file
|
|
|
38
38
|
<!-- mcp-name: io.github.homeassistant-ai/ha-mcp -->
|
|
39
39
|
|
|
40
40
|
<p align="center">
|
|
41
|
-
<img src="https://img.shields.io/badge/tools-
|
|
41
|
+
<img src="https://img.shields.io/badge/tools-86-blue" alt="95+ Tools">
|
|
42
42
|
<a href="https://github.com/homeassistant-ai/ha-mcp/releases"><img src="https://img.shields.io/github/v/release/homeassistant-ai/ha-mcp" alt="Release"></a>
|
|
43
43
|
<a href="https://github.com/homeassistant-ai/ha-mcp/actions/workflows/e2e-tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/homeassistant-ai/ha-mcp/e2e-tests.yml?branch=master&label=E2E%20Tests" alt="E2E Tests"></a>
|
|
44
44
|
<a href="LICENSE.md"><img src="https://img.shields.io/github/license/homeassistant-ai/ha-mcp.svg" alt="License"></a>
|
|
@@ -181,12 +181,12 @@ Spend less time configuring, more time enjoying your smart home.
|
|
|
181
181
|
<details>
|
|
182
182
|
<!-- TOOLS_TABLE_START -->
|
|
183
183
|
|
|
184
|
-
<summary><b>Complete Tool List (
|
|
184
|
+
<summary><b>Complete Tool List (86 tools)</b></summary>
|
|
185
185
|
|
|
186
186
|
| Category | Tools |
|
|
187
187
|
|----------|-------|
|
|
188
188
|
| **Add-ons** | `ha_get_addon`, `ha_manage_addon` |
|
|
189
|
-
| **Areas & Floors** | `
|
|
189
|
+
| **Areas & Floors** | `ha_list_floors_areas`, `ha_remove_area_or_floor`, `ha_set_area_or_floor` |
|
|
190
190
|
| **Assist** | `ha_manage_pipeline` |
|
|
191
191
|
| **Automations** | `ha_config_get_automation`, `ha_config_remove_automation`, `ha_config_set_automation` |
|
|
192
192
|
| **Blueprints** | `ha_get_blueprint`, `ha_import_blueprint` |
|
|
@@ -199,7 +199,7 @@ Spend less time configuring, more time enjoying your smart home.
|
|
|
199
199
|
| **Files** | `ha_delete_file` *(beta)*, `ha_list_files` *(beta)*, `ha_read_file` *(beta)*, `ha_write_file` *(beta)* |
|
|
200
200
|
| **Groups** | `ha_config_list_groups`, `ha_config_remove_group`, `ha_config_set_group` |
|
|
201
201
|
| **HACS** | `ha_hacs_add_repository`, `ha_hacs_download`, `ha_hacs_repository_info`, `ha_hacs_search` |
|
|
202
|
-
| **Helper Entities** | `ha_config_list_helpers`, `ha_config_set_helper`, `
|
|
202
|
+
| **Helper Entities** | `ha_config_list_helpers`, `ha_config_set_helper`, `ha_remove_helpers_integrations` |
|
|
203
203
|
| **History & Statistics** | `ha_get_automation_traces`, `ha_get_history`, `ha_get_logs` |
|
|
204
204
|
| **Integrations** | `ha_get_integration`, `ha_get_system_health`, `ha_set_integration_enabled` |
|
|
205
205
|
| **Labels & Categories** | `ha_config_get_category`, `ha_config_get_label`, `ha_config_remove_category`, `ha_config_remove_label`, `ha_config_set_category`, `ha_config_set_label` |
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<!-- mcp-name: io.github.homeassistant-ai/ha-mcp -->
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
11
|
-
<img src="https://img.shields.io/badge/tools-
|
|
11
|
+
<img src="https://img.shields.io/badge/tools-86-blue" alt="95+ Tools">
|
|
12
12
|
<a href="https://github.com/homeassistant-ai/ha-mcp/releases"><img src="https://img.shields.io/github/v/release/homeassistant-ai/ha-mcp" alt="Release"></a>
|
|
13
13
|
<a href="https://github.com/homeassistant-ai/ha-mcp/actions/workflows/e2e-tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/homeassistant-ai/ha-mcp/e2e-tests.yml?branch=master&label=E2E%20Tests" alt="E2E Tests"></a>
|
|
14
14
|
<a href="LICENSE.md"><img src="https://img.shields.io/github/license/homeassistant-ai/ha-mcp.svg" alt="License"></a>
|
|
@@ -151,12 +151,12 @@ Spend less time configuring, more time enjoying your smart home.
|
|
|
151
151
|
<details>
|
|
152
152
|
<!-- TOOLS_TABLE_START -->
|
|
153
153
|
|
|
154
|
-
<summary><b>Complete Tool List (
|
|
154
|
+
<summary><b>Complete Tool List (86 tools)</b></summary>
|
|
155
155
|
|
|
156
156
|
| Category | Tools |
|
|
157
157
|
|----------|-------|
|
|
158
158
|
| **Add-ons** | `ha_get_addon`, `ha_manage_addon` |
|
|
159
|
-
| **Areas & Floors** | `
|
|
159
|
+
| **Areas & Floors** | `ha_list_floors_areas`, `ha_remove_area_or_floor`, `ha_set_area_or_floor` |
|
|
160
160
|
| **Assist** | `ha_manage_pipeline` |
|
|
161
161
|
| **Automations** | `ha_config_get_automation`, `ha_config_remove_automation`, `ha_config_set_automation` |
|
|
162
162
|
| **Blueprints** | `ha_get_blueprint`, `ha_import_blueprint` |
|
|
@@ -169,7 +169,7 @@ Spend less time configuring, more time enjoying your smart home.
|
|
|
169
169
|
| **Files** | `ha_delete_file` *(beta)*, `ha_list_files` *(beta)*, `ha_read_file` *(beta)*, `ha_write_file` *(beta)* |
|
|
170
170
|
| **Groups** | `ha_config_list_groups`, `ha_config_remove_group`, `ha_config_set_group` |
|
|
171
171
|
| **HACS** | `ha_hacs_add_repository`, `ha_hacs_download`, `ha_hacs_repository_info`, `ha_hacs_search` |
|
|
172
|
-
| **Helper Entities** | `ha_config_list_helpers`, `ha_config_set_helper`, `
|
|
172
|
+
| **Helper Entities** | `ha_config_list_helpers`, `ha_config_set_helper`, `ha_remove_helpers_integrations` |
|
|
173
173
|
| **History & Statistics** | `ha_get_automation_traces`, `ha_get_history`, `ha_get_logs` |
|
|
174
174
|
| **Integrations** | `ha_get_integration`, `ha_get_system_health`, `ha_set_integration_enabled` |
|
|
175
175
|
| **Labels & Categories** | `ha_config_get_category`, `ha_config_get_label`, `ha_config_remove_category`, `ha_config_remove_label`, `ha_config_set_category`, `ha_config_set_label` |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-mcp-dev"
|
|
7
|
-
version = "7.5.0.
|
|
7
|
+
version = "7.5.0.dev589"
|
|
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"
|
|
@@ -277,12 +277,12 @@ class GroupTools:
|
|
|
277
277
|
**When NOT to use:** for typical "combine these entities into one controllable group"
|
|
278
278
|
requests, prefer `ha_config_set_helper(helper_type="group", ...)`. Config-entry-backed
|
|
279
279
|
groups are registered in the entity registry, so `ha_set_entity` can assign them to
|
|
280
|
-
areas and they are deletable via `
|
|
280
|
+
areas and they are deletable via `ha_remove_helpers_integrations`.
|
|
281
281
|
|
|
282
282
|
**When to use:** compatibility with existing groups already configured via group.set
|
|
283
283
|
or YAML, or the rare case where entity-registry membership is explicitly unwanted.
|
|
284
284
|
Groups created here are only removable via `ha_config_remove_group` —
|
|
285
|
-
`
|
|
285
|
+
`ha_remove_helpers_integrations` will not find them.
|
|
286
286
|
|
|
287
287
|
**For NEW groups:** Provide object_id and entities (required).
|
|
288
288
|
**For EXISTING groups:** Provide object_id and any fields to update.
|
|
@@ -414,7 +414,7 @@ class GroupTools:
|
|
|
414
414
|
Remove a service-based Home Assistant entity group via the group.remove service.
|
|
415
415
|
|
|
416
416
|
**When NOT to use:** for groups created through `ha_config_set_helper(helper_type="group", ...)`,
|
|
417
|
-
use `
|
|
417
|
+
use `ha_remove_helpers_integrations`. Those config-entry-backed groups are not reachable via the
|
|
418
418
|
group.remove service.
|
|
419
419
|
|
|
420
420
|
**When to use:** removing groups created with `ha_config_set_group` or defined in YAML
|
|
@@ -56,7 +56,7 @@ FlowLookupReason = Literal[
|
|
|
56
56
|
]
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
# Tool parameter type for
|
|
59
|
+
# Tool parameter type for ha_remove_helpers_integrations.helper_type.
|
|
60
60
|
# Must match SIMPLE_HELPER_TYPES | FLOW_HELPER_TYPES plus config_subentry —
|
|
61
61
|
# the drift assertion below catches accidental divergence at import time.
|
|
62
62
|
HelperTypeLiteral = Literal[
|
|
@@ -109,7 +109,7 @@ async def _get_entry_id_for_flow_helper(
|
|
|
109
109
|
) -> tuple[str | None, FlowLookupReason]:
|
|
110
110
|
"""Resolve a flow-helper target to its config_entry_id via entity_registry.
|
|
111
111
|
|
|
112
|
-
Used by
|
|
112
|
+
Used by ha_remove_helpers_integrations when target is an entity_id
|
|
113
113
|
(contains a '.') and helper_type is a known flow-helper type.
|
|
114
114
|
|
|
115
115
|
Args:
|
|
@@ -141,7 +141,13 @@ async def _get_entry_id_for_flow_helper(
|
|
|
141
141
|
except (HomeAssistantConnectionError, HomeAssistantAuthError):
|
|
142
142
|
# Typed errors must reach the outer handler — do not swallow.
|
|
143
143
|
raise
|
|
144
|
-
except
|
|
144
|
+
except (OSError, TimeoutError) as e:
|
|
145
|
+
# Network / transport errors from the WS layer (ConnectionError,
|
|
146
|
+
# BrokenPipeError, TimeoutError, …). Programmer-bug-shape
|
|
147
|
+
# exceptions (KeyError, AttributeError, TypeError) intentionally
|
|
148
|
+
# propagate — the response is shape-checked at the dict guard
|
|
149
|
+
# below, and a raise here would otherwise mask the bug as a
|
|
150
|
+
# transient WEBSOCKET_DISCONNECTED.
|
|
145
151
|
logger.debug(f"entity_registry/get failed for {entity_id}: {e}")
|
|
146
152
|
if warnings is not None:
|
|
147
153
|
warnings.append(f"entity_registry/get failed for {entity_id}: {e}")
|
|
@@ -1102,11 +1108,12 @@ class IntegrationTools:
|
|
|
1102
1108
|
exception_to_structured_error(e, context={"entry_id": entry_id})
|
|
1103
1109
|
|
|
1104
1110
|
@tool(
|
|
1105
|
-
name="
|
|
1111
|
+
name="ha_remove_helpers_integrations",
|
|
1106
1112
|
tags={"Helper Entities", "Integrations"},
|
|
1107
1113
|
annotations={
|
|
1108
1114
|
"destructiveHint": True,
|
|
1109
|
-
"
|
|
1115
|
+
"idempotentHint": True,
|
|
1116
|
+
"title": "Remove Helper or Integration",
|
|
1110
1117
|
},
|
|
1111
1118
|
)
|
|
1112
1119
|
@with_auto_backup(
|
|
@@ -1124,13 +1131,13 @@ class IntegrationTools:
|
|
|
1124
1131
|
id_param="target",
|
|
1125
1132
|
)
|
|
1126
1133
|
@log_tool_usage
|
|
1127
|
-
async def
|
|
1134
|
+
async def ha_remove_helpers_integrations(
|
|
1128
1135
|
self,
|
|
1129
1136
|
target: Annotated[
|
|
1130
1137
|
str,
|
|
1131
1138
|
Field(
|
|
1132
1139
|
description=(
|
|
1133
|
-
"What to
|
|
1140
|
+
"What to remove. One of: "
|
|
1134
1141
|
"(a) bare helper_id for SIMPLE helpers (requires helper_type), "
|
|
1135
1142
|
"e.g. 'my_button'; "
|
|
1136
1143
|
"(b) full entity_id (requires helper_type), "
|
|
@@ -1148,7 +1155,7 @@ class IntegrationTools:
|
|
|
1148
1155
|
description=(
|
|
1149
1156
|
"Helper type. Required when target is a helper_id (bare) "
|
|
1150
1157
|
"or entity_id. Set to None when target is a config entry_id "
|
|
1151
|
-
"to
|
|
1158
|
+
"to remove any integration. Use 'config_subentry' to remove "
|
|
1152
1159
|
"a config subentry under target."
|
|
1153
1160
|
),
|
|
1154
1161
|
default=None,
|
|
@@ -1158,7 +1165,7 @@ class IntegrationTools:
|
|
|
1158
1165
|
str | None,
|
|
1159
1166
|
Field(
|
|
1160
1167
|
description=(
|
|
1161
|
-
"Config subentry ID to
|
|
1168
|
+
"Config subentry ID to remove when helper_type='config_subentry'."
|
|
1162
1169
|
),
|
|
1163
1170
|
default=None,
|
|
1164
1171
|
),
|
|
@@ -1167,7 +1174,7 @@ class IntegrationTools:
|
|
|
1167
1174
|
bool | str,
|
|
1168
1175
|
Field(
|
|
1169
1176
|
description=(
|
|
1170
|
-
"Must be True to confirm
|
|
1177
|
+
"Must be True to confirm removal. Accepts bool or "
|
|
1171
1178
|
"string ('true'/'false'/'1'/'0'/'yes'/'no'/'on'/'off', "
|
|
1172
1179
|
"case-insensitive) for transport ergonomics."
|
|
1173
1180
|
),
|
|
@@ -1179,7 +1186,8 @@ class IntegrationTools:
|
|
|
1179
1186
|
Field(
|
|
1180
1187
|
description=(
|
|
1181
1188
|
"Wait for entity removal. Default: True. "
|
|
1182
|
-
"Ignored when helper_type=None
|
|
1189
|
+
"Ignored when helper_type=None or "
|
|
1190
|
+
"helper_type='config_subentry' (no entity poll, "
|
|
1183
1191
|
"require_restart returned). Accepts bool or string "
|
|
1184
1192
|
"('true'/'false'/'1'/'0'/'yes'/'no'/'on'/'off', "
|
|
1185
1193
|
"case-insensitive)."
|
|
@@ -1188,11 +1196,11 @@ class IntegrationTools:
|
|
|
1188
1196
|
),
|
|
1189
1197
|
] = True,
|
|
1190
1198
|
) -> dict[str, Any]:
|
|
1191
|
-
"""
|
|
1199
|
+
"""Remove a Home Assistant helper or integration config entry.
|
|
1192
1200
|
|
|
1193
|
-
|
|
1194
|
-
config-
|
|
1195
|
-
driven by helper_type.
|
|
1201
|
+
Unifies three backend removal mechanisms — simple-helper websocket
|
|
1202
|
+
delete, config-entry delete, and config-subentry delete — behind one
|
|
1203
|
+
entry point with four routing paths driven by helper_type.
|
|
1196
1204
|
|
|
1197
1205
|
WHEN NOT TO USE:
|
|
1198
1206
|
- Removing only an entity (without deleting its underlying helper or
|
|
@@ -1219,33 +1227,58 @@ class IntegrationTools:
|
|
|
1219
1227
|
- helper_type="config_subentry" + parent entry_id + subentry_id →
|
|
1220
1228
|
delete one config subentry.
|
|
1221
1229
|
|
|
1230
|
+
MISSING-TARGET CONTRACT:
|
|
1231
|
+
A target that is *confirmed absent* raises a structured error
|
|
1232
|
+
rather than returning silent success, so a typo'd or stale
|
|
1233
|
+
identifier surfaces immediately at the caller layer (the
|
|
1234
|
+
``success`` boolean is what agent wrappers branch on). The
|
|
1235
|
+
error code per-path follows the target shape:
|
|
1236
|
+
- SIMPLE (bare helper_id or entity_id): state-machine empty AND
|
|
1237
|
+
entity registry empty → raises ``ENTITY_NOT_FOUND``.
|
|
1238
|
+
- FLOW (entity_id): not in entity registry → raises
|
|
1239
|
+
``ENTITY_NOT_FOUND``. YAML-configured helpers (no config entry
|
|
1240
|
+
backing) raise ``RESOURCE_NOT_FOUND``. A bare helper_id (no
|
|
1241
|
+
``.``) on a FLOW target raises ``ENTITY_NOT_FOUND`` — FLOW
|
|
1242
|
+
resolution needs a full entity_id. TOCTOU 404 on the
|
|
1243
|
+
resolved entry_id raises ``RESOURCE_NOT_FOUND``.
|
|
1244
|
+
- Direct config entry (helper_type=None): backend returns HTTP
|
|
1245
|
+
404 → raises ``RESOURCE_NOT_FOUND``.
|
|
1246
|
+
- Config subentry: backend returns a "not_found" error → raises
|
|
1247
|
+
``RESOURCE_NOT_FOUND``.
|
|
1248
|
+
|
|
1249
|
+
Idempotency at the contract level still holds (call N times =
|
|
1250
|
+
same response). Transient connectivity failures (WebSocket
|
|
1251
|
+
disconnected, network timeouts) raise their own codes
|
|
1252
|
+
(``WEBSOCKET_DISCONNECTED``, ``CONNECTION_FAILED``) so retry
|
|
1253
|
+
logic can branch separately.
|
|
1254
|
+
|
|
1222
1255
|
EXAMPLES:
|
|
1223
|
-
-
|
|
1224
|
-
|
|
1256
|
+
- Remove SIMPLE button:
|
|
1257
|
+
ha_remove_helpers_integrations(
|
|
1225
1258
|
target="my_button", helper_type="input_button", confirm=True
|
|
1226
1259
|
)
|
|
1227
|
-
-
|
|
1228
|
-
|
|
1260
|
+
- Remove FLOW utility_meter (any sub-entity works):
|
|
1261
|
+
ha_remove_helpers_integrations(
|
|
1229
1262
|
target="sensor.energy_peak",
|
|
1230
1263
|
helper_type="utility_meter",
|
|
1231
1264
|
confirm=True,
|
|
1232
1265
|
)
|
|
1233
|
-
-
|
|
1234
|
-
|
|
1266
|
+
- Remove any integration by entry_id:
|
|
1267
|
+
ha_remove_helpers_integrations(
|
|
1235
1268
|
target="01HXYZ...", confirm=True
|
|
1236
1269
|
)
|
|
1237
|
-
-
|
|
1238
|
-
|
|
1270
|
+
- Remove a config subentry:
|
|
1271
|
+
ha_remove_helpers_integrations(
|
|
1239
1272
|
target="01HXYZ...", helper_type="config_subentry",
|
|
1240
1273
|
subentry_id="subentry-123", confirm=True
|
|
1241
1274
|
)
|
|
1242
1275
|
|
|
1243
|
-
**WARNING:**
|
|
1276
|
+
**WARNING:** Removing a helper or integration that is referenced by
|
|
1244
1277
|
automations, scripts, or other integrations may cause those to fail.
|
|
1245
1278
|
Use ha_search_entities() / ha_get_integration() to verify before
|
|
1246
|
-
|
|
1279
|
+
removal. Cannot be undone.
|
|
1247
1280
|
"""
|
|
1248
|
-
# === Confirm gate (uniform for all
|
|
1281
|
+
# === Confirm gate (uniform for all four paths) ===
|
|
1249
1282
|
confirm_bool = coerce_bool_param(confirm, "confirm", default=False)
|
|
1250
1283
|
if not confirm_bool:
|
|
1251
1284
|
raise_tool_error(
|
|
@@ -1263,14 +1296,15 @@ class IntegrationTools:
|
|
|
1263
1296
|
)
|
|
1264
1297
|
)
|
|
1265
1298
|
|
|
1266
|
-
# === Empty/whitespace target gate (uniform for all
|
|
1299
|
+
# === Empty/whitespace target gate (uniform for all four paths) ===
|
|
1267
1300
|
# Empty/whitespace ``target`` would reach the destructive backend call
|
|
1268
1301
|
# on every path: Path 1 (simple-helper websocket delete), Path 2
|
|
1269
1302
|
# (flow-helper entity-resolution → entry_id delete), Path 3
|
|
1270
|
-
# (_delete_direct_entry → client.delete_config_entry(""))
|
|
1271
|
-
#
|
|
1272
|
-
#
|
|
1273
|
-
#
|
|
1303
|
+
# (_delete_direct_entry → client.delete_config_entry("")), Path 4
|
|
1304
|
+
# (_delete_config_subentry → ws delete on empty parent entry_id).
|
|
1305
|
+
# Each path surfaces a different misleading error from HA. Reject
|
|
1306
|
+
# up-front so the caller learns the identifier was unusable before
|
|
1307
|
+
# any backend call.
|
|
1274
1308
|
validate_identifier_not_empty(
|
|
1275
1309
|
target,
|
|
1276
1310
|
"target",
|
|
@@ -1323,9 +1357,15 @@ class IntegrationTools:
|
|
|
1323
1357
|
)
|
|
1324
1358
|
)
|
|
1325
1359
|
|
|
1360
|
+
# Private helpers keep the ``_delete_*`` prefix because they wrap HA's
|
|
1361
|
+
# own backend verb — the WebSocket API is ``<type>/delete`` and the
|
|
1362
|
+
# REST API is HTTP DELETE. The public tool surface uses ``remove`` to
|
|
1363
|
+
# join the ``ha_remove_*`` behavioural family; the prefix asymmetry is
|
|
1364
|
+
# intentional and prevents future renames pulled by either side.
|
|
1365
|
+
|
|
1326
1366
|
# === Path 3: Direct config entry delete (any integration) ===
|
|
1327
1367
|
async def _delete_direct_entry(self, entry_id: str) -> dict[str, Any]:
|
|
1328
|
-
"""Delete a config entry directly via the
|
|
1368
|
+
"""Delete a config entry directly via the REST delete API."""
|
|
1329
1369
|
try:
|
|
1330
1370
|
result = await self._client.delete_config_entry(entry_id)
|
|
1331
1371
|
require_restart = result.get("require_restart", False)
|
|
@@ -1346,8 +1386,43 @@ class IntegrationTools:
|
|
|
1346
1386
|
}
|
|
1347
1387
|
except ToolError:
|
|
1348
1388
|
raise
|
|
1389
|
+
except HomeAssistantAPIError as e:
|
|
1390
|
+
# HA returns 404 for missing config entries (see
|
|
1391
|
+
# RestClient.delete_config_entry — the REST DELETE on a
|
|
1392
|
+
# nonexistent entry surfaces as HomeAssistantAPIError with
|
|
1393
|
+
# status_code=404). Surface as RESOURCE_NOT_FOUND so callers
|
|
1394
|
+
# can distinguish absent target from real failures; the typo
|
|
1395
|
+
# case (agent passed the wrong entry_id) is the failure mode
|
|
1396
|
+
# that "absent → success" would silently mask. Non-404 API
|
|
1397
|
+
# errors are real failures and bubble through
|
|
1398
|
+
# exception_to_structured_error below.
|
|
1399
|
+
if e.status_code == 404:
|
|
1400
|
+
raise_tool_error(
|
|
1401
|
+
create_error_response(
|
|
1402
|
+
ErrorCode.RESOURCE_NOT_FOUND,
|
|
1403
|
+
(
|
|
1404
|
+
f"Config entry {entry_id} not found. May "
|
|
1405
|
+
"indicate it was already removed, never "
|
|
1406
|
+
"existed, or the identifier is a typo. "
|
|
1407
|
+
"Verify with ha_get_integration() before "
|
|
1408
|
+
"retrying."
|
|
1409
|
+
),
|
|
1410
|
+
context={"entry_id": entry_id},
|
|
1411
|
+
suggestions=[
|
|
1412
|
+
"Use ha_get_integration() without entry_id "
|
|
1413
|
+
"to see all config entries",
|
|
1414
|
+
],
|
|
1415
|
+
)
|
|
1416
|
+
)
|
|
1417
|
+
exception_to_structured_error(
|
|
1418
|
+
e,
|
|
1419
|
+
context={"entry_id": entry_id},
|
|
1420
|
+
suggestions=[
|
|
1421
|
+
"Use ha_get_integration() without entry_id to "
|
|
1422
|
+
"see all config entries",
|
|
1423
|
+
],
|
|
1424
|
+
)
|
|
1349
1425
|
except Exception as e:
|
|
1350
|
-
logger.error(f"Failed to delete config entry: {e}")
|
|
1351
1426
|
exception_to_structured_error(
|
|
1352
1427
|
e,
|
|
1353
1428
|
context={"entry_id": entry_id},
|
|
@@ -1421,13 +1496,46 @@ class IntegrationTools:
|
|
|
1421
1496
|
},
|
|
1422
1497
|
)
|
|
1423
1498
|
)
|
|
1424
|
-
#
|
|
1425
|
-
#
|
|
1426
|
-
#
|
|
1427
|
-
# the dispatcher already checked SIMPLE_HELPER_TYPES /
|
|
1428
|
-
# FLOW_HELPER_TYPES; the assertion below enforces that
|
|
1429
|
-
# contract at runtime.
|
|
1499
|
+
# wrong_helper_type cannot occur here because the dispatcher
|
|
1500
|
+
# already checked SIMPLE_HELPER_TYPES / FLOW_HELPER_TYPES; the
|
|
1501
|
+
# assertion enforces that contract at runtime.
|
|
1430
1502
|
assert reason != "wrong_helper_type"
|
|
1503
|
+
if reason == "not_in_registry":
|
|
1504
|
+
# Target is absent from the entity registry. Surface
|
|
1505
|
+
# as ENTITY_NOT_FOUND (entity-shaped target) so the
|
|
1506
|
+
# caller learns the identifier is unusable — the typo
|
|
1507
|
+
# case is the failure mode "absent → success" would
|
|
1508
|
+
# silently mask. Matches the bare_id_not_supported
|
|
1509
|
+
# branch below and sibling ha_remove_entity.
|
|
1510
|
+
raise_tool_error(
|
|
1511
|
+
create_error_response(
|
|
1512
|
+
ErrorCode.ENTITY_NOT_FOUND,
|
|
1513
|
+
(
|
|
1514
|
+
f"Helper {target} not found in entity "
|
|
1515
|
+
f"registry (looked up as {entity_id}). "
|
|
1516
|
+
"May indicate it was already removed, "
|
|
1517
|
+
"never existed, or the identifier is a "
|
|
1518
|
+
"typo. Verify with ha_search_entities() "
|
|
1519
|
+
"before retrying."
|
|
1520
|
+
),
|
|
1521
|
+
context={
|
|
1522
|
+
"target": target,
|
|
1523
|
+
"helper_type": helper_type,
|
|
1524
|
+
"entity_id": entity_id,
|
|
1525
|
+
},
|
|
1526
|
+
suggestions=[
|
|
1527
|
+
"Use ha_search_entities() — flow helper "
|
|
1528
|
+
"types often expose entities under a "
|
|
1529
|
+
"different domain than the helper_type "
|
|
1530
|
+
"itself (e.g. utility_meter → sensor.*, "
|
|
1531
|
+
"switch_as_x → switch.* / light.*).",
|
|
1532
|
+
],
|
|
1533
|
+
)
|
|
1534
|
+
)
|
|
1535
|
+
# bare_id_not_supported → caller passed a bare ID where an
|
|
1536
|
+
# entity_id was required. That's a call-shape error, not
|
|
1537
|
+
# missing-target; surface as ENTITY_NOT_FOUND with the
|
|
1538
|
+
# search suggestion so the caller can self-correct.
|
|
1431
1539
|
raise_tool_error(
|
|
1432
1540
|
create_error_response(
|
|
1433
1541
|
ErrorCode.ENTITY_NOT_FOUND,
|
|
@@ -1459,6 +1567,38 @@ class IntegrationTools:
|
|
|
1459
1567
|
# Step 3: delete the config entry
|
|
1460
1568
|
try:
|
|
1461
1569
|
delete_result = await client.delete_config_entry(entry_id)
|
|
1570
|
+
except HomeAssistantAPIError as e:
|
|
1571
|
+
# TOCTOU window: entry_id resolved at step 1 was deleted
|
|
1572
|
+
# before step 3 reached HA. Surface as RESOURCE_NOT_FOUND
|
|
1573
|
+
# so the caller knows the config entry is gone — silent
|
|
1574
|
+
# success would hide the race from any wrapper that
|
|
1575
|
+
# acted on the intermediate state. Non-404 still surfaces.
|
|
1576
|
+
if e.status_code == 404:
|
|
1577
|
+
raise_tool_error(
|
|
1578
|
+
create_error_response(
|
|
1579
|
+
ErrorCode.RESOURCE_NOT_FOUND,
|
|
1580
|
+
(
|
|
1581
|
+
f"Config entry {entry_id} for {target} "
|
|
1582
|
+
"not found at delete time (resolved by "
|
|
1583
|
+
"registry but absent when DELETE reached "
|
|
1584
|
+
"Home Assistant). May indicate a "
|
|
1585
|
+
"concurrent removal."
|
|
1586
|
+
),
|
|
1587
|
+
context={
|
|
1588
|
+
"entry_id": entry_id,
|
|
1589
|
+
"target": target,
|
|
1590
|
+
"helper_type": helper_type,
|
|
1591
|
+
},
|
|
1592
|
+
)
|
|
1593
|
+
)
|
|
1594
|
+
exception_to_structured_error(
|
|
1595
|
+
e,
|
|
1596
|
+
context={
|
|
1597
|
+
"entry_id": entry_id,
|
|
1598
|
+
"target": target,
|
|
1599
|
+
"helper_type": helper_type,
|
|
1600
|
+
},
|
|
1601
|
+
)
|
|
1462
1602
|
except Exception as e:
|
|
1463
1603
|
exception_to_structured_error(
|
|
1464
1604
|
e,
|
|
@@ -1540,7 +1680,42 @@ class IntegrationTools:
|
|
|
1540
1680
|
"""Delete one config subentry under a parent config entry."""
|
|
1541
1681
|
result = await self._client.delete_config_subentry(entry_id, subentry_id)
|
|
1542
1682
|
if not isinstance(result, dict) or not result.get("success"):
|
|
1543
|
-
|
|
1683
|
+
error = result.get("error", "Operation failed")
|
|
1684
|
+
error_msg = websocket_error_message(error)
|
|
1685
|
+
# Detect "subentry already absent" by HA's structured
|
|
1686
|
+
# ``code="not_found"`` only. A generic ``"not found" in
|
|
1687
|
+
# error_msg`` substring match was rejected because it can
|
|
1688
|
+
# collide with unrelated HA error messages (e.g.
|
|
1689
|
+
# "repository not found", "integration not found") and
|
|
1690
|
+
# mis-classify a real failure as a missing target.
|
|
1691
|
+
# The HA ``config_entries/subentries/delete`` handler raises
|
|
1692
|
+
# with ``code="not_found"`` when entry_id or subentry_id is
|
|
1693
|
+
# missing; if a future HA version uses a different code, we
|
|
1694
|
+
# raise SERVICE_CALL_FAILED instead — safer than mis-classifying.
|
|
1695
|
+
error_code = error.get("code") if isinstance(error, dict) else None
|
|
1696
|
+
if error_code == "not_found":
|
|
1697
|
+
# Subentry absent under the parent config entry. Surface
|
|
1698
|
+
# as RESOURCE_NOT_FOUND so the caller learns the target
|
|
1699
|
+
# didn't exist — silent success would mask a typo'd
|
|
1700
|
+
# subentry_id (or wrong parent entry_id) until the user
|
|
1701
|
+
# noticed nothing was removed.
|
|
1702
|
+
raise_tool_error(
|
|
1703
|
+
create_error_response(
|
|
1704
|
+
ErrorCode.RESOURCE_NOT_FOUND,
|
|
1705
|
+
(
|
|
1706
|
+
f"Subentry {subentry_id} not found under "
|
|
1707
|
+
f"config entry {entry_id}. May indicate it "
|
|
1708
|
+
"was already removed, never existed, or one "
|
|
1709
|
+
"of the identifiers is a typo. Verify with "
|
|
1710
|
+
"ha_get_integration(entry_id=..., "
|
|
1711
|
+
"include_subentries=True) before retrying."
|
|
1712
|
+
),
|
|
1713
|
+
context={
|
|
1714
|
+
"entry_id": entry_id,
|
|
1715
|
+
"subentry_id": subentry_id,
|
|
1716
|
+
},
|
|
1717
|
+
)
|
|
1718
|
+
)
|
|
1544
1719
|
raise_tool_error(
|
|
1545
1720
|
create_error_response(
|
|
1546
1721
|
ErrorCode.SERVICE_CALL_FAILED,
|
|
@@ -1556,7 +1731,6 @@ class IntegrationTools:
|
|
|
1556
1731
|
"subentry_id": subentry_id,
|
|
1557
1732
|
"method": "config_subentry_delete",
|
|
1558
1733
|
"message": f"Successfully deleted config subentry: {subentry_id}",
|
|
1559
|
-
"result": result.get("result"),
|
|
1560
1734
|
}
|
|
1561
1735
|
|
|
1562
1736
|
# === Path 1: SIMPLE helper delete via websocket ===
|
|
@@ -1569,8 +1743,8 @@ class IntegrationTools:
|
|
|
1569
1743
|
"""Delete a SIMPLE helper via the websocket {type}/delete API.
|
|
1570
1744
|
|
|
1571
1745
|
Uses a 3-retry registry lookup with exponential backoff to find the
|
|
1572
|
-
helper's unique_id, then falls back to direct-id-delete and
|
|
1573
|
-
|
|
1746
|
+
helper's unique_id, then falls back to direct-id-delete and a
|
|
1747
|
+
confirmed-absent classification if the registry has no record.
|
|
1574
1748
|
"""
|
|
1575
1749
|
client = self._client
|
|
1576
1750
|
# Convert to entity_id form
|
|
@@ -1682,87 +1856,113 @@ class IntegrationTools:
|
|
|
1682
1856
|
)
|
|
1683
1857
|
return response
|
|
1684
1858
|
|
|
1685
|
-
# Fallback strategy 2:
|
|
1686
|
-
# registry too — a disabled entity is
|
|
1687
|
-
#
|
|
1688
|
-
# is not enough to
|
|
1859
|
+
# Fallback strategy 2: confirmed-absent classification.
|
|
1860
|
+
# Confirm via the registry too — a disabled entity is
|
|
1861
|
+
# state-absent but still registry-resident, so
|
|
1862
|
+
# state-absence alone is not enough to classify as
|
|
1863
|
+
# confirmed-absent. The APIError-404 branch routes the
|
|
1864
|
+
# never-existed-target case (HA returns 404 on
|
|
1865
|
+
# get_entity_state for unknown entity_ids) into the same
|
|
1866
|
+
# confirmed-absent path so the resulting ENTITY_NOT_FOUND
|
|
1867
|
+
# raise carries the structured "typo or removed" hint
|
|
1868
|
+
# message rather than a raw 404.
|
|
1869
|
+
state_gone = False
|
|
1689
1870
|
try:
|
|
1690
1871
|
final_state_check = await client.get_entity_state(entity_id)
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
if not registry_still_has_entry:
|
|
1714
|
-
logger.info(
|
|
1715
|
-
f"Entity {entity_id} absent from state and "
|
|
1716
|
-
"registry; treating as already deleted"
|
|
1717
|
-
)
|
|
1718
|
-
return {
|
|
1719
|
-
"success": True,
|
|
1720
|
-
"action": "delete",
|
|
1721
|
-
"target": target,
|
|
1722
|
-
"helper_type": helper_type,
|
|
1723
|
-
"method": "websocket_delete",
|
|
1724
|
-
"entry_id": None,
|
|
1725
|
-
"entity_ids": [entity_id],
|
|
1726
|
-
"require_restart": False,
|
|
1727
|
-
"message": (
|
|
1728
|
-
f"Helper {target} was already deleted or "
|
|
1729
|
-
"never properly registered."
|
|
1730
|
-
),
|
|
1731
|
-
"fallback_used": "already_deleted",
|
|
1872
|
+
state_gone = not final_state_check
|
|
1873
|
+
except HomeAssistantAPIError as e:
|
|
1874
|
+
# Only 404 confirms the entity is absent from the state
|
|
1875
|
+
# machine. Other API failures (500, 401, …) are transient
|
|
1876
|
+
# or auth issues and must propagate so they aren't
|
|
1877
|
+
# mis-classified as a missing target. Mirrors the
|
|
1878
|
+
# status_code == 404 narrow in _delete_direct_entry.
|
|
1879
|
+
if e.status_code != 404:
|
|
1880
|
+
raise
|
|
1881
|
+
logger.debug(
|
|
1882
|
+
f"State check for {entity_id} raised 404 "
|
|
1883
|
+
f"(treating as state-absent): {e}"
|
|
1884
|
+
)
|
|
1885
|
+
state_gone = True
|
|
1886
|
+
|
|
1887
|
+
if state_gone:
|
|
1888
|
+
registry_still_has_entry = False
|
|
1889
|
+
try:
|
|
1890
|
+
verify_result = await client.send_websocket_message(
|
|
1891
|
+
{
|
|
1892
|
+
"type": "config/entity_registry/get",
|
|
1893
|
+
"entity_id": entity_id,
|
|
1732
1894
|
}
|
|
1895
|
+
)
|
|
1896
|
+
if (verify_result or {}).get("success"):
|
|
1897
|
+
verify_entry = (verify_result or {}).get("result") or {}
|
|
1898
|
+
if verify_entry.get("entity_id"):
|
|
1899
|
+
registry_still_has_entry = True
|
|
1900
|
+
except HomeAssistantAPIError as verify_err:
|
|
1901
|
+
# On verify failure, conservatively assume the
|
|
1902
|
+
# entry is still there rather than misclassify
|
|
1903
|
+
# a verify failure as confirmed-absent.
|
|
1904
|
+
logger.debug(
|
|
1905
|
+
f"Registry verify for {entity_id} failed: {verify_err}"
|
|
1906
|
+
)
|
|
1907
|
+
registry_still_has_entry = True
|
|
1733
1908
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
"
|
|
1909
|
+
if not registry_still_has_entry:
|
|
1910
|
+
logger.info(
|
|
1911
|
+
f"Entity {entity_id} absent from state and "
|
|
1912
|
+
"registry; surfacing as ENTITY_NOT_FOUND"
|
|
1737
1913
|
)
|
|
1914
|
+
# Entity-shape target confirmed absent from both
|
|
1915
|
+
# the state machine and the entity registry.
|
|
1916
|
+
# Surface as ENTITY_NOT_FOUND — silent success
|
|
1917
|
+
# would mask the typo case (agent passed the
|
|
1918
|
+
# wrong helper_id / entity_id). Matches sibling
|
|
1919
|
+
# ha_remove_entity.
|
|
1738
1920
|
raise_tool_error(
|
|
1739
1921
|
create_error_response(
|
|
1740
|
-
ErrorCode.
|
|
1922
|
+
ErrorCode.ENTITY_NOT_FOUND,
|
|
1741
1923
|
(
|
|
1742
|
-
f"Helper {target}
|
|
1743
|
-
"
|
|
1744
|
-
"
|
|
1745
|
-
"
|
|
1924
|
+
f"Helper {target} not found (looked "
|
|
1925
|
+
f"up as {entity_id}). May indicate "
|
|
1926
|
+
"it was already removed, never "
|
|
1927
|
+
"existed, or the identifier is a "
|
|
1928
|
+
"typo. Verify with "
|
|
1929
|
+
"ha_search_entities() before "
|
|
1930
|
+
"retrying."
|
|
1746
1931
|
),
|
|
1747
|
-
suggestions=[
|
|
1748
|
-
"Re-enable the entity via "
|
|
1749
|
-
"ha_set_entity(enabled=True), then retry "
|
|
1750
|
-
"deletion.",
|
|
1751
|
-
"Or inspect the entity registry entry "
|
|
1752
|
-
"directly to confirm unique_id presence.",
|
|
1753
|
-
],
|
|
1754
1932
|
context={
|
|
1755
1933
|
"target": target,
|
|
1934
|
+
"helper_type": helper_type,
|
|
1756
1935
|
"entity_id": entity_id,
|
|
1757
1936
|
},
|
|
1758
1937
|
)
|
|
1759
1938
|
)
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1939
|
+
|
|
1940
|
+
logger.warning(
|
|
1941
|
+
f"Entity {entity_id} absent from state but still "
|
|
1942
|
+
"in registry; treating as SERVICE_CALL_FAILED"
|
|
1943
|
+
)
|
|
1944
|
+
raise_tool_error(
|
|
1945
|
+
create_error_response(
|
|
1946
|
+
ErrorCode.SERVICE_CALL_FAILED,
|
|
1947
|
+
(
|
|
1948
|
+
f"Helper {target} could not be deleted: "
|
|
1949
|
+
"registry entry exists but unique_id was "
|
|
1950
|
+
"absent and the direct-id fallback "
|
|
1951
|
+
"delete failed."
|
|
1952
|
+
),
|
|
1953
|
+
suggestions=[
|
|
1954
|
+
"Re-enable the entity via "
|
|
1955
|
+
"ha_set_entity(enabled=True), then retry "
|
|
1956
|
+
"deletion.",
|
|
1957
|
+
"Or inspect the entity registry entry "
|
|
1958
|
+
"directly to confirm unique_id presence.",
|
|
1959
|
+
],
|
|
1960
|
+
context={
|
|
1961
|
+
"target": target,
|
|
1962
|
+
"entity_id": entity_id,
|
|
1963
|
+
},
|
|
1964
|
+
)
|
|
1965
|
+
)
|
|
1766
1966
|
|
|
1767
1967
|
# All fallbacks exhausted
|
|
1768
1968
|
err_detail = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ha-mcp-dev
|
|
3
|
-
Version: 7.5.0.
|
|
3
|
+
Version: 7.5.0.dev589
|
|
4
4
|
Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
|
|
5
5
|
Author-email: Julien <github@qc-h.net>
|
|
6
6
|
License: MIT
|
|
@@ -38,7 +38,7 @@ Dynamic: license-file
|
|
|
38
38
|
<!-- mcp-name: io.github.homeassistant-ai/ha-mcp -->
|
|
39
39
|
|
|
40
40
|
<p align="center">
|
|
41
|
-
<img src="https://img.shields.io/badge/tools-
|
|
41
|
+
<img src="https://img.shields.io/badge/tools-86-blue" alt="95+ Tools">
|
|
42
42
|
<a href="https://github.com/homeassistant-ai/ha-mcp/releases"><img src="https://img.shields.io/github/v/release/homeassistant-ai/ha-mcp" alt="Release"></a>
|
|
43
43
|
<a href="https://github.com/homeassistant-ai/ha-mcp/actions/workflows/e2e-tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/homeassistant-ai/ha-mcp/e2e-tests.yml?branch=master&label=E2E%20Tests" alt="E2E Tests"></a>
|
|
44
44
|
<a href="LICENSE.md"><img src="https://img.shields.io/github/license/homeassistant-ai/ha-mcp.svg" alt="License"></a>
|
|
@@ -181,12 +181,12 @@ Spend less time configuring, more time enjoying your smart home.
|
|
|
181
181
|
<details>
|
|
182
182
|
<!-- TOOLS_TABLE_START -->
|
|
183
183
|
|
|
184
|
-
<summary><b>Complete Tool List (
|
|
184
|
+
<summary><b>Complete Tool List (86 tools)</b></summary>
|
|
185
185
|
|
|
186
186
|
| Category | Tools |
|
|
187
187
|
|----------|-------|
|
|
188
188
|
| **Add-ons** | `ha_get_addon`, `ha_manage_addon` |
|
|
189
|
-
| **Areas & Floors** | `
|
|
189
|
+
| **Areas & Floors** | `ha_list_floors_areas`, `ha_remove_area_or_floor`, `ha_set_area_or_floor` |
|
|
190
190
|
| **Assist** | `ha_manage_pipeline` |
|
|
191
191
|
| **Automations** | `ha_config_get_automation`, `ha_config_remove_automation`, `ha_config_set_automation` |
|
|
192
192
|
| **Blueprints** | `ha_get_blueprint`, `ha_import_blueprint` |
|
|
@@ -199,7 +199,7 @@ Spend less time configuring, more time enjoying your smart home.
|
|
|
199
199
|
| **Files** | `ha_delete_file` *(beta)*, `ha_list_files` *(beta)*, `ha_read_file` *(beta)*, `ha_write_file` *(beta)* |
|
|
200
200
|
| **Groups** | `ha_config_list_groups`, `ha_config_remove_group`, `ha_config_set_group` |
|
|
201
201
|
| **HACS** | `ha_hacs_add_repository`, `ha_hacs_download`, `ha_hacs_repository_info`, `ha_hacs_search` |
|
|
202
|
-
| **Helper Entities** | `ha_config_list_helpers`, `ha_config_set_helper`, `
|
|
202
|
+
| **Helper Entities** | `ha_config_list_helpers`, `ha_config_set_helper`, `ha_remove_helpers_integrations` |
|
|
203
203
|
| **History & Statistics** | `ha_get_automation_traces`, `ha_get_history`, `ha_get_logs` |
|
|
204
204
|
| **Integrations** | `ha_get_integration`, `ha_get_system_health`, `ha_set_integration_enabled` |
|
|
205
205
|
| **Labels & Categories** | `ha_config_get_category`, `ha_config_get_label`, `ha_config_remove_category`, `ha_config_remove_label`, `ha_config_set_category`, `ha_config_set_label` |
|
|
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.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/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.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/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.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/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.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/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.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/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.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev588 → ha_mcp_dev-7.5.0.dev589}/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
|