ha-mcp-dev 7.10.0.dev777__tar.gz → 7.10.0.dev779__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.
Files changed (161) hide show
  1. {ha_mcp_dev-7.10.0.dev777/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.10.0.dev779}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/__main__.py +15 -84
  4. ha_mcp_dev-7.10.0.dev779/src/ha_mcp/browser_landing.py +138 -0
  5. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  6. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
  7. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/LICENSE +0 -0
  8. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/MANIFEST.in +0 -0
  9. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/README.md +0 -0
  10. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/setup.cfg +0 -0
  11. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/__init__.py +0 -0
  12. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/_pypi_marker +0 -0
  13. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/_version.py +0 -0
  14. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/auth/__init__.py +0 -0
  15. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/auth/consent_form.py +0 -0
  16. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/auth/provider.py +0 -0
  17. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/backup_manager.py +0 -0
  18. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/client/__init__.py +0 -0
  19. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/client/rest_client.py +0 -0
  20. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/client/supervisor_client.py +0 -0
  21. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/client/websocket_client.py +0 -0
  22. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/client/websocket_listener.py +0 -0
  23. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/config.py +0 -0
  24. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  25. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  26. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  27. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/errors.py +0 -0
  28. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/policy/__init__.py +0 -0
  29. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/policy/approval_queue.py +0 -0
  30. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/policy/evaluator.py +0 -0
  31. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/policy/handlers.py +0 -0
  32. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/policy/middleware.py +0 -0
  33. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/policy/model.py +0 -0
  34. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/policy/persistence.py +0 -0
  35. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/policy/value_sources.py +0 -0
  36. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/py.typed +0 -0
  37. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/read_only.py +0 -0
  38. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  39. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  40. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  41. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  42. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  43. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  44. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  45. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  46. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  47. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  48. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  49. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  50. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  51. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  52. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  53. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  54. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  55. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  56. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  57. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  58. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  59. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  60. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  61. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  62. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  63. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/server.py +0 -0
  64. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/settings_ui/__init__.py +0 -0
  65. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/settings_ui/settings.css +0 -0
  66. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/settings_ui/settings.html +0 -0
  67. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/settings_ui/settings.js +0 -0
  68. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/smoke_test.py +0 -0
  69. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  70. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/__init__.py +0 -0
  71. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/auto_backup.py +0 -0
  72. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/backup.py +0 -0
  73. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  74. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  75. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/device_control.py +0 -0
  76. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/enhanced.py +0 -0
  77. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/helpers.py +0 -0
  78. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/radio/__init__.py +0 -0
  79. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/radio/base.py +0 -0
  80. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/radio/matter.py +0 -0
  81. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/radio/thread.py +0 -0
  82. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  83. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/radio/zwave.py +0 -0
  84. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/reference_validator.py +0 -0
  85. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/registry.py +0 -0
  86. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  87. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  88. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  89. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  90. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  91. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  92. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  93. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  94. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  95. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  96. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_addons.py +0 -0
  97. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_areas.py +0 -0
  98. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  99. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  100. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_calendar.py +0 -0
  101. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_camera.py +0 -0
  102. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_categories.py +0 -0
  103. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_code.py +0 -0
  104. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  105. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  106. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  107. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  108. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  109. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  110. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_energy.py +0 -0
  111. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_entities.py +0 -0
  112. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  113. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_groups.py +0 -0
  114. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_hacs.py +0 -0
  115. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_history.py +0 -0
  116. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_integrations.py +0 -0
  117. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_labels.py +0 -0
  118. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  119. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_radio.py +0 -0
  120. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_registry.py +0 -0
  121. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_resources.py +0 -0
  122. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_search.py +0 -0
  123. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_service.py +0 -0
  124. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_services.py +0 -0
  125. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_system.py +0 -0
  126. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_themes.py +0 -0
  127. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_todo.py +0 -0
  128. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_traces.py +0 -0
  129. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_updates.py +0 -0
  130. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_utility.py +0 -0
  131. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  132. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  133. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/tools_zones.py +0 -0
  134. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/util_helpers.py +0 -0
  135. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/tools/validation_middleware.py +0 -0
  136. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/transforms/__init__.py +0 -0
  137. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/transforms/categorized_search.py +0 -0
  138. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  139. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/transport_security.py +0 -0
  140. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/update_check.py +0 -0
  141. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/__init__.py +0 -0
  142. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/config_hash.py +0 -0
  143. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/data_paths.py +0 -0
  144. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/domain_handlers.py +0 -0
  145. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  146. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  147. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/operation_manager.py +0 -0
  148. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/python_sandbox.py +0 -0
  149. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/skill_loader.py +0 -0
  150. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/utils/usage_logger.py +0 -0
  151. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/visibility/__init__.py +0 -0
  152. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/visibility/model.py +0 -0
  153. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/visibility/persistence.py +0 -0
  154. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp/visibility/resolver.py +0 -0
  155. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  156. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  157. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  158. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  159. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/tests/__init__.py +0 -0
  160. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/tests/test_constants.py +0 -0
  161. {ha_mcp_dev-7.10.0.dev777 → ha_mcp_dev-7.10.0.dev779}/tests/test_env_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.10.0.dev777
3
+ Version: 7.10.0.dev779
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.10.0.dev777"
7
+ version = "7.10.0.dev779"
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
@@ -34,8 +34,10 @@ from typing import TYPE_CHECKING, Any # noqa: E402
34
34
 
35
35
  from fastmcp.exceptions import ToolError # noqa: E402
36
36
  from pydantic import ValidationError as PydanticValidationError # noqa: E402
37
- from starlette.requests import Request # noqa: E402
38
- from starlette.responses import PlainTextResponse # noqa: E402
37
+
38
+ from ha_mcp.browser_landing import ( # noqa: E402
39
+ register_browser_landing as _register_landing_route,
40
+ )
39
41
 
40
42
  if TYPE_CHECKING:
41
43
  from fastmcp import FastMCP
@@ -962,21 +964,20 @@ async def _run_http_with_graceful_shutdown(
962
964
  )
963
965
 
964
966
 
965
- _registered_landing_paths: set[str] = set()
966
-
967
-
968
967
  def register_browser_landing(
969
968
  mcp_instance: "FastMCP | _DeferredMCP",
970
969
  path: str,
971
970
  *,
972
971
  quiet_probe_log: bool = True,
973
972
  ) -> None:
974
- """Register a GET handler that returns 405 with a helpful message.
973
+ """Register the friendly browser landing page and tidy the uvicorn access log.
975
974
 
976
- Browsers and misconfigured clients that send GET instead of POST will see
977
- a human-readable explanation instead of a bare "Method Not Allowed" error.
978
- The 405 status and Allow header are set explicitly by this handler so
979
- automated clients still get the correct HTTP semantics.
975
+ Delegates the GET landing route to the reusable core in
976
+ :mod:`ha_mcp.browser_landing` (also used by the in-process ``ha_mcp_tools``
977
+ server) and, when the route is newly registered, attaches
978
+ :class:`ProbeAccessLogFilter` to the uvicorn access logger. Browsers and
979
+ misconfigured clients that send GET instead of POST see a human-readable
980
+ explanation instead of a bare "Method Not Allowed" error.
980
981
 
981
982
  Args:
982
983
  mcp_instance: The FastMCP server to register the route on.
