Skip to content

Commit fe8440a

Browse files
kingpanther13claude
andcommitted
fix: require restart to apply tool visibility changes
Runtime mcp.enable/disable calls accumulate visibility transforms in the provider's _transforms list every save, causing stale transforms to pile up. More importantly, they don't reliably remove tools from the LLM's tool list in practice — tools still appear in list_tools() output with full schema, just fail at call time with "Unknown tool". New approach: save changes to tool_config.json and require an add-on restart. Startup-time apply_tool_visibility() reads the config and applies visibility once, cleanly. Disabled tools are then fully absent from list_tools() on next startup. - Remove runtime mcp.enable/disable from POST handler - Show "Saved — restart required" status after save - Show prominent red restart-required banner in UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c3beb1e commit fe8440a

1 file changed

Lines changed: 23 additions & 33 deletions

File tree

src/ha_mcp/settings_ui.py

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,10 @@ def apply_tool_visibility(
308308
.pin-notice { background: #3a2e1a; border: 1px solid #7a5a1a; border-radius: 10px;
309309
padding: 10px 16px; margin-bottom: 12px; font-size: 0.85rem; color: #ffd680; display: none; }
310310
.pin-notice.show { display: block; }
311+
.restart-notice { background: #3a1a1a; border: 1px solid #7a1a1a; border-radius: 10px;
312+
padding: 12px 16px; margin-bottom: 12px; font-size: 0.9rem; color: #ff9090;
313+
font-weight: 500; display: none; }
314+
.restart-notice.show { display: block; }
311315
</style>
312316
</head>
313317
<body>
@@ -324,6 +328,10 @@ def apply_tool_visibility(
324328
configuration. Without Tool Search, all enabled tools are always visible
325329
and pinning has no extra effect.
326330
</div>
331+
<div class="restart-notice" id="restartNotice">
332+
⚠ Changes saved. Restart the add-on for them to take effect — disabled
333+
tools will be fully removed from the MCP tool list on next startup.
334+
</div>
327335
<div class="summary" id="summary"></div>
328336
<input type="text" class="search" id="search" placeholder="Search tools...">
329337
<div id="groups"></div>
@@ -527,7 +535,8 @@ def apply_tool_visibility(
527535
body: JSON.stringify({states: toolStates}),
528536
});
529537
if (resp.ok) {
530-
updateStatus('Saved', true);
538+
updateStatus('Saved — restart required', true);
539+
document.getElementById('restartNotice').classList.add('show');
531540
} else {
532541
updateStatus('Save failed!');
533542
}
@@ -602,35 +611,16 @@ async def _save_tools(request: Request) -> JSONResponse:
602611
config["tools"] = states
603612
save_tool_config(config)
604613

605-
disabled_names: set[str] = set()
606-
pinned_names: set[str] = set()
607-
608-
for name, state in states.items():
609-
if state == "disabled":
610-
disabled_names.add(name)
611-
elif state == "pinned":
612-
pinned_names.add(name)
613-
614-
if not server.settings.enable_yaml_config_editing:
615-
disabled_names.add("ha_config_set_yaml")
616-
617-
disabled_names -= MANDATORY_TOOLS
618-
619-
try:
620-
all_tools = await _get_tool_metadata(server)
621-
mcp.enable(names={t["name"] for t in all_tools})
622-
if disabled_names:
623-
mcp.disable(names=disabled_names)
624-
mcp.enable(names=MANDATORY_TOOLS)
625-
logger.info(
626-
"Applied tool visibility: %d disabled, %d pinned",
627-
len(disabled_names), len(pinned_names),
628-
)
629-
except Exception:
630-
logger.exception("Failed to apply tool visibility")
631-
return JSONResponse(
632-
{"success": False, "error": {"code": "INTERNAL_ERROR", "message": "Failed to apply tool visibility"}},
633-
status_code=500,
634-
)
635-
636-
return JSONResponse({"success": True, "disabled": len(disabled_names), "pinned": len(pinned_names)})
614+
disabled_count = sum(1 for s in states.values() if s == "disabled")
615+
pinned_count = sum(1 for s in states.values() if s == "pinned")
616+
logger.info(
617+
"Saved tool config (restart required to apply): %d disabled, %d pinned",
618+
disabled_count, pinned_count,
619+
)
620+
621+
return JSONResponse({
622+
"success": True,
623+
"disabled": disabled_count,
624+
"pinned": pinned_count,
625+
"restart_required": True,
626+
})

0 commit comments

Comments
 (0)