What is @after_tool_call_crew?
Once a tool finishes, @after_tool_call_crew receives the ToolCallHookContext populated with the result string or structured payload. Return a new string to replace what the agent reads — ideal for trimming verbose HTTP bodies, masking tokens, or normalizing JSON. Returning None keeps the original output.
This hook is the right place to implement cross-cutting observability for tools without modifying each BaseTool subclass. Keep transformations loss-aware: if you truncate HTML, append a marker so the model knows content was clipped. Combine with sanitize_tool_result helpers when you want shared scrubbers across crews.
Do not confuse this with Task.output_file or callbacks that run per task — after_tool_call_crew fires for every tool invocation regardless of which task scheduled it, making it ideal for centralized policy but unsuitable for task-specific business rules that belong next to the Task definition.
When to Use
Trimming/redacting/caching tool output before the agent sees it.
Use Cases
- • Trim verbose output
- • Cache results
- • Redact sensitive fields
Key Features
- ✓ Crew-scoped
- ✓ Can rewrite the result
When NOT to Use
Tool-internal cleanup — do it in _run().
Notes
None versus unchanged
Returning None preserves the tool output; returning an empty string removes content entirely and can confuse agents — prefer explicit truncation messages.
Binary payloads
Some tools return serialized blobs. Confirm whether ctx.result is text before running regex replacements.
Ordering with LLM hooks
Tool hooks fire around tool execution, independent of LLM hook ordering. Do not rely on interleaving for correctness.
Import
from crewai.hooks import after_tool_call_crew
How to Apply
@after_tool_call_crew
def cache(ctx):
cache[ctx.tool_name] = ctx.result
What It Enables
- ✓ Result post-processing
- ✓ Caching
Code Examples
Trim
@after_tool_call_crew
def trim(ctx):
return ctx.result[:2000]
Redact API keys in text
import re
_TOKEN = re.compile(r'sk-[A-Za-z0-9]{16,}')
@after_tool_call_crew
def mask(ctx):
return _TOKEN.sub('sk-REDACTED', ctx.result or '')
Cache idempotent reads
@after_tool_call_crew
def memo(ctx):
if ctx.tool_name == 'lookup_customer':
cache[getattr(ctx, 'tool_input', '')] = ctx.result
return None
Integration Patterns
Pairs with @before_tool_call_crew
Common Mistakes
❌ Forgetting to return the (possibly-trimmed) string
✅ Return the new result; returning None leaves the original untouched.
Related: Task class reference, Agent class reference, and the first Crew tutorial.
@after_tool_call_crew FAQ
What is @after_tool_call_crew in CrewAI?
Runs after every tool call within the crew — transform results, validate, or persist. Once a tool finishes, @after_tool_call_crew receives the ToolCallHookContext populated with the result string or structured payload. Return a new string to replace what the agent reads — ideal for trimming verbose HTTP bodies, masking tokens, or normalizing JSON. Returning None keeps the original output. This hook is the right place to implement cross-cutting observability for tools without modifying each BaseTool subclass. Keep transformations loss-aware: if you truncate HT…
Which module defines the CrewAI decorator @after_tool_call_crew?
DevShelfHub maps @after_tool_call_crew to Python module crewai.hooks. Pin your installed crewai version and match imports to the import snippet on this page.
When should I use @after_tool_call_crew?
Trimming/redacting/caching tool output before the agent sees it.
When should I avoid @after_tool_call_crew?
Tool-internal cleanup — do it in _run().
How do I apply @after_tool_call_crew in Python?
@after_tool_call_crew def cache(ctx): cache[ctx.tool_name] = ctx.result
Where can I explore more CrewAI API reference pages?
Open the CrewAI API reference index on DevShelfHub to search classes, methods, and decorators, each with runnable examples, parameters, common mistakes, and cross-links.