@@ -986,83 +987,13 @@ def register_browser_landing(
986
987
  access log (the handler logs an annotated replacement). Pass False
987
988
  for SSE, where a GET answers 200 and a 405 is a genuine fault.
988
989
  """
989
- if path in _registered_landing_paths:
990
- logger.warning(
991
- "register_browser_landing: %r already registered, skipping", path
992
- )
990
+ if not _register_landing_route(mcp_instance, path):
991
+ # Already registered for this path — don't double-attach the log filter.
993
992
  return
994
- _registered_landing_paths.add(path)
995
-
996
- _landing_message = (
997
- "HA-MCP server is up and running!\n"
998
- "\n"
999
- "To connect, paste the full URL (including the /private_... key) into the\n"
1000
- "connector or MCP settings of your AI/LLM client. No username or password required.\n"
1001
- "Setup instructions: https://homeassistant-ai.github.io/ha-mcp/\n"
1002
- "\n"
1003
- "--- Seeing this page? Your URL is set up correctly ---\n"
1004
- "\n"
1005
- "If this page loads in your browser, the MCP server is reachable and the\n"
1006
- "URL is correct. If your AI client still cannot connect, the problem is\n"
1007
- "not on HA-MCP's side. Common causes:\n"
1008
- "\n"
1009
- "- Geo / country blocking in your reverse proxy / CDN (Cloudflare, NGINX,\n"
1010
- " Traefik, Zoraxy, etc.). Most AI/LLM services connect from US-based\n"
1011
- " cloud infrastructure, so if you block US IP addresses (or only allow\n"
1012
- " your own country), that is why your client cannot connect. Allow your\n"
1013
- " provider's IP ranges (or your client's egress IPs). For example,\n"
1014
- # Anthropic's documented outbound range; re-verify at
1015
- # https://platform.claude.com/docs/en/api/ip-addresses if it ever changes.
1016
- " Claude.ai connects from Anthropic's network, 160.79.104.0/21.\n"
1017
- "- WAF, bot-blocking, or rate-limiting rules on the proxy that drop or\n"
1018
- " challenge the request.\n"
1019
- "- The AI client's network can sometimes be spotty -- you may just need\n"
1020
- " to try connecting again.\n"
1021
- "- The AI client itself refusing certain domains or proxy providers on\n"
1022
- " its end. This is rare and outside your control; try a different\n"
1023
- " hostname or proxy if you suspect it.\n"
1024
- "\n"
1025
- "Your proxy's access logs will show the blocked attempt -- look for the\n"
1026
- "request from your AI provider's IP (e.g. an Anthropic 160.79.x.x address).\n"
1027
- "\n"
1028
- "--- Cloudflare Users ---\n"
1029
- "\n"
1030
- 'If your LLM cannot connect, Cloudflare\'s "Block AI training bots"\n'
1031
- "setting is the most common cause. To disable it:\n"
1032
- "\n"
1033
- "1. Log in to Cloudflare (https://dash.cloudflare.com)\n"
1034
- "2. In the left sidebar, click Domains, then click Overview\n"
1035
- "3. Click on the domain you use for connecting to Home Assistant\n"
1036
- '4. On the right side, find "Control AI Crawlers"\n'
1037
- '5. Under "Block AI training bots", open the dropdown\n'
1038
- '6. Select "do not block (allow crawlers)"\n'
1039
- "\n"
1040
- "Screenshot of the setting:\n"
1041
- "https://homeassistant-ai.github.io/ha-mcp/images/cloudflare-ai-crawlers-setting.jpg\n"
1042
- )
1043
-
1044
- # Safe because FastMCP registers the MCP route with methods=["POST", "DELETE"]
1045
- # in stateless mode, so Starlette rejects GET requests before the MCP handler runs.
1046
- # Custom routes are registered at lowest precedence (after the MCP route).
1047
- @mcp_instance.custom_route(path, methods=["GET"])
1048
- async def _browser_landing(_: Request) -> PlainTextResponse:
1049
- # Any GET here is a non-MCP caller (browser, health check, proxy, or a
1050
- # connector's SSE-style pre-flight) hitting this POST-only Streamable HTTP
1051
- # endpoint, which answers 405 by design. Log one annotated line so the 405
1052
- # reads as expected; ProbeAccessLogFilter drops the raw uvicorn access line
1053
- # so there's no cryptic duplicate.
1054
- logger.info("GET %s -> 405 (NORMAL for most non-SSE connections)", path)
1055
- return PlainTextResponse(
1056
- _landing_message,
1057
- status_code=405,
1058
- # DELETE is included per the MCP Streamable HTTP spec (used for
1059
- # session termination), even though this deployment uses stateless mode.
1060
- headers={"Allow": "POST, DELETE"},
1061
- )
1062
993
 
1063
994
  # Tidy uvicorn's access log: always drop browser favicon 404s, and drop the
1064
- # raw by-design GET/HEAD-405 probe line on the MCP path (the handler above logs
1065
- # an annotated replacement). The 405 drop is skipped for SSE
995
+ # raw by-design GET/HEAD-405 probe line on the MCP path (the landing handler
996
+ # logs an annotated replacement). The 405 drop is skipped for SSE
1066
997
  # (quiet_probe_log=False), where a GET answers 200 and a 405 is a real fault.
1067
998
  # Attach to uvicorn.access directly — it has propagate=False, so a root-logger
1068
999
  # filter would miss it.
@@ -0,0 +1,138 @@
1
+ """Friendly browser landing page for the MCP endpoint (issue #777).
2
+
3
+ A browser (or any GET client) that hits the POST-only Streamable HTTP MCP
4
+ endpoint would otherwise see a bare "Method Not Allowed". This module registers
5
+ a GET handler that answers ``405`` with a human-readable explanation plus the
6
+ correct ``Allow`` header, so automated clients still get the right HTTP
7
+ semantics.
8
+
9
+ Extracted from :mod:`ha_mcp.__main__` so the in-process server (the
10
+ ``ha_mcp_tools`` custom-component worker thread) can register the same landing
11
+ page. Importing ``ha_mcp.__main__`` runs process-global side effects at module
12
+ load (``truststore.inject_into_ssl()``) that must never happen in-process, so
13
+ the reusable core lives here — free of those side effects. ``__main__`` keeps a
14
+ thin wrapper that additionally tidies the uvicorn access log.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ import weakref
21
+ from typing import Any, Protocol
22
+
23
+ from starlette.requests import Request
24
+ from starlette.responses import PlainTextResponse
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class CustomRouteServer(Protocol):
30
+ """Structural type for servers exposing FastMCP's ``custom_route`` decorator.
31
+
32
+ Matches the real FastMCP server and ``__main__``'s deferred proxy. Only the
33
+ ``(path, methods)`` call shape used here is pinned; the decorator's return
34
+ type is left to the implementation (fastmcp also takes optional ``name`` /
35
+ ``include_in_schema`` parameters this module never passes).
36
+ """
37
+
38
+ def custom_route(self, path: str, methods: list[str]) -> Any:
39
+ """Return a decorator that registers a handler for ``methods`` at ``path``."""
40
+
41
+
42
+ # The landing body. Plain text so no browser ever interprets it as markup, and
43
+ # so it survives the ha_mcp_tools webhook proxy (which forwards text/plain but
44
+ # coerces text/html to JSON as an XSS guard).
45
+ LANDING_MESSAGE = (
46
+ "HA-MCP server is up and running!\n"
47
+ "\n"
48
+ "To connect, paste the full URL (including the /private_... key) into the\n"
49
+ "connector or MCP settings of your AI/LLM client. No username or password required.\n"
50
+ "Setup instructions: https://homeassistant-ai.github.io/ha-mcp/\n"
51
+ "\n"
52
+ "--- Seeing this page? Your URL is set up correctly ---\n"
53
+ "\n"
54
+ "If this page loads in your browser, the MCP server is reachable and the\n"
55
+ "URL is correct. If your AI client still cannot connect, the problem is\n"
56
+ "not on HA-MCP's side. Common causes:\n"
57
+ "\n"
58
+ "- Geo / country blocking in your reverse proxy / CDN (Cloudflare, NGINX,\n"
59
+ " Traefik, Zoraxy, etc.). Most AI/LLM services connect from US-based\n"
60
+ " cloud infrastructure, so if you block US IP addresses (or only allow\n"
61
+ " your own country), that is why your client cannot connect. Allow your\n"
62
+ " provider's IP ranges (or your client's egress IPs). For example,\n"
63
+ # Anthropic's documented outbound range; re-verify at
64
+ # https://platform.claude.com/docs/en/api/ip-addresses if it ever changes.
65
+ " Claude.ai connects from Anthropic's network, 160.79.104.0/21.\n"
66
+ "- WAF, bot-blocking, or rate-limiting rules on the proxy that drop or\n"
67
+ " challenge the request.\n"
68
+ "- The AI client's network can sometimes be spotty -- you may just need\n"
69
+ " to try connecting again.\n"
70
+ "- The AI client itself refusing certain domains or proxy providers on\n"
71
+ " its end. This is rare and outside your control; try a different\n"
72
+ " hostname or proxy if you suspect it.\n"
73
+ "\n"
74
+ "Your proxy's access logs will show the blocked attempt -- look for the\n"
75
+ "request from your AI provider's IP (e.g. an Anthropic 160.79.x.x address).\n"
76
+ "\n"
77
+ "--- Cloudflare Users ---\n"
78
+ "\n"
79
+ 'If your LLM cannot connect, Cloudflare\'s "Block AI training bots"\n'
80
+ "setting is the most common cause. To disable it:\n"
81
+ "\n"
82
+ "1. Log in to Cloudflare (https://dash.cloudflare.com)\n"
83
+ "2. In the left sidebar, click Domains, then click Overview\n"
84
+ "3. Click on the domain you use for connecting to Home Assistant\n"
85
+ '4. On the right side, find "Control AI Crawlers"\n'
86
+ '5. Under "Block AI training bots", open the dropdown\n'
87
+ '6. Select "do not block (allow crawlers)"\n'
88
+ "\n"
89
+ "Screenshot of the setting:\n"
90
+ "https://homeassistant-ai.github.io/ha-mcp/images/cloudflare-ai-crawlers-setting.jpg\n"
91
+ )
92
+
93
+ # Landing routes already registered, keyed PER MCP INSTANCE (weakly, so a
94
+ # discarded server never leaks). The key must not be the path alone: the
95
+ # in-process server builds a NEW FastMCP on every config-entry reload in the
96
+ # SAME Python process while keeping the same secret path — a process-global
97
+ # path set would skip the re-registration and silently drop the landing page
98
+ # after the first reload. The CLI/add-on never sees this (their process exits).
99
+ _registered_landing_paths: weakref.WeakKeyDictionary[CustomRouteServer, set[str]] = (
100
+ weakref.WeakKeyDictionary()
101
+ )
102
+
103
+
104
+ def register_browser_landing(mcp_instance: CustomRouteServer, path: str) -> bool:
105
+ """Register a GET handler that returns 405 with the friendly landing message.
106
+
107
+ ``mcp_instance`` is a FastMCP-like object exposing ``custom_route`` (the real
108
+ server or ``__main__``'s deferred proxy). Returns True if the route was newly
109
+ registered, False if this instance already had one for ``path`` (idempotent
110
+ per instance + path).
111
+ """
112
+ paths = _registered_landing_paths.setdefault(mcp_instance, set())
113
+ if path in paths:
114
+ logger.warning(
115
+ "register_browser_landing: %r already registered, skipping", path
116
+ )
117
+ return False
118
+ paths.add(path)
119
+
120
+ # Safe because FastMCP registers the MCP route with methods=["POST", "DELETE"]
121
+ # in stateless mode, so Starlette rejects GET requests before the MCP handler
122
+ # runs. Custom routes are registered at lowest precedence (after the MCP route).
123
+ @mcp_instance.custom_route(path, methods=["GET"])
124
+ async def _browser_landing(_: Request) -> PlainTextResponse:
125
+ # Any GET here is a non-MCP caller (browser, health check, proxy, or a
126
+ # connector's SSE-style pre-flight) hitting this POST-only Streamable HTTP
127
+ # endpoint, which answers 405 by design. Log one annotated line so the 405
128
+ # reads as expected.
129
+ logger.info("GET %s -> 405 (NORMAL for most non-SSE connections)", path)
130
+ return PlainTextResponse(
131
+ LANDING_MESSAGE,
132
+ status_code=405,
133
+ # DELETE is included per the MCP Streamable HTTP spec (used for
134
+ # session termination), even though this deployment uses stateless mode.
135
+ headers={"Allow": "POST, DELETE"},
136
+ )
137
+
138
+ return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.10.0.dev777
3
+ Version: 7.10.0.dev779
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -7,6 +7,7 @@ src/ha_mcp/__main__.py
7
7
  src/ha_mcp/_pypi_marker
8
8
  src/ha_mcp/_version.py
9
9
  src/ha_mcp/backup_manager.py
10
+ src/ha_mcp/browser_landing.py
10
11
  src/ha_mcp/config.py
11
12
  src/ha_mcp/errors.py
12
13
  src/ha_mcp/py.typed