ha-mcp-dev 7.4.1.dev500__tar.gz → 7.4.1.dev501__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 (112) hide show
  1. {ha_mcp_dev-7.4.1.dev500/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.4.1.dev501}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/config.py +9 -0
  4. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/server.py +202 -0
  5. ha_mcp_dev-7.4.1.dev501/src/ha_mcp/transforms/__init__.py +15 -0
  6. ha_mcp_dev-7.4.1.dev501/src/ha_mcp/transforms/lite_docstrings.py +62 -0
  7. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  8. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
  9. ha_mcp_dev-7.4.1.dev500/src/ha_mcp/transforms/__init__.py +0 -9
  10. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/LICENSE +0 -0
  11. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/MANIFEST.in +0 -0
  12. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/README.md +0 -0
  13. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/setup.cfg +0 -0
  14. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/__init__.py +0 -0
  15. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/__main__.py +0 -0
  16. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/_pypi_marker +0 -0
  17. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/_version.py +0 -0
  18. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/auth/__init__.py +0 -0
  19. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/auth/consent_form.py +0 -0
  20. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/auth/provider.py +0 -0
  21. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/client/__init__.py +0 -0
  22. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/client/rest_client.py +0 -0
  23. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/client/supervisor_client.py +0 -0
  24. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/client/websocket_client.py +0 -0
  25. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/client/websocket_listener.py +0 -0
  26. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/errors.py +0 -0
  27. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/py.typed +0 -0
  28. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  29. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  30. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  31. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  32. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  33. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  34. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  35. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  36. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  37. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  38. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  39. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  40. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  41. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  42. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  43. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  44. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  45. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  46. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  47. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  48. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/settings_ui.py +0 -0
  49. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/smoke_test.py +0 -0
  50. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/__init__.py +0 -0
  51. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/backup.py +0 -0
  52. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  53. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/device_control.py +0 -0
  54. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/enhanced.py +0 -0
  55. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/helpers.py +0 -0
  56. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/reference_validator.py +0 -0
  57. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/registry.py +0 -0
  58. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/smart_search.py +0 -0
  59. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_addons.py +0 -0
  60. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_areas.py +0 -0
  61. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  62. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  63. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_calendar.py +0 -0
  64. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_camera.py +0 -0
  65. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_categories.py +0 -0
  66. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_code.py +0 -0
  67. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  68. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  69. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  70. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  71. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  72. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  73. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_energy.py +0 -0
  74. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_entities.py +0 -0
  75. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  76. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_groups.py +0 -0
  77. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_hacs.py +0 -0
  78. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_history.py +0 -0
  79. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_integrations.py +0 -0
  80. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_labels.py +0 -0
  81. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  82. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_registry.py +0 -0
  83. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_resources.py +0 -0
  84. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_search.py +0 -0
  85. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_service.py +0 -0
  86. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_services.py +0 -0
  87. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_system.py +0 -0
  88. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_todo.py +0 -0
  89. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_traces.py +0 -0
  90. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_updates.py +0 -0
  91. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_utility.py +0 -0
  92. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  93. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  94. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/tools_zones.py +0 -0
  95. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/tools/util_helpers.py +0 -0
  96. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/transforms/categorized_search.py +0 -0
  97. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/__init__.py +0 -0
  98. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/config_hash.py +0 -0
  99. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/data_paths.py +0 -0
  100. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/domain_handlers.py +0 -0
  101. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  102. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  103. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/operation_manager.py +0 -0
  104. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/python_sandbox.py +0 -0
  105. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp/utils/usage_logger.py +0 -0
  106. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  107. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  108. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  109. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  110. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/tests/__init__.py +0 -0
  111. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/tests/test_constants.py +0 -0
  112. {ha_mcp_dev-7.4.1.dev500 → ha_mcp_dev-7.4.1.dev501}/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.4.1.dev500
3
+ Version: 7.4.1.dev501
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.4.1.dev500"
7
+ version = "7.4.1.dev501"
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"
@@ -113,6 +113,15 @@ class Settings(BaseSettings):
113
113
  # supervisor UI rejects out-of-range values before they reach env vars.
114
114
  tool_search_max_results: int = Field(5, ge=2, le=10, alias="TOOL_SEARCH_MAX_RESULTS")
