the short answer
Deploy an enterprise hooks.json to the system path on every machine (/Library/Application Support/Cursor/hooks.json on macOS, /etc/cursor/hooks.json on Linux, C:\ProgramData\Cursor\hooks.json on Windows) with beforeShellExecution and beforeMCPExecution hooks that call your policy scripts. Set failClosed: true, because crashes and timeouts fail open by default. Enterprise plans can also distribute team hooks from the dashboard.
- Enterprise path
- macOS
/Library/Application Support/Cursor/hooks.json - Default on failure
- Crashes, timeouts and non-2 exits fail open, unless
failClosed: true - Deny response
{"permission":"deny","user_message":"...","agent_message":"..."}- Team hooks
- Dashboard, Enterprise only, sync about every 30 minutes
- Run Mode allowlist
- Documented as best-effort, not a security boundary
The Hooks File
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{ "command": "/opt/company/cursor-shell-policy.sh", "timeout": 10, "failClosed": true }
],
"beforeMCPExecution": [
{ "command": "/opt/company/cursor-mcp-policy.sh", "timeout": 10, "failClosed": true }
]
}
}Shell Policy Script
#!/bin/bash
# beforeShellExecution: reads {"command", "cwd", ...} on stdin.
cmd=$(jq -r '.command // ""')
if printf '%s' "$cmd" | grep -Eq 'terraform[[:space:]]+destroy|git[[:space:]]+push.*--force|rm[[:space:]]+-rf[[:space:]]+/'; then
printf '%s' '{"permission":"deny","user_message":"Blocked by company policy","agent_message":"This command is not allowed. Propose the change in a PR instead."}'
exit 0
fi
printf '%s' '{"permission":"allow"}'MCP Policy Script
Field names are snake_case: user_message and agent_message. Exit code 2 also denies; invalid JSON blocks. The MCP hook receives tool_name, tool_input and mcp_server_name; its command field, present only for stdio servers, is the server’s launch command, not the tool call. Both scripts need jq on every machine: with failClosed: true, a missing dependency blocks every call, so ship it with the scripts.
Failproof can run here too: the form failproofai config writes is failproofai --hook preToolUse --cli cursor in ~/.cursor/hooks.json, at user scope and without failClosed. For enforcement the user cannot remove, put that entry in the system hooks.json with failClosed: true. These are the pre-tool command forms failproofai config writes into each agent’s hook file: user scope, except Codex, which it writes to the managed /etc/codex/config.toml when it can run as root. They cover only the pre-tool gate. failproofai config also registers post-tool output redaction, prompt, stop and session events, so keep its install alongside any system entry. Placing them in the system or managed locations below, and keeping them there with MDM, is your configuration rather than a documented Failproof install path; talk to us about a managed deployment.
#!/bin/bash
# beforeMCPExecution: reads {"tool_name", "tool_input", "mcp_server_name", ...} on stdin.
input=$(cat)
server=$(printf '%s' "$input" | jq -r '.mcp_server_name // ""')
tool=$(printf '%s' "$input" | jq -r '.tool_name // ""')
case "$server:$tool" in
github:*|docs:*) printf '%s' '{"permission":"allow"}' ;;
*) printf '%s' '{"permission":"deny","user_message":"MCP server not approved","agent_message":"This MCP tool is not on the company allowlist."}' ;;
esacCursor’s Other Admin Controls
| Control | Where | Note |
|---|---|---|
| Run Mode (Auto-review, Allowlist, Run Everything) | Team dashboard policy (Cursor 3.6+) | Allowlist is best-effort |
terminalAllowlist, mcpAllowlist, autoRun | ~/.cursor/permissions.json, deployable by MDM | User-writable, and concatenated with a per-repo .cursor/permissions.json, so users and repositories can extend it unless the team dashboard sets that category |
| MCP allowlist | Team dashboard, MCP Configuration (Enterprise) | Matches full command string or URL |
| Privacy Mode | Enforced at team level; AllowedTeamId as MDM backstop | On by default for Enterprise |
FAQ
Can developers remove the enterprise hooks?
The system path needs admin rights to change. All matching hooks run: enterprise, team, project and user.
Do hooks see the agent’s prompts?
A beforeSubmitPrompt hook receives the prompt and can stop it with {"continue": false}.
Does `preToolUse` support ask?
Cursor documents ask as not enforced for preToolUse; use deny or allow.
Sources
Checked against the sources below on October 21, 2026. Harness settings, hook capabilities and cloud features change between versions.
- Cursor docs: Hooks
- Cursor docs: Enterprise deployment patterns
- Cursor docs: Model and integration management
- Cursor docs: Compliance and monitoring
- Failproof AI docs: Agent harnesses and enforcement capability