Skip to main content

MCP Cooperative Guard

@controlzero/mcp-server is a tiny MCP (Model Context Protocol) server that exposes Control Zero's DLP rules as tools an AI client can call before invoking other tools. It's the third leg of the Control Zero enforcement triangle:

The MCP guard is cooperative. The AI client could ignore the result. The point is to give well-behaved clients (and well- behaved agents acting on user intent) a way to surface DLP context without forcing every client to run the SDK.

Pair the MCP guard with the SDK or gateway when hard enforcement matters.

Why cooperative?

Hard enforcement requires either:

  • The Control Zero SDK in the same process as the agent (Python, Node, or Go), OR
  • The Control Zero gateway as an HTTP proxy in front of the LLM provider URL.

Some AI clients are neither: a desktop chat app like Claude Desktop runs Anthropic's API directly, with no SDK to wrap and no gateway to proxy through. The user is the only enforcement point. The MCP guard gives the agent a way to ask the Control Zero backend what the user's org allows, and surface that to the user before the agent commits to running a tool.

Install

npm install -g @controlzero/mcp-server

The package ships a controlzero-mcp binary that speaks MCP over stdio.

Configure

The exact config file depends on your AI client.

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
"mcpServers": {
"controlzero": {
"command": "controlzero-mcp",
"env": {
"CONTROLZERO_API_URL": "https://api.controlzero.ai/api/v1",
"CONTROLZERO_API_KEY": "cz_live_xxxxxxxxxxxxxxxx"
}
}
}
}

Restart Claude Desktop. The new tools appear in the tool picker.

Cursor

Cursor reads MCP config from its settings UI. Go to Settings → Features → MCP and add:

{
"name": "controlzero",
"command": "controlzero-mcp",
"env": {
"CONTROLZERO_API_KEY": "cz_live_xxxxxxxxxxxxxxxx"
}
}

Cline / Continue

Both clients read MCP config from a project-level file. Create .cline/mcp.json (or the equivalent for Continue) and add the controlzero entry as above.

Other clients

Any client that supports MCP stdio servers works. The command is always controlzero-mcp and the only required env var is CONTROLZERO_API_KEY.

The three guard tools

guard_check_tool_call

The main one. Asks Control Zero whether a proposed tool call is allowed by the org's DLP rules.

Inputs:

  • org_id — UUID of the org whose rules should evaluate
  • tool_name — name of the tool the client wants to invoke (e.g., bash, read_file, fetch, send_message)
  • tool_args_text — flat text representation of the tool args (the client should join all string args with newlines)
  • scope — optional, defaults to "sdk". Set to "gateway" to evaluate against gateway-scoped rules instead.

Output:

{
"decision": "deny",
"rule_matches": [
{
"rule_id": "abc-123",
"rule_name": "US Social Security Numbers",
"category": "pii",
"action": "block",
"matched_text": "123-45-6789"
}
],
"reason": "tool 'bash' blocked by DLP rule 'US Social Security Numbers' (category=pii)"
}

Decisions:

  • allow — no rules matched, the call is fine.
  • warn — at least one detect or mask rule matched. The client should show the warning to the user but the call is not blocked. If a mask rule matched, the response also includes masked_text with the matched span replaced by a placeholder.
  • deny — at least one block rule matched. The client should NOT invoke the tool.

guard_test_pattern

Compile a candidate regex and check whether it matches a sample. Useful for an admin iterating on a new DLP rule from inside an AI client without leaving the chat.

Inputs:

  • org_id
  • pattern — RE2 regex
  • sample — sample input text (max 64 KB)

Output:

{
"compiles": true,
"matched": true,
"matches": ["123-45-6789"]
}

If the pattern fails to compile, compiles is false and error contains the error message.

guard_list_active_rules

Read-only enumeration of currently-live DLP rules for the org. Useful for an agent that wants to proactively warn the user before they paste sensitive content.

