ha-mcp-dev 7.4.0.dev409__tar.gz → 7.4.1.dev411__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 (105) hide show
  1. {ha_mcp_dev-7.4.0.dev409/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.4.1.dev411}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/__main__.py +6 -0
  4. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/config.py +11 -0
  5. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/server.py +34 -3
  6. ha_mcp_dev-7.4.1.dev411/src/ha_mcp/settings_ui.py +862 -0
  7. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  8. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
  9. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/LICENSE +0 -0
  10. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/MANIFEST.in +0 -0
  11. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/README.md +0 -0
  12. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/setup.cfg +0 -0
  13. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/__init__.py +0 -0
  14. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/_pypi_marker +0 -0
  15. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/_version.py +0 -0
  16. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/auth/__init__.py +0 -0
  17. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/auth/consent_form.py +0 -0
  18. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/auth/provider.py +0 -0
  19. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/client/__init__.py +0 -0
  20. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/client/rest_client.py +0 -0
  21. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/client/websocket_client.py +0 -0
  22. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/client/websocket_listener.py +0 -0
  23. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/errors.py +0 -0
  24. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/py.typed +0 -0
  25. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  26. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  27. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  28. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  29. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  30. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  31. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  32. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  33. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  34. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  35. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  36. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  37. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  38. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  39. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  40. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  41. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  42. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  43. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  44. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  45. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/smoke_test.py +0 -0
  46. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/__init__.py +0 -0
  47. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/backup.py +0 -0
  48. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  49. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/device_control.py +0 -0
  50. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/enhanced.py +0 -0
  51. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/helpers.py +0 -0
  52. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/reference_validator.py +0 -0
  53. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/registry.py +0 -0
  54. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/smart_search.py +0 -0
  55. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_addons.py +0 -0
  56. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_areas.py +0 -0
  57. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  58. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  59. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_calendar.py +0 -0
  60. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_camera.py +0 -0
  61. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_categories.py +0 -0
  62. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  63. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  64. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  65. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  66. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  67. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_energy.py +0 -0
  68. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_entities.py +0 -0
  69. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  70. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_groups.py +0 -0
  71. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_hacs.py +0 -0
  72. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_history.py +0 -0
  73. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_integrations.py +0 -0
  74. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_labels.py +0 -0
  75. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  76. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_registry.py +0 -0
  77. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_resources.py +0 -0
  78. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_search.py +0 -0
  79. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_service.py +0 -0
  80. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_services.py +0 -0
  81. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_system.py +0 -0
  82. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_todo.py +0 -0
  83. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_traces.py +0 -0
  84. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_updates.py +0 -0
  85. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_utility.py +0 -0
  86. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  87. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  88. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/tools_zones.py +0 -0
  89. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/tools/util_helpers.py +0 -0
  90. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/transforms/__init__.py +0 -0
  91. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/transforms/categorized_search.py +0 -0
  92. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/utils/__init__.py +0 -0
  93. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/utils/config_hash.py +0 -0
  94. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/utils/domain_handlers.py +0 -0
  95. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  96. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/utils/operation_manager.py +0 -0
  97. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/utils/python_sandbox.py +0 -0
  98. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp/utils/usage_logger.py +0 -0
  99. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  100. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  101. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  102. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  103. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/tests/__init__.py +0 -0
  104. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/tests/test_constants.py +0 -0
  105. {ha_mcp_dev-7.4.0.dev409 → ha_mcp_dev-7.4.1.dev411}/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.0.dev409
3
+ Version: 7.4.1.dev411
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.0.dev409"
7
+ version = "7.4.1.dev411"
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"
@@ -733,8 +733,11 @@ def _run_http_server(transport: str, default_port: int = 8086) -> None:
733
733
  transport: Transport type (http or sse).
734
734
  default_port: Default port to use if MCP_PORT env var is not set.
