ha-mcp-dev 7.9.0.dev738__tar.gz → 7.9.0.dev739__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.9.0.dev738/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.9.0.dev739}/PKG-INFO +1 -1
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/pyproject.toml +1 -1
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/config.py +1 -1
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/policy/persistence.py +1 -1
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/read_only.py +1 -1
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/settings_ui/__init__.py +86 -26
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/settings_ui/settings.css +26 -9
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/settings_ui/settings.html +34 -28
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/settings_ui/settings.js +221 -85
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/transforms/__init__.py +2 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/transforms/categorized_search.py +11 -5
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/LICENSE +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/README.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/setup.cfg +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.9.0.dev738 → ha_mcp_dev-7.9.0.dev739}/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.9.0.
|
|
7
|
+
version = "7.9.0.dev739"
|
|
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"
|
|
@@ -535,7 +535,7 @@ def validate_settings() -> tuple[bool, str | None]:
|
|
|
535
535
|
RegistryFieldType = type[bool] | type[int] | type[float] | type[str]
|
|
536
536
|
|
|
537
537
|
# Closed set of UI section names. The advanced renderer in
|
|
538
|
-
# settings_ui.py picks a DOM container per section; a typo would render
|
|
538
|
+
# settings_ui/__init__.py picks a DOM container per section; a typo would render
|
|
539
539
|
# the row into nothing.
|
|
540
540
|
AdvancedSection = Literal[
|
|
541
541
|
"connection",
|
|
@@ -138,7 +138,7 @@ def _custom_tool_write(args: dict[str, Any]) -> str | None:
|
|
|
138
138
|
# cache is only listable here). Everything NOT in this table and not
|
|
139
139
|
# ``readOnlyHint=True`` is hidden and blocked outright.
|
|
140
140
|
#
|
|
141
|
-
# ``MANDATORY_TOOLS`` (settings_ui.py) intentionally needs no special
|
|
141
|
+
# ``MANDATORY_TOOLS`` (settings_ui/__init__.py) intentionally needs no special
|
|
142
142
|
# case here: every mandatory tool is either ``readOnlyHint=True`` or
|
|
143
143
|
# present in this table (``ha_manage_backup``). The e2e test
|
|
144
144
|
# ``test_real_catalog_mandatory_tools_stay_available``
|
|
@@ -83,12 +83,13 @@ _PROCESS_STARTED_AT: float = time.time()
|
|
|
83
83
|
logger = logging.getLogger(__name__)
|
|
84
84
|
|
|
85
85
|
# Tools that are always enabled regardless of saved config — the server
|
|
86
|
-
# strips them out of any disable list before applying.
|
|
86
|
+
# strips them out of any disable list before applying. Five of these
|
|
87
87
|
# overlap with DEFAULT_PINNED_TOOLS in transforms/categorized_search.py
|
|
88
|
-
# (ha_search, ha_get_overview, ha_report_issue
|
|
89
|
-
# is mandatory but not pinned-by-default
|
|
90
|
-
#
|
|
91
|
-
# sync where it matters and divergent where it
|
|
88
|
+
# (ha_search, ha_get_overview, ha_report_issue, ha_get_skill_guide,
|
|
89
|
+
# ha_manage_backup); ha_get_state is mandatory but not pinned-by-default
|
|
90
|
+
# because it is reachable via the ha_call_read_tool proxy when tool search
|
|
91
|
+
# is on. Keep these lists in sync where it matters and divergent where it
|
|
92
|
+
# matters — don't merge them.
|
|
92
93
|
MANDATORY_TOOLS: set[str] = {
|
|
93
94
|
"ha_search",
|
|
94
95
|
"ha_get_overview",
|
|
@@ -633,7 +634,7 @@ def apply_tool_visibility(
|
|
|
633
634
|
_SETTINGS_JS_PATH = Path(__file__).parent / "settings.js"
|
|
634
635
|
try:
|
|
635
636
|
_settings_js_template = _SETTINGS_JS_PATH.read_text(encoding="utf-8")
|
|
636
|
-
except
|
|
637
|
+
except OSError as exc: # pragma: no cover - packaging guard
|
|
637
638
|
raise ImportError(
|
|
638
639
|
f"settings.js missing at {_SETTINGS_JS_PATH}. It must ship in "
|
|
639
640
|
"package-data (wheel), MANIFEST.in (sdist), and the PyInstaller datas "
|
|
@@ -641,9 +642,10 @@ except FileNotFoundError as exc: # pragma: no cover - packaging guard
|
|
|
641
642
|
) from exc
|
|
642
643
|
# str.replace() silently no-ops on an absent token, and a *renamed* sentinel
|
|
643
644
|
# (e.g. PINNED_DEFAULTS) slips past both the "__HA_MCP_" not-in test and the
|
|
644
|
-
#
|
|
645
|
-
# JS (only a runtime ReferenceError), so a drifted
|
|
646
|
-
# broken page green. Assert both sentinels are present
|
|
645
|
+
# esbuild/jsdom JS harness (tests/js/harness.mjs) -- `const DEFAULT_PINNED =
|
|
646
|
+
# PINNED_DEFAULTS;` is valid JS (only a runtime ReferenceError), so a drifted
|
|
647
|
+
# settings.js would ship a broken page green. Assert both sentinels are present
|
|
648
|
+
# before substituting.
|
|
647
649
|
for _sentinel in ("__HA_MCP_DEFAULT_PINNED__", "__HA_MCP_MANDATORY__"):
|
|
648
650
|
if _sentinel not in _settings_js_template:
|
|
649
651
|
raise ImportError(
|
|
@@ -664,11 +666,11 @@ _SETTINGS_JS = _settings_js_template.replace(
|
|
|
664
666
|
# read, no token substitution -- and is injected inline between the same
|
|
665
667
|
# <style>/</style> tags so the served page stays byte-identical. It carries
|
|
666
668
|
# the same import-time packaging dependency as settings.js, so the same
|
|
667
|
-
#
|
|
669
|
+
# OSError -> ImportError packaging guard applies.
|
|
668
670
|
_SETTINGS_CSS_PATH = Path(__file__).parent / "settings.css"
|
|
669
671
|
try:
|
|
670
672
|
_SETTINGS_CSS = _SETTINGS_CSS_PATH.read_text(encoding="utf-8")
|
|
671
|
-
except
|
|
673
|
+
except OSError as exc: # pragma: no cover - packaging guard
|
|
672
674
|
raise ImportError(
|
|
673
675
|
f"settings.css missing at {_SETTINGS_CSS_PATH}. It must ship in "
|
|
674
676
|
"package-data (wheel), MANIFEST.in (sdist), and the PyInstaller datas "
|
|
@@ -683,21 +685,23 @@ except FileNotFoundError as exc: # pragma: no cover - packaging guard
|
|
|
683
685
|
# __HA_MCP_JS__ -> settings.js contents (inside <script>)
|
|
684
686
|
# __HA_MCP_THEME_PREFS__ -> per-request server-seeded theme prefs JSON,
|
|
685
687
|
# substituted in _render_settings_html()
|
|
686
|
-
# Same import-time packaging dependency as settings.js/css (wheel package-data
|
|
687
|
-
#
|
|
688
|
+
# Same import-time packaging dependency as settings.js/css (wheel package-data,
|
|
689
|
+
# MANIFEST.in, PyInstaller datas) and the same OSError guard -- but this loader
|
|
690
|
+
# raises RuntimeError, not the ImportError that settings.js/css raise.
|
|
688
691
|
_SETTINGS_HTML_PATH = Path(__file__).parent / "settings.html"
|
|
689
692
|
try:
|
|
690
693
|
_settings_html_template = _SETTINGS_HTML_PATH.read_text(encoding="utf-8")
|
|
691
694
|
except OSError as exc: # pragma: no cover - packaging guard
|
|
692
695
|
raise RuntimeError(
|
|
693
696
|
f"settings.html missing at {_SETTINGS_HTML_PATH}. It must ship in "
|
|
694
|
-
"
|
|
695
|
-
"(
|
|
697
|
+
"package-data (wheel), MANIFEST.in (sdist), and the PyInstaller datas "
|
|
698
|
+
"(binary) -- this is a packaging bug, not a usage error."
|
|
696
699
|
) from exc
|
|
697
700
|
|
|
698
701
|
# Fail fast if a marker was renamed in settings.html but not here (or vice
|
|
699
702
|
# versa) — str.replace() silently no-ops on an absent token, which would ship a
|
|
700
|
-
# page missing its CSS or
|
|
703
|
+
# page missing its CSS, JS, or server-seeded theme prefs. Assert all three
|
|
704
|
+
# markers are present.
|
|
701
705
|
for _sentinel in ("__HA_MCP_CSS__", "__HA_MCP_JS__", "__HA_MCP_THEME_PREFS__"):
|
|
702
706
|
if _sentinel not in _settings_html_template:
|
|
703
707
|
raise RuntimeError(
|
|
@@ -771,10 +775,10 @@ def _build_stub_policy_handlers(*, data_dir: Path) -> dict[str, Any]:
|
|
|
771
775
|
{
|
|
772
776
|
"error": (
|
|
773
777
|
"Tool security policies live approvals are not active. "
|
|
774
|
-
"Either the feature is turned off in
|
|
778
|
+
"Either the feature is turned off in App (add-on) config, the "
|
|
775
779
|
"settings UI is running in stdio-sidecar mode, or the "
|
|
776
780
|
"policy package failed to import at startup. Check the "
|
|
777
|
-
"
|
|
781
|
+
"App (add-on) log for ImportError / RuntimeError details if you "
|
|
778
782
|
"expected gating to be on."
|
|
779
783
|
)
|
|
780
784
|
},
|
|
@@ -1388,7 +1392,7 @@ def build_settings_handlers(
|
|
|
1388
1392
|
"Unset DISABLED_TOOLS / PINNED_TOOLS first.",
|
|
1389
1393
|
suggestions=[
|
|
1390
1394
|
"Unset the DISABLED_TOOLS / PINNED_TOOLS environment "
|
|
1391
|
-
"variables (or remove them from your
|
|
1395
|
+
"variables (or remove them from your App (add-on)/Docker "
|
|
1392
1396
|
"config), then restart to edit these tools from the UI.",
|
|
1393
1397
|
],
|
|
1394
1398
|
context={"rejected": rejected},
|
|
@@ -1451,7 +1455,7 @@ def build_settings_handlers(
|
|
|
1451
1455
|
return JSONResponse(
|
|
1452
1456
|
create_error_response(
|
|
1453
1457
|
ErrorCode.CONFIG_VALIDATION_FAILED,
|
|
1454
|
-
"Restart only available when running as an add-on",
|
|
1458
|
+
"Restart only available when running as an App (add-on)",
|
|
1455
1459
|
details="SUPERVISOR_TOKEN environment variable is not set",
|
|
1456
1460
|
),
|
|
1457
1461
|
status_code=400,
|
|
@@ -1583,6 +1587,42 @@ def build_settings_handlers(
|
|
|
1583
1587
|
}
|
|
1584
1588
|
)
|
|
1585
1589
|
|
|
1590
|
+
async def _live_addon_options() -> dict[str, Any]:
|
|
1591
|
+
"""Best-effort fetch of the add-on's current ``/data/options.json``.
|
|
1592
|
+
|
|
1593
|
+
Read-consistency with the add-on Configuration tab: the GET handlers
|
|
1594
|
+
below otherwise display boot-time env values, so a user who edits the
|
|
1595
|
+
add-on config (or saves from this web UI) WITHOUT restarting would see
|
|
1596
|
+
stale values. For add-on-synced / schema-backed fields the live value
|
|
1597
|
+
lives in Supervisor's options, so surface that — the latest SAVED
|
|
1598
|
+
value, which is what becomes active after restart (the existing
|
|
1599
|
+
"Restart required after save" banner conveys apply-on-restart).
|
|
1600
|
+
|
|
1601
|
+
Returns an empty dict outside add-on mode, without a live server, or
|
|
1602
|
+
on any fetch failure, so callers fall back to the boot-env value and
|
|
1603
|
+
the page never crashes or blanks a field.
|
|
1604
|
+
"""
|
|
1605
|
+
if not is_running_in_addon() or server is None:
|
|
1606
|
+
return {}
|
|
1607
|
+
try:
|
|
1608
|
+
options, err = await _supervisor_fetch_current_options(
|
|
1609
|
+
server.settings.verify_ssl
|
|
1610
|
+
)
|
|
1611
|
+
except Exception as exc: # pragma: no cover - defensive belt-and-braces
|
|
1612
|
+
logger.debug(
|
|
1613
|
+
"Live add-on options fetch raised %s — using boot-env values",
|
|
1614
|
+
exc,
|
|
1615
|
+
)
|
|
1616
|
+
return {}
|
|
1617
|
+
if err is not None:
|
|
1618
|
+
logger.debug(
|
|
1619
|
+
"Live add-on options fetch failed (%s): %s — using boot-env values",
|
|
1620
|
+
err.kind,
|
|
1621
|
+
err.message,
|
|
1622
|
+
)
|
|
1623
|
+
return {}
|
|
1624
|
+
return options
|
|
1625
|
+
|
|
1586
1626
|
async def _get_feature_flags(_: Request) -> JSONResponse:
|
|
1587
1627
|
"""Return live feature-flag values + per-field origin + editable flag.
|
|
1588
1628
|
|
|
@@ -1613,10 +1653,23 @@ def build_settings_handlers(
|
|
|
1613
1653
|
)
|
|
1614
1654
|
|
|
1615
1655
|
settings = get_global_settings()
|
|
1656
|
+
# Read-consistency with the add-on Configuration tab: a flag the user
|
|
1657
|
+
# saved in the Config tab is already in Supervisor's live options, but
|
|
1658
|
+
# the boot-env value goes stale until a restart. Surface the latest
|
|
1659
|
+
# SAVED value for any flag present in live_options (see
|
|
1660
|
+
# _live_addon_options) — not just origin=="addon" rows. On a dev add-on
|
|
1661
|
+
# a flag saved before restart has origin "default"/"file" (its env var
|
|
1662
|
+
# wasn't written at this boot), yet the fresh value is the one to show.
|
|
1663
|
+
# Only origin=="env" stays pinned (an explicit env var the user must
|
|
1664
|
+
# unset to change), and live_options is {} outside add-on mode so this
|
|
1665
|
+
# is a no-op standalone.
|
|
1666
|
+
live_options = await _live_addon_options()
|
|
1616
1667
|
flags: dict[str, Any] = {}
|
|
1617
1668
|
for field_name, env_name, ftype in FEATURE_FLAG_FIELDS:
|
|
1618
1669
|
origin = get_feature_flag_origin(env_name)
|
|
1619
1670
|
value = getattr(settings, field_name)
|
|
1671
|
+
if field_name in live_options and origin != "env":
|
|
1672
|
+
value = live_options[field_name]
|
|
1620
1673
|
entry: dict[str, Any] = {
|
|
1621
1674
|
"value": value,
|
|
1622
1675
|
"origin": origin,
|
|
@@ -1796,7 +1849,7 @@ def build_settings_handlers(
|
|
|
1796
1849
|
(
|
|
1797
1850
|
f"{field_name!r} is locked by {origin}. "
|
|
1798
1851
|
f"Adjust the {env_name} env var "
|
|
1799
|
-
"(or
|
|
1852
|
+
"(or App (add-on) configuration) instead."
|
|
1800
1853
|
),
|
|
1801
1854
|
),
|
|
1802
1855
|
status_code=400,
|
|
@@ -1917,7 +1970,7 @@ def build_settings_handlers(
|
|
|
1917
1970
|
"Supervisor helper returned ok=False with no error",
|
|
1918
1971
|
suggestions=[
|
|
1919
1972
|
"Check the Home Assistant Supervisor logs and "
|
|
1920
|
-
+ "the add-on logs for the underlying failure.",
|
|
1973
|
+
+ "the App (add-on) logs for the underlying failure.",
|
|
1921
1974
|
"Report this at "
|
|
1922
1975
|
+ "https://github.com/homeassistant-ai/ha-mcp/issues "
|
|
1923
1976
|
+ "if it persists. This indicates an internal bug.",
|
|
@@ -2286,6 +2339,11 @@ def build_settings_handlers(
|
|
|
2286
2339
|
# called per field, and re-reading the file 17+ times would
|
|
2287
2340
|
# produce duplicate WARNINGs on a corrupt file (one per field).
|
|
2288
2341
|
overrides = _read_feature_flag_override_file()
|
|
2342
|
+
# Read-consistency with the add-on Configuration tab: addon-synced
|
|
2343
|
+
# fields (origin "addon") live in /data/options.json, so the boot-env
|
|
2344
|
+
# value goes stale after a config edit without a restart. Surface the
|
|
2345
|
+
# latest SAVED options value for those (see _live_addon_options).
|
|
2346
|
+
live_options = await _live_addon_options()
|
|
2289
2347
|
fields: list[dict[str, Any]] = []
|
|
2290
2348
|
for (
|
|
2291
2349
|
fname,
|
|
@@ -2296,6 +2354,8 @@ def build_settings_handlers(
|
|
|
2296
2354
|
) in ADVANCED_SETTINGS_FIELDS:
|
|
2297
2355
|
origin = _origin_for_advanced_field(env_name, overrides=overrides)
|
|
2298
2356
|
value: Any = getattr(settings, fname, None)
|
|
2357
|
+
if origin == "addon" and fname in live_options:
|
|
2358
|
+
value = live_options[fname]
|
|
2299
2359
|
# Mask the token: never echo the actual long-lived access
|
|
2300
2360
|
# token to the UI. The OAuth-mode sentinel survives so
|
|
2301
2361
|
# operators can tell connection mode at a glance.
|
|
@@ -2456,7 +2516,7 @@ def build_settings_handlers(
|
|
|
2456
2516
|
create_error_response(
|
|
2457
2517
|
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
2458
2518
|
f"{fname!r} is display-only. Modify via env var "
|
|
2459
|
-
"or
|
|
2519
|
+
"or App (add-on) configuration.",
|
|
2460
2520
|
),
|
|
2461
2521
|
status_code=409,
|
|
2462
2522
|
)
|
|
@@ -2475,11 +2535,11 @@ def build_settings_handlers(
|
|
|
2475
2535
|
# add-on-aware copy instead of implying a lever exists.
|
|
2476
2536
|
if addon_mode:
|
|
2477
2537
|
message = (
|
|
2478
|
-
f"{fname!r} is fixed by the add-on runtime and "
|
|
2538
|
+
f"{fname!r} is fixed by the App (add-on) runtime and "
|
|
2479
2539
|
"cannot be changed from the web UI."
|
|
2480
2540
|
)
|
|
2481
2541
|
suggestions = [
|
|
2482
|
-
"This value is baked into the add-on and is not "
|
|
2542
|
+
"This value is baked into the App (add-on) and is not "
|
|
2483
2543
|
"exposed as an editable setting.",
|
|
2484
2544
|
]
|
|
2485
2545
|
else:
|
|
@@ -2640,7 +2700,7 @@ def build_settings_handlers(
|
|
|
2640
2700
|
"Supervisor helper returned ok=False with no error",
|
|
2641
2701
|
suggestions=[
|
|
2642
2702
|
"Check the Home Assistant Supervisor logs and "
|
|
2643
|
-
+ "the add-on logs for the underlying failure.",
|
|
2703
|
+
+ "the App (add-on) logs for the underlying failure.",
|
|
2644
2704
|
"Report this at "
|
|
2645
2705
|
+ "https://github.com/homeassistant-ai/ha-mcp/issues "
|
|
2646
2706
|
+ "if it persists. This indicates an internal bug.",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
/* App toolbar */
|
|
43
43
|
--header-bg: #455a64;
|
|
44
44
|
--header-text: #ffffff;
|
|
45
|
+
--header-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
|
|
45
46
|
|
|
46
47
|
/* Cards */
|
|
47
48
|
--card-shadow: none;
|
|
@@ -88,7 +89,7 @@
|
|
|
88
89
|
position: sticky;
|
|
89
90
|
top: 0;
|
|
90
91
|
z-index: 10;
|
|
91
|
-
box-shadow:
|
|
92
|
+
box-shadow: var(--header-shadow);
|
|
92
93
|
flex-shrink: 0;
|
|
93
94
|
}
|
|
94
95
|
.header-inner { display: flex; align-items: center; gap: 12px; }
|
|
@@ -148,11 +149,12 @@
|
|
|
148
149
|
}
|
|
149
150
|
@media (prefers-reduced-motion: reduce) {
|
|
150
151
|
.ha-toast, .ha-toast.leaving { animation: none; }
|
|
152
|
+
*, *::before, *::after { transition-duration: 0.01ms !important; }
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
/* ── MAIN CONTENT AREA ───────────────────────────────────────────────── */
|
|
154
156
|
main {
|
|
155
|
-
max-width:
|
|
157
|
+
max-width: 1280px;
|
|
156
158
|
margin: 0 auto;
|
|
157
159
|
padding: 0 16px 32px;
|
|
158
160
|
}
|
|
@@ -168,7 +170,7 @@
|
|
|
168
170
|
|
|
169
171
|
/* ── VERSION FOOTER ──────────────────────────────────────────────────── */
|
|
170
172
|
.version-footer {
|
|
171
|
-
|
|
173
|
+
max-width: 1280px; margin: 32px auto 0; padding: 12px 0; font-size: 0.75rem;
|
|
172
174
|
color: var(--text-secondary); text-align: center;
|
|
173
175
|
border-top: 1px solid var(--border); opacity: 0.7;
|
|
174
176
|
}
|
|
@@ -359,6 +361,7 @@
|
|
|
359
361
|
.badge.write { background: var(--badge-write-bg); color: var(--badge-write-text); }
|
|
360
362
|
.badge.destructive { background: var(--badge-destructive-bg); color: var(--badge-destructive-text); }
|
|
361
363
|
.badge.mandatory { background: var(--badge-mandatory-bg); color: var(--badge-mandatory-text); }
|
|
364
|
+
.badge.unknown { background: var(--surface-hover); color: var(--text-secondary); }
|
|
362
365
|
|
|
363
366
|
/* ── TOGGLE GROUPS & SWITCH ──────────────────────────────────────────── */
|
|
364
367
|
.tool-toggles { display: flex; gap: 16px; align-items: center; }
|
|
@@ -758,18 +761,32 @@
|
|
|
758
761
|
|
|
759
762
|
/* ── THEME CONTROL (in header) ───────────────────────────────────────── */
|
|
760
763
|
.theme-control { display: inline-flex; align-items: center; gap: 6px; }
|
|
761
|
-
.theme-control-label { font-size: 0.75rem; color:
|
|
764
|
+
.theme-control-label { font-size: 0.75rem; color: var(--header-text); opacity: 0.7; }
|
|
762
765
|
.theme-toggle {
|
|
763
|
-
background:
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
766
|
+
background: color-mix(in srgb, var(--header-text) 12%, transparent);
|
|
767
|
+
color: var(--header-text);
|
|
768
|
+
border: 1px solid color-mix(in srgb, var(--header-text) 25%, transparent);
|
|
769
|
+
border-radius: 4px;
|
|
770
|
+
padding: 4px 8px; font-size: 0.8rem; font-family: inherit; cursor: pointer;
|
|
771
|
+
}
|
|
772
|
+
/* The closed control sits on the header and uses the header text colour, so
|
|
773
|
+
it stays legible whether the header is dark (dark theme) or light (light
|
|
774
|
+
theme). The native <option> popup follows the page color-scheme, not the
|
|
775
|
+
header, so pin it to the surface palette; that keeps the options legible
|
|
776
|
+
in every theme regardless of the header text colour. */
|
|
777
|
+
.theme-toggle option { background: var(--surface); color: var(--text); }
|
|
778
|
+
.theme-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
768
779
|
|
|
769
780
|
/* ── LIGHT THEME ─────────────────────────────────────────────────────── */
|
|
770
781
|
:root[data-theme="light"] {
|
|
771
782
|
color-scheme: light;
|
|
772
783
|
--bg: #fafafa;
|
|
784
|
+
/* Light header to match the original UI — the #1695 redesign left the
|
|
785
|
+
header dark (#455a64) in both themes, which looked out of place in
|
|
786
|
+
light mode. A white header + hairline divider matches the page. */
|
|
787
|
+
--header-bg: #ffffff;
|
|
788
|
+
--header-text: #212121;
|
|
789
|
+
--header-shadow: 0 1px 0 var(--border);
|
|
773
790
|
--surface: #ffffff;
|
|
774
791
|
--surface-hover: #f5f5f5;
|
|
775
792
|
--text: #212121;
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
/favicon.ico (which would 404 and log a console error, since the
|
|
9
9
|
settings server serves no such asset in any deployment mode). -->
|
|
10
10
|
<link rel="icon" href="data:,">
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
<!-- Fonts are intentionally NOT loaded from a CDN: ha-mcp must stay self-contained
|
|
12
|
+
and work offline / air-gapped (no phoning out to Google on every load). The
|
|
13
|
+
font-family stack in settings.css falls back to Roboto-if-installed then the
|
|
14
|
+
system sans-serif. -->
|
|
14
15
|
<script data-purpose="server-prefs" data-prefs="__HA_MCP_THEME_PREFS__">
|
|
15
16
|
// #1574 review: theme/accessibility prefs also persist server-side
|
|
16
17
|
// (theme_prefs.json) because stdio respawns its settings sidecar on a
|
|
@@ -161,44 +162,45 @@
|
|
|
161
162
|
</div>
|
|
162
163
|
<div class="restart-notice" id="restartNotice">
|
|
163
164
|
<span class="restart-notice-text" id="restartNoticeText">
|
|
164
|
-
⚠ Changes saved. Restart
|
|
165
|
-
startup,
|
|
165
|
+
⚠ Changes saved. Restart HA-MCP for them to take effect. On next
|
|
166
|
+
startup, HA-MCP fully removes disabled tools from the MCP tool list.
|
|
166
167
|
Then reconnect or refresh the MCP server in your AI client (e.g.
|
|
167
|
-
re-add/refresh the connector in
|
|
168
|
-
Desktop) so it reloads the tool
|
|
169
|
-
Assistant does NOT refresh your
|
|
168
|
+
refresh tool list on claude.ai, re-add/refresh the connector in
|
|
169
|
+
ChatGPT, or close and reopen Claude Desktop) so it reloads the tool
|
|
170
|
+
list. Restarting the App (add-on) or Home Assistant does NOT refresh your
|
|
171
|
+
client's cached tool list.
|
|
170
172
|
</span>
|
|
171
|
-
<button class="restart-btn" id="restartBtn" style="display:none">Restart
|
|
173
|
+
<button class="restart-btn" id="restartBtn" style="display:none">Restart App (add-on)</button>
|
|
172
174
|
</div>
|
|
173
175
|
<div class="panel active" id="panel-tools" role="tabpanel" aria-labelledby="tab-tools" tabindex="0">
|
|
174
176
|
<div class="readonly-notice">
|
|
175
177
|
Server-wide features (Tool Search, YAML config editing, filesystem
|
|
176
178
|
tools, etc.) appear in both the <strong>Server Settings</strong>
|
|
177
|
-
tab and the add-on Configuration page; they're the same settings
|
|
179
|
+
tab and the App (add-on) Configuration page; they're the same settings
|
|
178
180
|
either way. Either surface stays in sync with the other after the
|
|
179
|
-
|
|
181
|
+
App (add-on) restarts. Changes require HA-MCP to be restarted.
|
|
180
182
|
</div>
|
|
181
183
|
<div class="pin-notice show" id="pinNotice">
|
|
182
184
|
Tools listed in the <code>DISABLED_TOOLS</code> or <code>PINNED_TOOLS</code>
|
|
183
185
|
env vars are locked read-only; unset the env var to edit them here.
|
|
184
186
|
Pin toggles only take effect when Tool Search is enabled (Server Settings tab
|
|
185
|
-
or add-on Configuration page).
|
|
187
|
+
or App (add-on) Configuration page).
|
|
186
188
|
</div>
|
|
187
189
|
<div class="summary" id="summary"></div>
|
|
188
190
|
<div class="feature-row" id="readOnlyModeRow">
|
|
189
191
|
<div class="feature-info">
|
|
190
|
-
<div class="feature-name">Read Only Mode</div>
|
|
192
|
+
<div class="feature-name" id="read-only-mode-label">Read Only Mode</div>
|
|
191
193
|
<div class="feature-help">
|
|
192
194
|
Turns off all write tools and blocks every tool from making
|
|
193
195
|
write or destructive calls. Mixed read/write tools
|
|
194
|
-
(backups, add-ons, energy preferences, voice pipelines, and code
|
|
196
|
+
(backups, Apps (add-ons), energy preferences, voice pipelines, and code
|
|
195
197
|
mode when enabled) stay available with their write operations
|
|
196
198
|
blocked.
|
|
197
199
|
</div>
|
|
198
200
|
</div>
|
|
199
201
|
<div class="feature-control">
|
|
200
202
|
<label class="switch">
|
|
201
|
-
<input type="checkbox" id="read-only-mode-toggle">
|
|
203
|
+
<input type="checkbox" id="read-only-mode-toggle" aria-labelledby="read-only-mode-label">
|
|
202
204
|
<span class="slider"></span>
|
|
203
205
|
</label>
|
|
204
206
|
</div>
|
|
@@ -207,7 +209,7 @@
|
|
|
207
209
|
Could not read server settings. Read Only Mode status unknown; this
|
|
208
210
|
view may not reflect what the server enforces.
|
|
209
211
|
</div>
|
|
210
|
-
<input type="search" class="search" id="search" placeholder="Search tools..."
|
|
212
|
+
<input type="search" class="search" id="search" placeholder="Search tools..." aria-label="Search tools"
|
|
211
213
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
|
|
212
214
|
<div id="groups"></div>
|
|
213
215
|
</div>
|
|
@@ -218,10 +220,10 @@
|
|
|
218
220
|
</svg>
|
|
219
221
|
<div class="ha-alert-body">
|
|
220
222
|
Settings on this tab save automatically. Changes take effect only after
|
|
221
|
-
you restart the add-on (applies them server-side) and reconnect or
|
|
223
|
+
you restart the App (add-on) (applies them server-side) and reconnect or
|
|
222
224
|
refresh the MCP server in your AI client (reloads the tool list), e.g.
|
|
223
|
-
re-add or refresh the connector in
|
|
224
|
-
Desktop.
|
|
225
|
+
refresh tool list on claude.ai, re-add or refresh the connector in
|
|
226
|
+
ChatGPT, or close and reopen Claude Desktop.
|
|
225
227
|
</div>
|
|
226
228
|
</div>
|
|
227
229
|
|
|
@@ -273,8 +275,8 @@
|
|
|
273
275
|
</div>
|
|
274
276
|
</div>
|
|
275
277
|
<div class="backup-filters">
|
|
276
|
-
<input type="text" id="backupDomain" placeholder="Domain (e.g. automation)">
|
|
277
|
-
<input type="text" id="backupEntity" placeholder="Entity ID">
|
|
278
|
+
<input type="text" id="backupDomain" placeholder="Domain (e.g. automation)" aria-label="Filter backups by domain">
|
|
279
|
+
<input type="text" id="backupEntity" placeholder="Entity ID" aria-label="Filter backups by entity ID">
|
|
278
280
|
<button id="backupRefresh">Refresh</button>
|
|
279
281
|
<button id="backupBulkDelete" class="danger">Bulk delete matching…</button>
|
|
280
282
|
</div>
|
|
@@ -293,17 +295,17 @@
|
|
|
293
295
|
<h3 style="font-size:1rem;margin-bottom:8px">Global settings</h3>
|
|
294
296
|
<div class="feature-row">
|
|
295
297
|
<div class="feature-info">
|
|
296
|
-
<div class="feature-name">Enable Tool Security Policies</div>
|
|
298
|
+
<div class="feature-name" id="policy-master-label">Enable Tool Security Policies</div>
|
|
297
299
|
<div class="feature-help">
|
|
298
300
|
Master switch. Mirrors the toggle in Server Settings. Off by
|
|
299
|
-
default. Toggle on and restart the
|
|
301
|
+
default. Toggle on and restart the App (add-on) to activate the
|
|
300
302
|
gating middleware. While off, the rules below persist but
|
|
301
303
|
aren't enforced.
|
|
302
304
|
</div>
|
|
303
305
|
</div>
|
|
304
306
|
<div class="feature-control">
|
|
305
307
|
<label class="switch">
|
|
306
|
-
<input type="checkbox" id="policy-master-toggle">
|
|
308
|
+
<input type="checkbox" id="policy-master-toggle" aria-labelledby="policy-master-label">
|
|
307
309
|
<span class="slider"></span>
|
|
308
310
|
</label>
|
|
309
311
|
</div>
|
|
@@ -314,7 +316,7 @@
|
|
|
314
316
|
<div class="feature-help">How long the middleware waits for an approval before timing out.</div>
|
|
315
317
|
</div>
|
|
316
318
|
<div class="feature-control">
|
|
317
|
-
<input type="number" id="policy-wait-seconds" min="5" max="600">
|
|
319
|
+
<input type="number" id="policy-wait-seconds" min="5" max="600" aria-label="Wait seconds (5-600)">
|
|
318
320
|
</div>
|
|
319
321
|
</div>
|
|
320
322
|
<div class="feature-row">
|
|
@@ -323,7 +325,7 @@
|
|
|
323
325
|
<div class="feature-help">How long a pending approval stays in the queue before expiring.</div>
|
|
324
326
|
</div>
|
|
325
327
|
<div class="feature-control">
|
|
326
|
-
<input type="number" id="policy-ttl-minutes" min="1" max="60">
|
|
328
|
+
<input type="number" id="policy-ttl-minutes" min="1" max="60" aria-label="Approval TTL minutes (1-60)">
|
|
327
329
|
</div>
|
|
328
330
|
</div>
|
|
329
331
|
<div style="margin-top:10px; display:flex; align-items:center; gap:12px">
|
|
@@ -403,14 +405,18 @@
|
|
|
403
405
|
</div>
|
|
404
406
|
</main>
|
|
405
407
|
<div class="modal-backdrop" id="modalBackdrop">
|
|
406
|
-
<div class="modal">
|
|
408
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" tabindex="-1">
|
|
407
409
|
<div class="modal-header">
|
|
408
410
|
<span class="modal-title" id="modalTitle"></span>
|
|
409
|
-
<button class="modal-close" id="modalClose">×</button>
|
|
411
|
+
<button class="modal-close" id="modalClose" aria-label="Close">×</button>
|
|
410
412
|
</div>
|
|
411
413
|
<div class="modal-body" id="modalBody"></div>
|
|
412
414
|
</div>
|
|
413
415
|
</div>
|
|
416
|
+
<!-- Static toast container: present in the DOM at load so the toast (which is
|
|
417
|
+
itself the ARIA live region) is appended into a stable parent instead of
|
|
418
|
+
being created from scratch on the first toast. -->
|
|
419
|
+
<div id="ha-toast-region"></div>
|
|
414
420
|
<script>__HA_MCP_JS__</script>
|
|
415
421
|
<footer id="versionFooter" class="version-footer">
|
|
416
422
|
<span id="versionFooterText"></span>
|