Skip to main content

Hook system

Use lifecycle hooks to observe agent activity, block an action, or rewrite a prompt, tool call, or result. Operators deploy hooks, and the model cannot change or disable them. For operator configuration, see Permissions and posture.

Hook phases​

PhaseWhen it firesCan block?Can mutate?Mutation target
SessionStartBefore the first prompt is recordedYes, aborts the runNo
UserPromptSubmitAfter command expansion, before recordingYes, ends the runYesPrompt text
PreToolUseAfter permission approval, before executionYes, returns an error instead of runningYesTool arguments
PostToolUseAfter execution, before emitting the resultNoYesTool result
StopWhen a run endsNoNo
SubagentStopWhen a child run endsNoNo
TeammateIdleWhen a team member becomes idleNoNo
TaskCreatedBefore the team supervisor creates a taskYes, prevents creationNo
TaskCompletedBefore a team member marks a task completeYes, prevents completionNo

An execution error in SessionStart or UserPromptSubmit ends the run. A PreToolUse execution error prevents the tool call and returns an error result to the model; the run can continue. A PostToolUse error becomes an annotation and leaves the already-executed call intact.

Shell hook contract​

Each hook is a shell command run as <shell> -c <command> (default /bin/sh). The JSON HookEvent is written to the process's stdin:

{
"Phase": "PreToolUse",
"Tool": "Shell",
"Input": { "command": "rm -rf build" },
"SessionID": "8867bdea940108c1dd82d13d3fb7fc61"
}

Input is phase-specific. For PreToolUse it is the tool's raw arguments JSON. For PostToolUse it is {"content": "...", "is_error": false}. For UserPromptSubmit it is {"prompt": "..."}.

Exit codes​

Exit codeOutcome
0Allow. Stdout can contain a message or mutation envelope.
2Block. Mecatl reads the reason from stdout, then stderr.
Any other valueReport a hook error. The phase determines whether the run aborts.

A single invocation is bounded by a 30-second timeout.

Mutation envelope​

On exit 0, if stdout is a JSON object, it is parsed as a control envelope:

{
"mutated": <phase-specific payload>,
"message": "optional human-readable note"
}

mutated must have the same shape as Input for that phase. For PreToolUse it replaces the tool's arguments before execution. For PostToolUse it replaces the result the model and client see. For UserPromptSubmit it replaces the recorded prompt text.

A non-object stdout value is treated as a message, and the original payload is unchanged.

Block example: guard Shell against rm -rf​

#!/bin/sh
# Use this script as a PreToolUse hook for Shell.
event="$(cat)"
if printf '%s' "$event" | grep -q '"rm -rf'; then
echo "blocked: 'rm -rf' is not permitted by policy"
exit 2
fi
exit 0

Exit 2 returns an error ToolResult without running the command.

Mutation examples​

Rewrite the prompt before it is recorded​

This UserPromptSubmit hook removes an API key pattern before the prompt reaches the model or session store:

#!/bin/sh
event="$(cat)"
prompt="$(printf '%s' "$event" | python3 -c "import sys,json; print(json.load(sys.stdin)['Input']['prompt'])")"
clean="$(printf '%s' "$prompt" | sed 's/sk-[A-Za-z0-9]\{32,\}/[REDACTED]/g')"
printf '%s' "$clean" | \
python3 -c "import json,sys; print(json.dumps({'mutated': {'prompt': sys.stdin.read()}}))"
exit 0

Mecatl records and sends the mutated prompt.

Redact a secret from a tool result​

This PostToolUse hook redacts AWS access key IDs from shell output while preserving the result's error status:

#!/bin/sh
python3 -c '
import json, re, sys
result = json.load(sys.stdin)["Input"]
result["content"] = re.sub(r"AKIA[A-Z0-9]{16}", "[REDACTED_KEY]", result["content"])
print(json.dumps({"mutated": result}))
'

The client stream and model history both receive the redacted result.

Permission policy evaluates original args​

For PreToolUse, the permission policy evaluates the original arguments. It does not evaluate rewritten arguments again because operator hooks are trusted. A hook can therefore widen a call beyond what the original permission decision covered. Keep security controls in the permission policy when a hook must not bypass them.

Model-backed guardrails​

Guardrails provide a separate content-review path around tool execution. They inspect effective actions after hook rewriting and effective results before release to the client or model. Configure a checker model to enable them. See Permissions and posture for coverage, enforcement modes, and approval behavior.

Next steps​