From 4c37aa83dfa7890aca1ba604a01199b041fa6feb Mon Sep 17 00:00:00 2001 From: Gilles Ceyssat Date: Tue, 12 May 2026 07:53:56 +0400 Subject: [PATCH] feat(executor): pull SystemBuilder MCP tools into agent's tool list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AgentExecutor builds agents from a template's `tools` whitelist by resolving each name against the static `ToolRegistry`. External MCP tools (discovered at SystemBuilder.build() time via _discover_external_mcp) were never picked up — they exist on system.tool_executor._tools but the per-agent build path never read from there. Result: any agent whose template declared MCP tool names by name got 0 of them at runtime, falling back to natives only. The agent's system prompt could still mention the tools, but the model could not call them (only shell_exec or other native fallbacks were available). This adds a fallback after the ToolRegistry loop: for any tool name not resolved from the registry, look it up in system.tool_executor._tools and append the already-instantiated MCP adapter. Native tools still take precedence (same name, the registry hit wins). Verified by configuring a CyberStrikeAI stdio MCP server (78 tools). Before this patch: agent built with 4 tools. After: 4 native + 78 MCP. --- src/openjarvis/agents/executor.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/openjarvis/agents/executor.py b/src/openjarvis/agents/executor.py index 8d6be76f..cc60c6a7 100644 --- a/src/openjarvis/agents/executor.py +++ b/src/openjarvis/agents/executor.py @@ -317,6 +317,21 @@ class AgentExecutor: tool_instances.append(tool) except Exception: logger.warning("Failed to instantiate tool %s", tname) + + # Pull tools already discovered by SystemBuilder (e.g. external MCP + # adapters) that aren't in the static ToolRegistry. Without this, + # agents declaring MCP-discovered tools in their template would + # silently fall back to natives only. + if self._system is not None and getattr(self._system, "tool_executor", None) is not None: + mcp_pool = getattr(self._system.tool_executor, "_tools", {}) or {} + existing = {t.spec.name for t in tool_instances} + for tname in tool_names: + if tname in existing: + continue + pooled = mcp_pool.get(tname) + if pooled is not None: + tool_instances.append(pooled) + if tool_instances: logger.info( "Agent %s: resolved %d/%d tools", @@ -332,6 +347,12 @@ class AgentExecutor: agent_kwargs["system_prompt"] = sys_prompt if getattr(agent_cls, "accepts_tools", False) and tool_instances: agent_kwargs["tools"] = tool_instances + # Propagate confirmation policy from the AgentExecutor down to the + # agent's own ToolExecutor. Set by CLI paths like `jarvis agents ask` + # so non-interactive runs can auto-approve tool execution. + if getattr(self, "_confirm_callback", None) is not None: + agent_kwargs["interactive"] = True + agent_kwargs["confirm_callback"] = self._confirm_callback try: agent_instance = agent_cls(engine, model, **agent_kwargs) except TypeError: