# Claude Code

> Connect Claude Code to Golem: hooks that block or hold a call before it runs, Golem's own MCP tools, and the shell jail on macOS.

Claude Code is connected when you [install Golem](/install): the installer runs `golem setup-client`, which writes Golem's hooks and MCP entries into your user settings. Restart Claude Code and every tool call it makes, built-in or MCP, is checked against your rules before it runs and signed into the record.

## What Golem adds

| File | What Golem adds |
|---|---|
| `~/.claude/settings.json` | Hooks on `PreToolUse`, `PostToolUse`, `SessionStart`, `UserPromptSubmit`, `UserPromptExpansion` and `PermissionDenied`, each calling the Golem agent on `127.0.0.1` |
| `~/.claude/settings.json` | On macOS, `CLAUDE_CODE_SHELL` in the `env` block, pointing at Golem's shell in `~/.golem/bin`, so Bash runs in [the shell jail](/shield#the-shell-jail) |
| `~/.claude.json` | Two MCP servers, `golem-witness` and `golem-context`, which give Claude Golem's own tools |
| `~/.claude/commands` | `/safely`, and `/sandbox` on a Mac with the sandbox |
| `~/.claude/skills` | The `golem-fleet` skill, and `golem-sandbox` on a Mac with the sandbox |

Golem only adds its own entries. Your other hooks, MCP servers and settings stay as they are, and a matcher you narrowed on Golem's hook is kept when `golem setup-client` repairs it. If you already set `CLAUDE_CODE_SHELL` to something else, Golem leaves it, and the shell jail is not attached.

The tool hooks are HTTP hooks. The `PreToolUse` one looks like this:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "http",
            "url": "http://127.0.0.1:39393/api/v1/hooks/tool-use",
            "timeout": 45,
            "headers": {
              "X-Golem-Hold-Seconds": "35",
              "X-Golem-Supports-Ask": "1"
            }
          }
        ]
      }
    ]
  }
}
```

If you manage Claude Code's settings from a repository, copy the URL exactly, with `127.0.0.1` rather than `localhost`. Claude Code treats two different URLs as two hooks, so every call would be checked and recorded twice.

## What a block looks like

A call a rule blocks is refused before it runs. Claude sees the reason and passes it on to you:

```text
blocked by Golem policy (rule "no-force-push-to-main", decision gd_3f9a1c07b2e4d815). Trying again won't change this; if you need it, ask your security team and quote the decision id.
```

The rule name and decision id are what your security team needs. To ask them to let it through, run:

```bash
golem exception gd_3f9a1c07b2e4d815 --reason "release branch cleanup" --days 7
```

The request goes to the console, where an admin decides it. See [Block, hold and contain](/response).

## What a hold looks like

A rule set to Ask holds the call until someone decides. Who decides is part of the rule, and it changes what you see in Claude Code:

- **The person at the keyboard.** Claude Code shows its own permission prompt, with Golem's reason: `held by Golem policy — approve to continue (rule "prod-changes-need-a-person", decision gd_9b04e2d61c7a35f8)`. Claude Code shows a hook's prompt in every permission mode, `auto` and `bypassPermissions` included. In a `claude -p` run or `dontAsk` mode nobody can answer, so the call is refused.
- **Golem's reviewer agent.** The hook holds the call for up to 35 seconds while the reviewer decides, then lets it through or refuses it. If no decision has come by then, Claude is told to wait and retry the same call, and the retry gets the answer.
- **Your team.** A hook cannot wait for someone to answer in the console, so the call is refused with a message saying your security team holds it, and the decision id to quote.

A rule set to Warn lets the call run and tells Claude it was flagged, so it can stop if that is not what you asked for.

Type `/safely` to lock the session to read-only tools before Claude reads something you don't trust. It only tightens: start a new session to loosen it. See [Safely](/response#safely).

## What is captured

| What | From |
|---|---|
| Every tool call before it runs: tool, arguments, working directory, repository | `PreToolUse` |
| Every tool call's result | `PostToolUse` |
| The session's permission mode on each call, for example `bypassPermissions` | the tool hooks |
| Claude Code's own refusals in auto mode | `PermissionDenied` |
| Each commit Claude makes, tied to its session | the Golem agent, from git |
| On macOS, every operation the shell jail refused, with the command that tried it | the shell jail |

MCP calls to your own servers are checked and recorded through the same hooks, as `mcp__server__tool`. The full content of each call goes to your storage; the record holds its hash and signature. See [What Golem captures](/coverage#what-golem-captures).

Your prompts and Claude's replies are not captured by the hooks. To add your prompts, turn on Claude Code's OpenTelemetry export and point it at the Golem agent, in the `env` block of `~/.claude/settings.json`:

```json
{
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
    "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL": "http/protobuf",
    "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://127.0.0.1:4318/v1/logs",
    "OTEL_LOG_USER_PROMPTS": "1",
    "OTEL_LOG_TOOL_DETAILS": "1"
  }
}
```

## Check it is working

1. Quit Claude Code and start a new session. Claude Code reads hooks and MCP servers only at startup.
2. Run `/mcp` in Claude Code. `golem-witness` and `golem-context` should be connected.
3. Run `golem doctor`. **client wiring** should be ok, and after Claude has run a tool, **inline enforcement (claude-code)** should say its tool calls reach the hooks.
4. Ask Claude to run a harmless command, such as `git status`. The session appears in the console under **Conversations** shortly after.

## Limits

- **The hooks fail open.** If the Golem agent is not running, or a hook gets no answer within its 45 seconds, Claude Code runs the call. Nothing is checked or recorded until the agent is back. `golem doctor` reports any tool that ran unchecked.
- **A wrong port fails silently.** Claude Code shows no error for a hook it cannot reach. If you change the agent's port, run `golem setup-client` and check with `golem doctor`.
- **The shell jail is macOS only,** and covers Claude Code's Bash tool. Reads and edits through Claude's own file tools are checked by the hooks, not by the jail.
- **Managed settings can switch Golem off.** Claude Code's `strictPluginOnlyCustomization` setting ignores hooks and MCP servers in user settings, which is where Golem's are. See [Roll out to a fleet](/fleet).
- **Settings files outside your user settings** are not edited. A project's `.claude/settings.json` that registers its own copy of Golem's hooks is yours to keep correct.

See also [Rules and CEL](/rules), [Block, hold and contain](/response), [Coverage](/coverage) and [Limits](/limits).
