How do I see actual request and response for Tools used during Tracing

Circling back to the question about “How do I see actual request and response for tools used during tracing” — like I mentioned in another thread, one way to handle this is by setting up custom event listeners specific to what you need.

Over there, I suggested listening for task events. Here, I’ve put together a working example that specifically grabs tool usage events. Hopefully, this gets you what you need.

from crewai.utilities.events.base_event_listener import BaseEventListener
from crewai.utilities.events.tool_usage_events import (
    ToolUsageFinishedEvent,
    ToolUsageErrorEvent,
    ToolUsageStartedEvent,
    ToolExecutionErrorEvent,
    ToolSelectionErrorEvent,
    ToolValidateInputErrorEvent
)
from pprint import pprint

import os
from crewai import Agent, Task, Crew, LLM, Process
from crewai_tools import ScrapeWebsiteTool

# Custom event listener

class MyToolEventListener(BaseEventListener):
    """
    A custom event listener that prints messages when specific tool-related events occur.
    """
    def __init__(self):
        super().__init__()
        print("MyToolEventListener initialized.")

    def setup_listeners(self, crewai_event_bus):
        """
        Registers listeners for various tool usage events on the provided event bus.
        """
        print("Setting up tool event listeners...")

        @crewai_event_bus.on(ToolUsageStartedEvent)
        def on_tool_usage_started(source, event: ToolUsageStartedEvent):
            print(f"\n--> Received 'ToolUsageStartedEvent':")
            pprint(event.__dict__)

        @crewai_event_bus.on(ToolUsageFinishedEvent)
        def on_tool_usage_finished(source, event: ToolUsageFinishedEvent):
            print(f"\n<-- Received 'ToolUsageFinishedEvent':")
            pprint(event.__dict__)

        @crewai_event_bus.on(ToolUsageErrorEvent)
        def on_tool_usage_error(source, event: ToolUsageErrorEvent):
            print(f"\n<-- [ERROR] Received 'ToolUsageErrorEvent':")
            pprint(event.__dict__)

        @crewai_event_bus.on(ToolExecutionErrorEvent)
        def on_tool_execution_error(source, event: ToolExecutionErrorEvent):
            print(f"\n<-- [ERROR] Received 'ToolExecutionErrorEvent':")
            pprint(event.__dict__)

        @crewai_event_bus.on(ToolSelectionErrorEvent)
        def on_tool_selection_error(source, event: ToolSelectionErrorEvent):
            print(f"\n--> [ERROR] Received 'ToolSelectionErrorEvent':")
            pprint(event.__dict__)

        @crewai_event_bus.on(ToolValidateInputErrorEvent)
        def on_tool_validate_input_error(source, event: ToolValidateInputErrorEvent):
            print(f"\n--> [ERROR] Received 'ToolValidateInputErrorEvent':")
            pprint(event.__dict__)

my_tool_listener = MyToolEventListener()

# A simple crew using tools

os.environ["GEMINI_API_KEY"] = "<YOUR-KEY>"

scrape_tool = ScrapeWebsiteTool()

llm = LLM(
    model="gemini/gemini-2.0-flash",
    temperature=0.7,
)

website_summarizer_agent = Agent(
    role="Website Content Summarizer",
    goal=(
        "Scrape the content of the given website using tools. "
        "Then, create a short, concise summary (one paragraph) "
        "of its main purpose or content based *only* on the "
        "scraped text."
    ),
    backstory=(
        "You are an AI assistant specialized in visiting websites "
        "using provided tools. You extract the text content and are "
        "excellent at summarizing the key information found directly "
        "on the page into a single, easy-to-understand paragraph."
    ),
    llm=llm,
    tools=[scrape_tool],
    verbose=False,
    allow_delegation=False,
)

summarize_website_task = Task(
    description=(
        "Scrape the content of the given website using available tools, then "
        "write a single paragraph summarizing the main topic, based STRICTLY "
        "on the text you found.\n"
        "Website: {target_website}"
    ),
    expected_output=(
        "A single paragraph of 90 to 100 words containing a concise summary."
    ),
    agent=website_summarizer_agent,
)

website_summary_crew = Crew(
    agents=[website_summarizer_agent],
    tasks=[summarize_website_task],
    process=Process.sequential,
    verbose=False,
)

crew_result = website_summary_crew.kickoff(
    inputs={
        "target_website": "www.anthropic.com/engineering/claude-think-tool"
    }
)

print(f"\n🤖 Final Summary:\n\n{crew_result.raw}\n")

In this sample code, I set verbose=False just to make the event output cleaner. You’ll notice I deliberately left out the http:// protocol from the URL. When I ran it, this initially triggered tool errors (you can see this in the ToolUsageErrorEvent), but the agent eventually figured it out and ran the tool successfully.

If you look at the output from this listener setup, you’ll see the arguments passed to the tool (check the ToolUsageStartedEvent), but I realized the actual response from the tool isn’t there. The event that fires when the tool finishes (ToolUsageFinishedEvent) doesn’t include the result. This makes sense why other listeners, like AgentOps, are missing this info too.

The following modifications add the tool’s output as an extra field to that ToolUsageFinishedEvent when it fires.

Modifications in crewai/utilities/events/tool_usage_events.py:

--- a/crewai/utilities/events/tool_usage_events.py
+++ b/crewai/utilities/events/tool_usage_events.py
@@ -11,6 +11,7 @@
     agent_role: str
     tool_name: str
     tool_args: Dict[str, Any] | str
+    tool_output: str | None = None
     tool_class: str
     run_attempts: int | None = None
     delegations: int | None = None

Modifications in crewai/tools/tool_usage.py:

--- a/crewai/tools/tool_usage.py
+++ b/crewai/tools/tool_usage.py
@@ -244,6 +244,7 @@
             tool_calling=calling,
             from_cache=from_cache,
             started_at=started_at,
+            tool_output=result,
         )
 
         if (
@@ -492,7 +493,7 @@
         crewai_event_bus.emit(self, ToolUsageErrorEvent(**{**event_data, "error": e}))
 
     def on_tool_use_finished(
-        self, tool: Any, tool_calling: ToolCalling, from_cache: bool, started_at: float
+        self, tool: Any, tool_calling: ToolCalling, from_cache: bool, started_at: float, tool_output: str
     ) -> None:
         finished_at = time.time()
         event_data = self._prepare_event_data(tool, tool_calling)
@@ -501,6 +502,7 @@
                 "started_at": datetime.datetime.fromtimestamp(started_at),
                 "finished_at": datetime.datetime.fromtimestamp(finished_at),
                 "from_cache": from_cache,
+                "tool_output": tool_output,
             }
         )
         crewai_event_bus.emit(self, ToolUsageFinishedEvent(**event_data))

With these tweaks in place, running the code again will show the tool’s result right in the ToolUsageFinishedEvent, giving you a much clearer view of how the tool is being used.

3 Likes