Elemental | Documentation
Toolbox and Tools

Toolbox and Tools

Elemental provides an interface for defining tools that agents may use to perform actions against external systems. Tools are standalone functions that are wrapped in the common interface that makes them exposed are executable through a common interface. This mechanism of detecting the tools and making them available to agents is done through a ToolBox object in Elemental.

Tool

Tools are defined in Elemental in toolbox module. This module includes both the definition of Tool and ToolBox where the later is used to discover and run tools that agent can use.

Tools use following interface which includes four classes ToolParameters, ToolInitParameters, ToolResult, and Tool. The interface defines them as:

class ToolParameters(BaseModel):

class ToolInitParameters(BaseModel):

class ToolResult(BaseModel):

class Tool(ABC):

    name: str = None
    description: str = None
    example: str = None

    def __init__(
            self, 
            init_params: Optional[ToolInitParameters] \
                = None
        ) -> None:
       
        self._init_params = init_params

Tools are defined as classes that inherit from the Tool class. The Tool class is an abstract base class that defines the interface for all tools. The Tool class has three main components:

Tools are defined as classes that inherit from the Tool class. The Tool class is an abstract base class that defines the interface for all tools. The Tool class has three main components:

  1. ToolParameters: This class is used to define the input parameters required by the tool. The input parameters are defined as a Pydantic model.
  2. ToolResult: This class is used to define the output parameters returned by the tool. The output parameters are defined as a Pydantic model.
  3. Tool: This class is the abstract base class for all tools. The Tool class defines the interface for all tools and provides the run method that is used to execute the tool.

The Tool class has the following attributes:

  • name: The name of the tool. This attribute is used to identify the tool in the toolbox.
  • description: The description of the tool. This attribute is used to provide a description of the tool in the toolbox.
  • example: The example of how to use the tool. This attribute is used to provide an example of how to use the tool in the toolbox.

The Tool class has the following methods:

  • run: The abstract method that is used to execute the tool. This method is used to run the tool with the specified parameters. The parameters are provided as a Pydantic model.
    @abstractmethod
    def run(
            self, 
            parameters: ToolParameters
        ) -> ToolResult
    
  • get_name: This method returns the name of the tool.
    @classmethod
    def get_name(cls) -> str
    
  • get_description: This method returns the description of the tool.
    @classmethod
    def get_description(cls) -> str
    
  • get_example: This method returns an example of how to use the tool.
    @classmethod
    def get_example(cls) -> str
    

Toolbox details

Tools are registered in the ToolBox class. The ToolBox class is responsible for managing the tools and providing an interface for the agent to use the tools.

ToolBox is initialized as

def __init__(self) -> None:

    self._tool_registry: Dict[str, Tuple[BaseModel, BaseModel, BaseModel]] = {}
    self._mcp_client_registry: Dict[str, MCPClientToolbox] = {}
    self._mcp_tool_registry: Dict[str, MCPTool] = {}

The ToolBox class is initialized with an empty dictionary that will be used to store the tools. The dictionary is used to register the tools in the toolbox. The keys of the dictionary are the names of the tools and the values are tuples that contain the class of the tool, the class of the parameters and the class of the initialization parameters. The initialization parameters are optional and are used to initialize the tool before it is executed.

