From eec31b008910df8805220fad321fa94f34e63188 Mon Sep 17 00:00:00 2001 From: teknium1 Date: Mon, 2 Mar 2026 19:25:06 -0800 Subject: [PATCH] fix(mcp): /reload-mcp now updates agent tools + injects history message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CLI: After reload, refreshes self.agent.tools and valid_tool_names so the model sees updated tools on its next API call - Both CLI and Gateway: Appends a [SYSTEM: ...] message at the END of conversation history explaining what changed (added/removed/ reconnected servers, tool count). This preserves prompt-cache for the system prompt and earlier messages — only the tail changes. - Gateway already creates a new AIAgent per message so tools refresh naturally; the injected message provides context for the model --- cli.py | 38 ++++++++++++++++++++++++++++++++++++-- gateway/run.py | 25 +++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/cli.py b/cli.py index 64e90c1fd..1808d91a2 100755 --- a/cli.py +++ b/cli.py @@ -1895,7 +1895,11 @@ class HermesCLI: logging.getLogger(quiet_logger).setLevel(logging.ERROR) def _reload_mcp(self): - """Reload MCP servers: disconnect all, re-read config.yaml, reconnect.""" + """Reload MCP servers: disconnect all, re-read config.yaml, reconnect. + + After reconnecting, refreshes the agent's tool list so the model + sees the updated tools on the next turn. + """ try: from tools.mcp_tool import shutdown_mcp_servers, discover_mcp_tools, _load_mcp_config, _servers, _lock @@ -1926,10 +1930,40 @@ class HermesCLI: if removed: print(f" āž– Removed: {', '.join(sorted(removed))}") if not connected_servers: - print(" (._.) No MCP servers connected.") + print(" No MCP servers connected.") else: print(f" šŸ”§ {len(new_tools)} tool(s) available from {len(connected_servers)} server(s)") + # Refresh the agent's tool list so the model can call new tools + if self.agent is not None: + from model_tools import get_tool_definitions + self.agent.tools = get_tool_definitions( + enabled_toolsets=self.agent.enabled_toolsets + if hasattr(self.agent, "enabled_toolsets") else None, + quiet_mode=True, + ) + self.agent.valid_tool_names = { + tool["function"]["name"] for tool in self.agent.tools + } if self.agent.tools else set() + + # Inject a message at the END of conversation history so the + # model knows tools changed. Appended after all existing + # messages to preserve prompt-cache for the prefix. + change_parts = [] + if added: + change_parts.append(f"Added servers: {', '.join(sorted(added))}") + if removed: + change_parts.append(f"Removed servers: {', '.join(sorted(removed))}") + if reconnected: + change_parts.append(f"Reconnected servers: {', '.join(sorted(reconnected))}") + tool_summary = f"{len(new_tools)} MCP tool(s) now available" if new_tools else "No MCP tools available" + change_detail = ". ".join(change_parts) + ". " if change_parts else "" + self.conversation_history.append({ + "role": "user", + "content": f"[SYSTEM: MCP servers have been reloaded. {change_detail}{tool_summary}. The tool list for this conversation has been updated accordingly.]", + }) + print(f" āœ… Agent updated — {len(self.agent.tools if self.agent else [])} tool(s) available") + except Exception as e: print(f" āŒ MCP reload failed: {e}") diff --git a/gateway/run.py b/gateway/run.py index 83b973722..7471bc553 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -1422,6 +1422,31 @@ class GatewayRunner: lines.append("No MCP servers connected.") else: lines.append(f"\nšŸ”§ {len(new_tools)} tool(s) available from {len(connected_servers)} server(s)") + + # Inject a message at the END of the session history so the + # model knows tools changed on its next turn. Appended after + # all existing messages to preserve prompt-cache for the prefix. + change_parts = [] + if added: + change_parts.append(f"Added servers: {', '.join(sorted(added))}") + if removed: + change_parts.append(f"Removed servers: {', '.join(sorted(removed))}") + if reconnected: + change_parts.append(f"Reconnected servers: {', '.join(sorted(reconnected))}") + tool_summary = f"{len(new_tools)} MCP tool(s) now available" if new_tools else "No MCP tools available" + change_detail = ". ".join(change_parts) + ". " if change_parts else "" + reload_msg = { + "role": "user", + "content": f"[SYSTEM: MCP servers have been reloaded. {change_detail}{tool_summary}. The tool list for this conversation has been updated accordingly.]", + } + try: + session_entry = self.session_store.get_or_create_session(event.source) + self.session_store.append_to_transcript( + session_entry.session_id, reload_msg + ) + except Exception: + pass # Best-effort; don't fail the reload over a transcript write + return "\n".join(lines) except Exception as e: