Every Layer Has Controls Now. The Gap Moved.
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
An argument we hear more often as the ecosystem matures: you don’t need a separate control layer, because control is already enforced elsewhere. The harness has approval modes and permission rules. The MCP servers have read-only flags and scoped tokens. The layer you’re describing already exists — twice.
It’s the best version of the objection, and it deserves a straight answer. The premise is true. The conclusion doesn’t follow, and the reason it doesn’t is structural rather than a matter of feature counting.
The premise is true, and we document it
We maintain the two comparison pages ourselves, in some detail.
The harnesses ship real controls. Claude Code has a layered rule system and a classifier screening calls by default. Codex ships a full hooks system and a sandbox on three OSes. Muse Code turns on an OS sandbox and an append-only event log out of the box. We compare fourteen of them across seven dimensions, and the honest summary is: genuinely good, improving fast.
The servers ship real controls too. The GitHub MCP server has a read-only mode, 23 toolsets, per-tool selection, and a lockdown mode. Supabase’s --read-only enforces as an actual read-only Postgres role. Stripe’s restricted keys are checked server-side — a read-only key cannot refund, whatever the client says. Two years ago none of this existed. The vendors did real work.
So: controls at the harness layer, controls at the server layer. In 2024 the case for a control plane was nobody enforces anything. That case is dead. The 2026 case is different, and stronger.
Three questions the layers can’t answer
Take any control in either table and ask three questions: who owns it, where else does it apply, and what can it see?
Who owns it. The server-side controls belong to the service’s admin plane — a Notion workspace admin, an Atlassian org admin, a Slack workspace owner. The client-side controls are launch-time flags in the MCP config, a file that lives where the agent works and that the agent can frequently edit; the filesystem reference server goes as far as letting a connected client replace the allowed-directories boundary at runtime. The harness controls are per-machine settings files. Now ask who answers for the agent’s behavior — the operator who deployed it. That person owns none of the three. The party carrying the risk has no surface where their policy lives. In every mature security architecture, that’s precisely the problem the control plane / data plane split exists to solve: enforcement that belongs to the operator, standing apart from the surfaces it governs.
Where else does it apply. A deny rule in Claude Code’s settings.json doesn’t exist in Codex’s config.toml. GitHub’s read-only is a flag; Supabase’s is a database role; Stripe’s is a key permission; Notion’s hosted server has none to offer. Every harness and every server is its own dialect, so “our agents never force-push, never write to production data, and always ask before moving money” isn’t one policy — it’s a dozen partial translations, each drifting independently, several inexpressible in the local vocabulary. The controls exist; your policy doesn’t, anywhere, as one artifact.
What can it see. This is the one that decides incidents. The GitHub MCP private-repo leak was a read of untrusted issue content followed by a write that exfiltrated private code. The Supabase lethal trifecta was untrusted data plus database access plus an exfiltration path — three capabilities, individually approved, jointly fatal. In both cases every individual call was legitimate to the layer that received it. The dangerous object was the sequence, and a per-server control cannot even express a rule about a sequence, because the server sees only its own slice. Same for the harness in the common case where what needs judging is the combination of what was just read and what’s about to be written. The controls are local; the risk is global.
And a fourth, for whoever runs agents at any scale: what got written down. Approvals are absent from the server category almost without exception — fifteen first-party servers, one native approval flow, and the vendor that built it (AWS, fail-closed, done right) is deprecating it in its own successor — and audit, where it exists at all, lands in one vendor console per vendor. The question that matters afterwards — show me everything the agent did, across every surface, and why each call was allowed — has no answer assembled from parts, because the parts don’t share a record.
We filed this finding against ourselves first
This isn’t a lesson we imported. Our own gateway includes an MCP client that mounts external servers, and its first internal security review — written before any of this content — recorded the day-one state plainly: an attached server with a delete_database tool would be callable by a background agent immediately, because nothing in the server, the protocol, or the harness had anywhere to say otherwise. Tool discovery worked, transport worked, auth worked; the rule had no place to live until we built one at the layer where the calls converge. The objection says that layer is redundant. Operating both sides of the wire, the thing we actually observed is that it was the only place the sentence “background agents don’t call destructive tools” could be written at all.
Defense in depth, not either/or
None of this argues against the native controls. It’s the opposite: turn all of them on. Scope the token, set the read-only flag, keep the sandbox on — the sandbox contains the blast; it doesn’t decide whether to fire. Layered enforcement is how every serious system works, and the layers below make the layer above cheaper and calmer.
The claim is narrower and harder to dodge: three properties — operator-owned policy, one rule set across every surface, visibility across the sequence — cannot emerge from per-layer controls, no matter how good each layer gets, because each layer is defined by the boundary it stops at. They can only exist in a layer that stands where the calls converge, on the runtime call path, owned by the party that answers for the outcome.
The harnesses got better. The servers got better. What neither grew, and structurally can’t, is each other’s context and your ownership. That’s the layer we build — and both comparison pages, harness and server, say precisely where each native surface ends.