diff --git a/hermes_cli/tools_config.py b/hermes_cli/tools_config.py index cddc664b4..65525d27d 100644 --- a/hermes_cli/tools_config.py +++ b/hermes_cli/tools_config.py @@ -554,6 +554,7 @@ def _get_platform_tools( # MCP servers are expected to be available on all platforms by default. # If the platform explicitly lists one or more MCP server names, treat that # as an allowlist. Otherwise include every globally enabled MCP server. + # Special sentinel: "no_mcp" in the toolset list disables all MCP servers. mcp_servers = config.get("mcp_servers") or {} enabled_mcp_servers = { name @@ -561,10 +562,15 @@ def _get_platform_tools( if isinstance(server_cfg, dict) and _parse_enabled_flag(server_cfg.get("enabled", True), default=True) } - explicit_mcp_servers = explicit_passthrough & enabled_mcp_servers - enabled_toolsets.update(explicit_passthrough - enabled_mcp_servers) + # Allow "no_mcp" sentinel to opt out of all MCP servers for this platform + if "no_mcp" in toolset_names: + explicit_mcp_servers = set() + enabled_toolsets.update(explicit_passthrough - enabled_mcp_servers - {"no_mcp"}) + else: + explicit_mcp_servers = explicit_passthrough & enabled_mcp_servers + enabled_toolsets.update(explicit_passthrough - enabled_mcp_servers) if include_default_mcp_servers: - if explicit_mcp_servers: + if explicit_mcp_servers or "no_mcp" in toolset_names: enabled_toolsets.update(explicit_mcp_servers) else: enabled_toolsets.update(enabled_mcp_servers) diff --git a/tests/hermes_cli/test_tools_config.py b/tests/hermes_cli/test_tools_config.py index b02b3c1fc..7371c89df 100644 --- a/tests/hermes_cli/test_tools_config.py +++ b/tests/hermes_cli/test_tools_config.py @@ -72,6 +72,45 @@ def test_get_platform_tools_keeps_enabled_mcp_servers_with_explicit_builtin_sele assert "web-search-prime" in enabled +def test_get_platform_tools_no_mcp_sentinel_excludes_all_mcp_servers(): + """The 'no_mcp' sentinel in platform_toolsets excludes all MCP servers.""" + config = { + "platform_toolsets": {"cli": ["web", "terminal", "no_mcp"]}, + "mcp_servers": { + "exa": {"url": "https://mcp.exa.ai/mcp"}, + "web-search-prime": {"url": "https://api.z.ai/api/mcp/web_search_prime/mcp"}, + }, + } + + enabled = _get_platform_tools(config, "cli") + + assert "web" in enabled + assert "terminal" in enabled + assert "exa" not in enabled + assert "web-search-prime" not in enabled + assert "no_mcp" not in enabled + + +def test_get_platform_tools_no_mcp_sentinel_does_not_affect_other_platforms(): + """The 'no_mcp' sentinel only affects the platform it's configured on.""" + config = { + "platform_toolsets": { + "api_server": ["web", "terminal", "no_mcp"], + }, + "mcp_servers": { + "exa": {"url": "https://mcp.exa.ai/mcp"}, + }, + } + + # api_server should exclude MCP + api_enabled = _get_platform_tools(config, "api_server") + assert "exa" not in api_enabled + + # cli (not configured with no_mcp) should include MCP + cli_enabled = _get_platform_tools(config, "cli") + assert "exa" in cli_enabled + + def test_toolset_has_keys_for_vision_accepts_codex_auth(tmp_path, monkeypatch): monkeypatch.setenv("HERMES_HOME", str(tmp_path)) (tmp_path / "auth.json").write_text(