feat: Add ToolCollection class for named tool access (#339)

This change allows accessing tools by name (tools["tool_name"]) in addition to
index (tools[0]), making it more intuitive and convenient to work with multiple
tools without needing to remember their position in the list
This commit is contained in:
Lucas Gomide
2025-06-20 10:27:10 -03:00
committed by GitHub
parent 9e92b84bcc
commit 31b3dd2b94
6 changed files with 316 additions and 10 deletions

View File

@@ -4,7 +4,7 @@ import logging
from typing import TYPE_CHECKING, Any
from crewai.tools import BaseTool
from crewai_tools.adapters.tool_collection import ToolCollection
"""
MCPServer for CrewAI.
@@ -114,7 +114,7 @@ class MCPServerAdapter:
self._adapter.__exit__(None, None, None)
@property
def tools(self) -> list[BaseTool]:
def tools(self) -> ToolCollection[BaseTool]:
"""The CrewAI tools available from the MCP server.
Raises:
@@ -127,7 +127,7 @@ class MCPServerAdapter:
raise ValueError(
"MCP server not started, run `mcp_server.start()` first before accessing `tools`"
)
return self._tools
return ToolCollection(self._tools)
def __enter__(self):
"""

View File

@@ -0,0 +1,59 @@
from typing import List, Optional, Union, TypeVar, Generic, Dict
from crewai.tools import BaseTool
T = TypeVar('T', bound=BaseTool)
class ToolCollection(list, Generic[T]):
"""
A collection of tools that can be accessed by index or name
This class extends the built-in list to provide dictionary-like
access to tools based on their name property.
Usage:
tools = ToolCollection(list_of_tools)
# Access by index (regular list behavior)
first_tool = tools[0]
# Access by name (new functionality)
search_tool = tools["search"]
"""
def __init__(self, tools: Optional[List[T]] = None):
super().__init__(tools or [])
self._name_cache: Dict[str, T] = {}
self._build_name_cache()
def _build_name_cache(self) -> None:
self._name_cache = {tool.name: tool for tool in self}
def __getitem__(self, key: Union[int, str]) -> T:
if isinstance(key, str):
return self._name_cache[key]
return super().__getitem__(key)
def append(self, tool: T) -> None:
super().append(tool)
self._name_cache[tool.name] = tool
def extend(self, tools: List[T]) -> None:
super().extend(tools)
self._build_name_cache()
def insert(self, index: int, tool: T) -> None:
super().insert(index, tool)
self._name_cache[tool.name] = tool
def remove(self, tool: T) -> None:
super().remove(tool)
if tool.name in self._name_cache:
del self._name_cache[tool.name]
def pop(self, index: int = -1) -> T:
tool = super().pop(index)
if tool.name in self._name_cache:
del self._name_cache[tool.name]
return tool
def clear(self) -> None:
super().clear()
self._name_cache.clear()

View File

@@ -7,6 +7,7 @@ import typing as t
import logging
from crewai.tools import BaseTool
from crewai_tools.adapters.enterprise_adapter import EnterpriseActionKitToolAdapter
from crewai_tools.adapters.tool_collection import ToolCollection
logger = logging.getLogger(__name__)
@@ -16,7 +17,7 @@ def CrewaiEnterpriseTools(
actions_list: t.Optional[t.List[str]] = None,
enterprise_action_kit_project_id: t.Optional[str] = None,
enterprise_action_kit_project_url: t.Optional[str] = None,
) -> t.List[BaseTool]:
) -> ToolCollection[BaseTool]:
"""Factory function that returns crewai enterprise tools.
Args:
@@ -24,9 +25,11 @@ def CrewaiEnterpriseTools(
If not provided, will try to use CREWAI_ENTERPRISE_TOOLS_TOKEN env var.
actions_list: Optional list of specific tool names to include.
If provided, only tools with these names will be returned.
enterprise_action_kit_project_id: Optional ID of the Enterprise Action Kit project.
enterprise_action_kit_project_url: Optional URL of the Enterprise Action Kit project.
Returns:
A list of BaseTool instances for enterprise actions
A ToolCollection of BaseTool instances for enterprise actions
"""
if enterprise_token is None:
enterprise_token = os.environ.get("CREWAI_ENTERPRISE_TOOLS_TOKEN")
@@ -47,7 +50,8 @@ def CrewaiEnterpriseTools(
all_tools = adapter.tools()
if actions_list is None:
return all_tools
return ToolCollection(all_tools)
# Filter tools based on the provided list
return [tool for tool in all_tools if tool.name in actions_list]
filtered_tools = [tool for tool in all_tools if tool.name in actions_list]
return ToolCollection(filtered_tools)