115
115
 
116
+ # Lite docstrings — replace selected heavy tool descriptions with
117
+ # shorter variants that defer detailed guidance to the
118
+ # ``ha_get_skill_home_assistant_best_practices`` skill tool/resource.
119
+ # Reduces idle catalog token usage at the cost of relying on the LLM
120
+ # to actually consult the skill when it needs detail. Beta feature
121
+ # (issue #1062); a startup WARNING is emitted when enabled so
122
+ # env-var users see the trade-off in their logs.
123
+ enable_lite_docstrings: bool = Field(False, alias="ENABLE_LITE_DOCSTRINGS")
124
+
116
125
  # Code Mode — sandboxed Python execution via pydantic-monty.
117
126
  # Provides an "escape hatch" tool (ha_manage_custom_tool) that lets LLMs write
118
127
  # custom one-off Python code when no existing tool covers the request.
@@ -263,6 +263,12 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
263
263
  # search indexing too).
264
264
  self._apply_settings_visibility()
265
265
 
266
+ # Replace heavy tool descriptions with lite variants when
267
+ # ENABLE_LITE_DOCSTRINGS=true. Must come BEFORE keyword
268
+ # enrichment so BM25 keywords append to the lite text (instead
269
+ # of the full description we just discarded).
270
+ self._apply_lite_docstrings()
271
+
266
272
  # Enrich tool descriptions with BM25 keyword boosts. Runs
267
273
  # unconditionally so Claude's native deferred-tool search
268
274
  # (claude.ai) benefits even when ENABLE_TOOL_SEARCH is off.
@@ -536,6 +542,147 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
536
542
  ),
537
543
  }
538
544
 
