# Cursor

This page is for an agent connecting a team's Cursor to Zaun. Cursor is covered through its
hooks, which can stop a prompt, a shell command, an MCP call or another tool before it runs,
and also record what the agent did. If you are the person, you deploy one system hooks file
per machine, or team hooks from the Cursor dashboard, and decide whether a hook failure should
block.

## Deploy the hooks file

The console generates the hooks file and the forwarder for your project. Write the file to
Cursor's system hooks location:

| System | Path |
| --- | --- |
| macOS | `/Library/Application Support/Cursor/hooks.json` |
| Linux and WSL | `/etc/cursor/hooks.json` |
| Windows | `C:\ProgramData\Cursor\hooks.json` |

On Cursor Enterprise, the same hooks can instead be distributed from the Cursor web dashboard.
Either way the hook commands run the forwarder on each machine, so install the forwarder and
its configuration file on every machine too.

## Block with hooks

Cursor hooks run commands, so the forwarder ships with the hooks file. It sends each check to
Zaun and answers Cursor in its own form for each event: `continue: false` stops a prompt, and
`permission: deny` stops a tool, shell command or MCP call.

```json
{
  "version": 1,
  "hooks": {
    "beforeSubmitPrompt": [{"command": "python3 <MANAGED_DIR>/zaun_hook.py", "failClosed": false}],
    "preToolUse": [{"command": "python3 <MANAGED_DIR>/zaun_hook.py", "failClosed": false}],
    "beforeShellExecution": [{"command": "python3 <MANAGED_DIR>/zaun_hook.py", "failClosed": false}],
    "beforeMCPExecution": [{"command": "python3 <MANAGED_DIR>/zaun_hook.py", "failClosed": false}]
  }
}
```

- `<MANAGED_DIR>` is the administrator-owned directory that holds the forwarder and its
  configuration file. The configuration file carries the endpoint and the key. On Windows the
  generated command uses the Windows path and launcher.
- The commands shown run the forwarder as a Python 3 script. Use the commands in the
  console's generated file, which match the forwarder it ships. When the forwarder is the
  Python script, every machine needs Python 3; without it the hook fails, and with
  `failClosed: false` the action goes ahead.
- `beforeSubmitPrompt` checks the prompt, `preToolUse` checks tool calls,
  `beforeShellExecution` checks shell commands, and `beforeMCPExecution` checks MCP tool
  calls, each before it runs.
- Hooks apply the same verdict as the API. Today a tool call is blocked by input checks such
  as the test string; a threat Hex v1.3 finds only in the agent's own tool calls normally raises
  an alert instead. See [what blocks today](capture-paths.md#what-each-path-can-and-cannot-do).
- The prompt hook does not run in the headless `cursor-agent` CLI. The tool hooks do.

### When Zaun cannot be reached

The generated hooks set `failClosed: false`: if the forwarder fails or times out, the action
goes ahead. This is deliberate. These hooks run on nearly every agent action, so with
`failClosed: true` any failure of the hook process, such as a missing Python after an OS
update or an unreadable configuration file, would stop every agent on the fleet at once, for
a reason unrelated to security. If the person wants fail closed anyway, set `failClosed: true`
on the hooks that matter most, such as `beforeShellExecution`, and test the failure first.

## The proof to show

On a machine with the hooks file in place:

1. Type the test string `ZAUN-TEST-VIOLATION-7f3a9c` from [Try it](try-it.md) into Cursor's
   agent. The prompt is stopped with Zaun's reason.
2. Ask the agent to create a file whose content is `ZAUN-TEST-VIOLATION-` followed directly by
   `7f3a9c`. The prompt does not contain the whole string, so it passes; the file write
   carries it and is denied.

Both appear on Violations.
