Limit exposed tools to the ones not excluded by the (fixed) agent context

This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2025-07-01 17:09:20 +02:00
1 parent 6cd7f8155c
commit 10629e3356
2 files changed
+26 -9

No files matched your search

+21 -8
View File
@@ -769,10 +769,20 @@ class SerenaAgent:
)
Logger.root.addHandler(self._gui_log_handler)
# set the agent context
if context is None:
context = SerenaAgentContext.load_default()
self._context = context
# instantiate all tool classes
self._all_tools: dict[type[Tool], Tool] = {tool_class: tool_class(self) for tool_class in ToolRegistry.get_all_tool_classes()}
tool_names = [tool.get_name_from_cls() for tool in self._all_tools.values()]
# determine the set exposed tools (which e.g. the MCP shall see), limited by the context
# (which is fixed for the session)
excluded_tool_classes = set(self._context.get_excluded_tool_classes())
self._exposed_tools = {tc: t for tc, t in self._all_tools.items() if tc not in excluded_tool_classes}
# If GUI log window is enabled, set the tool names for highlighting
if self._gui_log_handler is not None:
self._gui_log_handler.log_viewer.set_tool_names(tool_names)
@@ -807,14 +817,14 @@ class SerenaAgent:
self.ignore_spec: PathSpec # not set to None to avoid assert statements
"""Ignore spec, extracted from the project's gitignore files and the explicitly configured ignored paths."""
# Apply context and mode tool configurations
if context is None:
context = SerenaAgentContext.load_default()
# set the active modes
if modes is None:
modes = SerenaAgentMode.load_default_modes()
self._context = context
self._modes = modes
# log tool information
log.info(f"Loaded tools ({len(self._all_tools)}): {', '.join([tool.get_name_from_cls() for tool in self._all_tools.values()])}")
log.info(f"Number of exposed tools given {self._context}: {len(self._exposed_tools)}")
self._active_tools: dict[type[Tool], Tool] = {}
self._update_active_tools()
@@ -880,11 +890,14 @@ class SerenaAgent:
def get_exposed_tool_instances(self) -> list["Tool"]:
"""
:return: all tool instances, including the non-active ones. For MCP clients, we need to expose them all since typical
clients don't react to changes in the set of tools.
An attempt to use a non-active tool will result in an error.
:return: the tool instances which are exposed (e.g. to the MCP client).
Note that the set of exposed tools is fixed for the session, as
clients don't react to changes in the set of tools, so this is the superset
of tools that can be offered during the session.
If a client should attempt to use a tool that is dynamically disabled
(e.g. because a project is activated that disables it), it will receive an error.
"""
return list(self._all_tools.values())
return list(self._exposed_tools.values())
def get_active_project(self) -> Project | None:
"""
+5 -1
View File
@@ -11,6 +11,7 @@ from typing import TYPE_CHECKING, Self
import yaml
from sensai.util import logging
from sensai.util.string import ToStringMixin
from serena.constants import CONTEXT_YAMLS_DIR, DEFAULT_CONTEXT, DEFAULT_MODES, MODE_YAMLS_DIR
@@ -93,7 +94,7 @@ class SerenaAgentMode:
@dataclass
class SerenaAgentContext:
class SerenaAgentContext(ToStringMixin):
"""Represents a context where the agent is operating (an IDE, a chat, etc.), typically read off a YAML file.
An agent can only be in a single context at a time.
The contexts cannot be changed after the agent is running.
@@ -104,6 +105,9 @@ class SerenaAgentContext:
description: str = ""
excluded_tools: set[str] = field(default_factory=set)
def _tostring_includes(self) -> list[str]:
return ["name"]
def to_json_dict(self) -> dict[str, str | list[str]]:
result = asdict(self)
result["excluded_tools"] = list(result["excluded_tools"])