ha-mcp-dev 7.14.2.dev2103__tar.gz → 7.14.2.dev2107__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.14.2.dev2103/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.14.2.dev2107}/PKG-INFO +1 -1
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/pyproject.toml +1 -1
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/backup_manager.py +26 -7
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/config.py +56 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/locales/de.json +2 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/locales/en.json +2 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/locales/ru.json +2 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/settings.js +22 -11
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_filesystem.py +142 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_yaml_config.py +11 -1
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/LICENSE +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/README.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/setup.cfg +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/llm_exposure.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_i18n.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_theme.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/locales/README.md +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/strict_bps.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/component_api.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/component_config_reads.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/component_devices.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/component_registries.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_dev.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/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.14.2.
|
|
7
|
+
version = "7.14.2.dev2107"
|
|
8
8
|
description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
# Upper bound widened to <3.15 so the package installs on Home Assistant's
|
|
@@ -2128,8 +2128,19 @@ async def _restore_yaml(client: Any, entity_id: str, config: Any) -> Any:
|
|
|
2128
2128
|
``edit_yaml_config`` is the only write path that reaches HA config
|
|
2129
2129
|
files (``write_file`` rejects them), so YAML restore goes through it
|
|
2130
2130
|
with ``action="replace"``.
|
|
2131
|
+
|
|
2132
|
+
The operator's extra write keys (#1887) must ride along: the write that
|
|
2133
|
+
produced this snapshot was allowed only because of them, so omitting
|
|
2134
|
+
them here would make an auto-captured backup unrestorable. Note the
|
|
2135
|
+
asymmetry with ``disabled_packages_keys``, whose default is permissive
|
|
2136
|
+
and can therefore be left off.
|
|
2131
2137
|
"""
|
|
2132
|
-
from .
|
|
2138
|
+
from .config import get_global_settings
|
|
2139
|
+
from .tools.tools_filesystem import (
|
|
2140
|
+
assert_extra_yaml_keys_supported,
|
|
2141
|
+
call_mcp_tools_service,
|
|
2142
|
+
effective_extra_yaml_write_keys,
|
|
2143
|
+
)
|
|
2133
2144
|
from .tools.util_helpers import unwrap_service_response
|
|
2134
2145
|
|
|
2135
2146
|
# Split on the LAST "::" (see _fetch_yaml) so an exotic file path
|
|
@@ -2137,15 +2148,23 @@ async def _restore_yaml(client: Any, entity_id: str, config: Any) -> Any:
|
|
|
2137
2148
|
file, sep, yaml_path = entity_id.rpartition("::")
|
|
2138
2149
|
if not sep or not file or not yaml_path:
|
|
2139
2150
|
raise ValueError(f"Invalid yaml snapshot target: {entity_id!r}")
|
|
2151
|
+
service_data: dict[str, Any] = {
|
|
2152
|
+
"file": file,
|
|
2153
|
+
"action": "replace",
|
|
2154
|
+
"yaml_path": yaml_path,
|
|
2155
|
+
"content": str(config),
|
|
2156
|
+
}
|
|
2157
|
+
extra_keys = await effective_extra_yaml_write_keys(client, get_global_settings())
|
|
2158
|
+
if extra_keys:
|
|
2159
|
+
# Same version gate as the write path: without it a component that
|
|
2160
|
+
# predates the field rejects the whole restore call over an option
|
|
2161
|
+
# the snapshot being restored may not even use.
|
|
2162
|
+
await assert_extra_yaml_keys_supported(client, extra_keys)
|
|
2163
|
+
service_data["extra_allowed_keys"] = extra_keys
|
|
2140
2164
|
result = await call_mcp_tools_service(
|
|
2141
2165
|
client,
|
|
2142
2166
|
"edit_yaml_config",
|
|
2143
|
-
|
|
2144
|
-
"file": file,
|
|
2145
|
-
"action": "replace",
|
|
2146
|
-
"yaml_path": yaml_path,
|
|
2147
|
-
"content": str(config),
|
|
2148
|
-
},
|
|
2167
|
+
service_data,
|
|
2149
2168
|
)
|
|
2150
2169
|
if isinstance(result, dict):
|
|
2151
2170
|
result = unwrap_service_response(result)
|
|
@@ -203,6 +203,25 @@ class Settings(BaseSettings):
|
|
|
203
203
|
)
|
|
204
204
|
enable_yaml_packages_scene: bool = Field(False, alias="ENABLE_YAML_PACKAGES_SCENE")
|
|
205
205
|
|
|
206
|
+
# Operator-configured extra top-level keys ha_config_set_yaml may write,
|
|
207
|
+
# comma-separated, on top of the custom component's built-in allowlist
|
|
208
|
+
# (#1887). For YAML-first integrations that are valid on one install but
|
|
209
|
+
# not worth hardcoding globally. Additive only, and never a way past the
|
|
210
|
+
# component's YAML_KEY_DENYLIST: that floor is enforced component-side
|
|
211
|
+
# (the authoritative layer) and is deliberately not mirrored here, so
|
|
212
|
+
# there is one copy to keep correct. A denied key typed into this setting
|
|
213
|
+
# is simply ignored, with the component's explanation on first use.
|
|
214
|
+
# Nor does it lift the packages-only restriction on automation/script/
|
|
215
|
+
# scene: those keep reaching packages/*.yaml through their own per-key
|
|
216
|
+
# toggles and stay rejected in configuration.yaml.
|
|
217
|
+
# Empty (the default) keeps today's behaviour exactly.
|
|
218
|
+
# Registered in ADVANCED_SETTINGS_FIELDS (section ``beta_yamlkeys``), not
|
|
219
|
+
# FEATURE_FLAG_FIELDS, because it is a value rather than a toggle – the
|
|
220
|
+
# same placement the code-mode sub-settings use. Meaningful only when
|
|
221
|
+
# ``enable_yaml_config_editing`` is on; the UI nests it under that parent
|
|
222
|
+
# like the per-key toggles above.
|
|
223
|
+
extra_yaml_write_keys: str = Field("", alias="HA_MCP_EXTRA_YAML_KEYS")
|
|
224
|
+
|
|
206
225
|
# Seed values for tool visibility (comma-separated tool names).
|
|
207
226
|
# Used as initial config when no tool_config.json exists.
|
|
208
227
|
# The web settings UI (/settings) is the primary interface for managing these.
|
|
@@ -621,6 +640,7 @@ AdvancedSection = Literal[
|
|
|
621
640
|
"tools_surface",
|
|
622
641
|
"sidecar",
|
|
623
642
|
"beta_codemode",
|
|
643
|
+
"beta_yamlkeys",
|
|
624
644
|
"developer",
|
|
625
645
|
]
|
|
626
646
|
|
|
@@ -896,6 +916,18 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
|
|
|
896
916
|
"beta_codemode",
|
|
897
917
|
True,
|
|
898
918
|
),
|
|
919
|
+
# Extra YAML write keys (issue #1887). Same shape as the code-mode
|
|
920
|
+
# sub-settings above: a non-bool value that belongs visually under a
|
|
921
|
+
# feature toggle rather than in the advanced panel, so it lives here
|
|
922
|
+
# with its own section and the features renderer nests it beneath
|
|
923
|
+
# "Enable YAML config editing".
|
|
924
|
+
AdvancedField(
|
|
925
|
+
"extra_yaml_write_keys",
|
|
926
|
+
"HA_MCP_EXTRA_YAML_KEYS",
|
|
927
|
+
str,
|
|
928
|
+
"beta_yamlkeys",
|
|
929
|
+
True,
|
|
930
|
+
),
|
|
899
931
|
# Developer mode (issue #1775). Lives in ADVANCED_SETTINGS_FIELDS —
|
|
900
932
|
# not FEATURE_FLAG_FIELDS — so it renders in its own "Developer"
|
|
901
933
|
# section at the very bottom of the Server Settings tab instead of
|
|
@@ -1185,6 +1217,30 @@ def _apply_one_feature_flag_override(
|
|
|
1185
1217
|
)
|
|
1186
1218
|
|
|
1187
1219
|
|
|
1220
|
+
def parse_extra_yaml_write_keys(settings: "Settings") -> list[str]:
|
|
1221
|
+
"""Parse ``extra_yaml_write_keys`` into a clean key list (#1887).
|
|
1222
|
+
|
|
1223
|
+
Whitespace and empty entries are dropped and the result is deduplicated
|
|
1224
|
+
and sorted, so the service payload is deterministic and unaffected by how
|
|
1225
|
+
the operator spaced the setting.
|
|
1226
|
+
|
|
1227
|
+
The component's ``YAML_KEY_DENYLIST`` is NOT applied here: that floor
|
|
1228
|
+
lives component-side, in the layer that authorizes the write. Mirroring it
|
|
1229
|
+
would mean a second copy to keep in lockstep for no gain: a denied key sent
|
|
1230
|
+
on the wire is dropped there anyway.
|
|
1231
|
+
|
|
1232
|
+
Lives in this module rather than next to the YAML tool because the backup
|
|
1233
|
+
restore path needs it too, and ``backup_manager`` importing a ``tools_*``
|
|
1234
|
+
module would add an import edge that binds ``tools_yaml_config``'s
|
|
1235
|
+
module-level names at restore time.
|
|
1236
|
+
"""
|
|
1237
|
+
# Direct attribute access, matching ``_disabled_packages_keys``: a future
|
|
1238
|
+
# rename must raise loudly rather than silently return an empty list,
|
|
1239
|
+
# which would read as "the operator configured nothing".
|
|
1240
|
+
raw = settings.extra_yaml_write_keys or ""
|
|
1241
|
+
return sorted({segment.strip() for segment in raw.split(",") if segment.strip()})
|
|
1242
|
+
|
|
1243
|
+
|
|
1188
1244
|
def _apply_beta_master_gate(settings: "Settings") -> None:
|
|
1189
1245
|
"""Force BETA_FEATURE_FIELDS to False when ``enable_beta_features`` is off.
|
|
1190
1246
|
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/locales/de.json
RENAMED
|
@@ -412,6 +412,8 @@
|
|
|
412
412
|
"advanced.code_mode_max_invocations.help": "API-/Tool-Aufruf-Grenze pro Sandbox-Lauf. Neustart erforderlich.",
|
|
413
413
|
"advanced.code_mode_saved_tools_path.label": "Pfad für gespeicherte Tools",
|
|
414
414
|
"advanced.code_mode_saved_tools_path.help": "JSON-Datei, die verwendet wird, um benutzerdefinierte Tools über Neustarts hinweg beizubehalten. Neustart erforderlich.",
|
|
415
|
+
"advanced.extra_yaml_write_keys.label": "Zusätzliche YAML-Schreib-Keys",
|
|
416
|
+
"advanced.extra_yaml_write_keys.help": "Kommagetrennte Top-Level-Keys, die ha_config_set_yaml zusätzlich zu den eingebauten schreiben darf – für YAML-first-Integrationen auf dieser Installation (z. B. alert2). Keys, die die Vertrauensgrenze von Home Assistant selbst verändern, lassen sich nie hinzufügen und werden ignoriert. Erfordert Custom Component 1.2.4 oder neuer.",
|
|
415
417
|
"advanced.sidecar_pin_port.label": "Einstellungs-UI-Sidecar-Port",
|
|
416
418
|
"advanced.sidecar_pin_port.help": "0 wählt bei jedem Neustart einen freien Port; 1024-65535 heftet einen bevorzugten Port an. Neustart erforderlich.",
|
|
417
419
|
"advanced.enable_dev_mode.label": "Entwicklermodus",
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/locales/en.json
RENAMED
|
@@ -412,6 +412,8 @@
|
|
|
412
412
|
"advanced.code_mode_max_invocations.help": "API/tool-call cap per sandbox run. Restart required.",
|
|
413
413
|
"advanced.code_mode_saved_tools_path.label": "Saved-tools path",
|
|
414
414
|
"advanced.code_mode_saved_tools_path.help": "JSON file used to persist custom tools across restarts. Restart required.",
|
|
415
|
+
"advanced.extra_yaml_write_keys.label": "Extra YAML write keys",
|
|
416
|
+
"advanced.extra_yaml_write_keys.help": "Comma-separated top-level keys ha_config_set_yaml may write in addition to the built-in ones, for YAML-first integrations on this install (e.g. alert2). Keys that redefine Home Assistant's own trust boundary can never be added and are ignored. Requires custom component 1.2.4 or newer.",
|
|
415
417
|
"advanced.sidecar_pin_port.label": "Settings UI sidecar port",
|
|
416
418
|
"advanced.sidecar_pin_port.help": "0 chooses a free port each restart; 1024–65535 pins a preferred port. Restart required.",
|
|
417
419
|
"advanced.enable_dev_mode.label": "Developer mode",
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/settings_ui/locales/ru.json
RENAMED
|
@@ -412,6 +412,8 @@
|
|
|
412
412
|
"advanced.code_mode_max_invocations.help": "Лимит вызовов API и инструментов за один запуск. Нужен перезапуск.",
|
|
413
413
|
"advanced.code_mode_saved_tools_path.label": "Путь к сохранённым инструментам",
|
|
414
414
|
"advanced.code_mode_saved_tools_path.help": "JSON-файл для хранения пользовательских инструментов между перезапусками. Нужен перезапуск.",
|
|
415
|
+
"advanced.extra_yaml_write_keys.label": "Дополнительные YAML-ключи для записи",
|
|
416
|
+
"advanced.extra_yaml_write_keys.help": "Список верхнеуровневых ключей через запятую, которые ha_config_set_yaml может записывать вдобавок к встроенным – для YAML-ориентированных интеграций на этой установке (например, alert2). Ключи, меняющие саму границу доверия Home Assistant, добавить нельзя, они игнорируются. Требуется пользовательский компонент 1.2.4 или новее.",
|
|
415
417
|
"advanced.sidecar_pin_port.label": "Порт sidecar интерфейса настроек",
|
|
416
418
|
"advanced.sidecar_pin_port.help": "0 выбирает свободный порт при каждом запуске; 1024–65535 закрепляет желаемый порт. Нужен перезапуск.",
|
|
417
419
|
"advanced.enable_dev_mode.label": "Режим разработчика",
|
|
@@ -2167,7 +2167,9 @@ function renderFeatureFlags(flags) {
|
|
|
2167
2167
|
// bottom.
|
|
2168
2168
|
if (fieldName === 'enable_code_mode') {
|
|
2169
2169
|
const codeModeOn = !!f.value;
|
|
2170
|
-
|
|
2170
|
+
renderAdvancedSubRows(
|
|
2171
|
+
targetBody, 'beta_codemode', 'codemode-sub', !masterOn || !codeModeOn
|
|
2172
|
+
);
|
|
2171
2173
|
}
|
|
2172
2174
|
// After rendering the enable_yaml_config_editing parent, inject
|
|
2173
2175
|
// its 3 per-key sub-rows (automation/script/scene). Dimmed when
|
|
@@ -2179,6 +2181,11 @@ function renderFeatureFlags(flags) {
|
|
|
2179
2181
|
cssClass: 'yaml-packages-sub',
|
|
2180
2182
|
lockedByGate: !masterOn || !parentOn,
|
|
2181
2183
|
});
|
|
2184
|
+
// Extra write keys (#1887) – same nesting depth as the per-key
|
|
2185
|
+
// toggles above, but a text value from the advanced cache.
|
|
2186
|
+
renderAdvancedSubRows(
|
|
2187
|
+
targetBody, 'beta_yamlkeys', 'yaml-packages-sub', !masterOn || !parentOn
|
|
2188
|
+
);
|
|
2182
2189
|
}
|
|
2183
2190
|
// After the enable_mandatory_bps parent row, inject its strict-mode
|
|
2184
2191
|
// sub-row. This whole group is non-beta, so the only gate is the
|
|
@@ -2210,7 +2217,7 @@ function renderFeatureFlags(flags) {
|
|
|
2210
2217
|
// master beta AND parent; mandatory-bps: parent only) and pass the CSS
|
|
2211
2218
|
// class that carries the group's indent/guide-bar depth. The number/text
|
|
2212
2219
|
// code-mode sub-numerics are NOT rendered here — they save through
|
|
2213
|
-
// commitAdvancedEdit and live in
|
|
2220
|
+
// commitAdvancedEdit and live in renderAdvancedSubRows.
|
|
2214
2221
|
function renderSubFlagRows(flags, parentEl, subFieldNames, { cssClass, lockedByGate }) {
|
|
2215
2222
|
subFieldNames.forEach(fieldName => {
|
|
2216
2223
|
const f = flags[fieldName];
|
|
@@ -2276,8 +2283,13 @@ function renderSubFlagRows(flags, parentEl, subFieldNames, { cssClass, lockedByG
|
|
|
2276
2283
|
});
|
|
2277
2284
|
}
|
|
2278
2285
|
|
|
2279
|
-
|
|
2280
|
-
|
|
2286
|
+
// Shared renderer for non-bool sub-rows that live in ADVANCED_SETTINGS_FIELDS
|
|
2287
|
+
// but render nested under a feature toggle (code-mode numerics under
|
|
2288
|
+
// enable_code_mode, extra YAML write keys under enable_yaml_config_editing).
|
|
2289
|
+
// They save through commitAdvancedEdit rather than saveFeatureFlag, which is
|
|
2290
|
+
// why renderSubFlagRows (checkbox-only) cannot serve them.
|
|
2291
|
+
function renderAdvancedSubRows(parentEl, section, cssClass, lockedByGate) {
|
|
2292
|
+
const cmRows = (_advancedFields || []).filter(x => x.section === section);
|
|
2281
2293
|
cmRows.forEach(f => {
|
|
2282
2294
|
const meta = localizeMeta(
|
|
2283
2295
|
'advanced',
|
|
@@ -2285,9 +2297,7 @@ function renderCodeModeSubRows(parentEl, masterOn, codeModeOn) {
|
|
|
2285
2297
|
ADVANCED_FIELD_META[f.field] || { label: f.field, help: '' }
|
|
2286
2298
|
);
|
|
2287
2299
|
const row = document.createElement('div');
|
|
2288
|
-
|
|
2289
|
-
const dimmed = lockedByGate;
|
|
2290
|
-
row.className = 'feature-row codemode-sub' + (dimmed ? ' dimmed' : '');
|
|
2300
|
+
row.className = 'feature-row ' + cssClass + (lockedByGate ? ' dimmed' : '');
|
|
2291
2301
|
|
|
2292
2302
|
const info = document.createElement('div');
|
|
2293
2303
|
info.className = 'feature-info';
|
|
@@ -3390,6 +3400,7 @@ const ADVANCED_FIELD_META = {
|
|
|
3390
3400
|
code_mode_max_recursion: { label: "Code-mode max recursion", help: "Recursion-depth cap per sandbox run. Restart required." },
|
|
3391
3401
|
code_mode_max_invocations: { label: "Code-mode max invocations", help: "API/tool-call cap per sandbox run. Restart required." },
|
|
3392
3402
|
code_mode_saved_tools_path:{ label: "Saved-tools path", help: "JSON file where ha_manage_custom_tool persists saved tools across restarts. Restart required." },
|
|
3403
|
+
extra_yaml_write_keys: { label: "Extra YAML write keys", help: "Comma-separated top-level keys ha_config_set_yaml may write in addition to the built-in ones, for YAML-first integrations on this install (e.g. alert2). Keys that redefine Home Assistant's own trust boundary can never be added and are ignored. Requires custom component 1.2.4 or newer." },
|
|
3393
3404
|
sidecar_pin_port: { label: "Settings UI sidecar port", help: "0 = a new free port each restart (default); set 1024–65535 to pin a fixed port so the settings URL stays stable across restarts. Falls back to a free port if the pinned one is busy. Restart required." },
|
|
3394
3405
|
enable_dev_mode: { label: "Developer mode", help: "⚠ DANGER: registers hidden developer tools (ha_dev_manage_server, ha_dev_manage_settings) that let AI agents change server settings and replace the running server version (e.g. install a PR build). For development and testing only. Restart required." },
|
|
3395
3406
|
};
|
|
@@ -3493,10 +3504,10 @@ async function loadAdvancedSettings() {
|
|
|
3493
3504
|
renderAdvancedSection('advSidecar', bySection.sidecar || []);
|
|
3494
3505
|
renderAdvancedSection('advDeveloper', bySection.developer || []);
|
|
3495
3506
|
applySidecarAvailability(data.is_stdio !== false);
|
|
3496
|
-
// Re-render feature flags so the
|
|
3497
|
-
//
|
|
3498
|
-
//
|
|
3499
|
-
// flags haven't loaded yet.
|
|
3507
|
+
// Re-render feature flags so the advanced-backed sub-rows (code_mode
|
|
3508
|
+
// numerics, extra YAML write keys) show up beneath their parent toggles
|
|
3509
|
+
// (race: loadFeatureFlags may have run before _advancedFields was
|
|
3510
|
+
// populated). Cheap no-op if feature flags haven't loaded yet.
|
|
3500
3511
|
if (Object.keys(_lastFeatureFlags).length > 0) {
|
|
3501
3512
|
renderFeatureFlags(_lastFeatureFlags);
|
|
3502
3513
|
}
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_filesystem.py
RENAMED
|
@@ -51,6 +51,11 @@ MCP_TOOLS_DOMAIN = "ha_mcp_tools"
|
|
|
51
51
|
# subsequent call comes back unauthorized (covers token rotation).
|
|
52
52
|
CALLER_TOKEN_FIELD = "_ha_mcp_token"
|
|
53
53
|
CALLER_TOKEN_BOOTSTRAP_SERVICE = "get_caller_token"
|
|
54
|
+
# Component service that returns the operator's component-side extra YAML write
|
|
55
|
+
# keys (#1887). Ships in the same component version as the ``extra_allowed_keys``
|
|
56
|
+
# field, so it is read only when the component is new enough (see
|
|
57
|
+
# ``effective_extra_yaml_write_keys``).
|
|
58
|
+
GET_EXTRA_YAML_KEYS_SERVICE = "get_extra_yaml_keys"
|
|
54
59
|
|
|
55
60
|
# Minimum version of the ha_mcp_tools custom component that this ha-mcp
|
|
56
61
|
# release expects. Bumps in lockstep with ``manifest.json`` whenever a
|
|
@@ -108,6 +113,15 @@ def _version_tuple(version: str) -> tuple[int, ...]:
|
|
|
108
113
|
# when a client is garbage-collected (avoids id() reuse if a freed client's
|
|
109
114
|
# address gets recycled before the unauthorized-retry fires).
|
|
110
115
|
_CALLER_TOKEN_CACHE: weakref.WeakKeyDictionary[Any, str] = weakref.WeakKeyDictionary()
|
|
116
|
+
# Component version as reported by the REST bootstrap, keyed like the token
|
|
117
|
+
# cache. Populated in ``_fetch_caller_token``, which already parses and
|
|
118
|
+
# validates it. Feature-scoped version gates read this rather than the
|
|
119
|
+
# WebSocket capability handshake: caps come back ``None`` for a transport
|
|
120
|
+
# blip just as they do for an old component, so gating on them would report
|
|
121
|
+
# a momentary socket drop as "your component is too old".
|
|
122
|
+
_COMPONENT_VERSION_CACHE: weakref.WeakKeyDictionary[Any, str] = (
|
|
123
|
+
weakref.WeakKeyDictionary()
|
|
124
|
+
)
|
|
111
125
|
_CALLER_TOKEN_LOCKS: weakref.WeakKeyDictionary[Any, asyncio.Lock] = (
|
|
112
126
|
weakref.WeakKeyDictionary()
|
|
113
127
|
)
|
|
@@ -286,6 +300,7 @@ async def _fetch_caller_token(client: Any) -> str:
|
|
|
286
300
|
if parsed < _version_tuple(MIN_COMPONENT_VERSION):
|
|
287
301
|
_raise_component_too_old(f"reported version is {version}")
|
|
288
302
|
_CALLER_TOKEN_CACHE[client] = token
|
|
303
|
+
_COMPONENT_VERSION_CACHE[client] = version
|
|
289
304
|
return token
|
|
290
305
|
|
|
291
306
|
|
|
@@ -961,3 +976,130 @@ def register_filesystem_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
961
976
|
|
|
962
977
|
logger.info("Filesystem tools enabled via feature flag")
|
|
963
978
|
register_tool_methods(mcp, FilesystemTools(client))
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
# First custom-component version whose ``edit_yaml_config`` schema accepts
|
|
982
|
+
# ``extra_allowed_keys`` (#1887).
|
|
983
|
+
MIN_COMPONENT_VERSION_EXTRA_YAML_KEYS = "1.2.4"
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
async def assert_extra_yaml_keys_supported(client: Any, extra_keys: list[str]) -> None:
|
|
987
|
+
"""Block with an actionable prompt if the component predates #1887.
|
|
988
|
+
|
|
989
|
+
``edit_yaml_config``'s service schema is strict, so sending
|
|
990
|
+
``extra_allowed_keys`` to a component that does not declare the field makes
|
|
991
|
+
Home Assistant reject the whole call with an opaque "extra keys not
|
|
992
|
+
allowed". Callers only send the field when the operator configured keys,
|
|
993
|
+
and this turns the remaining server-ahead-of-component window into a clear
|
|
994
|
+
message. Both the write path and the backup restore path use it, so a
|
|
995
|
+
snapshot taken under the setting fails the same recognisable way.
|
|
996
|
+
|
|
997
|
+
Deliberately NOT a bump of ``MIN_COMPONENT_VERSION``: an operator who never
|
|
998
|
+
sets extra keys must not be forced to update the component to keep using
|
|
999
|
+
the filesystem tools.
|
|
1000
|
+
|
|
1001
|
+
The version comes from the REST bootstrap cache, which every call to this
|
|
1002
|
+
component populates and validates, so a WebSocket hiccup cannot be
|
|
1003
|
+
mistaken for an outdated component. An absent entry means the bootstrap
|
|
1004
|
+
has not run yet; the caller below performs it first, and a component too
|
|
1005
|
+
old to report a version at all is already rejected there.
|
|
1006
|
+
"""
|
|
1007
|
+
if not extra_keys:
|
|
1008
|
+
return
|
|
1009
|
+
|
|
1010
|
+
def _current() -> tuple[int, ...] | None:
|
|
1011
|
+
reported = _COMPONENT_VERSION_CACHE.get(client, "")
|
|
1012
|
+
if not reported:
|
|
1013
|
+
return None
|
|
1014
|
+
try:
|
|
1015
|
+
return _version_tuple(reported)
|
|
1016
|
+
except ValueError:
|
|
1017
|
+
return None
|
|
1018
|
+
|
|
1019
|
+
floor = _version_tuple(MIN_COMPONENT_VERSION_EXTRA_YAML_KEYS)
|
|
1020
|
+
await _ensure_caller_token(client)
|
|
1021
|
+
parsed = _current()
|
|
1022
|
+
if parsed is None or parsed < floor:
|
|
1023
|
+
# The token cache is keyed by the long-lived REST client and survives
|
|
1024
|
+
# a Home Assistant restart, so a component updated after this process
|
|
1025
|
+
# first bootstrapped would keep reporting its old version forever and
|
|
1026
|
+
# the remediation this error prints ("update, then restart HA") would
|
|
1027
|
+
# never take effect. Re-bootstrap once before blocking, so the update
|
|
1028
|
+
# heals the gate on the next call rather than needing an ha-mcp
|
|
1029
|
+
# restart. Only on the failure path: a satisfied gate costs nothing.
|
|
1030
|
+
await _ensure_caller_token(client, force_refresh=True)
|
|
1031
|
+
parsed = _current()
|
|
1032
|
+
reported = _COMPONENT_VERSION_CACHE.get(client, "")
|
|
1033
|
+
if parsed is not None and parsed >= floor:
|
|
1034
|
+
return
|
|
1035
|
+
raise_tool_error(
|
|
1036
|
+
create_error_response(
|
|
1037
|
+
ErrorCode.COMPONENT_NOT_INSTALLED,
|
|
1038
|
+
"Extra YAML write keys are configured, but the installed "
|
|
1039
|
+
"ha_mcp_tools custom component does not support them "
|
|
1040
|
+
f"(reported version: {reported or 'unknown'}; requires >= "
|
|
1041
|
+
f"{MIN_COMPONENT_VERSION_EXTRA_YAML_KEYS}).",
|
|
1042
|
+
suggestions=[
|
|
1043
|
+
"HACS → Integrations → HA-MCP Custom Component → Update",
|
|
1044
|
+
"Restart Home Assistant after the update completes",
|
|
1045
|
+
# Parenthesised so this reads as one suggestion rather than
|
|
1046
|
+
# a list entry with a missing comma (py/implicit-string-
|
|
1047
|
+
# concatenation-in-list).
|
|
1048
|
+
(
|
|
1049
|
+
"Or clear the extra YAML write keys setting to write "
|
|
1050
|
+
"only the built-in allowed keys"
|
|
1051
|
+
),
|
|
1052
|
+
],
|
|
1053
|
+
)
|
|
1054
|
+
)
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
async def effective_extra_yaml_write_keys(client: Any, settings: Any) -> list[str]:
|
|
1058
|
+
"""Return the write allowlist extension in force: server setting + component.
|
|
1059
|
+
|
|
1060
|
+
The operator can set extra YAML write keys in two places (#1887): the ha-mcp
|
|
1061
|
+
server's own ``HA_MCP_EXTRA_YAML_KEYS`` and, via the integration UI, a store
|
|
1062
|
+
the component owns. A key set only on the component would otherwise be
|
|
1063
|
+
rejected by the server's own pre-dispatch allowlist before the write ever
|
|
1064
|
+
reaches the component, so the server reads that store here (via
|
|
1065
|
+
``get_extra_yaml_keys``) and unions it with its own setting.
|
|
1066
|
+
|
|
1067
|
+
That service ships in the same component version as the ``extra_allowed_keys``
|
|
1068
|
+
field (``MIN_COMPONENT_VERSION_EXTRA_YAML_KEYS``), so it is read only when the
|
|
1069
|
+
bootstrap reports a new-enough component; on an older component, or any read
|
|
1070
|
+
failure, fall back to the server setting alone and let
|
|
1071
|
+
``assert_extra_yaml_keys_supported`` turn a real mismatch into an actionable
|
|
1072
|
+
prompt. The store is read per write rather than cached: it changes at runtime
|
|
1073
|
+
from the options flow, and a stale cache is the failure mode #1887's own
|
|
1074
|
+
version-cache heal exists to avoid. YAML writes are rare and human-driven, so
|
|
1075
|
+
the extra round-trip is negligible.
|
|
1076
|
+
"""
|
|
1077
|
+
from ..config import parse_extra_yaml_write_keys
|
|
1078
|
+
|
|
1079
|
+
keys: set[str] = set(parse_extra_yaml_write_keys(settings))
|
|
1080
|
+
try:
|
|
1081
|
+
await _ensure_caller_token(client)
|
|
1082
|
+
reported = _COMPONENT_VERSION_CACHE.get(client, "")
|
|
1083
|
+
floor = _version_tuple(MIN_COMPONENT_VERSION_EXTRA_YAML_KEYS)
|
|
1084
|
+
try:
|
|
1085
|
+
current = _version_tuple(reported) if reported else None
|
|
1086
|
+
except ValueError:
|
|
1087
|
+
current = None
|
|
1088
|
+
if current is not None and current >= floor:
|
|
1089
|
+
result = await call_mcp_tools_service(
|
|
1090
|
+
client, GET_EXTRA_YAML_KEYS_SERVICE, {}
|
|
1091
|
+
)
|
|
1092
|
+
inner = (
|
|
1093
|
+
unwrap_service_response(result) if isinstance(result, dict) else None
|
|
1094
|
+
)
|
|
1095
|
+
if isinstance(inner, dict) and inner.get("success"):
|
|
1096
|
+
stored = inner.get("keys", [])
|
|
1097
|
+
if isinstance(stored, list):
|
|
1098
|
+
keys.update(k for k in stored if isinstance(k, str) and k)
|
|
1099
|
+
except Exception:
|
|
1100
|
+
logger.debug(
|
|
1101
|
+
"Could not read the component extra-YAML-keys store; "
|
|
1102
|
+
"using the server setting alone.",
|
|
1103
|
+
exc_info=True,
|
|
1104
|
+
)
|
|
1105
|
+
return sorted(keys)
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/tools/tools_yaml_config.py
RENAMED
|
@@ -33,7 +33,9 @@ from .helpers import (
|
|
|
33
33
|
from .tools_config_dashboards import fetch_dashboards_list
|
|
34
34
|
from .tools_filesystem import (
|
|
35
35
|
_assert_mcp_tools_available,
|
|
36
|
+
assert_extra_yaml_keys_supported,
|
|
36
37
|
call_mcp_tools_service,
|
|
38
|
+
effective_extra_yaml_write_keys,
|
|
37
39
|
)
|
|
38
40
|
from .util_helpers import (
|
|
39
41
|
attach_skill_content,
|
|
@@ -57,7 +59,7 @@ _LOVELACE_DASHBOARD_PREFIX = "lovelace.dashboards."
|
|
|
57
59
|
# here, otherwise a key would silently be unreachable through the
|
|
58
60
|
# wrapper. The parity invariant (keys == PACKAGES_ONLY_YAML_KEYS, and
|
|
59
61
|
# every value is a real Settings field) is enforced by
|
|
60
|
-
#
|
|
62
|
+
# test_yaml_dashboards.py::test_flag_map_matches_packages_only_keys.
|
|
61
63
|
_YAML_PACKAGES_FLAG_BY_KEY = {
|
|
62
64
|
"automation": "enable_yaml_packages_automation",
|
|
63
65
|
"script": "enable_yaml_packages_script",
|
|
@@ -464,6 +466,9 @@ class YamlConfigTools:
|
|
|
464
466
|
# Check if custom component is available
|
|
465
467
|
await _assert_mcp_tools_available(self._client)
|
|
466
468
|
|
|
469
|
+
extra_keys = await effective_extra_yaml_write_keys(self._client, settings)
|
|
470
|
+
await assert_extra_yaml_keys_supported(self._client, extra_keys)
|
|
471
|
+
|
|
467
472
|
# Build service data
|
|
468
473
|
service_data: dict[str, Any] = {
|
|
469
474
|
"file": file,
|
|
@@ -472,6 +477,11 @@ class YamlConfigTools:
|
|
|
472
477
|
"disabled_packages_keys": disabled_keys,
|
|
473
478
|
"require_confirm": settings.enable_yaml_edit_confirm,
|
|
474
479
|
}
|
|
480
|
+
# Only sent when the operator actually configured extra keys, so a
|
|
481
|
+
# component that predates the field keeps working unchanged for
|
|
482
|
+
# everyone else (its schema is strict and would reject the call).
|
|
483
|
+
if extra_keys:
|
|
484
|
+
service_data["extra_allowed_keys"] = extra_keys
|
|
475
485
|
if content is not None:
|
|
476
486
|
service_data["content"] = content
|
|
477
487
|
if confirm_token is not None:
|
|
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.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/client/supervisor_client.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/client/websocket_client.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/client/websocket_listener.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/capture.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/content.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/dashboard_screenshot/paths.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2103 → ha_mcp_dev-7.14.2.dev2107}/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
|