diff --git a/tests/hermes_cli/test_completer_config_reads.py b/tests/hermes_cli/test_completer_config_reads.py index 3318f44ae5..1db4814c50 100644 --- a/tests/hermes_cli/test_completer_config_reads.py +++ b/tests/hermes_cli/test_completer_config_reads.py @@ -52,9 +52,13 @@ class TestToolsCompletionsReadonlyConfig: return {} # The completer imports the loader inside the function, so patch the - # source module. + # source module. Portable-MCP lookup is stubbed because it triggers + # one-time plugin discovery (which legitimately calls load_config + # during process init) — this test asserts on the completer's own + # per-keystroke reads, not discovery's one-off startup reads. with patch("hermes_cli.config.load_config", counting_deepcopy), \ patch("hermes_cli.config.load_config_readonly", counting_readonly), \ + patch("hermes_cli.plugins.get_portable_mcp_server_names_nowait", lambda: set()), \ patch("hermes_cli.tools_config._get_plugin_toolset_keys", lambda: set()), \ patch("hermes_cli.tools_config._homeassistant_credentials_present", lambda: False), \ patch("hermes_cli.tools_config._xai_credentials_present", lambda: False): diff --git a/toolsets.py b/toolsets.py index 46ad8205d9..8bb86024f5 100644 --- a/toolsets.py +++ b/toolsets.py @@ -870,6 +870,10 @@ def resolve_toolset(name: str, visited: Set[str] = None, *, include_registry: bo except Exception: registry_id = 0 generation = 0 + # Entries from previous registry generations are never hit again; + # keep the memo bounded across long sessions with many MCP refreshes. + if len(_resolve_toolset_memo) >= 256: + _resolve_toolset_memo.clear() _resolve_toolset_memo[(name, include_registry, registry_id, generation)] = list(result) return result