The ToolBox class includes the following methods:

  • register_tool - Register a tool in the toolbox. The tool is registered with the name and the class of the tool. The class of the tool is used to create an instance of the tool.
    def register_tool(
            self,
            tool_name: str,
            tool_class: BaseModel,
            param_model_class: BaseModel,
            init_model_class: Optional[BaseModel] = None,
        ) -> None
    
  • call_tool - Call a tool with the specified parameters. The parameters are provided as a JSON string. The call_tool method returns the result of the tool execution.
    def call_tool(self, 
            tool_name: str, 
            parameters_json: str
        ) -> ToolResult | MCPToolResult
    
  • describe - Describe the tools in the toolbox. The describe method returns a dictionary with the name, description, arguments and example of use for the tools.
    def describe(self) -> Dict[str, str]
    
  • describe_short - Describe the tools in the toolbox. The describe_short method returns a short version of the description as string with only tool name and their description.
    def describe_short(self) -> str
    
  • discover_tools - Discover tools from multiple locations in the system and return a dictionary of tools. This method does not register the tool in the agents toolbox, it only returns the list of tools that are available in the system. Outside of the default module location, this method will check paths defined in two environmental variables i.e.ATTO_SYSTEM_TOOLS_DIR and ATTO_USER_TOOLS_DIR. This allows to register tools from multiple locations without the need to modify the original code.
    def discover_tools(self) -> Dict[str, Tuple]
    
  • load_classes_from_file - Load classes from Python files in a directory and return a dictionary of tools. This method is used to load tools from a directory. The directory should contain Python files with the tool classes.
    def load_classes_from_files(
            self, 
            directory: Path
        ) -> Dict[str, tuple]
    
  • register_tool_by_name - Register a tool by name. The tool is registered with the name and the class of the tool. This method will register the tool given only by its name in the toolbox.
    def register_tool_by_name(
            self, 
            tool_name: str
        ) -> None
    
  • call_mcp_tool - Call an MCP tool by name with the specified parameters provided as a JSON string. This function is used from within the call_tool method if the tools is provided by MCP server.
    def call_mcp_tool(
            self, 
            tool_name: str, 
            parameters_json: str
        ) -> ToolResult | MCPToolResult
    
  • register_mcp_tool_by_name - Register an MCP tool by using MCP server. This method is called from the register_tool_by_name if the tool name is defined MCP|ServerName|ToolName. This method sets the MCP server and registers the tool within the toolbox.
    def register_mcp_tool_by_name(
            self, 
            tool_name: str
        ) -> None
    

ToolBox Example

box = ToolBox()

box.register_tool_by_name("PythonRunner")
box.register_tool_by_name("Calculator")

description = box.describe()

CALCULATOR_JSON = '{"expression": "2 + 3 * 4"}'

final_result = box.call_tool("Calculator", CALCULATOR_JSON)

In the example above, we show how to register tools in the toolbox and how to call them. The ToolBox class is used to manage the tools and to provide an interface for the agent to use the tools.

Model Context Protocol Tools

Warning

Model Context Protocol Servers are external applications executed on your device. Be cautious about the selection of MCP servers and verify their origin. Using not verified servers may bride the security of your agents or your device.

Elemental provides an interface to bring tools provided by Model Context Protocol (MCP) server. The tools are registered in the toolbox and can be used by the agent. The tools are brought to the ToolBox object with the same interface, however, definition of MCP servers must be present in the environment.

The MCP servers can be defined in the .env file in the mcpServers variable. Using Github MCP server as an example, the mcpServers entry could be

mcpServers='{"Github": {"command": "npx", "args": ["-y","@modelcontextprotocol/server-github"], "env": {"GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"}}}'

The mcpServers variable should be a JSON string with name of the server as a key and three variables command, args, and env present for the key. More than one server may be defined.

The MCP tools in Elemental require following particular naming convention to bring them together with native tools. The name of the tool must be MCP|server_name|tool_name, where MCP indicates that the tool needs to come from an MCP server, server_name will indicate the correct entry in the mcpServers definition to use for this server, tool_name is the name of the individual tool in the given server.

Example below illustrates how to bring MCP defined tools together with native tools

box = ToolBox()

box.register_tool_by_name("PythonRunner")
box.register_tool_by_name("Calculator")

box.register_tool_by_name("MCP|Github|list_issues")
box.register_tool_by_name("MCP|Github|search_repositories")

description = box.describe()

If you want to include all tools provided by a given MCP server, you may use MCP|server_name|* instead of listing individual tools. The above toolbox specification would change to:

box = ToolBox()

box.register_tool_by_name("PythonRunner")
box.register_tool_by_name("Calculator")

box.register_tool_by_name("MCP|Github|*")

description = box.describe()

Tools are included in the description and called using the same mechanism as the native tools and may be used with any language model.