Hey @paco,
First off, you’re not doing anything wrong. Your code is perfectly fine. Your tool definition is pretty solid, I’d say. You even tested it standalone (with _run or, even better, run), and it worked, so your code is great.
Now, about the error you’re running into – like I said, it’s not anything you did wrong. It seems to be something the framework isn’t handling correctly.
You’re trying to use a CrewStructuredTool in the tools argument of your Task. From a user’s perspective, that makes perfect sense. But let’s take a look at the definition of that argument in the source code:
class Task(BaseModel):
    """Class that represents a task to be executed.
    Each task must have a description, an expected output and an agent responsible for execution.
    Attributes:
        [...]
        tools: List of tools/resources limited for task execution.
    """
    # [...]
    tools: Optional[List[BaseTool]] = Field(
        default_factory=list,
        description="Tools the agent is limited to use for this task.",
    )
So, Task.tools expects a list of BaseTool instances, right? Let’s check if the new CrewStructuredTool meets that requirement.
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
is_subclass = issubclass(CrewStructuredTool, BaseTool)
print(f"Is `CrewStructuredTool` a subclass of `BaseTool`? {is_subclass}!")
Nope, it doesn’t. Just to be absolutely sure, let’s look at the definition of CrewStructuredTool:
class CrewStructuredTool:
    """A structured tool that can operate on any number of inputs.
    This tool intends to replace StructuredTool with a custom implementation
    that integrates better with CrewAI's ecosystem.
    """
So, CrewStructuredTool is definitely the third way to declare tools (do we really need so many ways?  ) and it’s not a subclass of
) and it’s not a subclass of BaseTool. That’s why you’re getting that error when you try to pass a CrewStructuredTool to the Task.tools argument.
Of course, my deduction here might be limited by my own understanding, so please double-check my reasoning. And if you think it makes sense, I’d suggest opening a GitHub issue, referencing this thread, and suggesting/requesting the following:
- That Task.toolsbe updated to accept objects inheriting from bothBaseToolandCrewStructuredTool.
- If CrewStructuredToolis intended to be a more robust way (and I have to say, it does look pretty solid!) to replace one of the other two methods for defining tools, then the method it’s meant to replace should start issuing deprecation warnings, signaling its removal in a future version of the framework to improve the Developer Experience (DX).
- That CrewStructuredTooladopt better error handling and reporting practices (like usingPydanticCustomErrorif it becomes apydantic.BaseModel, orRuntimeErrorotherwise). This would improve error communication with the user and make debugging easier.
Hope this helps shed some light on things! This thread definitely helped clarify things for me too.