ha-mcp-dev 8.4.3.dev2563__tar.gz → 8.4.3.dev2564__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-8.4.3.dev2563/src/ha_mcp_dev.egg-info → ha_mcp_dev-8.4.3.dev2564}/PKG-INFO +1 -1
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/pyproject.toml +1 -1
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/config.py +10 -0
- ha_mcp_dev-8.4.3.dev2564/src/ha_mcp/ha_request_queue.py +145 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/middleware.py +2 -37
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/read_only.py +1 -1
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/server.py +9 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_advanced.py +12 -11
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/en.json +4 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/settings.js +2 -0
- ha_mcp_dev-8.4.3.dev2564/src/ha_mcp/tool_dispatch.py +39 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/enforcement.py +1 -1
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/SOURCES.txt +2 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/LICENSE +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/MANIFEST.in +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/README.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/setup.cfg +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_rich_compat.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/requirements.txt +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/LICENSE +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/MANIFEST.sha256 +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/VENDORED +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/__main__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/client.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/connection.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/messages.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/router.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/server.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/auth.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/cli.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/client.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/connection.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/datastructures.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/exceptions.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/extensions/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/extensions/base.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/extensions/permessage_deflate.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/frames.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/headers.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/http11.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/imports.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/auth.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/client.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/exceptions.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/framing.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/handshake.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/http.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/protocol.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/server.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/protocol.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/proxy.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/py.typed +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/server.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/streams.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/client.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/connection.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/messages.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/router.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/server.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/utils.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/client.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/connection.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/messages.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/router.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/server.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/utils.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/typing.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/uri.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/utils.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/version.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/auth/oidc_compat.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/hacs_auto_refresh.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/llm_exposure.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/log_filters.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/editing.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/redaction.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/renamed_tools.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/area-target-over-entity-list/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/arrive-home-automation/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/assist-shortcut-card/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/avg-temperature/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/backup-deletion-confirmation/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/blueprint-authoring/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/button-event-entity/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/device-id-trigger-migration/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/helper-flow-fields-not-platform-yaml/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/media-player-song/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/motion-light-automation-mode/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/rename-entity-impact-analysis/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/template-attributes-yaml-exception/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/timer-finished-automation/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/vacuum-last-clean/case.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/scripts/check_eval_cases.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/backups.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_i18n.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_locale_policy.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_persistence.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_theme.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/README.md +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/cs.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/de.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/eo.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/es.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/fr.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/it.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/ko.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/nl.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/pl.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/sv.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/tlh.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/strict_bps.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/best_practice_result.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/best_practice_variables.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/blueprint_sources.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/blueprint_substitute.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/blueprint_write.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/bulk_selector.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_api.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_config_entries.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_config_reads.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_devices.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_registries.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_flow_form.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_flow_menu.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_flow_walker.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_identity.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_reconfigure.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_reload_watch.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/error_log_parsing.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/hacs_registration.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/integration_reconfigure.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/log_common.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/log_sources.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/log_sources_fault.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/log_sources_supervisor.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/renamed_tool_middleware.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_graph.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_assist_pipeline.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_dev.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_logs.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_security_policy.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/config_write_lock.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/device_registry_semantics.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/entity_membership.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/tests/__init__.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/tests/test_constants.py +0 -0
- {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/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 = "8.4.3.
|
|
7
|
+
version = "8.4.3.dev2564"
|
|
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
|
|
@@ -62,6 +62,10 @@ class Settings(BaseSettings):
|
|
|
62
62
|
# Tool configuration
|
|
63
63
|
fuzzy_threshold: int = Field(60, alias="FUZZY_THRESHOLD")
|
|
64
64
|
|
|
65
|
+
# Optional process-wide outer tool-call concurrency. Zero preserves the
|
|
66
|
+
# existing unlimited behavior; constrained installs can opt into queuing.
|
|
67
|
+
ha_tool_concurrency: int = Field(0, ge=0, le=32, alias="HA_TOOL_CONCURRENCY")
|
|
68
|
+
|
|
65
69
|
# Smart-search config-fetch time budgets (seconds). Bound how long
|
|
66
70
|
# ha_search spends fetching automation/script/scene
|
|
67
71
|
# definitions during the per-id fallback before reporting a partial
|
|
@@ -890,6 +894,9 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
|
|
|
890
894
|
True,
|
|
891
895
|
),
|
|
892
896
|
# Operations.
|
|
897
|
+
AdvancedField(
|
|
898
|
+
"ha_tool_concurrency", "HA_TOOL_CONCURRENCY", int, "operations", True
|
|
899
|
+
),
|
|
893
900
|
AdvancedField("backup_hint", "BACKUP_HINT", str, "operations", True),
|
|
894
901
|
AdvancedField("enable_websocket", "ENABLE_WEBSOCKET", bool, "operations", True),
|
|
895
902
|
# Dashboard-screenshot engine URL (#1538): docker/.env users could set
|
|
@@ -1000,6 +1007,7 @@ _ADVANCED_SETTINGS_BOUNDS: dict[str, tuple[float, float]] = {
|
|
|
1000
1007
|
"scene_config_time_budget": (1.0, 600.0),
|
|
1001
1008
|
"individual_config_timeout": (1.0, 600.0),
|
|
1002
1009
|
"individual_fetch_batch_size": (1, 100),
|
|
1010
|
+
"ha_tool_concurrency": (1, 32),
|
|
1003
1011
|
"code_mode_max_duration": (1.0, 300.0),
|
|
1004
1012
|
"code_mode_max_memory": (1_048_576, 268_435_456),
|
|
1005
1013
|
"code_mode_max_recursion": (1, 10_000),
|
|
@@ -1015,6 +1023,7 @@ _ADVANCED_SETTINGS_BOUNDS: dict[str, tuple[float, float]] = {
|
|
|
1015
1023
|
# emits min=sentinel so the number input can still express "off"; the
|
|
1016
1024
|
# override-apply and UI-POST paths accept the sentinel OR the bounded range.
|
|
1017
1025
|
_ADVANCED_SETTINGS_SENTINELS: dict[str, int] = {
|
|
1026
|
+
"ha_tool_concurrency": 0,
|
|
1018
1027
|
"sidecar_pin_port": 0,
|
|
1019
1028
|
}
|
|
1020
1029
|
|
|
@@ -1036,6 +1045,7 @@ _ADVANCED_SETTINGS_CHOICES: dict[str, tuple[str, ...]] = {
|
|
|
1036
1045
|
# batches addon-origin writes and POSTs them via Supervisor.
|
|
1037
1046
|
ADDON_SYNCED_ADVANCED_FIELDS: tuple[str, ...] = (
|
|
1038
1047
|
"backup_hint",
|
|
1048
|
+
"ha_tool_concurrency",
|
|
1039
1049
|
"verify_ssl",
|
|
1040
1050
|
)
|
|
1041
1051
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Cross-session concurrency control for Home Assistant tool calls."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
from contextvars import ContextVar
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from fastmcp.server.middleware.middleware import CallNext, Middleware, MiddlewareContext
|
|
11
|
+
|
|
12
|
+
from .errors import create_timeout_error
|
|
13
|
+
from .tool_dispatch import (
|
|
14
|
+
CALL_PROXY_META_TOOLS,
|
|
15
|
+
LOCAL_ONLY_TOOLS,
|
|
16
|
+
OUTER_QUEUE_ORCHESTRATORS,
|
|
17
|
+
is_approval_management_call,
|
|
18
|
+
)
|
|
19
|
+
from .tools.helpers import raise_tool_error, safe_progress
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
_QUEUE_WAIT_TIMEOUT_SECONDS = 60.0
|
|
24
|
+
_QUEUE_WAIT_HEARTBEAT_SECONDS = 15.0
|
|
25
|
+
_QUEUE_WAIT_WARNING_SECONDS = 15.0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _bypasses_outer_queue(name: str, args: dict[str, Any]) -> bool:
|
|
29
|
+
"""Return whether an outer envelope must run without a queue slot.
|
|
30
|
+
|
|
31
|
+
Proxy and orchestration envelopes leave admission to their nested dispatches.
|
|
32
|
+
Local search does not contact Home Assistant. Approval-management actions
|
|
33
|
+
must remain available while all configured slots are occupied.
|
|
34
|
+
"""
|
|
35
|
+
return (
|
|
36
|
+
name in CALL_PROXY_META_TOOLS
|
|
37
|
+
or name in LOCAL_ONLY_TOOLS
|
|
38
|
+
or name in OUTER_QUEUE_ORCHESTRATORS
|
|
39
|
+
or is_approval_management_call(name, args)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class HomeAssistantRequestQueueMiddleware(Middleware):
|
|
44
|
+
"""Bound concurrent outer tool calls while allowing nested redispatch."""
|
|
45
|
+
|
|
46
|
+
def __init__(self, max_concurrency: int = 1) -> None:
|
|
47
|
+
if not 1 <= max_concurrency <= 32:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
"ha_tool_concurrency middleware requires a value between 1 and 32; "
|
|
50
|
+
"0 is handled by not registering the middleware"
|
|
51
|
+
)
|
|
52
|
+
self._semaphore = asyncio.Semaphore(max_concurrency)
|
|
53
|
+
self._depth: ContextVar[int] = ContextVar(
|
|
54
|
+
f"ha_request_queue_depth_{id(self)}", default=0
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
async def _acquire_slot(self, context: MiddlewareContext, name: str) -> None:
|
|
58
|
+
"""Acquire an outer-call slot with progress and a bounded wait."""
|
|
59
|
+
loop = asyncio.get_running_loop()
|
|
60
|
+
started = loop.time()
|
|
61
|
+
warned = False
|
|
62
|
+
|
|
63
|
+
if not self._semaphore.locked():
|
|
64
|
+
await self._semaphore.acquire()
|
|
65
|
+
logger.debug("HA tool queue: acquired slot tool=%s wait=0.000s", name)
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
logger.debug("HA tool queue: waiting for slot tool=%s", name)
|
|
69
|
+
|
|
70
|
+
while True:
|
|
71
|
+
elapsed = loop.time() - started
|
|
72
|
+
remaining = _QUEUE_WAIT_TIMEOUT_SECONDS - elapsed
|
|
73
|
+
if remaining <= 0:
|
|
74
|
+
logger.warning(
|
|
75
|
+
"HA tool queue: timed out waiting for slot tool=%s wait=%.1fs",
|
|
76
|
+
name,
|
|
77
|
+
elapsed,
|
|
78
|
+
)
|
|
79
|
+
raise_tool_error(
|
|
80
|
+
create_timeout_error(
|
|
81
|
+
"Home Assistant tool queue admission",
|
|
82
|
+
_QUEUE_WAIT_TIMEOUT_SECONDS,
|
|
83
|
+
details=(
|
|
84
|
+
f"Tool '{name}' did not start because all configured "
|
|
85
|
+
"Home Assistant tool slots remained busy."
|
|
86
|
+
),
|
|
87
|
+
context={"tool": name, "phase": "queue_wait"},
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
ctx = getattr(context, "fastmcp_context", None)
|
|
92
|
+
await safe_progress(
|
|
93
|
+
ctx,
|
|
94
|
+
progress=0,
|
|
95
|
+
total=0,
|
|
96
|
+
message=f"Waiting for Home Assistant tool capacity: {name}",
|
|
97
|
+
)
|
|
98
|
+
try:
|
|
99
|
+
await asyncio.wait_for(
|
|
100
|
+
self._semaphore.acquire(),
|
|
101
|
+
timeout=min(_QUEUE_WAIT_HEARTBEAT_SECONDS, remaining),
|
|
102
|
+
)
|
|
103
|
+
except TimeoutError:
|
|
104
|
+
elapsed = loop.time() - started
|
|
105
|
+
if not warned and elapsed >= _QUEUE_WAIT_WARNING_SECONDS:
|
|
106
|
+
warned = True
|
|
107
|
+
logger.warning(
|
|
108
|
+
"HA tool queue: still waiting for slot tool=%s wait=%.1fs",
|
|
109
|
+
name,
|
|
110
|
+
elapsed,
|
|
111
|
+
)
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
logger.debug(
|
|
115
|
+
"HA tool queue: acquired slot tool=%s wait=%.3fs",
|
|
116
|
+
name,
|
|
117
|
+
loop.time() - started,
|
|
118
|
+
)
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
async def on_call_tool(
|
|
122
|
+
self, context: MiddlewareContext, call_next: CallNext
|
|
123
|
+
) -> Any:
|
|
124
|
+
if _bypasses_outer_queue(context.message.name, context.message.arguments or {}):
|
|
125
|
+
return await call_next(context)
|
|
126
|
+
|
|
127
|
+
depth = self._depth.get()
|
|
128
|
+
if depth:
|
|
129
|
+
token = self._depth.set(depth + 1)
|
|
130
|
+
try:
|
|
131
|
+
return await call_next(context)
|
|
132
|
+
finally:
|
|
133
|
+
self._depth.reset(token)
|
|
134
|
+
|
|
135
|
+
name = context.message.name
|
|
136
|
+
await self._acquire_slot(context, name)
|
|
137
|
+
try:
|
|
138
|
+
token = self._depth.set(1)
|
|
139
|
+
try:
|
|
140
|
+
return await call_next(context)
|
|
141
|
+
finally:
|
|
142
|
+
self._depth.reset(token)
|
|
143
|
+
finally:
|
|
144
|
+
self._semaphore.release()
|
|
145
|
+
logger.debug("HA tool queue: released slot tool=%s", name)
|
|
@@ -13,6 +13,7 @@ from fastmcp.server.middleware.middleware import CallNext, Middleware, Middlewar
|
|
|
13
13
|
|
|
14
14
|
from ..errors import ErrorCode, create_error_response
|
|
15
15
|
from ..renamed_tools import current_tool_name
|
|
16
|
+
from ..tool_dispatch import CALL_PROXY_META_TOOLS, is_approval_management_call
|
|
16
17
|
from ..tools.helpers import raise_tool_error, safe_progress
|
|
17
18
|
from .approval_queue import ApprovalQueue, PendingApproval, compute_args_hash
|
|
18
19
|
from .evaluator import (
|
|
@@ -26,48 +27,12 @@ from .model import Policy, Rule
|
|
|
26
27
|
|
|
27
28
|
logger = logging.getLogger(__name__)
|
|
28
29
|
|
|
29
|
-
# Dispatching call proxies are a strict subset of the ungated tool-search
|
|
30
|
-
# meta-tools. Only these three execute their envelope's ``name``; search merely
|
|
31
|
-
# returns catalog metadata and must not be unwrapped by other middleware.
|
|
32
|
-
# Gating a call proxy directly would be wrong: rule predicates target the real
|
|
33
|
-
# tool's args (for example args.domain), while the proxy receives a wrapped
|
|
34
|
-
# {"name": "...", "arguments": {...}} envelope. Its dispatch re-enters the
|
|
35
|
-
# middleware chain with the real name and args, so the inner call is gated there.
|
|
36
|
-
CALL_PROXY_META_TOOLS = frozenset(
|
|
37
|
-
{
|
|
38
|
-
"ha_call_read_tool",
|
|
39
|
-
"ha_call_write_tool",
|
|
40
|
-
"ha_call_delete_tool",
|
|
41
|
-
}
|
|
42
|
-
)
|
|
43
|
-
# Policy itself also leaves catalog search ungated; unlike the call proxies it
|
|
44
|
-
# never dispatches a tool named in client-supplied arguments.
|
|
45
30
|
PROXY_META_TOOLS = CALL_PROXY_META_TOOLS | {"ha_search_tools"}
|
|
46
31
|
|
|
47
|
-
# ha_dev_manage_server actions that MANAGE the approval queue itself.
|
|
48
|
-
# Gating these deadlocks by construction: with a wildcard (or
|
|
49
|
-
# ha_dev_manage_server) rule in place, an MCP-only "approve" call would
|
|
50
|
-
# itself require approval — creating a second pending entry instead of
|
|
51
|
-
# deciding the first, so nothing can ever be approved through the tool.
|
|
52
|
-
# Only the queue-management actions are exempt; update_source / restart
|
|
53
|
-
# remain gateable like any other high-stakes action. The exemption is not
|
|
54
|
-
# a free self-approval: approve/deny separately require the
|
|
55
|
-
# dev_tools_security_policy_access setting (off by default, issue #2141).
|
|
56
|
-
_APPROVAL_MANAGEMENT_TOOL = "ha_dev_manage_server"
|
|
57
|
-
_APPROVAL_MANAGEMENT_ACTIONS = frozenset({"list_pending", "approve", "deny"})
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _is_approval_management(name: str, args: dict[str, Any]) -> bool:
|
|
61
|
-
"""True for dev-tool calls that manage the approval queue itself."""
|
|
62
|
-
return (
|
|
63
|
-
name == _APPROVAL_MANAGEMENT_TOOL
|
|
64
|
-
and args.get("action") in _APPROVAL_MANAGEMENT_ACTIONS
|
|
65
|
-
)
|
|
66
|
-
|
|
67
32
|
|
|
68
33
|
def _passes_ungated(name: str, args: dict[str, Any]) -> bool:
|
|
69
34
|
"""Calls that must bypass gating: proxy meta-tools + queue management."""
|
|
70
|
-
return name in PROXY_META_TOOLS or
|
|
35
|
+
return name in PROXY_META_TOOLS or is_approval_management_call(name, args)
|
|
71
36
|
|
|
72
37
|
|
|
73
38
|
class PolicyMiddleware(Middleware):
|
|
@@ -39,8 +39,8 @@ from fastmcp.tools import Tool
|
|
|
39
39
|
|
|
40
40
|
from .config import get_global_settings
|
|
41
41
|
from .errors import ErrorCode, create_error_response
|
|
42
|
-
from .policy.middleware import CALL_PROXY_META_TOOLS
|
|
43
42
|
from .renamed_tools import current_tool_name
|
|
43
|
+
from .tool_dispatch import CALL_PROXY_META_TOOLS
|
|
44
44
|
from .tools.helpers import raise_tool_error
|
|
45
45
|
|
|
46
46
|
if TYPE_CHECKING:
|
|
@@ -289,6 +289,15 @@ class HomeAssistantSmartMCPServer:
|
|
|
289
289
|
# wraps the final tool surface (including the search proxies).
|
|
290
290
|
self._apply_tool_security_policies()
|
|
291
291
|
|
|
292
|
+
# Constrained installs can opt into cross-session tool queuing. This
|
|
293
|
+
# sits after approval gates so a call awaiting approval holds no slot.
|
|
294
|
+
if self.settings.ha_tool_concurrency:
|
|
295
|
+
from .ha_request_queue import HomeAssistantRequestQueueMiddleware
|
|
296
|
+
|
|
297
|
+
self.mcp.add_middleware(
|
|
298
|
+
HomeAssistantRequestQueueMiddleware(self.settings.ha_tool_concurrency)
|
|
299
|
+
)
|
|
300
|
+
|
|
292
301
|
# Known-secret-value scrub (#2157) — registered just before the
|
|
293
302
|
# visibility outbound half so that half stays innermost (its scan
|
|
294
303
|
# must see truly raw output; a secret value could embed a hidden
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_advanced.py
RENAMED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Factory returning the ``get_advanced_settings`` / ``save_advanced_settings``
|
|
4
4
|
handlers over the ``ADVANCED_SETTINGS_FIELDS`` registry. Most advanced
|
|
5
5
|
fields write to the shared ``feature_flags.json`` override file;
|
|
6
|
-
``ADDON_SYNCED_ADVANCED_FIELDS`` (``backup_hint``, ``
|
|
6
|
+
``ADDON_SYNCED_ADVANCED_FIELDS`` (``backup_hint``, ``ha_tool_concurrency``,
|
|
7
|
+
``verify_ssl``) route
|
|
7
8
|
through the Supervisor add-on options in add-on mode instead.
|
|
8
9
|
|
|
9
10
|
Handlers are module-level (own C901 budget); ``build_advanced_handlers``
|
|
@@ -131,10 +132,10 @@ async def _get_advanced_settings(
|
|
|
131
132
|
Mirrors ``_get_feature_flags`` / ``_get_backup_config`` but for the
|
|
132
133
|
``ADVANCED_SETTINGS_FIELDS`` registry. Most advanced fields write to
|
|
133
134
|
``feature_flags.json`` via the shared override file in either deployment
|
|
134
|
-
mode. ``ADDON_SYNCED_ADVANCED_FIELDS`` (``backup_hint``,
|
|
135
|
-
are an exception: in addon mode
|
|
136
|
-
and saves route through Supervisor
|
|
137
|
-
the web UI share state.
|
|
135
|
+
mode. ``ADDON_SYNCED_ADVANCED_FIELDS`` (``backup_hint``,
|
|
136
|
+
``ha_tool_concurrency``, ``verify_ssl``) are an exception: in addon mode
|
|
137
|
+
they have ``origin="addon"`` (editable) and saves route through Supervisor
|
|
138
|
+
so the add-on Configuration tab and the web UI share state.
|
|
138
139
|
"""
|
|
139
140
|
from ha_mcp.settings_ui import is_http_settings_mounted
|
|
140
141
|
|
|
@@ -327,7 +328,7 @@ def _validate_one_advanced_field(
|
|
|
327
328
|
status_code=409,
|
|
328
329
|
),
|
|
329
330
|
)
|
|
330
|
-
# Addon-synced fields (
|
|
331
|
+
# Addon-synced fields (backup_hint, ha_tool_concurrency, verify_ssl) are editable in
|
|
331
332
|
# addon mode even though their env vars are set — start.py rewrites them
|
|
332
333
|
# from /data/options.json on every boot, so route the write through
|
|
333
334
|
# Supervisor instead of the override file.
|
|
@@ -525,11 +526,11 @@ async def _save_advanced_settings(
|
|
|
525
526
|
) -> JSONResponse:
|
|
526
527
|
"""Persist UI-edited advanced settings.
|
|
527
528
|
|
|
528
|
-
Addon-synced fields (``backup_hint``, ``
|
|
529
|
-
route through Supervisor; everything else
|
|
530
|
-
shared ``feature_flags.json`` override file.
|
|
531
|
-
``restart_required=True`` (most advanced fields
|
|
532
|
-
paths).
|
|
529
|
+
Addon-synced fields (``backup_hint``, ``ha_tool_concurrency``,
|
|
530
|
+
``verify_ssl``) in add-on mode route through Supervisor; everything else
|
|
531
|
+
atomically merges into the shared ``feature_flags.json`` override file.
|
|
532
|
+
Either sink responds with ``restart_required=True`` (most advanced fields
|
|
533
|
+
gate one-time startup paths).
|
|
533
534
|
"""
|
|
534
535
|
from ..config import ADVANCED_SETTINGS_FIELDS
|
|
535
536
|
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/en.json
RENAMED
|
@@ -404,6 +404,8 @@
|
|
|
404
404
|
"advanced.individual_config_timeout.help": "Timeout for each automation/script/scene config fetch. Range 1–600. Restart required.",
|
|
405
405
|
"advanced.individual_fetch_batch_size.label": "Config fetch batch size",
|
|
406
406
|
"advanced.individual_fetch_batch_size.help": "Concurrent per-ID config fetches. Lower toward 1 on serial HA servers. Range 1–100. Restart required.",
|
|
407
|
+
"advanced.ha_tool_concurrency.label": "Home Assistant tool concurrency",
|
|
408
|
+
"advanced.ha_tool_concurrency.help": "Optional limit on outer Home Assistant tool calls across all sessions. Internal REST, WebSocket, and per-tool fan-out concurrency is unchanged. 0 keeps unlimited behavior. A call waiting for capacity fails after 60 seconds. Range 0–32. Restart required.",
|
|
407
409
|
"advanced.backup_hint.label": "Backup-hint level",
|
|
408
410
|
"advanced.backup_hint.help": "Controls how strongly the LLM is prompted to take a full HA snapshot before risky writes.",
|
|
409
411
|
"advanced.backup_hint.choices.strong": "Strong",
|
|
@@ -468,6 +470,8 @@
|
|
|
468
470
|
"addon.pinned_tools.description": "Tools listed here are locked pinned — they appear at the top of the Tools tab and cannot be unpinned from the web Settings UI without unsetting this option first. Useful in combination with Tool Search. Comma-separated tool names.",
|
|
469
471
|
"addon.verify_ssl.name": "Verify TLS certificate",
|
|
470
472
|
"addon.verify_ssl.description": "Verify the Home Assistant server's TLS certificate. The app (add-on) connects via the Supervisor proxy, so this normally has no effect and should stay enabled. Disable only if you've reconfigured the app (add-on) to talk to HA over a public HTTPS hostname with a self-signed certificate or hostname mismatch. Disabling weakens transport security — leave on unless you know you need it. Requires restart to take effect.",
|
|
473
|
+
"addon.ha_tool_concurrency.name": "Home Assistant tool concurrency",
|
|
474
|
+
"addon.ha_tool_concurrency.description": "Optional limit on outer Home Assistant tool calls across all sessions. Internal REST, WebSocket, and per-tool fan-out concurrency is unchanged. 0 keeps unlimited behavior. A call waiting for capacity fails after 60 seconds.",
|
|
471
475
|
"addon_stable.enable_tool_search.description": "Replace the full tool catalog with search-based discovery. Reduces idle context by roughly 90%, to about 5K tokens. ⚠️ Do NOT enable this in clients with their own built-in tool search / deferred tools (claude.ai, Claude Desktop, Claude Code) — the two search layers conflict, and the client's built-in tool search is the better choice there; leave this off. Whether tools are deferred depends on your client and model combination: use this when your setup loads the full catalog up front — models without native deferred tools (e.g. Gemini, OpenAI-compatible local models, Claude Haiku), clients that inline all tool schemas regardless of model (e.g. GitHub Copilot CLI), or smaller context windows. Some Codex models and ChatGPT include deferred tools too — check your client/model directly to confirm its features so you don't leave this enabled unnecessarily. Tools are found via ha_search_tools and executed via categorized proxies (read/write/delete). Requires an app (add-on) restart to take effect — then reconnect or refresh the MCP server in your AI client so it reloads the tool list. Restarting the app (add-on) alone does NOT refresh a client's cached tool list; this applies after changing any setting here."
|
|
472
476
|
},
|
|
473
477
|
"tool_groups": {},
|
|
@@ -3868,6 +3868,7 @@ const ADVANCED_FIELD_META = {
|
|
|
3868
3868
|
timeout: { label: "HA request timeout (s)", help: "Per-request HTTP timeout. Range 1–600. Restart required." },
|
|
3869
3869
|
max_retries: { label: "HA request max retries", help: "Retry budget per failed REST call. Range 0–20. Restart required." },
|
|
3870
3870
|
verify_ssl: { label: "Verify SSL certificates", help: "Skip TLS verification only on trusted networks (self-signed certs, hostname mismatch). Restart required." },
|
|
3871
|
+
ha_tool_concurrency: { label: "Home Assistant tool concurrency", help: "Optional limit on outer Home Assistant tool calls across all MCP sessions. Internal REST, WebSocket, and per-tool fan-out concurrency is unchanged. 0 keeps unlimited behavior. A call waiting for capacity fails after 60 seconds. Range 0–32. Restart required." },
|
|
3871
3872
|
fuzzy_threshold: { label: "Fuzzy-search threshold", help: "Lower = looser entity match. Range 0–100." },
|
|
3872
3873
|
automation_config_time_budget: { label: "Automation config time budget (s)", help: "Max seconds deep search spends fetching automation configs before returning a partial result. Raise on instances with many automations. Range 1–600. Restart required." },
|
|
3873
3874
|
script_config_time_budget: { label: "Script config time budget (s)", help: "Max seconds deep search spends fetching script configs before returning a partial result. Range 1–600. Restart required." },
|
|
@@ -3902,6 +3903,7 @@ const ADVANCED_FIELD_META = {
|
|
|
3902
3903
|
// is per-request.
|
|
3903
3904
|
const ADVANCED_RESTART_REQUIRED = new Set([
|
|
3904
3905
|
"timeout", "max_retries", "verify_ssl",
|
|
3906
|
+
"ha_tool_concurrency",
|
|
3905
3907
|
"enabled_tool_modules", "enable_websocket",
|
|
3906
3908
|
"log_level", "debug",
|
|
3907
3909
|
"mcp_server_name", "mcp_server_version", "environment",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Shared classifications for tool dispatch middleware."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
# Call proxies dispatch the envelope's ``name`` back through middleware. Policy
|
|
8
|
+
# rules and read-only checks must apply to the real tool name and arguments, not
|
|
9
|
+
# the proxy envelope, so these outer calls bypass those checks.
|
|
10
|
+
CALL_PROXY_META_TOOLS = frozenset(
|
|
11
|
+
{
|
|
12
|
+
"ha_call_read_tool",
|
|
13
|
+
"ha_call_write_tool",
|
|
14
|
+
"ha_call_delete_tool",
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
LOCAL_ONLY_TOOLS = frozenset({"ha_search_tools"})
|
|
19
|
+
|
|
20
|
+
# Code mode is local orchestration that can issue many nested HA calls. Its
|
|
21
|
+
# outer envelope must not hold one slot while nested calls reuse that admission;
|
|
22
|
+
# each nested HA dispatch instead acquires an outer-call slot normally.
|
|
23
|
+
OUTER_QUEUE_ORCHESTRATORS = frozenset({"ha_manage_custom_tool"})
|
|
24
|
+
|
|
25
|
+
_APPROVAL_MANAGEMENT_TOOL = "ha_dev_manage_server"
|
|
26
|
+
_APPROVAL_MANAGEMENT_ACTIONS = frozenset({"list_pending", "approve", "deny"})
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_approval_management_call(name: str, args: dict[str, Any]) -> bool:
|
|
30
|
+
"""Return whether a dispatch manages the policy approval queue.
|
|
31
|
+
|
|
32
|
+
Gating or queueing these actions can deadlock approval by making the action
|
|
33
|
+
that decides a pending request wait behind that same blocked workload.
|
|
34
|
+
Update and restart actions remain subject to normal middleware behavior.
|
|
35
|
+
"""
|
|
36
|
+
return (
|
|
37
|
+
name == _APPROVAL_MANAGEMENT_TOOL
|
|
38
|
+
and args.get("action") in _APPROVAL_MANAGEMENT_ACTIONS
|
|
39
|
+
)
|
|
@@ -62,8 +62,8 @@ from fastmcp.exceptions import ToolError
|
|
|
62
62
|
from fastmcp.server.middleware.middleware import CallNext, Middleware, MiddlewareContext
|
|
63
63
|
|
|
64
64
|
from ..errors import ErrorCode, create_entity_not_found_error, create_error_response
|
|
65
|
-
from ..policy.middleware import CALL_PROXY_META_TOOLS
|
|
66
65
|
from ..renamed_tools import current_tool_name
|
|
66
|
+
from ..tool_dispatch import CALL_PROXY_META_TOOLS
|
|
67
67
|
from ..tools.helpers import raise_tool_error
|
|
68
68
|
from . import resolver
|
|
69
69
|
from .model import VisibilityConfig
|
|
@@ -11,6 +11,7 @@ src/ha_mcp/backup_manager.py
|
|
|
11
11
|
src/ha_mcp/browser_landing.py
|
|
12
12
|
src/ha_mcp/config.py
|
|
13
13
|
src/ha_mcp/errors.py
|
|
14
|
+
src/ha_mcp/ha_request_queue.py
|
|
14
15
|
src/ha_mcp/hacs_auto_refresh.py
|
|
15
16
|
src/ha_mcp/llm_exposure.py
|
|
16
17
|
src/ha_mcp/log_filters.py
|
|
@@ -22,6 +23,7 @@ src/ha_mcp/server.py
|
|
|
22
23
|
src/ha_mcp/smoke_test.py
|
|
23
24
|
src/ha_mcp/stdio_settings_sidecar.py
|
|
24
25
|
src/ha_mcp/strict_bps.py
|
|
26
|
+
src/ha_mcp/tool_dispatch.py
|
|
25
27
|
src/ha_mcp/transport_security.py
|
|
26
28
|
src/ha_mcp/update_check.py
|
|
27
29
|
src/ha_mcp/_vendor/__init__.py
|
|
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-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/MANIFEST.sha256
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/VENDORED
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/client.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/connection.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/frames.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/headers.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/http11.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/imports.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/auth.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/http.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/server.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/protocol.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/proxy.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/py.typed
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/server.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/streams.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/client.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/messages.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/router.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/server.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/utils.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/client.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/messages.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/router.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/server.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/utils.py
RENAMED
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/typing.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/utils.py
RENAMED
|
File without changes
|