# How the OpenHuman Configurable Hooks System (`hooks.json`) Intercepts Tool Calls and Prompts

> Learn how OpenHuman's `hooks.json` intercepts tool calls and prompts with custom scripts. Modify or block agent interactions seamlessly using `ToolHook` and `PostTurnHook`.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: internals
- Published: 2026-08-27

---

**The OpenHuman configurable hooks system uses a declarative [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json) file to register external scripts that intercept, modify, or block tool invocations and agent prompts through `ToolHook` and `PostTurnHook` implementations injected into the core runtime.**

The [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json) configuration file provides a user-friendly interface for extending the behavior of the [tinyhumansai/openhuman](https://github.com/tinyhumansai/openhuman) framework without modifying internal source code. By mapping specific event types to executable scripts, developers can audit, mutate, or reject operations at critical points in the agent execution pipeline.

## Hook Architecture and Core Components

The configurable hooks system bridges external scripts with OpenHuman’s internal runtime through four primary components working in concert.

### [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json) Configuration

The [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json) file serves as the declarative entry point, mapping event names such as `"tool_call"` and `"post_turn"` to script paths, arguments, and environment variables. This file lives alongside the main OpenHuman configuration and is validated at startup by the hook bridge to ensure all referenced executables exist and event types are supported.

### Bridge Implementation

Located in [`src/openhuman/hooks/bridge.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/hooks/bridge.rs), the **hook bridge** reads the JSON configuration and instantiates internal `ToolHook` and `PostTurnHook` structures. These implementations act as adapters that translate internal Rust events into JSON payloads streamed to external scripts via stdin, then interpret exit codes to determine execution flow.

### Core Registration

During runtime initialization, [`src/openhuman/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/core/runtime/builder.rs) registers the bridge’s hooks via `CoreBuilder::tool_hook()` and `CoreBuilder::post_turn_hook()`. This registration ensures that every subsequent tool invocation and turn completion automatically flows through the external scripts defined in [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json).

### Decision Logic

The [`src/openhuman/hooks/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/hooks/ops.rs) module defines the decision enums and execution logic that govern hook behavior. When a script returns exit code `0`, the operation is **allowed**; exit code `2` triggers a **denial** that aborts the current operation; any other code represents an **error** that is logged and treated as a failure.

## How Tool Call Interception Works

When the agent produces a tool call, the core invokes `ToolHook::decide()` before executing the underlying function.

The bridge looks up the `"tool_call"` event in [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json), serializes the tool invocation payload (including tool name and arguments), and spawns the configured script with this data on stdin. The script’s response determines the next phase:

- **Allow** (exit 0): The core proceeds with the original or modified arguments.
- **Deny** (exit 2): The core aborts the tool execution and returns an error to the model.
- **Modify**: The script may output a JSON payload on stdout containing mutated arguments; the bridge merges these changes before allowing the call.
- **Ask**: The script can request user confirmation before proceeding.

Because hooks are **gate-closing**, a single denial short-circuits the entire operation, preventing accidental execution of privileged actions.

## Post-Turn Prompt Interception

After the core generates a response, the `PostTurnHook::run()` method triggers to process the completed turn.

This hook receives the full transcript of the conversation turn as a JSON payload, enabling scripts to audit the exchange, inject additional context, or rewrite the assistant’s final message. 

If the script exits with code `0` and outputs modified JSON on stdout, the bridge replaces the original response with the processed version before it reaches the user. Exit code `2` causes the entire turn to be discarded, effectively blocking the prompt from being delivered.

## Configuring [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json) for Your Use Case

The configuration file structure is straightforward, requiring only the event type and script path. Each entry may optionally specify environment variables and arguments.

```json
{
  "tool_call": {
    "script": "./scripts/my_tool_hook.sh",
    "env": { "HOOK_MODE": "audit" }
  },
  "post_turn": {
    "script": "./scripts/append_footer.sh",
    "env": {}
  }
}

```

The `tool_call` entry executes for every tool invocation, while `post_turn` runs after each agent response cycle. You may define one or both hooks depending on your interception requirements.

## Implementing Custom Hook Scripts

Hook scripts can be written in any language, provided they consume JSON from stdin and return appropriate exit codes.

### Audit-Only Tool Hook

This bash script logs tool names for compliance without blocking execution:

```bash
#!/usr/bin/env sh

# Read the tool payload from stdin

payload=$(cat)

# Extract tool name

tool_name=$(echo "$payload" | jq -r '.name')
echo "Audit: tool $tool_name called" >> /tmp/hook_audit.log

# Exit 0 to allow the call unchanged

exit 0

```

### Argument Modification Hook

To modify tool arguments, output the modified JSON structure:

```bash
#!/usr/bin/env sh

# Read input and modify the URL argument

cat > /tmp/hook_input.json
jq '.args.url = "https://api.example.com/modified"' /tmp/hook_input.json
exit 0

```

### Post-Turn Content Injection

This script appends a disclaimer to every assistant response:

```bash
#!/usr/bin/env sh

# Receive full turn transcript

turn=$(cat)

# Append disclaimer to last message

updated=$(echo "$turn" | jq '.messages[-1].content += "\n\n---\n*This response was processed by a custom hook.*"')
echo "$updated"
exit 0

```

## Integration with the OpenHuman Core Runtime

The hook system integrates tightly with the core initialization sequence. When constructing a new `Core` instance, the `CoreBuilder` pattern in [`src/openhuman/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/core/runtime/builder.rs) accepts optional `tool_hook` and `post_turn_hook` implementations.

By passing the bridge instances created from [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json), the builder ensures that all agent tool executions and turn completions route through the external scripts. This design allows the hooks system to operate as a cross-cutting concern, applying consistent policies across different agent configurations without requiring changes to individual tool implementations or prompt templates.

## Summary

- The [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json) file provides a declarative interface for registering external scripts that intercept tool calls and prompt generation in OpenHuman.
- **Exit code `0`** allows the operation, **exit code `2`** denies it, and other codes indicate errors.
- The [`src/openhuman/hooks/bridge.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/hooks/bridge.rs) module implements `ToolHook` and `PostTurnHook` traits that serialize events to external scripts.
- Tool call hooks receive payloads on stdin and can modify arguments by outputting JSON, enabling runtime mutation of API requests.
- Post-turn hooks process the complete conversation transcript after response generation, supporting content rewriting and compliance auditing.
- Registration occurs during core construction in [`src/openhuman/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/core/runtime/builder.rs), ensuring universal coverage across the runtime.

## Frequently Asked Questions

### What exit codes should my hook scripts return to OpenHuman?

Your hook scripts must return specific exit codes to communicate decisions: **exit code `0`** signals that the operation should proceed (optionally with modified data output to stdout), **exit code `2`** explicitly denies or blocks the operation, and any other exit code represents an error that OpenHuman will log and treat as a failure condition.

### Can hooks modify the arguments of a tool call before execution?

Yes, tool call hooks can intercept and mutate arguments. When the script exits with code `0`, any JSON output written to stdout is parsed by the bridge in [`src/openhuman/hooks/bridge.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/hooks/bridge.rs) and merged into the original tool call payload, allowing you to override specific parameters like URLs or filtering criteria before the tool executes.

### Where does OpenHuman register the hooks system during startup?

The hooks are registered during the core initialization phase in [`src/openhuman/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/core/runtime/builder.rs). The `CoreBuilder` accepts hook implementations through `CoreBuilder::tool_hook()` and `CoreBuilder::post_turn_hook()` methods, which permanently bind the external script invocations defined in [`hooks.json`](https://github.com/tinyhumansai/openhuman/blob/main/hooks.json) to the runtime’s execution pipeline.