Roll ACP out to every Cursor seat
Cursor is the one harness whose admin console distributes the hook configuration itself, on a schedule, without device management. The script still has to be on disk. Cursor’s hooks, deployment patterns, model and integration management, and Admin API pages are the source for every path and name below. Read the shape all four harnesses share first if you haven’t.
Before you push
- Claim your company domain on the ACP workspace’s Team page so enrollments land in your workspace (why).
- Decide the posture for unenrolled seats. The wrapper below sets
ACP_REQUIRE_ENROLLMENT=1, so a seat with no credential blocks each call with the enrollment step. Drop the variable for the self-serve posture (run, warn, log). - Check who already ran the self-serve installer. Their
~/.cursor/hooks.jsonhas a user-level ACP entry that will run in addition to the managed one. Have them remove it, or accept a doubled hook latency during transition.
Step 1: Put the script on the machine
Cursor’s docs are explicit: “Cursor does not deploy or manage files through your MDM solution.” Your MDM drops two files into a directory users can’t write to. macOS:
COMMIT=<sha> # a commit of github.com/agentic-control-plane/claude-code-acp-plugin
install -d -m 755 /etc/acp
curl -sf "https://raw.githubusercontent.com/agentic-control-plane/claude-code-acp-plugin/$COMMIT/bin/govern.mjs" -o /etc/acp/govern.mjs
echo "<expected-sha256> /etc/acp/govern.mjs" | shasum -a 256 -c
cat > /etc/acp/cursor-hook.sh <<'SH'
#!/bin/sh
ACP_CLIENT=cursor ACP_REQUIRE_ENROLLMENT=1 exec node /etc/acp/govern.mjs
SH
chmod 755 /etc/acp/cursor-hook.sh
chmod 644 /etc/acp/govern.mjs
Linux is identical. Windows, into C:\ProgramData\acp\: the same govern.mjs, plus cursor-hook.cmd:
@echo off
set ACP_CLIENT=cursor
set ACP_REQUIRE_ENROLLMENT=1
node "C:\ProgramData\acp\govern.mjs"
Node 18 or newer must be on the signed-in user’s PATH. The script has no other dependency.
Step 2: The hook file
The same entries the self-serve installer writes to ~/.cursor/hooks.json, pointed at the managed path:
{
"version": 1,
"hooks": {
"preToolUse": [
{
"matcher": ".*",
"hooks": [
{ "type": "command", "command": "/etc/acp/cursor-hook.sh", "timeout": 5 }
]
}
],
"postToolUse": [
{
"matcher": ".*",
"hooks": [
{ "type": "command", "command": "/etc/acp/cursor-hook.sh", "timeout": 5 }
]
}
]
}
}
preToolUse fires before every tool execution and honors a deny; postToolUse records the result. Cursor also exposes narrower events, beforeShellExecution and beforeMCPExecution among them, which carry richer input for their one tool class; the ACP script speaks the preToolUse contract, which covers all of them.
Two Cursor options are worth a deliberate choice, and the sample leaves both at their defaults:
failClosed(defaultfalse): whether a hook that errors or times out blocks the action. ACP’s script fails loud, not closed, when the gateway is unreachable in an interactive session, on the principle that a broken hook should never brick a fleet silently. SetfailClosed: trueonly if that is the posture you want on hook errors, which is a different question from policy denies.matcher:".*"is what the self-serve installer uses. Cursor’s matcher is a pattern against the tool; leave it broad and let workspace policy decide.
Step 3: Deliver it
| Channel | Where | Notes |
|---|---|---|
| Team hooks (dashboard) | Cursor dashboard, Enterprise plans | Cursor delivers configured hooks to every member’s client on login and syncs every thirty minutes. OS targeting is supported. No access to individual machines needed, for the config. |
Enterprise hooks.json (MDM) |
macOS /Library/Application Support/Cursor/hooks.json · Linux and WSL /etc/cursor/hooks.json · Windows C:\ProgramData\Cursor\hooks.json |
Highest-precedence layer. Your MDM writes it alongside the script. |
Precedence, highest first: Enterprise, Team, Project, User. Layers merge, so higher layers win on conflicting responses but don’t suppress lower-layer hooks. That’s why step 0 asks about existing user-level entries.
Team and enterprise hooks also run in Cursor’s cloud agents; user-level hooks don’t, because cloud agents have no home directory.
Step 4: Allowlist the MCP endpoint
Under MCP Configuration in the dashboard (Enterprise), add ACP as a URL entry:
https://api.agenticcontrolplane.com/mcp
Once any allowlist exists, only matching servers can run. Cursor’s docs are clear that “adding a server to the allowlist does not push it to users’ machines.” To distribute the server itself, link it to your Team Marketplace, or let each user add it; either way sign-in is MCP OAuth in the browser, so attribution comes with it.
The same page lets you set a sandbox network mode and a tool filter per server. ACP’s server exposes read-only introspection tools plus one that drafts a rule for a human to confirm; none of them applies policy by itself.
Step 5: Each person enrolls once
The pushed config identifies no one. Each developer opens cloud.agenticcontrolplane.com/plugin/authorize, signs in with their work identity, and pastes the one line it shows:
echo 'gsk_…' > ~/.acp/credentials
Until they do, with ACP_REQUIRE_ENROLLMENT=1 every tool call is denied with that instruction in the message.
What you’ll see
- Console rows with client
cursor, one per person. - On an unenrolled seat:
[ACP] Not enrolled…and the call doesn’t run. - In Cursor’s own audit log, via the Admin API’s
/teams/audit-logs: ateam_hookevent when the hook was pushed andmcp_server_configwhen the allowlist changed. Those are the receipts that the rollout itself happened, separate from ACP’s per-call ledger.
What’s verified and what isn’t
The hook entries above are the ones the self-serve installer writes today and the Cursor integration documents. The enterprise and team channels are described from Cursor’s published docs; we have not yet exercised them on a Cursor Enterprise seat ourselves. The issue tracking that is gatewaystack-connect #943.
Related
- Enterprise rollout: the shape all harnesses share
- Cursor integration — the self-serve install
- Cursor native controls
- Cursor: Hooks · Deployment patterns · Model and integration management · Admin API
Run agents? ACP lets you see, control, and price every tool call they make — allow/ask/deny policy, per-session cost, and a full audit log for Claude Code, Cursor, Codex, and OpenClaw, in one command:
curl -sf https://agenticcontrolplane.com/install.sh | bash
Getting started → · see your first governed call → · free up to 5 agents · prefer fully on-device? add --local