545
+ # Lite docstrings — beta opt-in (enable_lite_docstrings, #1062).
546
+ # Each entry replaces the full docstring on a heavy tool with a
547
+ # shorter variant that defers schema/example detail to
548
+ # ha_get_skill_home_assistant_best_practices. Every entry preserves
549
+ # a pointer to that skill so the LLM still has a path to the full
550
+ # guidance from inside the trimmed description. The trade-off
551
+ # (LLMs that skip the skill tool get less guidance) is surfaced in
552
+ # the dev-addon toggle, docs/beta.md, and a startup WARNING.
553
+ _LITE_DOCSTRINGS: ClassVar[dict[str, str]] = {
554
+ "ha_config_get_automation": (
555
+ "Get a Home Assistant automation configuration by "
556
+ "entity_id or unique_id. Returns the full config "
557
+ "(trigger, condition, action, mode) plus a stable "
558
+ "config_hash for use with python_transform on "
559
+ "ha_config_set_automation.\n\n"
560
+ "For schema and field-level details, see "
561
+ "ha_get_skill_home_assistant_best_practices."
562
+ ),
563
+ "ha_config_set_automation": (
564
+ "Create or update a Home Assistant automation.\n\n"
565
+ "Supports two modes: full `config` replacement, or surgical "
566
+ "`python_transform` on an existing automation (requires "
567
+ "`identifier` and `config_hash` from "
568
+ "ha_config_get_automation). Omit `identifier` to create a "
569
+ "new automation.\n\n"
570
+ "For schema details, examples, and native-vs-template "
571
+ "guidance, see ha_get_skill_home_assistant_best_practices."
572
+ ),
573
+ "ha_config_get_script": (
574
+ "Get a Home Assistant script configuration by "
575
+ "script_id or entity_id. Returns the full config (sequence, "
576
+ "mode, fields) plus a stable config_hash for use with "
577
+ "python_transform on ha_config_set_script.\n\n"
578
+ "For schema details, see "
579
+ "ha_get_skill_home_assistant_best_practices."
580
+ ),
581
+ "ha_config_set_script": (
582
+ "Create or update a Home Assistant script.\n\n"
583
+ "Supports two modes: full `config` replacement, or surgical "
584
+ "`python_transform` on an existing script (requires "
585
+ "`identifier` and `config_hash` from "
586
+ "ha_config_get_script). Omit `identifier` to create a new "
587
+ "script.\n\n"
588
+ "For schema details and examples, see "
589
+ "ha_get_skill_home_assistant_best_practices."
590
+ ),
591
+ "ha_config_get_scene": (
592
+ "Get a Home Assistant scene configuration by "
593
+ "scene_id or entity_id. Returns the full config plus a "
594
+ "stable config_hash for use with python_transform on "
595
+ "ha_config_set_scene.\n\n"
596
+ "For schema details, see "
597
+ "ha_get_skill_home_assistant_best_practices."
598
+ ),
599
+ "ha_config_set_scene": (
600
+ "Create or update a Home Assistant scene.\n\n"
601
+ "Supports two modes: full `config` replacement, or surgical "
602
+ "`python_transform` on an existing scene (requires "
603
+ "`identifier` and `config_hash`).\n\n"
604
+ "For schema details and examples, see "
605
+ "ha_get_skill_home_assistant_best_practices."
606
+ ),
607
+ "ha_config_list_helpers": (
608
+ "List Home Assistant helpers of a given simple type. "
609
+ "Accepts the 12 storage-backed helper types only: "
610
+ "input_button, input_boolean, input_select, input_number, "
611
+ "input_text, input_datetime, counter, timer, schedule, "
612
+ "zone, person, tag. Flow-based helpers (template, group, "
613
+ "utility_meter, derivative, statistics, trend, threshold, "
614
+ "filter, switch_as_x, etc.) cannot be listed through this "
615
+ "tool — use ha_search_entities or ha_deep_search.\n\n"
616
+ "For per-type schemas, see ha_get_helper_schema and "
617
+ "ha_get_skill_home_assistant_best_practices."
618
+ ),
619
+ "ha_config_set_helper": (
620
+ "Create or update a Home Assistant helper. Supports all "
621
+ "supported helper types: the simple types (input_*, "
622
+ "counter, timer, schedule, zone, person, tag) and the "
623
+ "flow-based types (template, group, utility_meter, "
624
+ "derivative, statistics, trend, threshold, filter, "
625
+ "switch_as_x, and others).\n\n"
626
+ "For per-type config schemas, call "
627
+ "ha_get_helper_schema(helper_type) first. For decision "
628
+ "matrix and worked examples (which helper type for which "
629
+ "use case), see ha_get_skill_home_assistant_best_practices."
630
+ ),
631
+ "ha_config_get_dashboard": (
632
+ "Get Home Assistant dashboard info (list mode, search "
633
+ "mode, or full config).\n\n"
634
+ "Three modes: (1) list — `list_only=True` returns all "
635
+ "storage-mode dashboards with metadata. (2) search — pass "
636
+ "any of `entity_id`, `card_type`, `heading` to find cards "
637
+ "(and their `jq_path`) inside a specific dashboard; the "
638
+ "result includes a `config_hash` you can pair with "
639
+ "ha_config_set_dashboard(python_transform=...) to edit "
640
+ "matched cards surgically. (3) get — no search params "
641
+ "returns the full Lovelace config plus a stable "
642
+ "`config_hash`. Use `url_path='default'` for the main "
643
+ "dashboard.\n\n"
644
+ "For card-type taxonomy and search workflow examples, see "
645
+ "ha_get_skill_home_assistant_best_practices."
646
+ ),
647
+ "ha_config_set_dashboard": (
648
+ "Create or update a Home Assistant dashboard.\n\n"
649
+ "Supports two modes: full `config` replacement (new "
650
+ "dashboards or full restructures), or surgical "
651
+ "`python_transform` on an existing dashboard (requires "
652
+ "`config_hash` from ha_config_get_dashboard; recommended "
653
+ "for edits). Use `url_path` of 'default' or 'lovelace' "
654
+ "to target the built-in dashboard.\n\n"
655
+ "For card types, layout patterns, and python_transform "
656
+ "security rules, see "
657
+ "ha_get_skill_home_assistant_best_practices."
658
+ ),
659
+ "ha_call_service": (
660
+ "Execute a Home Assistant service to control entities or "
661
+ "trigger automations. Calls `<domain>.<service>` "
662
+ "(e.g., light.turn_on, climate.set_temperature). Use "
663
+ "ha_search_entities to find entity IDs and ha_get_state "
664
+ "to read current values before changing them.\n\n"
665
+ "For service-parameter details and per-domain guidance, "
666
+ "see ha_get_skill_home_assistant_best_practices."
667
+ ),
668
+ "ha_config_set_yaml": (
669
+ "Update raw YAML in configuration.yaml or packages/*.yaml "
670
+ "via add / replace / remove on a single top-level key "
671
+ "(LAST RESORT).\n\n"
672
+ "Dedicated tools (ha_config_set_automation, "
673
+ "ha_config_set_script, ha_config_set_scene, "
674
+ "ha_config_set_helper) cover almost every use case and "
675
+ "should be preferred. Use this only for YAML-only "
676
+ "integrations (command_line, rest, shell_command, notify) "
677
+ "or registering YAML-mode dashboards via "
678
+ "`lovelace.dashboards.<url_path>`. Most edits require a "
679
+ "full HA restart; template, mqtt, and group support "
680
+ "reload.\n\n"
681
+ "For routing guidance and the full allowlist, see "
682
+ "ha_get_skill_home_assistant_best_practices."
683
+ ),
684
+ }
685
+
539
686
  # Description overrides that REPLACE the original description for BM25.