735
735
  """
736
+ from ha_mcp.settings_ui import register_settings_routes
737
+
736
738
  port, path = _get_http_runtime(default_port)
737
739
  register_browser_landing(_get_mcp(), path)
740
+ register_settings_routes(_get_mcp(), _get_server(), secret_path=path)
738
741
 
739
742
  _run_entrypoint(
740
743
  _run_http_with_graceful_shutdown(transport, port, path),
@@ -871,6 +874,9 @@ async def _run_oauth_server(ha_url: str, base_url: str, port: int, path: str) ->
871
874
  logger.info("Server created with OAuthProxyClient")
872
875
  register_browser_landing(mcp, path)
873
876
 
877
+ from ha_mcp.settings_ui import register_settings_routes
878
+ register_settings_routes(mcp, _server, secret_path=path)
879
+
874
880
  tools = await mcp.list_tools()
875
881
  logger.info(
876
882
  f"Starting OAuth-enabled MCP server with {len(tools)} tools on {base_url}{path}"
@@ -110,6 +110,17 @@ class Settings(BaseSettings):
110
110
  # files. Disabled by default; only for YAML-only features with no UI/API path.
111
111
  enable_yaml_config_editing: bool = Field(False, alias="ENABLE_YAML_CONFIG_EDITING")
112
112
 
113
+ # Seed values for tool visibility (comma-separated tool names).
114
+ # Used as initial config when no tool_config.json exists.
115
+ # The web settings UI (/settings) is the primary interface for managing these.
116
+ disabled_tools: str = Field("", alias="DISABLED_TOOLS")
117
+ pinned_tools: str = Field("", alias="PINNED_TOOLS")
118
+
119
+ # Max results returned by ha_search_tools. Pydantic enforces the
120
+ # 2-10 range; the addon-dev schema also uses ``int(2,10)?`` so the
121
+ # supervisor UI rejects out-of-range values before they reach env vars.
122
+ tool_search_max_results: int = Field(5, ge=2, le=10, alias="TOOL_SEARCH_MAX_RESULTS")
123
+
113
124
  @model_validator(mode="after")
114
125
  def _skills_dependency(self) -> "Settings":
115
126
  """Auto-enable skills (resources) when skills-as-tools is on.
@@ -70,6 +70,8 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
70
70
  self._device_tools: Any = None
71
71
  self._tools_registry: ToolsRegistry | None = None
72
72
  self._skill_tool_names: list[str] = []
73
+ # Populated by _apply_settings_visibility from tool_config.json on startup
74
+ self._user_pinned_tools: list[str] = []
73
75
 
74
76
  # Get server name/version from settings if no client provided
75
77
  if not self._client_provided:
@@ -143,6 +145,11 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
143
145
  # Register bundled skills as MCP resources
144
146
  self._register_skills()
145
147
 
148
+ # Apply user-configured tool visibility (must come before keyword
149
+ # enrichment / tool search so disabled tools are excluded from
150
+ # search indexing too).
151
+ self._apply_settings_visibility()
152
+
146
153
  # Enrich tool descriptions with BM25 keyword boosts. Runs
147
154
  # unconditionally so Claude's native deferred-tool search
148
155
  # (claude.ai) benefits even when ENABLE_TOOL_SEARCH is off.
@@ -313,6 +320,25 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
313
320
 
314
321
  return f"\n### Skill: {skill_name} ({uri})\n{description.strip()}"
315
322
 
323
+ def _apply_settings_visibility(self) -> None:
324
+ """Apply persisted tool visibility from ``tool_config.json``.
325
+
326
+ Reads the saved enable/disable/pin state and applies it to the
327
+ FastMCP instance via ``apply_tool_visibility``. HTTP routes for
328
+ the settings UI are registered separately by entry-point callers
329
+ (start.py / main_web) so they can be mounted under the secret
330
+ path; that keeps the routes inert in stdio mode and behind the
331
+ same auth posture as the MCP endpoint in HTTP mode.
332
+ """
333
+ from .settings_ui import apply_tool_visibility, load_tool_config
334
+
335
+ config = load_tool_config(self.settings)
336
+ if config:
337
+ pinned = apply_tool_visibility(self.mcp, config, self.settings)
338
+ if pinned:
339
+ self._user_pinned_tools = list(pinned)
340
+ logger.info("Applied persisted tool config (%d entries)", len(config.get("tools", {})))
341
+
316
342
  # Tools pinned outside the search transform for individual permission gating.
317
343
  # These are always visible in list_tools() regardless of search transform.
318
344
  _PINNED_TOOLS: ClassVar[list[str]] = list(DEFAULT_PINNED_TOOLS)
@@ -487,8 +513,9 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
487
513
  )
488
514
  return
489
515
 
490
- # Build the always_visible list
516
+ # Build the always_visible list: defaults + user-configured pins
491
517
  pinned = list(self._PINNED_TOOLS)
518
+ pinned.extend(self._user_pinned_tools)
492
519
 
493
520
  # Pin ResourcesAsTools and skill guidance tools if skills-as-tools is enabled
494
521
  if self.settings.enable_skills_as_tools:
@@ -513,12 +540,16 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
513
540
  try:
514
541
  self.mcp.add_transform(
515
542
  CategorizedSearchTransform(
516
- max_results=5,
543
+ max_results=self.settings.tool_search_max_results,
517
544
  always_visible=pinned,
518
545
  search_tool_description=description,
519
546
  )
520
547
  )
521
- logger.info("Tool search transform applied (%d pinned tools)", len(pinned))
548
+ logger.info(
549
+ "Tool search transform applied (%d pinned tools, max_results=%d)",
550
+ len(pinned),
551
+ self.settings.tool_search_max_results,
552
+ )
522
553
  except Exception:
523
554
  logger.exception("Failed to apply tool search transform")
524
555