MCP Governance
AIControls intercepts every MCP tool call from Claude Code and other MCP-enabled agents — applying policies, logging arguments and results, and routing high-risk operations for human approval.
How MCP proxying works
When Claude Code (or another MCP client) connects through the AIControls proxy, MCP tool calls are intercepted at the protocol layer before being forwarded to the upstream MCP server. The proxy evaluates policies against the tool name and arguments, then either allows, denies, or holds the call for approval.
No changes are needed on the MCP server side — the proxy is transparent to both the agent and the server.
What gets logged
Every MCP tool call produces an audit event containing:
| Field | Description |
|---|---|
| Tool name | e.g. bash, read_file, write_file, kubectl |
| Arguments | Full argument map passed to the tool |
| Result | Tool output (configurable — can be redacted for sensitive tools) |
| Decision | Allow, Deny, Approve, Ask, Warn, or Audit — and which policy triggered |
| Duration | Time from call to response |
| Resource | File path, URL, or resource identifier extracted from arguments |
Built-in SSRF protection
Any tool that accepts a URL argument (web fetch, browser automation, HTTP clients) can be directed by an adversarial prompt toward internal endpoints — cloud metadata services, internal APIs, or loopback addresses. AIControls blocks this by default: every tool-call argument that is a URL is resolved via DNS at call time and checked against private, loopback, link-local, and other internal ranges, across every tool and every argument — no per-tool configuration needed.
Resolving DNS at call time (rather than only pattern-matching the URL string) also catches DNS rebinding, where a hostname is deliberately made to resolve to an internal address.
If an agent legitimately needs to reach an internal address (e.g. an internal search API), add it to your workspace's SSRF allowlist rather than disabling the protection. See Policy Context for the object.mcp.ssrf.* fields available if you want to layer custom policy rules on top of the default protection.
Policy-based tool authorization
MCP tool calls are governed the same way as any other request: a policy declares a matchCondition that scopes it to specific tools or servers, and one or more validation rules that inspect the tool name and arguments to decide whether the call is allowed, denied, audited, or held for approval.
From the MCP Servers page, you can create these governance policies scoped to a specific server directly from the server list. Click the action menu (⋮) on any server to see two options:
| Action | What it does |
|---|---|
| Create a policy | Opens the policy editor pre-populated with a matchCondition scoped to this server's tool namespace. Add your validation logic and save — the policy applies immediately. |
| Add to existing policy | Adds a new rule to an existing policy, pre-scoped to this server. Use this to consolidate related rules into a single policy rather than creating many single-rule policies. |
This is the recommended starting point for governing a newly registered server. A typical workflow:
- Register the MCP server and confirm it appears in the server list.
- Use Create a policy to add an Audit rule that logs all tool calls from this server. Observe the audit log for a few days to understand usage patterns.
- Use Add to existing policy to layer in Deny or Approve (
warn-approve) rules for specific high-risk tools, using the argument patterns you observed.
Policies generated this way are full CEL ValidatingPolicy documents — you can edit them freely in Policies after creation. The server-scoped match condition is a starting point, not a constraint.
Tool-definition pinning (rug-pull defense)
MCP servers can change behavior after you've already reviewed them — a tool's description or input schema can be silently rewritten between one tools/list response and the next. Because tool descriptions are fed directly to the model, a changed description can steer an agent into unintended behavior without anyone touching a config file. This is sometimes called a "rug-pull."
AIControls defends against it by treating a server's tool set as a snapshot to compare against, not just a reachable endpoint:
- At scan time, AIControls hashes every tool's name, description, and input schema from the server's
tools/listresponse and stores the snapshot. - On every later scan, the current
tools/listresponse is compared against the pinned snapshot, tool by tool.
Each tool carries a drift state:
| State | Meaning |
|---|---|
| Pinned | Matches the snapshot from the last scan — unchanged. |
| Drifted | The tool existed in the snapshot, but its description, name, or input schema no longer matches. |
| New | The tool doesn't appear in the snapshot — the server started advertising it since the last scan. |
Drift and new-tool states appear as badges on the tool list in Settings → MCP Servers, and every detected change is written to the audit log so you can see exactly what changed and when. The current snapshot and hash for a server are also available read-only at GET /api/v1/mcp-servers/{name}/tool-schemas if you want to check it from a script.
Drift response mode
Each server has a drift response mode, defaulting to a workspace-wide setting (Alert unless changed) and overridable per server:
| Mode | Effect |
|---|---|
| Alert | A drifted or new tool raises an admin alert but stays callable — the default. |
| Block | Drifted and new tools are hidden from tools/list — clients can't see or call them — until an admin re-pins them. |
Re-pinning accepts a tool's current definition as the new approved baseline: its state returns to pinned (and, under Block mode, it becomes visible again). Re-pin from the tool list in Settings → MCP Servers, or via POST /api/v1/mcp-servers/{name}/tool-schemas/{tool}/repin. Re-pinning is an admin action — reviewing the schema diff before accepting it is the point.
Block mode hides a drifted tool at the catalog (tools/list) layer. A per-call RequireApproval-on-drift flow — parking each individual call to a drifted tool for approval rather than hiding the tool outright — is planned but not yet available.
Shadow MCP traffic & quarantine
Every MCP tool call targets a server. If that server isn't one you've configured as a datasource, AIControls still sees the traffic — the proxy sits inline — and treats it as a signal instead of silently ignoring it.
The first time a tool call reaches a server that is in neither your configured datasources nor the server catalog, AIControls:
- Automatically creates a catalog entry for that server in the quarantined state.
- Raises a one-time admin alert — you're notified once per newly discovered server, not once per call.
A quarantined entry is an audit-only placeholder: traffic to it keeps being logged, but the server has no owning team, no bindings, and no policy of its own — it exists so the discovery isn't lost while an admin decides what to do with it.
State (quarantine_state) | Meaning |
|---|---|
| (empty) | Normal — a known, admin-managed server. |
| quarantined | Traffic was observed to an unregistered server; pending admin resolution. |
| denied | An admin reviewed the quarantined server and rejected it — further traffic is blocked by policy. |
An admin resolves a quarantined entry from Settings → MCP Servers by either promoting it into a normal governed server (eligible for policies and Access Bindings) or denying it. See Resolve a quarantined server for the exact steps.