540
687
  # Used to narrow overly broad tools so they stop matching generic queries
541
688
  # against ha-mcp's internal BM25 search tool. Only applied when
@@ -552,6 +699,61 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
552
699
  ),
553
700
  }
554
701
 
702
+ def _apply_lite_docstrings(self) -> None:
703
+ """Swap heavy tool descriptions for shorter variants if enabled.
704
+
705
+ Beta feature gated on ``settings.enable_lite_docstrings`` /
706
+ ``ENABLE_LITE_DOCSTRINGS=true``. Replaces the description on
707
+ each tool listed in ``_LITE_DOCSTRINGS`` with a shorter variant
708
+ that defers detail to
709
+ ``ha_get_skill_home_assistant_best_practices``. Tools not in the
710
+ mapping pass through unchanged.
711
+
712
+ Emits a startup WARNING when enabled so non-addon users (Docker,
713
+ uvx, pip) see the trade-off in their logs — the addon UI surfaces
714
+ the same warning via the toggle description. A second WARNING is
715
+ emitted if the transform install fails, so users don't silently
716
+ get full descriptions back after explicitly enabling the toggle.
717
+
718
+ Runs before ``_apply_search_keyword_enrichment`` so BM25 keywords
719
+ append to the lite text instead of the discarded full description.
720
+ """
721
+ if not self.settings.enable_lite_docstrings:
722
+ return
723
+
724
+ logger.warning(
725
+ "ENABLE_LITE_DOCSTRINGS=true: replacing %d tool descriptions "
726
+ "with shorter variants. This reduces idle catalog token usage "
727
+ "but may degrade LLM performance — the trimmed descriptions "
728
+ "rely on the LLM calling ha_get_skill_home_assistant_best_practices "
729
+ "(or reading skill:// resources) for detail, which is not "
730
+ "guaranteed. See docs/beta.md.",
731
+ len(self._LITE_DOCSTRINGS),
732
+ )
733
+
734
+ try:
735
+ from .transforms import LiteDocstringsTransform
736
+ except ImportError:
737
+ logger.exception(
738
+ "LiteDocstringsTransform not importable — please file a "
739
+ "bug. ENABLE_LITE_DOCSTRINGS=true is in effect but full "
740
+ "tool descriptions will be exposed."
741
+ )
742
+ return
743
+
744
+ try:
745
+ self.mcp.add_transform(
746
+ LiteDocstringsTransform(replacements=self._LITE_DOCSTRINGS)
747
+ )
748
+ except Exception:
749
+ logger.exception("Failed to apply LiteDocstringsTransform")
750
+ logger.warning(
751
+ "ENABLE_LITE_DOCSTRINGS=true was set but the transform "
752
+ "failed to install — full tool descriptions remain in "
753
+ "effect. Catalog token usage will be unchanged from the "
754
+ "default."
755
+ )
756
+
555
757
  def _apply_search_keyword_enrichment(self) -> None:
556
758
  """Append BM25 keyword boosts to tool descriptions.
557
759
 
@@ -0,0 +1,15 @@
1
+ """Custom FastMCP transforms for ha-mcp."""
2
+
3
+ from .categorized_search import (
4
+ DEFAULT_PINNED_TOOLS,
5
+ CategorizedSearchTransform,
6
+ SearchKeywordsTransform,
7
+ )
8
+ from .lite_docstrings import LiteDocstringsTransform
9
+
10
+ __all__ = [
11
+ "CategorizedSearchTransform",
12
+ "DEFAULT_PINNED_TOOLS",
13
+ "LiteDocstringsTransform",
14
+ "SearchKeywordsTransform",
15
+ ]
@@ -0,0 +1,62 @@
1
+ """Lite docstrings transform for ha-mcp.
2
+
3
+ Replaces the description on a configurable set of tools with a shorter,
4
+ "lite" variant that defers detailed guidance to the
5
+ ``ha_get_skill_home_assistant_best_practices`` tool (or its skill://
6
+ resource). Trades catalog token usage for the assumption that the LLM
7
+ will read the skill when it needs more detail — see issue #1062.
8
+
9
+ Opt-in via ``enable_lite_docstrings``; see ``docs/beta.md`` for trade-offs.
10
+ Tools NOT in the mapping pass through unchanged, so smaller tools keep
11
+ their existing descriptions without us having to enumerate them.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from collections.abc import Sequence
17
+ from typing import TYPE_CHECKING
18
+
19
+ from fastmcp.server.transforms import Transform
20
+ from fastmcp.tools import Tool
21
+
22
+ if TYPE_CHECKING:
23
+ from fastmcp.server.transforms import GetToolNext
24
+ from fastmcp.utilities.versions import VersionSpec
25
+
26
+
27
+ class LiteDocstringsTransform(Transform):
28
+ """Replace heavy tool descriptions with shorter variants.
29
+
30
+ Mirrors ``SearchKeywordsTransform``: an empty mapping is a no-op,
31
+ so installing the transform unconditionally would be safe. The
32
+ caller (``server._apply_lite_docstrings``) only installs it when
33
+ the feature flag is on, but the empty-dict-as-no-op contract keeps
34
+ the transform self-contained.
35
+
36
+ The transform deliberately does not auto-append a pointer to
37
+ ``ha_get_skill_home_assistant_best_practices`` — the mapped lite
38
+ description owns its own pointer text so per-tool wording can stay
39
+ natural.
40
+ """
41
+
42
+ def __init__(self, replacements: dict[str, str] | None = None) -> None:
43
+ self._replacements: dict[str, str] = replacements or {}
44
+
45
+ def _rewrite(self, tool: Tool) -> Tool:
46
+ lite = self._replacements.get(tool.name)
47
+ if lite is None:
48
+ return tool
49
+ return tool.model_copy(update={"description": lite})
50
+
51
+ async def list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
52
+ return [self._rewrite(t) for t in tools]
53
+
54
+ async def get_tool(
55
+ self,
56
+ name: str,
57
+ call_next: GetToolNext,
58
+ *,
59
+ version: VersionSpec | None = None,
60
+ ) -> Tool | None:
61
+ tool = await call_next(name, version=version)
62
+ return self._rewrite(tool) if tool else None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.4.1.dev500
3
+ Version: 7.4.1.dev501
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
@@ -88,6 +88,7 @@ src/ha_mcp/tools/tools_zones.py
88
88
  src/ha_mcp/tools/util_helpers.py
89
89
  src/ha_mcp/transforms/__init__.py
90
90
  src/ha_mcp/transforms/categorized_search.py
91
+ src/ha_mcp/transforms/lite_docstrings.py
91
92
  src/ha_mcp/utils/__init__.py
92
93
  src/ha_mcp/utils/config_hash.py
93
94
  src/ha_mcp/utils/data_paths.py
@@ -1,9 +0,0 @@
1
- """Custom FastMCP transforms for ha-mcp."""
2
-
3
- from .categorized_search import (
4
- DEFAULT_PINNED_TOOLS,
5
- CategorizedSearchTransform,
6
- SearchKeywordsTransform,
7
- )
8
-
9
- __all__ = ["CategorizedSearchTransform", "DEFAULT_PINNED_TOOLS", "SearchKeywordsTransform"]