ha-mcp-dev 7.3.0.dev397__tar.gz → 7.3.0.dev399__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 (104) hide show
  1. {ha_mcp_dev-7.3.0.dev397/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.3.0.dev399}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/__init__.py +3 -1
  4. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/__main__.py +57 -2
  5. ha_mcp_dev-7.3.0.dev399/src/ha_mcp/_version.py +58 -0
  6. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/config.py +4 -6
  7. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  8. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
  9. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/LICENSE +0 -0
  10. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/MANIFEST.in +0 -0
  11. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/README.md +0 -0
  12. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/setup.cfg +0 -0
  13. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/_pypi_marker +0 -0
  14. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/auth/__init__.py +0 -0
  15. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/auth/consent_form.py +0 -0
  16. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/auth/provider.py +0 -0
  17. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/client/__init__.py +0 -0
  18. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/client/rest_client.py +0 -0
  19. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/client/websocket_client.py +0 -0
  20. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/client/websocket_listener.py +0 -0
  21. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/errors.py +0 -0
  22. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/py.typed +0 -0
  23. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  24. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  25. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  26. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  27. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  28. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  29. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  30. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  31. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  32. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  33. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  34. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  35. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  36. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  37. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  38. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  39. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  40. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  41. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  42. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  43. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/server.py +0 -0
  44. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/smoke_test.py +0 -0
  45. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/__init__.py +0 -0
  46. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/backup.py +0 -0
  47. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  48. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/device_control.py +0 -0
  49. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/enhanced.py +0 -0
  50. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/helpers.py +0 -0
  51. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/reference_validator.py +0 -0
  52. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/registry.py +0 -0
  53. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/smart_search.py +0 -0
  54. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_addons.py +0 -0
  55. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_areas.py +0 -0
  56. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  57. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  58. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_calendar.py +0 -0
  59. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_camera.py +0 -0
  60. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_categories.py +0 -0
  61. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  62. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  63. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  64. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  65. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  66. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_energy.py +0 -0
  67. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_entities.py +0 -0
  68. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  69. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_groups.py +0 -0
  70. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_hacs.py +0 -0
  71. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_history.py +0 -0
  72. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_integrations.py +0 -0
  73. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_labels.py +0 -0
  74. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  75. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_registry.py +0 -0
  76. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_resources.py +0 -0
  77. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_search.py +0 -0
  78. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_service.py +0 -0
  79. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_services.py +0 -0
  80. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_system.py +0 -0
  81. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_todo.py +0 -0
  82. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_traces.py +0 -0
  83. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_updates.py +0 -0
  84. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_utility.py +0 -0
  85. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  86. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  87. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/tools_zones.py +0 -0
  88. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/tools/util_helpers.py +0 -0
  89. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/transforms/__init__.py +0 -0
  90. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/transforms/categorized_search.py +0 -0
  91. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/utils/__init__.py +0 -0
  92. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/utils/config_hash.py +0 -0
  93. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/utils/domain_handlers.py +0 -0
  94. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  95. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/utils/operation_manager.py +0 -0
  96. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/utils/python_sandbox.py +0 -0
  97. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp/utils/usage_logger.py +0 -0
  98. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  99. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  100. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  101. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  102. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/tests/__init__.py +0 -0
  103. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/tests/test_constants.py +0 -0
  104. {ha_mcp_dev-7.3.0.dev397 → ha_mcp_dev-7.3.0.dev399}/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.3.0.dev397
3
+ Version: 7.3.0.dev399
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.3.0.dev397"
7
+ version = "7.3.0.dev399"
8
8
  description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13,<3.14"
@@ -5,7 +5,9 @@ A Model Context Protocol server that provides complete control over Home Assista
5
5
  through REST API and WebSocket integration with 20+ enhanced tools.
6
6
  """
7
7
 
8
- __version__ = "7.3.0"
8
+ from ._version import get_version
9
+
10
+ __version__ = get_version()
9
11
  __author__ = "Julien"
10
12
  __license__ = "MIT"
11
13
 
@@ -30,6 +30,8 @@ import threading # noqa: E402
30
30
  from collections.abc import Coroutine # noqa: E402
31
31
  from typing import TYPE_CHECKING, Any # noqa: E402
32
32
 
33
+ from fastmcp.exceptions import ToolError # noqa: E402
34
+ from pydantic import ValidationError as PydanticValidationError # noqa: E402
33
35
  from starlette.requests import Request # noqa: E402
34
36
  from starlette.responses import PlainTextResponse # noqa: E402
35
37
 
@@ -281,6 +283,7 @@ def _setup_standard_mode() -> None:
281
283
  settings = get_settings()
282
284
  _validate_standard_credentials(settings)
283
285
  _setup_logging(settings.log_level)
286
+ _log_startup_version()
284
287
 
285
288
 
286
289
  def _http_run_kwargs(transport: str, port: int, path: str) -> dict:
@@ -365,6 +368,36 @@ class StatelessSessionLogFilter(logging.Filter):
365
368
  return True
366
369
 
367
370
 
371
+ class ToolValidationLogFilter(logging.Filter):
372
+ """Demote fastmcp tool-failure tracebacks to single-line warnings.
373
+
374
+ Pydantic ValidationError and tool-raised ToolError aren't server bugs,
375
+ so the traceback through fastmcp/pydantic internals is just noise. The
376
+ structured error detail is preserved in the WARNING message; stack is
377
+ intentionally dropped because these are user-input errors, not bugs.
378
+ """
379
+
380
+ def filter(self, record: logging.LogRecord) -> bool:
381
+ if record.name != "fastmcp.server.server" or not record.exc_info:
382
+ return True
383
+
384
+ msg = record.getMessage()
385
+ err = record.exc_info[1]
386
+ if "Error validating tool" in msg and isinstance(err, PydanticValidationError):
387
+ record.msg = f"{msg}: {err.errors(include_url=False)}"
388
+ elif "Error calling tool" in msg and isinstance(err, ToolError):
389
+ record.msg = f"{msg}: {err}"
390
+ else:
391
+ return True
392
+
393
+ record.args = ()
394
+ record.levelno = logging.WARNING
395
+ record.levelname = "WARNING"
396
+ record.exc_info = None
397
+ record.exc_text = None
398
+ return True
399
+
400
+
368
401
  def _setup_logging(log_level_str: str, force: bool = False) -> None:
369
402
  """Configure root logger with consistent timestamp format."""
370
403
  logging.basicConfig(
@@ -376,6 +409,26 @@ def _setup_logging(log_level_str: str, force: bool = False) -> None:
376
409
  logging.getLogger("mcp.server.streamable_http").addFilter(
377
410
  StatelessSessionLogFilter()
378
411
  )
412
+ logging.getLogger("fastmcp.server.server").addFilter(ToolValidationLogFilter())
413
+
414
+
415
+ def _log_startup_version() -> None:
416
+ """Log ha-mcp version at startup, plus a dev-channel banner when relevant.
417
+
418
+ The dev banner only fires for standalone dev installs (Docker ``:dev`` /
419
+ ``:latest``, or ``pip install ha-mcp-dev``). It is suppressed under the HA
420
+ Supervisor because add-on users already pick dev vs stable in the HAOS UI.
421
+ """
422
+ from ha_mcp._version import get_version, is_dev_version, is_running_in_addon
423
+
424
+ version = get_version()
425
+ logger.info(f"ha-mcp {version}")
426
+ if is_dev_version(version) and not is_running_in_addon():
427
+ logger.warning(
428
+ "This is the dev channel. For the stable release use the "
429
+ "'ghcr.io/homeassistant-ai/ha-mcp:stable' Docker tag "
430
+ "(or 'pip install ha-mcp' on PyPI)."
431
+ )
379
432
 
380
433
 
381
434
  def _get_timestamped_uvicorn_log_config() -> dict:
@@ -547,9 +600,9 @@ def main() -> None:
547
600
  """Run server via CLI using FastMCP's stdio transport."""
548
601
  # Handle --version flag early, before server creation requires config
549
602
  if "--version" in sys.argv or "-V" in sys.argv:
550
- from importlib.metadata import version
603
+ from ha_mcp._version import get_version
551
604
 
552
- print(f"ha-mcp {version('ha-mcp-dev')}")
605
+ print(f"ha-mcp {get_version()}")
553
606
  sys.exit(0)
554
607
 
555
608
  # Check for smoke test flag
@@ -572,6 +625,7 @@ def main() -> None:
572
625
  sys.exit(1)
573
626
 
574
627
  _setup_logging(settings.log_level)
628
+ _log_startup_version()
575
629
 
576
630
  _run_entrypoint(_run_with_graceful_shutdown(), "Server")
577
631
 
@@ -747,6 +801,7 @@ def main_oauth() -> None:
747
801
  for logger_name in ["ha_mcp", "ha_mcp.auth", "ha_mcp.auth.provider"]:
