Skip to main content
Claude Code routes model traffic through the gateway via ANTHROPIC_BASE_URL, but coding assistants also perform work the model API never sees — running shell commands, editing files, and calling MCP tools. TrueFoundry governs that surface through Claude Code hooks: each hook payload is sent to a dedicated gateway endpoint that runs your configured guardrails and returns Claude Code’s native hook response, so a violation blocks the action (or rewrites it) directly in the client.
This is complementary to the standard Claude Code integration. That covers model requests (cost, routing, LLM guardrails on completions); this page covers prompt submissions and tool calls via hooks. You can use either or both.

How it works

  1. Claude Code fires a hook (e.g. before a tool runs) and sends the event payload on stdin.
  2. A hook command POSTs that payload to the gateway endpoint POST /hooks/claude-code.
  3. The gateway runs the guardrails you nominated for that hook and returns a JSON response containing Claude Code’s native hook fields (decision, permissionDecision, updatedInput, updatedToolOutput).
  4. Claude Code acts on that response — allowing, blocking, or applying the rewritten content.

What you can and can’t do

There are two things a guardrail can do at a hook:
  • Validation — decide block or allow. A block stops the prompt or tool call (or, for PostToolUse, replaces the already-produced result — see caveats).
  • Mutationrewrite the content in place (e.g. redact an email from a tool’s input or output) and let the action continue with the sanitized version.
The three supported hook events differ in which of these they support: In short:
  • Validation works on all three events.
  • Mutation works only on the MCP tool pathsPreToolUse (tool input) and PostToolUse (tool result). The prompt path (UserPromptSubmit) is validation-only.
There is no LLM-output direction here: Claude Code has no hook for the assistant’s generated response. To guardrail model responses, use output guardrails on the model request path (see Guardrails).

Choosing which guardrails run

Guardrails for the hook endpoint are selected per request via the x-tfy-guardrails header (the same JSON format used elsewhere — see Applying guardrails via header). The endpoint does not read tenant Guardrail Config rules; the caller nominates the selectors.
Each selector is group/guardrail-name. If no selector is nominated for a hook’s direction, that request passes through.

Prerequisites

  1. A guardrail group and at least one guardrail integration — see Create a guardrail. Note the selector string, e.g. security/email-regex.
  2. A TrueFoundry API key with access to that guardrail group.
  3. Claude Code installed and (optionally) already pointed at the gateway per the Claude Code integration.

Configure the hooks

Add the hooks to your project’s .claude/settings.json (or ~/.claude/settings.json). This example wires all three events to the endpoint and blocks anything the security/email-regex guardrail flags. Replace the selector with your own.
TFY_GUARDRAIL_URL is your gateway host root — the same host as ANTHROPIC_BASE_URL but without the /api/llm suffix (the hook endpoint is mounted at the root, e.g. https://your-gateway-host/hooks/claude-code).
Claude Code also supports native "type": "http" hooks. If you use those, put x-tfy-api-key and x-tfy-guardrails in the hook’s headers, reference secrets as $TFY_API_KEY, and list them in allowedEnvVars so they are interpolated from the environment.

Verify

Send a payload through the endpoint directly before wiring it into Claude Code:
A blocked request returns decision: "block" (and, for PreToolUse, hookSpecificOutput.permissionDecision: "deny"). A passing request returns {"verdict": true, "transformed": false}.

Response contract

  • verdict: false — the guardrail blocked. The response carries Claude Code’s native block fields for the event (decision: "block", plus permissionDecision: "deny" for PreToolUse; for PostToolUse the flagged result is replaced with the guardrail message via updatedToolOutput).
  • transformed: true — a mutating guardrail rewrote the content. The rewrite is applied through the native field for the event (updatedInput for PreToolUse, updatedToolOutput for PostToolUse).
  • Fail-closed — if the gateway hits an error (an internal failure, or a misconfigured/unresolvable guardrail selector) it returns verdict: false with the event’s native block fields, so a request it couldn’t verify is blocked rather than let through.

Caveats

  • PostToolUse cannot prevent a tool from executing. It fires after the tool has run, so its side effects (a file written, a command executed, an API called) have already happened. Blocking at PostToolUse only replaces the result the model sees — it does not undo the action. To actually stop a dangerous tool call, use PreToolUse.
  • UserPromptSubmit is validation-only. You can block a prompt, but you cannot redact or rewrite it — Claude Code has no mechanism to substitute a modified prompt. If a prompt violates policy, it’s blocked, not sanitized.
  • No LLM-output guardrails via hooks. Claude Code exposes no hook for the assistant’s generated response, so model output can’t be scanned here. Use output guardrails on the model request path instead (Guardrails).
  • PreToolUse mutation auto-allows the call. When a guardrail rewrites the tool input, the response sets permissionDecision: "allow" alongside updatedInput, so the sanitized call proceeds without the usual permission prompt. This is intentional (the guardrail already vetted it), but it does bypass interactive approval for that call.
  • Selection is header-driven, not rule-based. The endpoint runs exactly the guardrails named in the x-tfy-guardrails header; it does not consult tenant Guardrail Config rules. Each Claude Code hook config carries its own selector list.
  • Fail-closed on errors. If the gateway errors internally — including when a nominated guardrail selector can’t be resolved (unknown name, invalid placement, unsupported type) — it returns verdict: false and blocks the action, so a request that couldn’t be verified never slips through. This only applies when the gateway is actually reached: if it’s unreachable and curl returns nothing, Claude Code has no decision to act on and the action proceeds (see the next point) — so hooks still shouldn’t be your only control for hard-security requirements.
  • Command hooks depend on curl output. For "type": "command" hooks, Claude Code reads the guardrail decision from the command’s stdout (curl … -d @-). A network failure, wrong URL, or missing x-tfy-api-key yields no decision and the action proceeds.
  • matcher: "*" on the tool hooks runs the guardrail on every tool. Narrow it (e.g. "Bash", "Edit|Write", or a specific MCP tool) if you only want to guard certain tools.