mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-20 15:33:54 +00:00
fix(mcp): address adversarial review round 2 (stale-publish race, parity holes)
Second review pass (Codex + Hermes subagent). Codex reproduced a real race with a two-thread harness; both converged on the remaining issues. - Generation-aware publish (fixes a lost-update race): two refresh callers (the late-refresh daemon and the between-turns prologue around turn 1) could each compute a snapshot outside the lock; a SLOWER caller holding an OLDER registry generation could acquire the publish lock after a newer caller and clobber it, deleting just-landed tools. refresh_agent_mcp_tools now captures registry._generation before computing and refuses to publish a stale set; agent._tool_snapshot_generation tracks the published generation. - Context-engine routing names (_context_engine_tool_names) are now staged on a local and published atomically with the snapshot, and only claimed when this rebuild actually appended the schema — matching agent_init's dedup so a registry/plugin tool of the same name keeps its own dispatch. (Previously mutated live, before the publish lock, and on no-change refreshes.) - CLI /reload-mcp: self.enabled_toolsets is resolved once at startup, so a server newly ENABLED in config mid-session wasn't picked up (TUI already re-resolved). Merge now-connected MCP server names into the override (unless the user pinned all/*), mirroring startup, and keep self.enabled_toolsets in sync. Closes the CLI/TUI parity hole. - ACP (acp_adapter/server.py) routed through the shared helper — it was a 5th sibling rebuild that re-injected memory tools but NOT context-engine tools and bypassed the atomic/name-diff path (inert today, fragile). - mcp_startup._resolve_discovery_timeout pulls its default from DEFAULT_CONFIG (single source of truth) instead of a stale hardcoded 5.0 literal. - Tests: stale-generation-no-clobber, _skip_mcp_refresh honored, timeout fallback uses DEFAULT_CONFIG.
This commit is contained in:
parent
b6e2a54a94
commit
88d523220f
7 changed files with 137 additions and 40 deletions
21
cli.py
21
cli.py
|
|
@ -9668,13 +9668,28 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
|
|||
# memory-provider and context-engine tools survive the rebuild).
|
||||
if self.agent is not None:
|
||||
from tools.mcp_tool import refresh_agent_mcp_tools
|
||||
# Explicit reload: re-resolve enabled toolsets so a server the
|
||||
# user just enabled in config this session is picked up.
|
||||
# Explicit reload: pick up MCP servers the user ENABLED in config
|
||||
# this session. self.enabled_toolsets was resolved once at
|
||||
# startup; merge in any now-connected server names (unless the
|
||||
# user pinned `all`/`*`, which already includes everything) so a
|
||||
# freshly-added server isn't filtered out. Mirrors startup, where
|
||||
# MCP server names are part of enabled_toolsets (see __init__).
|
||||
enabled_override = None
|
||||
et = self.enabled_toolsets
|
||||
if et and "all" not in et and "*" not in et:
|
||||
merged = list(et)
|
||||
for _name in sorted(connected_servers):
|
||||
if _name not in merged:
|
||||
merged.append(_name)
|
||||
enabled_override = merged
|
||||
refresh_agent_mcp_tools(
|
||||
self.agent,
|
||||
enabled_override=self.enabled_toolsets,
|
||||
enabled_override=enabled_override,
|
||||
quiet_mode=True,
|
||||
)
|
||||
# Keep the CLI's own list in sync with what the agent now uses.
|
||||
if enabled_override is not None:
|
||||
self.enabled_toolsets = enabled_override
|
||||
|
||||
# Inject a message at the END of conversation history so the
|
||||
# model knows tools changed. Appended after all existing
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue