> ## Documentation Index
> Fetch the complete documentation index at: https://conductorone-sathya-add-agent-guardrails-doc.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool call hooks

> Intercept MCP tool calls with built-in patterns or custom functions to redact, modify, or block calls at runtime.

<Note>
  **Activation required.** AI access management must be enabled for your tenant before you can use it. To get started, [contact the C1 support team](mailto:support@c1.ai) for a walkthrough.
</Note>

Tool call hooks are interception points that run on every governed MCP tool call. They can observe a call, modify its inputs or outputs, or deny it outright. Use them to redact sensitive data, cap risky parameters, or enforce conditional access rules that see beyond the entitlement grant model.

## How hooks work

Each hook fires on one of three events:

| Event             | When it runs                                                                                                           | What it can do                                                               |
| :---------------- | :--------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- |
| **Pre-tool use**  | Before C1 forwards the call to the MCP server                                                                          | Inspect or rewrite the input, or deny the call                               |
| **Post-tool use** | After the MCP server returns                                                                                           | Inspect or rewrite the output, or deny the response from reaching the client |
| **Pre-output**    | Before a chunk of the assistant's generated response leaves the process (Slack or web chat). No tool call is involved. | Inspect or rewrite the outgoing response chunk, or withhold it               |

Hooks run in **priority order** (lower priority numbers run first). Each hook is independently enabled or disabled and can be scoped to specific tools with a [CEL](/product/admin/expressions) filter expression. Multiple hooks can stack on the same call — input or output modifications from earlier hooks are passed to later ones, and any hook can short-circuit the chain by denying.

Hooks are **fail-closed**. If a hook errors, times out, or its filter expression fails to evaluate, the call is denied. Custom function hooks have a 60-second invocation timeout.

Every hook execution is recorded in the [audit log](/product/admin/audit-ai-tool-usage) with one of these statuses: `ALLOWED`, `MUTATED`, `DENIED`, `ERROR`, `TIMEOUT`, or `FILTER_ERROR`.

## Configure a hook

Create hooks from the **Hooks** section under **AI > Guardrails**.