Inputs:

  • org_id

Output:

{
"active_rules": [
{
"id": "abc-123",
"name": "US Social Security Numbers",
"category": "pii",
"action": "block",
"scopes": ["sdk", "gateway"]
}
]
}

Patterns and prompts

If you're a power user setting up an AI agent that uses these tools, here are some prompts that work well:

Pre-flight check before a destructive command

Before running any shell command that touches /etc, /var,
or any file owned by another user, call guard_check_tool_call
with `tool_name="bash"` and `tool_args_text` set to the proposed command.
If decision is deny, abort and show the rule name to the user.
If decision is warn, ask the user for explicit confirmation
before proceeding.

Proactive warning at conversation start

At the start of every conversation, call guard_list_active_rules
once and remember the result. If any rule has category="pii" or
"secret", warn the user upfront: "This org has DLP rules that
will block content matching: [rule names]. Don't paste real
customer data, credentials, or financial info into the chat."

Mid-stream paste check

Whenever the user pastes text containing what looks like an SSN,
credit card, API key, or email, call guard_check_tool_call with
`tool_name="paste_check"` and `tool_args_text` set to the pasted text
before proceeding. If decision is deny, refuse to use the
content and explain why.

Cost and rate limits

Each guard call is cheap on the backend side: it's one read of the org's DLP rules (cached) and a regex scan over the supplied text. No analytical store writes, no audit entries, no policy bundle downloads.

Rate limits per API key:

ToolLimit
guard_check_tool_call100 / min
guard_test_pattern30 / min
guard_list_active_rules60 / min

If you hit a limit, the MCP server returns an error with the HTTP 429 details. Back off and retry.

Security model

The MCP guard tool only reads DLP rules. It cannot modify them, publish drafts, approve pending rules, or do anything else admin-facing. The CZ API key only needs the dlp:read scope.

The guard tool does NOT receive or store any sensitive content itself. The tool_args_text you pass to guard_check_tool_call is scanned client-side in the MCP server process (which lives on your laptop), not transmitted to Control Zero. Only the rule metadata (which patterns to evaluate) comes from the Control Zero backend.

This is intentional: a cooperative guard call should not become a way for sensitive content to leave the machine just to be scanned. The local-only scan also avoids a network round-trip for every call, even on multi-KB inputs.

Troubleshooting

"401 Unauthorized" on every call

The CONTROLZERO_API_KEY is missing, expired, or doesn't have the dlp:read scope. Generate a new one from the dashboard's Settings → API Keys page.

"Tool not found: guard_check_tool_call"

The MCP server isn't loading the guard tools. Verify:

  1. npm list -g @controlzero/mcp-server — installed at the global location your client looks at
  2. controlzero-mcp --version — runs without error
  3. The client's MCP config points at the binary by absolute path (some clients don't search $PATH for stdio commands)

Decisions are stale after I publish a new rule

The MCP server fetches the rule list on every guard_check_tool_call invocation, so there's no client-side cache to invalidate. If you see stale results, the staleness is on the backend side: the DLP rule store writes are immediately visible. Force a refresh by calling guard_list_active_rules; if the new rule isn't there, the publish didn't actually land.

Calls feel slow

Open the MCP server logs (controlzero-mcp writes to stderr by default). Look for the callAPI request log lines. If the backend round-trip is slow, the bottleneck is your network, not the regex scan. Switch to a CZ API endpoint closer to your location or use a regional cache.

Version compatibility

@controlzero/mcp-server >= 0.2.0 includes the guard tools. Earlier versions only ship the policy/project/audit/governance/ blueprint tool families.

controlzero-mcp --version

If you're on an older version, upgrade:

npm install -g @controlzero/mcp-server@latest

The MCP protocol version is 2024-11-05 (the spec version the @modelcontextprotocol/sdk v1 ships with). Any client that supports MCP 2024-11-05 stdio transport works.