748
802
  logging.getLogger(logger_name).setLevel(getattr(logging, log_level))
749
803
  logger.info(f"OAuth mode logging configured at {log_level} level")
804
+ _log_startup_version()
750
805
 
751
806
  port, path = _get_http_runtime(default_port=8086)
752
807
  base_url = os.getenv("MCP_BASE_URL")
@@ -0,0 +1,58 @@
1
+ """Version resolution for the ha-mcp package.
2
+
3
+ Kept as a standalone module (no other ``ha_mcp`` imports) so it can be used from
4
+ ``__init__.py`` and ``config.py`` without circular-import risk.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import importlib.metadata
10
+ import logging
11
+ import os
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def get_version() -> str:
17
+ """Return the installed ha-mcp version.
18
+
19
+ Resolution order:
20
+ 1. ``HA_MCP_BUILD_VERSION`` env var — set by Docker/add-on builds that can't
21
+ rewrite ``pyproject.toml`` before install, so the dev suffix still reaches
22
+ the running process. Stable builds leave it unset.
23
+ 2. ``ha-mcp`` package metadata — stable PyPI + stable Docker.
24
+ 3. ``ha-mcp-dev`` package metadata — PyPI dev channel (renamed package).
25
+
26
+ If none of the above resolve, logs a warning and returns ``"unknown"``.
27
+ The "unknown" string is itself diagnostic in bug reports and startup logs
28
+ — it tells triagers the install didn't register package metadata (e.g. a
29
+ source checkout without ``pip install -e .``, or a broken Docker layer).
30
+ """
31
+ if override := os.environ.get("HA_MCP_BUILD_VERSION"):
32
+ return override
33
+ for pkg_name in ("ha-mcp", "ha-mcp-dev"):
34
+ try:
35
+ return importlib.metadata.version(pkg_name)
36
+ except importlib.metadata.PackageNotFoundError:
37
+ continue
38
+ logger.warning(
39
+ "ha-mcp package metadata not found and HA_MCP_BUILD_VERSION unset — "
40
+ "version will be reported as 'unknown'. Reinstall the package or set "
41
+ "HA_MCP_BUILD_VERSION if this is an intentional source-tree run."
42
+ )
43
+ return "unknown"
44
+
45
+
46
+ def is_dev_version(version: str) -> bool:
47
+ """Return True when the version string contains a PEP 440 ``.dev`` suffix."""
48
+ return ".dev" in version
49
+
50
+
51
+ def is_running_in_addon() -> bool:
52
+ """Return True when running inside a Home Assistant add-on container.
53
+
54
+ The HA Supervisor injects ``SUPERVISOR_TOKEN`` into every add-on's env.
55
+ Checked so the standalone-Docker ``:stable`` banner isn't shown to add-on
56
+ users, who already see the dev/stable distinction in the HAOS add-on UI.
57
+ """
58
+ return bool(os.environ.get("SUPERVISOR_TOKEN"))
@@ -2,22 +2,20 @@
2
2
  Configuration management for Home Assistant MCP Server.
3
3
  """
4
4
 
5
- import importlib.metadata
6
5
  import os
7
6
 
8
7
  # Load environment variables from .env file with HAMCP_ENV_FILE support
9
8
  # Use absolute path to ensure .env is found regardless of cwd
10
9
  from pathlib import Path
11
10
 
12
- try:
13
- _PACKAGE_VERSION = importlib.metadata.version("ha-mcp")
14
- except importlib.metadata.PackageNotFoundError:
15
- _PACKAGE_VERSION = "unknown"
16
-
17
11
  from dotenv import load_dotenv
18
12
  from pydantic import Field, field_validator, model_validator
19
13
  from pydantic_settings import BaseSettings, SettingsConfigDict
20
14
 
15
+ from ha_mcp._version import get_version
16
+
17
+ _PACKAGE_VERSION = get_version()
18
+
21
19
  project_root = Path(__file__).parent.parent.parent
22
20
 
23
21
  # Demo environment token - use HOMEASSISTANT_TOKEN="demo" to connect to the public demo
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.3.0.dev397
3
+ Version: 7.3.0.dev399
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
@@ -5,6 +5,7 @@ pyproject.toml
5
5
  src/ha_mcp/__init__.py
6
6
  src/ha_mcp/__main__.py
7
7
  src/ha_mcp/_pypi_marker
8
+ src/ha_mcp/_version.py
8
9
  src/ha_mcp/config.py
9
10
  src/ha_mcp/errors.py
10
11
  src/ha_mcp/py.typed