diff --git a/cli-config.yaml.example b/cli-config.yaml.example index f1858e9b72..64927c2b68 100644 --- a/cli-config.yaml.example +++ b/cli-config.yaml.example @@ -776,7 +776,7 @@ delegation: # max_concurrent_children: 3 # Max parallel child agents (default: 3) # max_spawn_depth: 1 # Tree depth cap (1-3, default: 1 = flat). Raise to 2 or 3 to allow orchestrator children to spawn their own workers. # orchestrator_enabled: true # Kill switch for role="orchestrator" children (default: true). - # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: false). + # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection. # model: "google/gemini-3-flash-preview" # Override model for subagents (empty = inherit parent) # provider: "openrouter" # Override provider for subagents (empty = inherit parent) # # Resolves full credentials (base_url, api_key) automatically. diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 14bfb205ee..fb8c0ca1f4 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -713,9 +713,11 @@ DEFAULT_CONFIG = { "base_url": "", # direct OpenAI-compatible endpoint for subagents "api_key": "", # API key for delegation.base_url (falls back to OPENAI_API_KEY) # When delegate_task narrows child toolsets explicitly, preserve any - # MCP toolsets the parent already has enabled. Off by default so - # narrowed child toolsets stay strict unless the operator opts in. - "inherit_mcp_toolsets": False, + # MCP toolsets the parent already has enabled. On by default so + # narrowing (e.g. toolsets=["web","browser"]) expresses "I want these + # extras" without silently stripping MCP tools the parent already has. + # Set to false for strict intersection. + "inherit_mcp_toolsets": True, "max_iterations": 50, # per-subagent iteration cap (each subagent gets its own budget, # independent of the parent's max_iterations) "reasoning_effort": "", # reasoning effort for subagents: "xhigh", "high", "medium", @@ -927,7 +929,7 @@ DEFAULT_CONFIG = { }, # Config schema version - bump this when adding new required fields - "_config_version": 23, + "_config_version": 22, } # ============================================================================= diff --git a/tests/tools/test_delegate.py b/tests/tools/test_delegate.py index f5720fff0e..356f72d889 100644 --- a/tests/tools/test_delegate.py +++ b/tests/tools/test_delegate.py @@ -1059,35 +1059,7 @@ class TestChildCredentialPoolResolution(unittest.TestCase): self.assertEqual(mock_child._credential_pool, mock_pool) @patch("tools.delegate_tool._load_config", return_value={}) - def test_build_child_agent_does_not_preserve_mcp_toolsets_by_default(self, mock_cfg): - parent = _make_mock_parent() - parent.enabled_toolsets = ["web", "browser", "mcp-MiniMax"] - - with patch("run_agent.AIAgent") as MockAgent: - mock_child = MagicMock() - MockAgent.return_value = mock_child - - _build_child_agent( - task_index=0, - goal="Test narrowed toolsets", - context=None, - toolsets=["web", "browser"], - model=None, - max_iterations=10, - parent_agent=parent, - task_count=1, - ) - - self.assertEqual( - MockAgent.call_args[1]["enabled_toolsets"], - ["web", "browser"], - ) - - @patch( - "tools.delegate_tool._load_config", - return_value={"inherit_mcp_toolsets": True}, - ) - def test_build_child_agent_can_preserve_parent_mcp_toolsets(self, mock_cfg): + def test_build_child_agent_preserves_mcp_toolsets_by_default(self, mock_cfg): parent = _make_mock_parent() parent.enabled_toolsets = ["web", "browser", "mcp-MiniMax"] @@ -1111,6 +1083,34 @@ class TestChildCredentialPoolResolution(unittest.TestCase): ["web", "browser", "mcp-MiniMax"], ) + @patch( + "tools.delegate_tool._load_config", + return_value={"inherit_mcp_toolsets": False}, + ) + def test_build_child_agent_strict_intersection_when_opted_out(self, mock_cfg): + parent = _make_mock_parent() + parent.enabled_toolsets = ["web", "browser", "mcp-MiniMax"] + + with patch("run_agent.AIAgent") as MockAgent: + mock_child = MagicMock() + MockAgent.return_value = mock_child + + _build_child_agent( + task_index=0, + goal="Test narrowed toolsets", + context=None, + toolsets=["web", "browser"], + model=None, + max_iterations=10, + parent_agent=parent, + task_count=1, + ) + + self.assertEqual( + MockAgent.call_args[1]["enabled_toolsets"], + ["web", "browser"], + ) + class TestChildCredentialLeasing(unittest.TestCase): def test_run_single_child_acquires_and_releases_lease(self): diff --git a/tools/delegate_tool.py b/tools/delegate_tool.py index 72c76773f8..19a9916da2 100644 --- a/tools/delegate_tool.py +++ b/tools/delegate_tool.py @@ -379,7 +379,7 @@ def _get_orchestrator_enabled() -> bool: def _get_inherit_mcp_toolsets() -> bool: """Whether narrowed child toolsets should keep the parent's MCP toolsets.""" cfg = _load_config() - return is_truthy_value(cfg.get("inherit_mcp_toolsets"), default=False) + return is_truthy_value(cfg.get("inherit_mcp_toolsets"), default=True) def _is_mcp_toolset_name(name: str) -> bool: