ha-mcp-dev 7.10.0.dev779__tar.gz → 7.11.0.dev1825__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.10.0.dev779/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.11.0.dev1825}/PKG-INFO +1 -1
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/pyproject.toml +1 -1
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/_version.py +12 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/config.py +16 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/settings_ui/__init__.py +71 -2
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/settings_ui/settings.html +7 -1
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/settings_ui/settings.js +35 -1
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_bug_report.py +104 -10
- ha_mcp_dev-7.11.0.dev1825/src/ha_mcp/tools/tools_dev.py +954 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/update_check.py +9 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/LICENSE +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/README.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/setup.cfg +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/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.
|
|
7
|
+
version = "7.11.0.dev1825"
|
|
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
|
|
@@ -30,6 +30,18 @@ def get_version() -> str:
|
|
|
30
30
|
"""
|
|
31
31
|
if override := os.environ.get("HA_MCP_BUILD_VERSION"):
|
|
32
32
|
return override
|
|
33
|
+
# Prefer the distribution that actually OWNS the installed ha_mcp package.
|
|
34
|
+
# Both channel dists (ha-mcp / ha-mcp-dev) can leave metadata behind — an
|
|
35
|
+
# interrupted channel switch, a best-effort uninstall that failed — and a
|
|
36
|
+
# fixed name order then reports the leftover dist's version instead of the
|
|
37
|
+
# one whose files are really installed.
|
|
38
|
+
try:
|
|
39
|
+
owners = importlib.metadata.packages_distributions().get("ha_mcp", [])
|
|
40
|
+
unique = sorted(set(owners))
|
|
41
|
+
if len(unique) == 1:
|
|
42
|
+
return importlib.metadata.version(unique[0])
|
|
43
|
+
except Exception as exc:
|
|
44
|
+
logger.debug("packages_distributions probe failed: %s", exc)
|
|
33
45
|
for pkg_name in ("ha-mcp", "ha-mcp-dev"):
|
|
34
46
|
try:
|
|
35
47
|
return importlib.metadata.version(pkg_name)
|
|
@@ -256,6 +256,15 @@ class Settings(BaseSettings):
|
|
|
256
256
|
"", alias="HAMCP_DASHBOARD_SCREENSHOT_ENGINE_URL"
|
|
257
257
|
)
|
|
258
258
|
|
|
259
|
+
# Developer mode (issue #1775) — registers the hidden ha_dev_* tools
|
|
260
|
+
# (server update/restart, direct settings editing). Deliberately NOT a
|
|
261
|
+
# beta flag: it is a development aid, not a feature preview, and must
|
|
262
|
+
# not ride the beta master gate. The toggle renders in its own
|
|
263
|
+
# "Developer" section at the very bottom of the web UI's Server
|
|
264
|
+
# Settings tab; it is intentionally absent from the add-on config
|
|
265
|
+
# schemas so it stays out of the add-on Configuration page.
|
|
266
|
+
enable_dev_mode: bool = Field(False, alias="HAMCP_ENABLE_DEV_MODE")
|
|
267
|
+
|
|
259
268
|
# Code Mode — sandboxed Python execution via pydantic-monty.
|
|
260
269
|
# Provides an "escape hatch" tool (ha_manage_custom_tool) that lets LLMs write
|
|
261
270
|
# custom one-off Python code when no existing tool covers the request.
|
|
@@ -556,6 +565,7 @@ AdvancedSection = Literal[
|
|
|
556
565
|
"tools_surface",
|
|
557
566
|
"sidecar",
|
|
558
567
|
"beta_codemode",
|
|
568
|
+
"developer",
|
|
559
569
|
]
|
|
560
570
|
|
|
561
571
|
|
|
@@ -814,6 +824,12 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
|
|
|
814
824
|
"beta_codemode",
|
|
815
825
|
True,
|
|
816
826
|
),
|
|
827
|
+
# Developer mode (issue #1775). Lives in ADVANCED_SETTINGS_FIELDS —
|
|
828
|
+
# not FEATURE_FLAG_FIELDS — so it renders in its own "Developer"
|
|
829
|
+
# section at the very bottom of the Server Settings tab instead of
|
|
830
|
+
# among the feature toggles, and stays independent of the beta
|
|
831
|
+
# master gate.
|
|
832
|
+
AdvancedField("enable_dev_mode", "HAMCP_ENABLE_DEV_MODE", bool, "developer", True),
|
|
817
833
|
)
|
|
818
834
|
|
|
819
835
|
|
|
@@ -27,7 +27,7 @@ import httpx
|
|
|
27
27
|
from starlette.requests import Request
|
|
28
28
|
from starlette.responses import HTMLResponse, JSONResponse, Response
|
|
29
29
|
|
|
30
|
-
from .._version import get_version, is_running_in_addon
|
|
30
|
+
from .._version import get_version, is_embedded, is_running_in_addon
|
|
31
31
|
from ..backup_manager import get_backup_manager
|
|
32
32
|
from ..client.supervisor_client import make_supervisor_httpx_client
|
|
33
33
|
from ..config import (
|
|
@@ -1502,6 +1502,62 @@ def build_settings_handlers(
|
|
|
1502
1502
|
)
|
|
1503
1503
|
|
|
1504
1504
|
async def _restart_addon(request: Request) -> JSONResponse:
|
|
1505
|
+
# Embedded (in-process custom-component server): "restart" means
|
|
1506
|
+
# reloading the ha_mcp_tools server config entry. Checked before the
|
|
1507
|
+
# Supervisor path — the HA core container carries SUPERVISOR_TOKEN,
|
|
1508
|
+
# but the Supervisor add-on API is not ours to call from here, and
|
|
1509
|
+
# post-reload the new worker imports the freshly installed code
|
|
1510
|
+
# (the manager purges the module cache per start).
|
|
1511
|
+
if server is not None and is_embedded():
|
|
1512
|
+
from ..tools.tools_dev import (
|
|
1513
|
+
abort_options_flow_quietly,
|
|
1514
|
+
find_server_config_entry,
|
|
1515
|
+
schedule_deferred_entry_reload,
|
|
1516
|
+
)
|
|
1517
|
+
|
|
1518
|
+
try:
|
|
1519
|
+
found = await find_server_config_entry(server.client)
|
|
1520
|
+
except Exception as exc:
|
|
1521
|
+
# A discovery FAILURE is not "entry not found" — masking a
|
|
1522
|
+
# WS/connection hiccup as not-found steers users toward
|
|
1523
|
+
# reinstalling a running component. Also: the restart JS
|
|
1524
|
+
# treats any 5xx as "restart in flight" and starts its
|
|
1525
|
+
# poll-reload cycle, so a restart that was never initiated
|
|
1526
|
+
# must answer BELOW 500.
|
|
1527
|
+
logger.warning("Embedded restart: entry discovery failed: %s", exc)
|
|
1528
|
+
return JSONResponse(
|
|
1529
|
+
create_error_response(
|
|
1530
|
+
ErrorCode.CONNECTION_FAILED,
|
|
1531
|
+
"Could not reach Home Assistant to locate the "
|
|
1532
|
+
f"in-process server's config entry: {exc}",
|
|
1533
|
+
suggestions=[
|
|
1534
|
+
"Retry once Home Assistant is responsive",
|
|
1535
|
+
],
|
|
1536
|
+
),
|
|
1537
|
+
status_code=409,
|
|
1538
|
+
)
|
|
1539
|
+
if found is None:
|
|
1540
|
+
return JSONResponse(
|
|
1541
|
+
create_error_response(
|
|
1542
|
+
ErrorCode.INTERNAL_ERROR,
|
|
1543
|
+
"Could not locate the in-process server's config "
|
|
1544
|
+
"entry to reload",
|
|
1545
|
+
suggestions=[
|
|
1546
|
+
"Reload the HA-MCP integration from Settings > "
|
|
1547
|
+
"Devices & Services instead",
|
|
1548
|
+
],
|
|
1549
|
+
),
|
|
1550
|
+
# Below 500 on purpose: no restart was initiated, and the
|
|
1551
|
+
# restart JS interprets 5xx as restart-in-flight.
|
|
1552
|
+
status_code=409,
|
|
1553
|
+
)
|
|
1554
|
+
entry_id, flow, _options = found
|
|
1555
|
+
await abort_options_flow_quietly(server.client, flow)
|
|
1556
|
+
schedule_deferred_entry_reload(server.client, entry_id)
|
|
1557
|
+
return JSONResponse(
|
|
1558
|
+
{"success": True, "message": "In-process server reload scheduled"}
|
|
1559
|
+
)
|
|
1560
|
+
|
|
1505
1561
|
# The sidecar process (server is None) has no Supervisor context
|
|
1506
1562
|
# and no live server settings — refuse cleanly. The HTTP modes
|
|
1507
1563
|
# that do pass a server still go through the SUPERVISOR_TOKEN
|
|
@@ -1628,14 +1684,27 @@ def build_settings_handlers(
|
|
|
1628
1684
|
# either channel; standalone Docker / uvx falls back to the
|
|
1629
1685
|
# installed package's metadata.
|
|
1630
1686
|
try:
|
|
1631
|
-
|
|
1687
|
+
# Executor: get_version's distribution-ownership scan reads
|
|
1688
|
+
# metadata for every installed package — too heavy for the
|
|
1689
|
+
# event loop on an endpoint the restart cycle polls.
|
|
1690
|
+
version = await asyncio.to_thread(get_version)
|
|
1632
1691
|
except Exception: # pragma: no cover — defensive only
|
|
1633
1692
|
logger.warning("get_version() raised; omitting version from info")
|
|
1634
1693
|
version = None
|
|
1694
|
+
# ``deployment_mode`` reuses the bug-report detector so the UI and
|
|
1695
|
+
# ha_report_issue can never disagree about where the server runs —
|
|
1696
|
+
# the embedded (in-process custom component) server would otherwise
|
|
1697
|
+
# read as plain "docker" here (/.dockerenv exists in the HA core
|
|
1698
|
+
# container).
|
|
1699
|
+
from ..tools.tools_bug_report import _detect_installation_method
|
|
1700
|
+
|
|
1635
1701
|
return JSONResponse(
|
|
1636
1702
|
{
|
|
1637
1703
|
"is_addon": addon,
|
|
1638
1704
|
"is_sidecar": is_sidecar,
|
|
1705
|
+
"deployment_mode": (
|
|
1706
|
+
"sidecar" if is_sidecar else _detect_installation_method()
|
|
1707
|
+
),
|
|
1639
1708
|
"instance_id": _PROCESS_INSTANCE_ID,
|
|
1640
1709
|
"started_at": _PROCESS_STARTED_AT,
|
|
1641
1710
|
"version": version,
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
<button class="tab" data-panel="entity-visibility" role="tab" id="tab-entity-visibility" aria-controls="panel-entity-visibility" aria-selected="false" tabindex="-1">Entity Visibility</button>
|
|
162
162
|
<button class="tab" data-panel="accessibility" role="tab" id="tab-accessibility" aria-controls="panel-accessibility" aria-selected="false" tabindex="-1">Accessibility</button>
|
|
163
163
|
</div>
|
|
164
|
-
<div class="restart-notice" id="restartNotice">
|
|
164
|
+
<div class="restart-notice" id="restartNotice" role="status" aria-live="polite">
|
|
165
165
|
<span class="restart-notice-text" id="restartNoticeText">
|
|
166
166
|
⚠ Changes saved. Restart HA-MCP for them to take effect. On next
|
|
167
167
|
startup, HA-MCP fully removes disabled tools from the MCP tool list.
|
|
@@ -265,6 +265,12 @@
|
|
|
265
265
|
title="Permanently disables the settings UI: stops this server AND writes ~/.ha-mcp/settings_ui_disabled so it does not respawn on future ha-mcp launches. Delete that file to re-enable."
|
|
266
266
|
>Permanently disable settings server</button>
|
|
267
267
|
</div>
|
|
268
|
+
|
|
269
|
+
<!-- Developer section sits at the ABSOLUTE bottom of the panel by
|
|
270
|
+
design (issue #1775): the dev-mode toggle registers hidden
|
|
271
|
+
ha_dev_* tools and should not be obvious to casual users. -->
|
|
272
|
+
<h2 class="adv-section-title beta-section-title">Developer</h2>
|
|
273
|
+
<div id="advDeveloper" class="adv-section"></div>
|
|
268
274
|
</div>
|
|
269
275
|
<div class="panel" id="panel-backups" role="tabpanel" aria-labelledby="tab-backups" tabindex="0">
|
|
270
276
|
<div class="backup-state" id="backupState">Loading backup state…</div>
|
|
@@ -217,6 +217,22 @@ async function applyInfoChrome() {
|
|
|
217
217
|
'Desktop. Restarting the App (add-on) alone does not refresh your ' +
|
|
218
218
|
'client\'s cached tool list.';
|
|
219
219
|
}
|
|
220
|
+
} else if (info.deployment_mode === 'embedded') {
|
|
221
|
+
// In-process (custom component) server: the restart endpoint reloads
|
|
222
|
+
// the server config entry, which reinstalls-if-newer and swaps the
|
|
223
|
+
// worker onto the freshly installed code.
|
|
224
|
+
const rbtn = document.getElementById('restartBtn');
|
|
225
|
+
rbtn.style.display = '';
|
|
226
|
+
rbtn.textContent = 'Restart HA-MCP Server';
|
|
227
|
+
if (noticeEl) {
|
|
228
|
+
noticeEl.textContent =
|
|
229
|
+
'⚠ Changes saved. Click "Restart HA-MCP Server" (reloads the ' +
|
|
230
|
+
'in-process server integration) for them to take effect. ' +
|
|
231
|
+
'Disabled tools will be fully removed from the MCP tool list on ' +
|
|
232
|
+
'next startup. Then refresh the tool list in your AI client — ' +
|
|
233
|
+
'e.g. refresh tool list on claude.ai, re-add or refresh the ' +
|
|
234
|
+
'connector in ChatGPT, or close and reopen Claude Desktop.';
|
|
235
|
+
}
|
|
220
236
|
} else if (info.is_sidecar) {
|
|
221
237
|
if (noticeEl) {
|
|
222
238
|
noticeEl.textContent =
|
|
@@ -414,7 +430,7 @@ async function _runRestartReloadCycle(previousInstanceId) {
|
|
|
414
430
|
async function restartAddon() {
|
|
415
431
|
if (restartInProgress) return;
|
|
416
432
|
const btn = document.getElementById('restartBtn');
|
|
417
|
-
if (!confirm('Restart
|
|
433
|
+
if (!confirm('Restart HA-MCP now? The page will reload automatically once it is back online.')) return;
|
|
418
434
|
restartInProgress = true;
|
|
419
435
|
btn.disabled = true;
|
|
420
436
|
btn.textContent = 'Restarting…';
|
|
@@ -2946,6 +2962,7 @@ const ADVANCED_FIELD_META = {
|
|
|
2946
2962
|
code_mode_max_invocations: { label: "Code-mode max invocations", help: "API/tool-call cap per sandbox run. Restart required." },
|
|
2947
2963
|
code_mode_saved_tools_path:{ label: "Saved-tools path", help: "JSON file where ha_manage_custom_tool persists saved tools across restarts. Restart required." },
|
|
2948
2964
|
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." },
|
|
2965
|
+
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." },
|
|
2949
2966
|
};
|
|
2950
2967
|
|
|
2951
2968
|
// Fields that require an MCP-host restart to take effect when changed
|
|
@@ -2974,6 +2991,9 @@ const ADVANCED_RESTART_REQUIRED = new Set([
|
|
|
2974
2991
|
// The sidecar binds its port once at spawn (run_main), so changing the
|
|
2975
2992
|
// pin needs a restart to respawn the sidecar on the new port.
|
|
2976
2993
|
"sidecar_pin_port",
|
|
2994
|
+
// Dev-mode tools register at startup; toggling needs a restart to
|
|
2995
|
+
// (un)register them.
|
|
2996
|
+
"enable_dev_mode",
|
|
2977
2997
|
]);
|
|
2978
2998
|
|
|
2979
2999
|
let _advancedFields = [];
|
|
@@ -3039,6 +3059,7 @@ async function loadAdvancedSettings() {
|
|
|
3039
3059
|
renderAdvancedSection('advToolsSurface', bySection.tools_surface || []);
|
|
3040
3060
|
renderAdvancedSection('advDiagnostics', bySection.diagnostics || []);
|
|
3041
3061
|
renderAdvancedSection('advSidecar', bySection.sidecar || []);
|
|
3062
|
+
renderAdvancedSection('advDeveloper', bySection.developer || []);
|
|
3042
3063
|
applySidecarAvailability(data.is_stdio !== false);
|
|
3043
3064
|
// Re-render feature flags so the code_mode sub-numerics show up
|
|
3044
3065
|
// beneath enable_code_mode (race: loadFeatureFlags may have run
|
|
@@ -3138,6 +3159,19 @@ function renderAdvancedSection(containerId, fields) {
|
|
|
3138
3159
|
const fname = input.dataset.advField;
|
|
3139
3160
|
const f = _advancedFields.find(x => x.field === fname);
|
|
3140
3161
|
if (!f) return;
|
|
3162
|
+
// Dev mode arms tools that can rewrite server settings and swap
|
|
3163
|
+
// the running server version — confirm before enabling, matching
|
|
3164
|
+
// the stopSidecar / restart danger-action convention.
|
|
3165
|
+
if (fname === 'enable_dev_mode' && input.checked && !confirm(
|
|
3166
|
+
'⚠ Enable developer mode?\n\n'
|
|
3167
|
+
+ 'After the next restart, hidden developer tools are exposed to '
|
|
3168
|
+
+ 'connected AI agents. They can change server settings and '
|
|
3169
|
+
+ 'replace the running server version. Only enable this for '
|
|
3170
|
+
+ 'development and testing.'
|
|
3171
|
+
)) {
|
|
3172
|
+
input.checked = false;
|
|
3173
|
+
return;
|
|
3174
|
+
}
|
|
3141
3175
|
let v;
|
|
3142
3176
|
if (input.type === 'checkbox') v = input.checked;
|
|
3143
3177
|
else if (input.type === 'number') v = (f.type === 'float') ? parseFloat(input.value) : parseInt(input.value, 10);
|
|
@@ -5,11 +5,14 @@ This module provides a tool to collect diagnostic information and guide users
|
|
|
5
5
|
on how to create effective bug reports.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import asyncio
|
|
9
|
+
import importlib
|
|
8
10
|
import logging
|
|
9
11
|
import os
|
|
10
12
|
import platform
|
|
11
13
|
import re
|
|
12
14
|
import sys
|
|
15
|
+
import time
|
|
13
16
|
from pathlib import Path
|
|
14
17
|
from typing import Annotated, Any
|
|
15
18
|
from urllib.parse import quote_plus
|
|
@@ -21,7 +24,7 @@ from pydantic import Field
|
|
|
21
24
|
|
|
22
25
|
from ha_mcp import __version__
|
|
23
26
|
|
|
24
|
-
from .._version import is_running_in_addon
|
|
27
|
+
from .._version import get_version, is_embedded, is_running_in_addon
|
|
25
28
|
from ..client.supervisor_client import make_supervisor_httpx_client
|
|
26
29
|
from ..config import Settings, get_global_settings
|
|
27
30
|
from ..utils.usage_logger import (
|
|
@@ -72,21 +75,28 @@ def _detect_installation_method() -> str:
|
|
|
72
75
|
"""
|
|
73
76
|
Detect how ha-mcp was installed.
|
|
74
77
|
|
|
75
|
-
Returns one of: pyinstaller, addon, docker, git, pypi, unknown
|
|
78
|
+
Returns one of: pyinstaller, embedded, addon, docker, git, pypi, unknown
|
|
76
79
|
"""
|
|
77
80
|
# 1. PyInstaller binary
|
|
78
81
|
if getattr(sys, "frozen", False):
|
|
79
82
|
return "pyinstaller"
|
|
80
83
|
|
|
81
|
-
# 2.
|
|
84
|
+
# 2. In-process server inside HA core (the ha_mcp_tools custom
|
|
85
|
+
# component's "server" entry). Checked BEFORE the docker probe: the
|
|
86
|
+
# HA core container carries /.dockerenv, so without this branch
|
|
87
|
+
# embedded installs misreport as plain docker.
|
|
88
|
+
if is_embedded():
|
|
89
|
+
return "embedded"
|
|
90
|
+
|
|
91
|
+
# 3. Home Assistant Add-on (has supervisor token)
|
|
82
92
|
if is_running_in_addon():
|
|
83
93
|
return "addon"
|
|
84
94
|
|
|
85
|
-
#
|
|
95
|
+
# 4. Docker container (non-addon)
|
|
86
96
|
if Path("/.dockerenv").exists():
|
|
87
97
|
return "docker"
|
|
88
98
|
|
|
89
|
-
#
|
|
99
|
+
# 5. Git clone - check for .git directory relative to package
|
|
90
100
|
try:
|
|
91
101
|
# Go up from tools_bug_report.py -> tools -> ha_mcp -> src -> project_root
|
|
92
102
|
project_root = Path(__file__).parent.parent.parent.parent
|
|
@@ -97,7 +107,7 @@ def _detect_installation_method() -> str:
|
|
|
97
107
|
# fall through to the next detection heuristic.
|
|
98
108
|
pass
|
|
99
109
|
|
|
100
|
-
#
|
|
110
|
+
# 6. PyPI install - marker file exists in package
|
|
101
111
|
try:
|
|
102
112
|
marker_path = Path(__file__).parent.parent / "_pypi_marker"
|
|
103
113
|
if marker_path.exists():
|
|
@@ -107,10 +117,62 @@ def _detect_installation_method() -> str:
|
|
|
107
117
|
# fall through to the default "unknown" result.
|
|
108
118
|
pass
|
|
109
119
|
|
|
110
|
-
#
|
|
120
|
+
# 7. Default - unknown
|
|
111
121
|
return "unknown"
|
|
112
122
|
|
|
113
123
|
|
|
124
|
+
def _detect_installed_version() -> str | None:
|
|
125
|
+
"""Return the ha-mcp version installed ON DISK right now.
|
|
126
|
+
|
|
127
|
+
``__version__`` is frozen when the process first imports ha_mcp, so after
|
|
128
|
+
an in-place update the running process keeps reporting the old version
|
|
129
|
+
while the disk already carries the new one. Reporting both (plus
|
|
130
|
+
``version_mismatch``) turns a "which server am I talking to" hunt into a
|
|
131
|
+
single tool call.
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
importlib.invalidate_caches()
|
|
135
|
+
return get_version()
|
|
136
|
+
except Exception as e:
|
|
137
|
+
logger.info("Installed-version probe failed: %s", e)
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _instance_identity() -> dict[str, Any]:
|
|
142
|
+
"""Return process identity (id, start time, uptime).
|
|
143
|
+
|
|
144
|
+
Mirrors the ``/api/settings/info`` fields so a report and the web UI can
|
|
145
|
+
be matched to the same (or different) server process.
|
|
146
|
+
"""
|
|
147
|
+
from ..settings_ui import _PROCESS_INSTANCE_ID, _PROCESS_STARTED_AT
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
"instance_id": _PROCESS_INSTANCE_ID,
|
|
151
|
+
"started_at": _PROCESS_STARTED_AT,
|
|
152
|
+
"uptime_seconds": round(time.time() - _PROCESS_STARTED_AT, 1),
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _format_version_value(diagnostic_info: dict[str, Any]) -> str:
|
|
157
|
+
"""Render the version for report surfaces, flagging a stale worker.
|
|
158
|
+
|
|
159
|
+
A failed installed-version probe must stay distinguishable from
|
|
160
|
+
"checked, versions match" — otherwise the exact stale-worker
|
|
161
|
+
condition this field exists to expose disappears whenever the probe
|
|
162
|
+
itself hiccups.
|
|
163
|
+
"""
|
|
164
|
+
running = diagnostic_info.get("ha_mcp_version", "Unknown")
|
|
165
|
+
installed = diagnostic_info.get("installed_version")
|
|
166
|
+
if diagnostic_info.get("version_mismatch"):
|
|
167
|
+
return (
|
|
168
|
+
f"{running} (running) — {installed} is installed; "
|
|
169
|
+
"restart to finish applying the update"
|
|
170
|
+
)
|
|
171
|
+
if installed is None:
|
|
172
|
+
return f"{running} (installed-on-disk version could not be verified)"
|
|
173
|
+
return str(running)
|
|
174
|
+
|
|
175
|
+
|
|
114
176
|
def _detect_platform() -> dict[str, str]:
|
|
115
177
|
"""Detect platform information."""
|
|
116
178
|
return {
|
|
@@ -473,7 +535,8 @@ def _build_formatted_report(
|
|
|
473
535
|
report_lines = [
|
|
474
536
|
"=== ha-mcp Bug Report Info ===",
|
|
475
537
|
"",
|
|
476
|
-
f"ha-mcp Version: {diagnostic_info
|
|
538
|
+
f"ha-mcp Version: {_format_version_value(diagnostic_info)}",
|
|
539
|
+
f"Custom Component: {diagnostic_info.get('component_version') or 'not detected (not installed, or probe failed)'}",
|
|
477
540
|
f"Installation Method: {diagnostic_info['installation_method']}",
|
|
478
541
|
f"MCP Transport: {mcp_transport}",
|
|
479
542
|
f"MCP Client: {_format_client_info_for_template(client_info)}",
|
|
@@ -518,6 +581,27 @@ class BugReportTools:
|
|
|
518
581
|
def __init__(self, client: Any) -> None:
|
|
519
582
|
self._client = client
|
|
520
583
|
|
|
584
|
+
async def _detect_component_version(self) -> str | None:
|
|
585
|
+
"""Read the ha_mcp_tools custom component's version, best-effort.
|
|
586
|
+
|
|
587
|
+
Uses the component's ``get_caller_token`` bootstrap service, whose
|
|
588
|
+
response carries the manifest version (the same field the filesystem
|
|
589
|
+
tools' version gate reads). Returns None when the component is not
|
|
590
|
+
installed or the call fails — the report path must never break on it.
|
|
591
|
+
"""
|
|
592
|
+
try:
|
|
593
|
+
resp = await self._client.call_service(
|
|
594
|
+
"ha_mcp_tools", "get_caller_token", {}, return_response=True
|
|
595
|
+
)
|
|
596
|
+
payload = (
|
|
597
|
+
resp.get("service_response", resp) if isinstance(resp, dict) else {}
|
|
598
|
+
)
|
|
599
|
+
version = payload.get("version") if isinstance(payload, dict) else None
|
|
600
|
+
return str(version) if version else None
|
|
601
|
+
except Exception as e:
|
|
602
|
+
logger.info("Component version probe failed: %s", e)
|
|
603
|
+
return None
|
|
604
|
+
|
|
521
605
|
@tool(
|
|
522
606
|
name="ha_report_issue",
|
|
523
607
|
tags={"Utilities"},
|
|
@@ -592,9 +676,17 @@ class BugReportTools:
|
|
|
592
676
|
config_toggles = _get_config_toggles()
|
|
593
677
|
mcp_transport = _detect_mcp_transport()
|
|
594
678
|
client_info = _extract_client_info(ctx)
|
|
679
|
+
installed_version = await asyncio.to_thread(_detect_installed_version)
|
|
680
|
+
component_version = await self._detect_component_version()
|
|
595
681
|
|
|
596
682
|
diagnostic_info: dict[str, Any] = {
|
|
597
683
|
"ha_mcp_version": __version__,
|
|
684
|
+
"installed_version": installed_version,
|
|
685
|
+
"version_mismatch": bool(
|
|
686
|
+
installed_version and installed_version != __version__
|
|
687
|
+
),
|
|
688
|
+
"component_version": component_version,
|
|
689
|
+
"instance": _instance_identity(),
|
|
598
690
|
"installation_method": install_method,
|
|
599
691
|
"platform": platform_info,
|
|
600
692
|
"mcp_transport": mcp_transport,
|
|
@@ -1093,7 +1185,8 @@ ha_call_service(domain="light", service="turn_on", entity_id="light.example")
|
|
|
1093
1185
|
|
|
1094
1186
|
## 🔧 Environment
|
|
1095
1187
|
|
|
1096
|
-
- **ha-mcp Version:** {diagnostic_info
|
|
1188
|
+
- **ha-mcp Version:** {_format_version_value(diagnostic_info)}
|
|
1189
|
+
- **Custom Component:** {diagnostic_info.get("component_version") or "not detected (not installed, or probe failed)"}
|
|
1097
1190
|
- **Installation Method:** {diagnostic_info.get("installation_method", "Unknown")}
|
|
1098
1191
|
- **MCP Transport:** {mcp_transport} _(auto-detected — correct if wrong)_
|
|
1099
1192
|
- **MCP Client:** {_format_client_info_for_template(client_info)} _(auto-detected from the MCP `initialize` handshake)_
|
|
@@ -1257,7 +1350,8 @@ def _generate_agent_behavior_template(
|
|
|
1257
1350
|
|
|
1258
1351
|
## 📊 Environment
|
|
1259
1352
|
|
|
1260
|
-
- **ha-mcp Version:** {diagnostic_info
|
|
1353
|
+
- **ha-mcp Version:** {_format_version_value(diagnostic_info)}
|
|
1354
|
+
- **Custom Component:** {diagnostic_info.get("component_version") or "not detected (not installed, or probe failed)"}
|
|
1261
1355
|
- **Installation Method:** {diagnostic_info.get("installation_method", "Unknown")}
|
|
1262
1356
|
- **MCP Transport:** {mcp_transport} _(auto-detected — correct if wrong)_
|
|
1263
1357
|
- **MCP Client:** {_format_client_info_for_template(client_info)} _(auto-detected from the MCP `initialize` handshake)_
|