# Grok Build Tool-Call Control & Audit — Install Guide

Install ACP for Grok Build via its native hook system. Every tool call policy-checked before it runs — even in always-approve mode — with an audit record, output scanning, and a session receipt.

# Govern Grok Build with Agentic Control Plane

<p style="font-size:17px;line-height:1.6;color:var(--acp-text);max-width:660px;margin:8px 0 6px;">See, control, and price <strong>every tool call Grok Build makes</strong> — shell, file edits, search, subagents — from one dashboard. Grok Build <a href="/controls/grok-build">fires PreToolUse hooks in every mode, including always-approve</a>; this hook puts your policy on that path.</p>

## TL;DR



The installer detects Grok Build, drops the hook at `~/.acp/hooks/grok-build/hook.mjs`, registers it in `~/.grok/hooks/acp.json` (user-global — always trusted, no per-project prompt), and opens your browser once to provision a workspace into `~/.acp/credentials`. The next Grok Build session is governed.

<p style="font-size:12.5px;color:var(--acp-text-faint);margin:8px 0 0;">MIT-licensed, zero dependencies, one readable file — <a href="https://github.com/agentic-control-plane/grok-build-acp-plugin">read it before you run it</a>.</p>

## Why a purpose-built hook (your Claude Code hook already fires here — and does nothing)

Grok Build reads hooks out of your real `~/.claude/settings.json` by default. If you have our Claude Code hook installed, Grok Build is already *running* it on every call — and ignoring every verdict it returns. The [output contract differs](/controls/grok-build): Grok parses a top-level `decision` field and does not parse Claude's `hookSpecificOutput.permissionDecision`, so a Claude-vocabulary deny reads as decision-absent, which is an allow. A hook that looks installed and governs nothing is the exact failure class a control plane exists to remove, so this integration is its own hook speaking Grok's verified vocabulary — with Claude's alongside for the day xAI's forward-compatibility note becomes parsing.

Two more Grok-specific translations the hook makes:

- **Tool names.** Grok's native names (`run_terminal_command`, `read_file`, `search_replace`, …) are mapped to the canonical vocabulary before the policy check, because content floors key on it — in live verification, `rm -rf /` sailed past the hardline floor under the native name and was denied under the mapped one. The native name is preserved in the audit record as `client_tool_name`.
- **The missing ask.** Grok has no `ask` decision. An ACP ask resolves by mode: attended (`default`/`plan`) defers to Grok's own prompt, which still stands after the hook; unattended (`auto`/always-approve/headless) becomes a deny carrying the console link. One caveat to know: an explicit local allow rule outranks an ACP ask in attended modes. ACP denies always hold.

## What's on the wire

| Grok event | ACP call | Effect |
|---|---|---|
| `PreToolUse` | `POST /govern/tool-use` | allow / deny before anything runs — in every permission mode |
| `PostToolUse` / `PostToolUseFailure` | `POST /govern/tool-output` | audit + PII scan; post-hoc flags surface loudly (only `PreToolUse` can block in Grok) |
| `Stop` | — | session receipt in the scrollback: calls governed, denies, holds, and a console review link |

## Failure posture

Grok Build's hooks are [fail-open by design](/controls/grok-build) — a timed-out or crashed hook allows. This hook doesn't inherit that: its registered timeout (30s) sits far above its internal 4-second decision budget, so the hook always answers, and the posture is its own. Gateway unreachable: attended sessions fail **open, loudly** — `[ACP] ⚠ UNGOVERNED` in the scrollback plus a durable `~/.acp/lapse.log` line — because an ACP outage must never brick your session. Unattended tiers fail **closed** — nobody is watching, so the block is the safety net. One retry on transport failure; an HTTP error status is the server answering and is never retried.

## Manual install

```sh
mkdir -p ~/.acp/hooks/grok-build ~/.grok/hooks
curl -fsSL https://raw.githubusercontent.com/agentic-control-plane/grok-build-acp-plugin/main/hook.mjs \
  -o ~/.acp/hooks/grok-build/hook.mjs
curl -fsSL https://raw.githubusercontent.com/agentic-control-plane/grok-build-acp-plugin/main/hooks/acp.json \
  -o ~/.grok/hooks/acp.json
```

Then put a workspace key in `~/.acp/credentials` (mint one at [cloud.agenticcontrolplane.com](https://cloud.agenticcontrolplane.com)). Requires Node ≥ 20 on PATH.

## Add the cost X-ray

The hook governs and audits tool calls; it never sees the model call, so it cannot price it. The installer appends a `[model.acp]` block to `~/.grok/config.toml` (base URL + `env_key = "ACP_KEY"`, never a literal key; your `[models] default` is left alone) and installs `grok-acp` next to `claude-acp` and `codex-acp`:

```bash
grok-acp
```

`grok-acp` reads your workspace key from `~/.acp/credentials`, exports it as `ACP_KEY`, passes `-m acp` for that launch only, and starts plain `grok` with a one-line note if the gateway is unreachable. The block routes `gemini-3.5-flash` by default; change `model =` under `[model.acp]` to any `gpt-*` / `claude-*` / `gemini-*` id the proxy routes. Full walkthrough: [Turn on Cost X-Ray](/cost-tracking).

## Verify it

```sh
echo '{"hookEventName":"pre_tool_use","toolName":"run_terminal_command","permissionMode":"default","toolInput":{"command":"rm -rf /"}}' \
  | node ~/.acp/hooks/grok-build/hook.mjs
```

Expected: a `decision:"deny"` with the hardline-floor reason, exit code 2 — and the denied call in your [activity log](https://cloud.agenticcontrolplane.com/activity) seconds later. That exact check runs in the plugin's test suite and ran live against production before this page shipped.

## Which ACP?

Grok Build also runs as an **Agent Client Protocol** host (`grok agent stdio`) — Zed's editor-integration protocol, which shares the acronym. This page is the **Agentic Control Plane**. [Disambiguation.](/acp-vs-acp)
