Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4660543
feat: web-based settings UI for per-tool enable/disable/pin
kingpanther13 Apr 12, 2026
0089c27
fix: address CI failures and Gemini review comments
kingpanther13 Apr 12, 2026
915cd66
feat: add ingress config and text field fallbacks for addon
kingpanther13 Apr 12, 2026
d8623e6
fix: add ingress_stream, serve settings at root for ingress
kingpanther13 Apr 12, 2026
59c1eda
fix: add ingress + text fields to addon-dev config
kingpanther13 Apr 12, 2026
041f1b6
fix: revert homeassistant-addon/ to master (release pipeline handles it)
kingpanther13 Apr 12, 2026
1da91b2
fix: use live FastMCP list_tools() and relative fetch URLs
kingpanther13 Apr 13, 2026
0b5e8db
feat: dual toggles, feature-gated stubs, tool_search_max_results, gro…
kingpanther13 Apr 13, 2026
ad46b8a
fix: remove unused type: ignore on _list_tools
kingpanther13 Apr 13, 2026
c3beb1e
feat: preserve group open state, add per-group master toggle
kingpanther13 Apr 13, 2026
fe8440a
fix: require restart to apply tool visibility changes
kingpanther13 Apr 13, 2026
93a3e52
feat: add in-UI restart button using Supervisor API
kingpanther13 Apr 13, 2026
1b05ca9
fix: treat dropped connection as success in restart handler
kingpanther13 Apr 13, 2026
d8cc976
docs: document settings UI in addon DOCS and .env.example
kingpanther13 Apr 13, 2026
9c6e8de
fix: address Gemini review feedback
kingpanther13 Apr 13, 2026
c44b323
fix: remove unnecessary type annotation quotes (UP037)
kingpanther13 Apr 13, 2026
72cd3ca
Merge upstream/master into feat/settings-ui + address Patch76 review
kingpanther13 May 2, 2026
3477a39
fix: pass real FastMCP to register_settings_routes (mypy)
kingpanther13 May 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/ha_mcp/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ class Settings(BaseSettings):
# files. Disabled by default; only for YAML-only features with no UI/API path.
enable_yaml_config_editing: bool = Field(False, alias="ENABLE_YAML_CONFIG_EDITING")

# Seed values for tool visibility (comma-separated tool names).
# Used as initial config when no tool_config.json exists.
# The web settings UI (/settings) is the primary interface for managing these.
disabled_tools: str = Field("", alias="DISABLED_TOOLS")
pinned_tools: str = Field("", alias="PINNED_TOOLS")

@model_validator(mode="after")
def _skills_dependency(self) -> "Settings":
"""Auto-enable skills (resources) when skills-as-tools is on.
Expand Down
24 changes: 23 additions & 1 deletion src/ha_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ def _initialize_server(self) -> None:
# Register bundled skills as MCP resources
self._register_skills()

# Apply user-configured tool visibility (after all tools registered,
# before tool search transform wraps them)
self._apply_settings_ui()

# Apply tool search transform (must come after all tools and
# ResourcesAsTools are registered so it can wrap everything)
self._apply_tool_search()
Expand Down Expand Up @@ -306,6 +310,23 @@ def _build_skill_block(self, skill_name: str, main_file: Path) -> str | None:

return f"\n### Skill: {skill_name} ({uri})\n{description.strip()}"

def _apply_settings_ui(self) -> None:
"""Register settings web UI and apply persisted tool visibility."""
from .settings_ui import (
apply_tool_visibility,
load_tool_config,
register_settings_routes,
)

register_settings_routes(self.mcp, self)

config = load_tool_config(self.settings)
if config:
pinned = apply_tool_visibility(self.mcp, config, self.settings)
if pinned:
self._user_pinned_tools = list(pinned)
logger.info("Applied persisted tool config (%d entries)", len(config.get("tools", {})))

# Tools pinned outside the search transform for individual permission gating.
# These are always visible in list_tools() regardless of search transform.
_PINNED_TOOLS: ClassVar[list[str]] = list(DEFAULT_PINNED_TOOLS)
Expand Down Expand Up @@ -419,8 +440,9 @@ def _apply_tool_search(self) -> None:
)
return

# Build the always_visible list
# Build the always_visible list: defaults + user-configured pins
pinned = list(self._PINNED_TOOLS)
pinned.extend(getattr(self, "_user_pinned_tools", []))
Comment thread
kingpanther13 marked this conversation as resolved.
Outdated

# Pin ResourcesAsTools and skill guidance tools if skills-as-tools is enabled
if self.settings.enable_skills_as_tools:
Expand Down
Loading