Passing CrewStructuredTool to tools

Trying to pass a CrewStructuredTool to a task’s tools and I get this error:

result from product research: Error researching products: 1 validation error for Task
tools.1
Input should be a valid dictionary or instance of BaseTool [type=model_type, input_value=CrewStructuredTool(name=‘… the specified website’), input_type=CrewStructuredTool]

Confused how to pass the CrewStructuredTool to tools.

Same problem here. and the docs don’t show how to do it. No examples found online either.

1 Like

Can you share how you are using the structured tools and instantiating them? I have a repo that shows how to use custom tools in CrewAI, it might help you crewai_yaml_tools/src/crewai_yaml_tools/crew.py at main · zinyando/crewai_yaml_tools · GitHub

Take a looks at the tools and how they are input into the agents. Let me know if you have any questions.

Any movement on this issue? Have the same problem: StructuredTool is not a BaseTool .. so how to pass them as tools ? The docs don’t help.

Can you share your tool code or minimal reproduction so that I can help you debug?

Do you have any example of using a tool created using CrewStructuredTool ?? In the documentation (here: Tools - CrewAI) it shows how to create a CrewStructuredTool but it doesn’t show how to use it with an agent or a task. Can you show how to use the exact example that is in the documentation with an Agent or a Task? Meaning how would someone use the tool called “structured_tool” created in the documentation??

For example, let’s say I create this tool:

from crewai.tools.structured_tool import CrewStructuredTool
from pydantic import BaseModel, Field
import json

# Define the schema for the tool's input using Pydantic
class UserPayloadInput(BaseModel):
    user_name: str = Field(..., description="Name of the user.")
    intent: str = Field(..., description="Action or goal the user wants to achieve.")
    is_premium_user: bool = Field(default=None, description="Indicates if the user is a premium customer (optional).")

# Wrapper function to execute the logic
def user_payload_wrapper(*args, **kwargs):
    user_name = kwargs.get("user_name")
    intent = kwargs.get("intent")
    is_premium_user = kwargs.get("is_premium_user", None)

    user_type = "Premium" if is_premium_user else "Standard"
    return (
        f"User '{user_name}' ({user_type} User) expressed intent to '{intent}'. "
        f"Action logged and analyzed for next steps."
    )

# Create and return the structured tool
def create_user_payload_logger():
    return CrewStructuredTool.from_function(
        name="User Payload Logger",
        description="Logs and processes user intent and premium status from a structured payload.",
        args_schema=UserPayloadInput,
        func=user_payload_wrapper,
    )

# Example usage
structured_tool = create_user_payload_logger()
result = structured_tool._run(**{
    "user_name": "Alice",
    "intent": "upgrade subscription",
    "is_premium_user": True
})

How do I use it?

This doesn’t work:

log_and_refresh = Task(
    description="Log the query metadata to analytics.",
    expected_output="The chaching status",
    agent=retrieval_specialist,
    context=[classify_intent],
    tools=[structured_tool] 
)

and assigning it to the Agent doesn’t work either. It returns:

ValidationError: 1 validation error for Task
tools.0
  Input should be a valid dictionary or instance of BaseTool [type=model_type, input_value=CrewStructuredTool(name='... a structured payload.'), input_type=CrewStructuredTool]

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? :thinking:) 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.tools be updated to accept objects inheriting from both BaseTool and CrewStructuredTool.
  • If CrewStructuredTool is 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 CrewStructuredTool adopt better error handling and reporting practices (like using PydanticCustomError if it becomes a pydantic.BaseModel, or RuntimeError otherwise). 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.