Skip to content

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.

toml
[[hooks]]
event = "post_tool"
match = "edit_file"
command = 'gofmt -w "$TAPIOCA_TOOL_PATH"'
timeout = 30                    # seconds; 0 means 30

Events

eventWhen
pre_toolbefore a tool call runs
post_toolafter it has run
session_startonce, at startup
session_endonce, 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:

VariableMeaning
TAPIOCA_EVENTwhich event fired
TAPIOCA_TOOLthe tool name
TAPIOCA_TOOL_PATHthe path, for file tools
TAPIOCA_TOOL_COMMANDthe command, for bash
TAPIOCA_TOOL_ERRORthe error, on post_tool
TAPIOCA_CWDthe 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.

Released under the MIT License.