Skip to content
Agentic Control Plane

Your AI agents need a control plane, not another gateway

David Crowe · · 7 min read
architecture governance control-plane mcp

If you’re running AI agents in production — Claude Code, OpenClaw, Cursor, Codex — you’ve probably thought about governance. The instinct is to reach for something familiar: an API gateway, an LLM proxy, a permissions layer.

These don’t work. Not because they’re bad tools, but because they solve the wrong problem.


The three layers of AI infrastructure

Every AI agent stack has three layers:

┌─────────────────────────────┐
│  LLM Layer                  │  Claude, GPT-4, Gemini
│  (generates decisions)      │
├─────────────────────────────┤
│  Tool Layer                 │  Bash, Read, Write, APIs, MCP
│  (executes actions)         │  ← this is ungoverned
├─────────────────────────────┤
│  Infrastructure Layer       │  Servers, databases, files
│  (where damage happens)     │
└─────────────────────────────┘

LLM gateways (Helicone, LiteLLM, Portkey) sit between you and the model. They log prompts, route between providers, manage API keys. They see what you ask the model. They don’t see what the model does with your tools.

API gateways (Kong, Apigee, AWS API Gateway) sit in front of your APIs. They handle rate limiting, auth, and routing for HTTP traffic. They don’t know that the caller is an AI agent, what the agent is trying to accomplish, or whether this particular tool call should be allowed for this particular agent type.

Neither layer governs the tool call. When Claude Code runs Bash("rm -rf /tmp/project") or WebFetch("https://internal.company.com/api/users"), no gateway sees it. The model decided to make the call. The tool executed it. Nobody was in between.


What a control plane does differently

A control plane sits at the tool layer — between the model’s decision and the tool’s execution:

Agent decides: "I need to run Bash(git push --force)"
         │
         ▼
┌─────────────────────────────┐
│  Control Plane              │
│  - Who is calling? (identity)│
│  - What tier? (interactive   │
│    vs background agent)     │
│  - Allowed? (policy check)  │
│  - Log it (audit trail)     │
└────────────┬────────────────┘
             │ allow / deny
             ▼
Tool executes (or doesn't)

This is fundamentally different from proxying. The control plane doesn’t handle the traffic — the tool call still executes locally or goes directly to the API. The control plane observes, decides, and logs. It’s a governance layer, not a routing layer.

The analogy is Kubernetes. The Kubernetes data plane runs your containers. The Kubernetes control plane decides which containers run where, with what resources, under what policies. You don’t route traffic through the control plane — you route decisions through it.


Why this matters for AI agents specifically

Traditional software makes API calls you can predict. You wrote the code, you know the call patterns, you can set up conventional auth and rate limiting.

AI agents are different:

  1. The call pattern is unpredictable. The model decides at runtime which tools to call, with what arguments, in what order. You can’t whitelist paths ahead of time.

  2. There are tiers of autonomy. An interactive session where the user approves each call is different from a background agent running overnight. The governance policy should reflect this.

  3. Agents spawn agents. Claude Code’s subagent system, OpenClaw’s multi-agent delegation — an agent can spawn other agents with decreasing human oversight. Each level needs progressively tighter controls.

  4. Multiple clients, one governance model. Your team uses Claude Code, someone else uses Cursor, your CI pipeline uses Codex. Each client has different tool-calling patterns, but you want one set of policies.

None of these properties map to API gateway concepts. There’s no “route” to configure. There’s no “endpoint” to protect. There’s a model making autonomous decisions about tool use, and you need a way to observe and govern those decisions.


The implementation: hooks, not proxies

Here’s how we implemented the control plane for Claude Code. It’s a PreToolUse hook — a shell command that fires before every tool call:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": ".*",
      "hooks": [{
        "type": "command",
        "command": "node $HOME/.acp/govern.mjs",
        "timeout": 5
      }]
    }]
  }
}

