@@ -143,6 +143,10 @@ def _initialize_server(self) -> None:
143143 # Register bundled skills as MCP resources
144144 self ._register_skills ()
145145
146+ # Apply user-configured tool visibility (after all tools registered,
147+ # before tool search transform wraps them)
148+ self ._apply_settings_ui ()
149+
146150 # Apply tool search transform (must come after all tools and
147151 # ResourcesAsTools are registered so it can wrap everything)
148152 self ._apply_tool_search ()
@@ -306,6 +310,23 @@ def _build_skill_block(self, skill_name: str, main_file: Path) -> str | None:
306310
307311 return f"\n ### Skill: { skill_name } ({ uri } )\n { description .strip ()} "
308312
313+ def _apply_settings_ui (self ) -> None :
314+ """Register settings web UI and apply persisted tool visibility."""
315+ from .settings_ui import (
316+ apply_tool_visibility ,
317+ load_tool_config ,
318+ register_settings_routes ,
319+ )
320+
321+ register_settings_routes (self .mcp , self )
322+
323+ config = load_tool_config (self .settings )
324+ if config :
325+ pinned = apply_tool_visibility (self .mcp , config , self .settings )
326+ if pinned :
327+ self ._user_pinned_tools = list (pinned )
328+ logger .info ("Applied persisted tool config (%d entries)" , len (config .get ("tools" , {})))
329+
309330 # Tools pinned outside the search transform for individual permission gating.
310331 # These are always visible in list_tools() regardless of search transform.
311332 _PINNED_TOOLS : ClassVar [list [str ]] = list (DEFAULT_PINNED_TOOLS )
@@ -419,8 +440,9 @@ def _apply_tool_search(self) -> None:
419440 )
420441 return
421442
422- # Build the always_visible list
443+ # Build the always_visible list: defaults + user-configured pins
423444 pinned = list (self ._PINNED_TOOLS )
445+ pinned .extend (getattr (self , "_user_pinned_tools" , []))
424446
425447 # Pin ResourcesAsTools and skill guidance tools if skills-as-tools is enabled
426448 if self .settings .enable_skills_as_tools :
0 commit comments