Skip to main content

MCP Governance

Concepts

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:

FieldDescription
Tool namee.g. bash, read_file, write_file, kubectl
ArgumentsFull argument map passed to the tool
ResultTool output (configurable — can be redacted for sensitive tools)
DecisionAllow, Deny, Approve, Ask, Warn, or Audit — and which policy triggered
DurationTime from call to response
ResourceFile 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:

ActionWhat it does
Create a policyOpens 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 policyAdds 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:

  1. Register the MCP server and confirm it appears in the server list.
  2. 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.
  3. 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/list response and stores the snapshot.
  • On every later scan, the current tools/list response is compared against the pinned snapshot, tool by tool.

Each tool carries a drift state:

StateMeaning
PinnedMatches the snapshot from the last scan — unchanged.
DriftedThe tool existed in the snapshot, but its description, name, or input schema no longer matches.
NewThe 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:

ModeEffect
AlertA drifted or new tool raises an admin alert but stays callable — the default.
BlockDrifted 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.

note

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:

  1. Automatically creates a catalog entry for that server in the quarantined state.
  2. 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.
quarantinedTraffic was observed to an unregistered server; pending admin resolution.
deniedAn 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.

See also