The hook receives the tool name, arguments, session ID, and permission mode on stdin. It sends this to the control plane API. The API evaluates the policy and returns allow or deny.

For OpenClaw, it’s a before_tool_call plugin hook:

api.on("before_tool_call", async (event, ctx) => {
  const result = await checkAcpGovernance({
    toolName: event.toolName,
    toolInput: JSON.stringify(event.params),
    agentId: ctx.agentId,
    agentTier: resolveAgentTier(ctx.agentId),
  });

  if (!result.allowed) {
    return { block: true, blockReason: result.reason };
  }
  return {};
});

Same API. Same policies. Same dashboard. Different client integration, same governance model. That’s the control plane pattern — the enforcement point is in each client, but the decision-making is centralized.


What you don’t need

You don’t need to proxy tool calls through a gateway. The tool calls still execute locally (Claude Code runs Bash on your machine) or go directly to the API (OpenClaw calls the GitHub API directly). The control plane doesn’t sit in the data path.

You don’t need to pre-register tools. The control plane discovers tools through usage. The first time an agent calls a new tool, it appears in your dashboard. You can then set policies for it.

You don’t need to change how you use your AI tools. The governance hook is transparent. Your workflow doesn’t change. You just get visibility and control.


The policy model: agent tiers × tools

The control plane evaluates two dimensions:

Agent tier — how much human oversight is there?

  • Interactive: user is watching every call
  • Subagent: spawned for a task, semi-autonomous
  • Background: fully autonomous, no human
  • API / CI: programmatic access

Tool — what is being called?

  • Bash, Write, Read, Edit — local operations
  • WebFetch — network access
  • MCP tools — external API calls

The policy matrix lets you set permissions, rate limits, and data transforms for each combination:

  Interactive Subagent Background
Bash Allow Allow (60/min) Deny
Write Allow Allow Deny
WebFetch Allow Allow (30/min) Deny
Read Allow Allow Allow

This is a real policy from a working deployment. Background agents can read but can’t write, execute, or fetch. Subagents can do everything but are rate-limited. Interactive sessions are unrestricted.


Audit mode: start without risk

The control plane defaults to audit mode. Every tool call is logged — tool name, arguments, decision, identity, latency — but nothing is blocked.

This is the right starting point. You see what your agents are actually doing before you set rules. Most teams are surprised by what they find: agents accessing files they shouldn’t, making network requests they didn’t expect, running shell commands more aggressively than anticipated.

When you’re ready to enforce, flip one switch in the dashboard. Policies take effect immediately. Denied calls return a reason to the model, which adapts its approach.


The fail-safe question

What happens when the control plane is unreachable?

In audit mode: the hook fails open. Your tools keep working. The log entry doesn’t get recorded for that one call.

In enforce mode: the hook fails closed. If the control plane can’t verify a call, the call is blocked. This is the correct behavior for compliance-sensitive environments — better to pause than to run ungoverned.

This is a configurable choice, not a hardcoded assumption. The team running a development workstation wants fail-open. The team running agents against production databases wants fail-closed.


What’s next: multi-client governance

Today, the control plane supports Claude Code and OpenClaw. The architecture supports any client that can fire a hook before tool execution — Cursor, Codex, Windsurf, Replit, custom frameworks.

The installer auto-detects which clients are present:

curl -sf https://agenticcontrolplane.com/install.sh | bash

If you have Claude Code, it registers the PreToolUse hook. If you have OpenClaw, it installs the plugin. If you have both, it sets up both. One command, any client, same governance.

The control plane is the layer that makes this possible. Each client has its own hook mechanism, its own tool-calling patterns, its own autonomy model. The control plane normalizes all of it into a consistent governance framework: identity, policy, audit.

That’s what a gateway can’t do. A gateway handles one protocol. A control plane handles a concept.


Try it

curl -sf https://agenticcontrolplane.com/install.sh | bash

Free. No credit card. No config. Every tool call, governed.

View the dashboard →

Share: Twitter LinkedIn
Related posts

← back to blog