Hooks
Hooks run a command of yours around tool calls. They are the way to enforce something the permission rules cannot express — a formatter after every edit, a policy check before a push, a notification when a session ends.
[[hooks]]
event = "post_tool"
match = "edit_file"
command = 'gofmt -w "$TAPIOCA_TOOL_PATH"'
timeout = 30 # seconds; 0 means 30Events
event | When |
|---|---|
pre_tool | before a tool call runs |
post_tool | after it has run |
session_start | once, at startup |
session_end | once, at exit |
match globs the tool name and covers every tool when omitted. MCP tools match as mcp:server__tool.
What the hook is told
The call is described in the environment, with the exact arguments as JSON on stdin:
| Variable | Meaning |
|---|---|
TAPIOCA_EVENT | which event fired |
TAPIOCA_TOOL | the tool name |
TAPIOCA_TOOL_PATH | the path, for file tools |
TAPIOCA_TOOL_COMMAND | the command, for bash |
TAPIOCA_TOOL_ERROR | the error, on post_tool |
TAPIOCA_CWD | the working directory |
JSON on stdin is the complete form; the variables are there so simple hooks can be a single line of shell without parsing anything.
A hook can refuse, and only refuse
A pre_tool hook that exits non-zero blocks the call, and its stderr is shown as the reason. That includes when the hook is missing, not executable, or times out — a policy that cannot run refuses rather than waving things through. The failure mode of a security control should be the safe one, and the common case for "the hook did not run" is that it was configured wrong, not that everything is fine.
What a hook cannot do is the other direction. It never overrides a deny rule and it never skips a prompt you would otherwise have seen. It can subtract permission, never add it — so installing one can tighten what runs and can never loosen it.
Environment
Provider API keys and secret_env variables are scrubbed from a hook's environment, as they are for every other subprocess. A hook inherits everything else you exported.
Hooks come from the config, and the config can come from a repository
[[hooks]] names commands that run around tool calls, which makes it one of the keys withdrawn when the config Tapioca loaded turns out to live inside the working tree — a repository that gets its own file read as your config would otherwise be choosing commands to run. You are told when that happens. See Untrusted Repositories.