ha-mcp-dev 7.6.0.dev662__tar.gz → 7.6.0.dev664__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.dev662/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev664}/PKG-INFO +1 -1
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/pyproject.toml +1 -1
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/__main__.py +86 -1
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_utility.py +36 -17
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/LICENSE +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/README.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/setup.cfg +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/settings.css +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/settings.js +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/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.dev664"
|
|
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"
|
|
@@ -22,6 +22,7 @@ truststore.inject_into_ssl()
|
|
|
22
22
|
import asyncio # noqa: E402
|
|
23
23
|
import copy # noqa: E402
|
|
24
24
|
import hashlib # noqa: E402
|
|
25
|
+
import ipaddress # noqa: E402
|
|
25
26
|
import logging # noqa: E402
|
|
26
27
|
import os # noqa: E402
|
|
27
28
|
import signal # noqa: E402
|
|
@@ -755,10 +756,93 @@ def _get_http_runtime(default_port: int = 8086) -> tuple[str, int, str]:
|
|
|
755
756
|
except ValueError:
|
|
756
757
|
logger.error(f"Invalid MCP_PORT value: {port_str!r}. Must be an integer.")
|
|
757
758
|
sys.exit(1)
|
|
758
|
-
path = os.getenv("MCP_SECRET_PATH",
|
|
759
|
+
path = os.getenv("MCP_SECRET_PATH", DEFAULT_MCP_PATH)
|
|
759
760
|
return host, port, path
|
|
760
761
|
|
|
761
762
|
|
|
763
|
+
# Default ``MCP_SECRET_PATH`` value, shared by ``_get_http_runtime`` (the
|
|
764
|
+
# read-from-env fallback) and ``_warn_if_default_path_exposed`` (the
|
|
765
|
+
# hardening-nudge predicate). Single source of truth so the two sites
|
|
766
|
+
# can't drift.
|
|
767
|
+
DEFAULT_MCP_PATH = "/mcp"
|
|
768
|
+
|
|
769
|
+
# Hostname literals (not IP addresses) treated as loopback by
|
|
770
|
+
# ``_is_loopback_host``. IP literals — the whole ``127.0.0.0/8`` block,
|
|
771
|
+
# ``::1``, bracketed forms, zone-suffixed forms, and IPv4-mapped IPv6 — are
|
|
772
|
+
# handled by the ``ipaddress`` parse before this set is consulted.
|
|
773
|
+
_LOOPBACK_HOSTNAMES = frozenset({"localhost", "ip6-localhost", "ip6-loopback"})
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
def _is_loopback_host(host: str) -> bool:
|
|
777
|
+
"""Return True when ``host`` names the local machine only.
|
|
778
|
+
|
|
779
|
+
Accepts IPv6 hosts in bracketed (``[::1]``) or zone-suffixed
|
|
780
|
+
(``::1%eth0``) form, the full ``127.0.0.0/8`` range, IPv4-mapped IPv6
|
|
781
|
+
loopback (``::ffff:127.0.0.1``, which ``is_loopback`` resolves on its
|
|
782
|
+
own), and the names in ``_LOOPBACK_HOSTNAMES``. A value that is neither
|
|
783
|
+
an IP literal nor a known loopback name (a real hostname, or a malformed
|
|
784
|
+
string) is treated as non-loopback.
|
|
785
|
+
"""
|
|
786
|
+
try:
|
|
787
|
+
candidate = host.strip("[]").split("%", 1)[0]
|
|
788
|
+
return ipaddress.ip_address(candidate).is_loopback
|
|
789
|
+
except ValueError:
|
|
790
|
+
# Not an IP literal — fall back to the known loopback hostnames.
|
|
791
|
+
return host.lower() in _LOOPBACK_HOSTNAMES
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
def _is_running_in_container() -> bool:
|
|
795
|
+
"""Best-effort detection of containerized execution.
|
|
796
|
+
|
|
797
|
+
Inside a container the server binds ``0.0.0.0`` regardless of how the
|
|
798
|
+
operator restricted host-side exposure (``docker run -p 127.0.0.1:...``),
|
|
799
|
+
so the bind host alone can't tell a loopback-only deployment from a
|
|
800
|
+
LAN-reachable one — the default-path warning would be a false positive
|
|
801
|
+
for every container. Container deployments are hardened through the
|
|
802
|
+
published guidance instead (AGENTS.md -> Docker; the add-on
|
|
803
|
+
auto-generates a secret path).
|
|
804
|
+
"""
|
|
805
|
+
# Docker writes /.dockerenv; Podman writes /run/.containerenv.
|
|
806
|
+
if os.path.exists("/.dockerenv") or os.path.exists("/run/.containerenv"):
|
|
807
|
+
return True
|
|
808
|
+
# The Home Assistant add-on runs under the Supervisor.
|
|
809
|
+
return bool(os.getenv("SUPERVISOR_TOKEN"))
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def _warn_if_default_path_exposed(host: str, port: int, path: str) -> None:
|
|
813
|
+
"""Warn on a direct run that leaves the default path on a LAN bind.
|
|
814
|
+
|
|
815
|
+
Standard-mode HTTP/SSE authenticates by URL-path secrecy (see
|
|
816
|
+
SECURITY.md → Threat Model). The default ``/mcp`` is not the
|
|
817
|
+
high-entropy secret that model assumes once the bind leaves loopback.
|
|
818
|
+
|
|
819
|
+
Fires only for a direct ``ha-mcp-web`` / ``ha-mcp-sse`` start (uvx, pip,
|
|
820
|
+
source) that uses the default path on a non-loopback host. Operators
|
|
821
|
+
silence it the same way they harden — bind ``MCP_HOST=127.0.0.1`` or set
|
|
822
|
+
a high-entropy ``MCP_SECRET_PATH``. Containers are skipped: an
|
|
823
|
+
in-container ``0.0.0.0`` bind says nothing about real exposure, which is
|
|
824
|
+
set by the ``docker -p`` mapping the process can't observe (see
|
|
825
|
+
``_is_running_in_container``).
|
|
826
|
+
"""
|
|
827
|
+
if path != DEFAULT_MCP_PATH:
|
|
828
|
+
return
|
|
829
|
+
if _is_running_in_container():
|
|
830
|
+
return
|
|
831
|
+
if _is_loopback_host(host):
|
|
832
|
+
return
|
|
833
|
+
logger.warning(
|
|
834
|
+
"ha-mcp listening on %s:%s%s with default MCP_SECRET_PATH. "
|
|
835
|
+
"Standard-mode HTTP/SSE authenticates by URL-path secrecy and assumes "
|
|
836
|
+
"a high-entropy MCP_SECRET_PATH for non-loopback binds "
|
|
837
|
+
"(see SECURITY.md → Threat Model). "
|
|
838
|
+
"Either bind loopback (MCP_HOST=127.0.0.1) or set MCP_SECRET_PATH "
|
|
839
|
+
"to a high-entropy value (e.g. /private_<token_urlsafe(16)>).",
|
|
840
|
+
host,
|
|
841
|
+
port,
|
|
842
|
+
path,
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
|
|
762
846
|
async def _run_http_with_graceful_shutdown(
|
|
763
847
|
transport: str,
|
|
764
848
|
host: str,
|
|
@@ -861,6 +945,7 @@ def _run_http_server(transport: str, default_port: int = 8086) -> None:
|
|
|
861
945
|
from ha_mcp.settings_ui import register_settings_routes
|
|
862
946
|
|
|
863
947
|
host, port, path = _get_http_runtime(default_port)
|
|
948
|
+
_warn_if_default_path_exposed(host, port, path)
|
|
864
949
|
register_browser_landing(_get_mcp(), path)
|
|
865
950
|
register_settings_routes(_get_mcp(), _get_server(), secret_path=path)
|
|
866
951
|
_log_settings_url(host, port, path)
|
|
@@ -1099,6 +1099,39 @@ def register_utility_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
1099
1099
|
Home Assistant automations, scripts, and configurations. It provides real-time evaluation with
|
|
1100
1100
|
access to all Home Assistant states, functions, and template variables.
|
|
1101
1101
|
|
|
1102
|
+
**When NOT to use this for automation/script logic:**
|
|
1103
|
+
Templates have legitimate uses (notification bodies, dynamic `data.*` values,
|
|
1104
|
+
debugging existing templates), but `condition:` / `trigger:` positions and
|
|
1105
|
+
action service names are better expressed as native HA constructs:
|
|
1106
|
+
native constructs are schema-validated at config load and surface
|
|
1107
|
+
structural errors loudly, whereas equivalent template logic only errors
|
|
1108
|
+
at runtime — and a template that renders a non-truthy value is silently
|
|
1109
|
+
treated as false.
|
|
1110
|
+
Prefer:
|
|
1111
|
+
- `condition: numeric_state` over `{{ states('x') | float > N }}`
|
|
1112
|
+
- `condition: state` over `{{ is_state(...) }}`
|
|
1113
|
+
- `condition: time` / `condition: sun` over `now().hour` / `is_state('sun.sun', ...)`
|
|
1114
|
+
- Native `for:` field on state/numeric_state triggers and state conditions over
|
|
1115
|
+
`{{ now() - X.last_changed > timedelta(...) }}` duration math
|
|
1116
|
+
- `choose` action over templated `service:` / `action:` strings
|
|
1117
|
+
See `ha_get_skill_guide` (best-practices skill) for the full anti-pattern list.
|
|
1118
|
+
|
|
1119
|
+
**When to use (reach for this tool, don't compute it yourself):**
|
|
1120
|
+
Any one-shot question whose answer is DERIVED from current HA state — an
|
|
1121
|
+
average/sum/min/max across sensors, a count of entities matching a
|
|
1122
|
+
condition, a boolean comparison, or a rendered message with live values.
|
|
1123
|
+
One render call beats fetching N states and doing the math yourself, and
|
|
1124
|
+
it is the canonical way to *test* a template before embedding it. This is
|
|
1125
|
+
for one-shot answers and template testing only — NOT for putting templates
|
|
1126
|
+
into automation logic; for `condition:` / `trigger:` positions native
|
|
1127
|
+
constructs win.
|
|
1128
|
+
- "average temperature across the bedroom sensors"
|
|
1129
|
+
-> `{{ ([states('sensor.a'), states('sensor.b')] | map('float', 0) | sum) / 2 }}`
|
|
1130
|
+
- "how many lights are on"
|
|
1131
|
+
-> `{{ states.light | selectattr('state', 'eq', 'on') | list | count }}`
|
|
1132
|
+
NOT for a plain single-entity value ("what's the state of X") — that is
|
|
1133
|
+
`ha_get_state` / `ha_search`; rendering `{{ states('X') }}` there is over-use.
|
|
1134
|
+
|
|
1102
1135
|
**Parameters:**
|
|
1103
1136
|
- template: The Jinja2 template string to evaluate
|
|
1104
1137
|
- timeout: Maximum evaluation time in seconds (default: 3)
|
|
@@ -1117,8 +1150,8 @@ def register_utility_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
1117
1150
|
**Numeric Operations:**
|
|
1118
1151
|
```jinja2
|
|
1119
1152
|
{{ states('sensor.temperature') | float(0) }} # Convert to float with default
|
|
1120
|
-
{{ states('sensor.humidity') | int }}
|
|
1121
|
-
{{ (states('sensor.temp') | float + 5) | round(1) }} # Math operations
|
|
1153
|
+
{{ states('sensor.humidity') | int(0) }} # Convert to integer with default
|
|
1154
|
+
{{ (states('sensor.temp') | float(0) + 5) | round(1) }} # Math operations
|
|
1122
1155
|
```
|
|
1123
1156
|
|
|
1124
1157
|
**Time and Date:**
|
|
@@ -1163,20 +1196,6 @@ def register_utility_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
1163
1196
|
{{ device_id('light.bedroom') }} # Get device ID for entity
|
|
1164
1197
|
```
|
|
1165
1198
|
|
|
1166
|
-
**When NOT to use this for automation/script logic:**
|
|
1167
|
-
Templates have legitimate uses (notification bodies, dynamic `data.*` values,
|
|
1168
|
-
debugging existing templates), but `condition:` / `trigger:` positions and
|
|
1169
|
-
action service names are better expressed as native HA constructs — they
|
|
1170
|
-
validate at config load, fail loudly, and avoid silent runtime failures.
|
|
1171
|
-
Prefer:
|
|
1172
|
-
- `condition: numeric_state` over `{{ states('x') | float > N }}`
|
|
1173
|
-
- `condition: state` over `{{ is_state(...) }}`
|
|
1174
|
-
- `condition: time` / `condition: sun` over `now().hour` / `is_state('sun.sun', ...)`
|
|
1175
|
-
- Native `for:` field on state/numeric_state triggers and state conditions over
|
|
1176
|
-
`{{ now() - X.last_changed > timedelta(...) }}` duration math
|
|
1177
|
-
- `choose` action over templated `service:` / `action:` strings
|
|
1178
|
-
See `ha_get_skill_guide` (best-practices skill) for the full anti-pattern list.
|
|
1179
|
-
|
|
1180
1199
|
**Common Use Cases (legitimate template positions):**
|
|
1181
1200
|
|
|
1182
1201
|
**Dynamic Service Data:**
|
|
@@ -1202,7 +1221,7 @@ def register_utility_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
1202
1221
|
|
|
1203
1222
|
**Test mathematical operations:**
|
|
1204
1223
|
```python
|
|
1205
|
-
ha_eval_template("{{ (states('sensor.temperature') | float + 5) | round(1) }}")
|
|
1224
|
+
ha_eval_template("{{ (states('sensor.temperature') | float(0) + 5) | round(1) }}")
|
|
1206
1225
|
```
|
|
1207
1226
|
|
|
1208
1227
|
**Test entity counting:**
|
|
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.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/dashboard_screenshot/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/dashboard_screenshot/capture.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/dashboard_screenshot/provision.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
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/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
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/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
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_config.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_entities.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_overview.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_scenes.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/smart_search/_scoring.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
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/tools_dashboard_screenshot.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
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/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.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/tools/validation_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/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.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp/utils/kill_signal_diagnostics.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.dev662 → ha_mcp_dev-7.6.0.dev664}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev662 → ha_mcp_dev-7.6.0.dev664}/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
|