From 7fe6782a25f4aeb6b792162c946cba825813beef Mon Sep 17 00:00:00 2001 From: Jonathan Barket Date: Mon, 6 Apr 2026 23:03:14 -0500 Subject: [PATCH] feat(tools): add "no_mcp" sentinel to exclude MCP servers per platform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, MCP servers are included on all platforms by default. If a platform's toolset list does not explicitly name any MCP servers, every globally enabled MCP server is injected. There is no way to opt a platform out of MCP servers entirely. This matters for the API server platform when used as an execution backend — each spawned agent session gets the full MCP tool schema injected into its system prompt, dramatically inflating token usage (e.g. 57K tokens vs 9K without MCP tools) and slowing response times. Add a "no_mcp" sentinel value for platform_toolsets. When present in a platform's toolset list, all MCP servers are excluded for that platform. Other platforms are unaffected. Usage in config.yaml: platform_toolsets: api_server: - terminal - file - web - no_mcp # exclude all MCP servers The sentinel is filtered out of the final toolset — it does not appear as an actual toolset name. --- hermes_cli/tools_config.py | 12 ++++++--- tests/hermes_cli/test_tools_config.py | 39 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/hermes_cli/tools_config.py b/hermes_cli/tools_config.py index cddc664b..65525d27 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 b02b3c1f..7371c89d 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(