<Steps>
  <Step>
    Go to **AI > Guardrails**, open the **General** tab, and find the **Hooks** section.
  </Step>

  <Step>
    Click **New hook**.
  </Step>

  <Step>
    Fill out the form:

    | Field                     | Notes                                                                                                                                                                  |
    | :------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | **Name**                  | Required. 1–100 characters.                                                                                                                                            |
    | **Description**           | Optional. Up to 2048 characters.                                                                                                                                       |
    | **Hook type**             | **Built-in pattern** for one of the patterns below, or **Custom function** to invoke a [function](/product/admin/functions).                                           |
    | **Event**                 | **Pre-tool use**, **Post-tool use**, or **Pre-output**. Some built-in patterns only support one event.                                                                 |
    | **Filter**                | Optional CEL expression. Empty matches all calls for the event. Available variables depend on the event — see [Filter variables by event](#filter-variables-by-event). |
    | **Priority**              | 0–1000. Lower runs first.                                                                                                                                              |
    | **Managed by guardrails** | Off by default. See [Managed by guardrails](#managed-by-guardrails) below.                                                                                             |
    | **Enabled**               | Toggle on to activate the hook immediately on save.                                                                                                                    |
  </Step>

  <Step>
    If you selected **Built-in pattern**, choose the pattern and configure its options. If you selected **Custom function**, pick the function from the dropdown.
  </Step>

  <Step>
    Click **Save**.
  </Step>
</Steps>

### Filter variables by event

A filter expression only sees the variables available for its event. Referencing one that isn't set for that event makes the expression fail to evaluate, which **denies the call** — see the fail-closed note above.

| Event                               | Variables                                                                                                                                                                  |
| :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Pre-tool use**, **Post-tool use** | `ctx.tool_name` — for example, `ctx.tool_name.startsWith("github_")`                                                                                                       |
| **Pre-output**                      | `ctx.untrusted_class`, one of the strings `"LOW"`, `"MEDIUM"`, `"HIGH"`; and `ctx.surface`, `"slack"` or `"web"`. `ctx.tool_name` is **unset** — no tool call is involved. |

<Warning>
  `ctx.untrusted_class` (a hook filter, comparing **strings**) and `ctx.untrusted_content` (a [guardrail rule](/product/admin/agent-guardrails-reference#cel-variables) variable, comparing **ordered levels**) carry the same underlying risk score under two different names and two different types. In a pre-output hook filter write `ctx.untrusted_class == "HIGH"`; in a guardrail rule write `ctx.untrusted_content == HIGH`. Using the rule form in a hook filter fails to evaluate, and the call is denied.
</Warning>

### Event and pattern must be compatible

Each built-in pattern is only valid for certain events, and C1 rejects a mismatch when you save the hook. The **Event** column in [Built-in patterns](#built-in-patterns) is the authoritative list.

<Note>
  **Custom function hooks cannot use the Pre-output event.** Saving one is rejected. Only the **Block output** and **Link filter** built-in patterns run on that stage.
</Note>

## Managed by guardrails

Each hook has a **Managed by guardrails** toggle:

* **Off (default)** — the hook always evaluates on every matching call.
* **On** — the hook only evaluates when a [guardrail](/product/admin/agent-guardrails) rule explicitly selects it.

See [Agent guardrails](/product/admin/agent-guardrails) for how guardrail rules select hooks.

## Built-in patterns

C1 ships eleven pre-built hook patterns. Each one is a self-contained handler with its own configuration; no function code is required.

| Pattern                    | Event            | What it does                                                                                                        |
| :------------------------- | :--------------- | :------------------------------------------------------------------------------------------------------------------ |
| **PII field redaction**    | Post             | Replaces values in output fields whose names match a configurable list with a placeholder string                    |
| **Credit card blocking**   | Post             | Denies the response if output contains a Luhn-valid credit card number                                              |
| **Query scope limit**      | Pre              | Caps numeric input fields at a maximum, to prevent oversized queries                                                |
| **Write authorization**    | Pre              | Denies calls whose classification is in a blocked list, optionally only outside business hours                      |
| **Sensitive file guard**   | Pre              | Denies calls that reference sensitive file paths or directories                                                     |
| **Tool output size guard** | Post             | Denies output exceeding a byte cap                                                                                  |
| **Secrets masking**        | Post             | Redacts secret-shaped values from output — AWS keys, bearer tokens, JWTs, private keys, Slack/GitHub tokens         |
| **Link filter**            | Post, Pre-output | Redacts or annotates URLs and markdown images whose host is not allowlisted, mitigating markdown-image exfiltration |
| **Encoded content guard**  | Pre              | Denies input containing long base64 blobs, long hex runs, or invisible unicode used to smuggle instructions         |
| **Prompt injection scan**  | Post             | Scores output for prompt injection using the AI-governance judge and acts at or above a threshold                   |
| **Block output**           | Pre-output       | Withholds the in-flight response chunk                                                                              |

### Pattern configuration and defaults

<Warning>
  Where a pattern takes a list, a value you configure **replaces** the default list — it does not extend it. If you add one field name to **PII field redaction**, the built-in field names stop applying unless you re-include them.
</Warning>

| Pattern                    | Configuration                                                                                                   | Defaults                                                                                                                          |
| :------------------------- | :-------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| **PII field redaction**    | Fields to redact; replacement text                                                                              | `ssn`, `social_security_number`, `date_of_birth`, `dob`, `salary`, `bank_account`, `routing_number`; replacement `***REDACTED***` |
| **Credit card blocking**   | None                                                                                                            | —                                                                                                                                 |
| **Query scope limit**      | Fields to cap; maximum value                                                                                    | `limit`, `page_size`, `count`, `max_results`; maximum `100`                                                                       |
| **Write authorization**    | Blocked classifications (at least one); optional business-hours window with timezone, start/end times, and days | Read is not offered as a blockable classification                                                                                 |
| **Sensitive file guard**   | Two separate lists: blocked file patterns, and blocked directories                                              | Patterns `.env`, `.env.*`, `credentials.json`, `*.pem`, `*.key`, `id_rsa`, `*.pfx`; directories `.ssh`, `.aws`, `.config`         |
| **Tool output size guard** | Maximum output size in bytes                                                                                    | `131072` (128 KB)                                                                                                                 |
| **Secrets masking**        | None                                                                                                            | —                                                                                                                                 |
| **Link filter**            | Allowed hosts; action (Redact or Annotate); whether to also act on images                                       | Action defaults to Redact                                                                                                         |
| **Encoded content guard**  | None                                                                                                            | —                                                                                                                                 |
| **Prompt injection scan**  | Risk threshold at which to act                                                                                  | —                                                                                                                                 |
| **Block output**           | Message; output surfaces                                                                                        | Surfaces default to **none** — see the warning below                                                                              |

<Warning>
  **A Block output hook with no surfaces selected never fires.** An empty surface list means *no* surface, not all of them — deliberately, so that forgetting to choose can't silently apply the hook everywhere. The failure mode is a hook that looks configured and enabled but withholds nothing. Select **Slack**, **Web**, or both.
</Warning>

## Custom function hooks

When the built-in patterns don't fit, write a [function](/product/admin/functions) and attach it to a hook. C1 invokes the function with a JSON payload describing the call and uses the return value to decide whether to allow, modify, or deny.

See the [Functions overview](/product/admin/functions) and [Create a function](/product/admin/functions-create) for how to author and deploy a function.

### Pre-tool-use payload

The function receives:

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "tool_name": "github_create_issue",
  "input": { /* the tool's input arguments */ },
  "context": {
    "tool_source": "connector",
    "classification": "WRITE"
  },
  "caller": {
    "id": "<principal id>",
    "token_id": "...",
    "mcp_client_id": "...",
    "mcp_client_type": "..."
  }
}
```

`tool_source` is `builtin`, `connector`, or `claw`. `classification` is the tool's configured action class (`READ`, `WRITE`, `DESTRUCTIVE`, `SENSITIVE`, or `DANGEROUS`). `caller` identifies who made the call; `token_id`, `mcp_client_id`, and `mcp_client_type` are omitted when empty.

<Warning>
  `caller` is for logging and call-shape decisions only — it is **never an authorization input**, because the grant decision is made before the hook runs. Identity-based scope belongs in [access profiles](/product/admin/tools-and-toolsets), not hooks.
</Warning>

### Post-tool-use payload

The function receives the same fields plus the call result:

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "tool_name": "github_create_issue",
  "input": { /* original input */ },
  "output": { /* whatever the tool returned */ },
  "error": "",
  "context": { /* same as pre */ },
  "caller": { /* same as pre */ }
}
```

### Return value

In both events the function returns an object with any subset of these fields:

```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
  "deny": false,
  "reason": "explanation shown in audit log",
  "input":  { /* pre only: replacement input */ },
  "output": { /* post only: replacement output */ }
}
```

* Set `deny: true` to block the call. The `reason` is recorded in the audit log and surfaced to the AI client as a denial.
* Omit `input` (pre) or `output` (post) when you don't need to modify the payload.
* Returning an empty object `{}` is equivalent to allowing the call unchanged.

If the function throws, exceeds the 60-second timeout, or returns invalid JSON, the call is denied and the failure is recorded as `ERROR` or `TIMEOUT` in the audit log.
