Skip to main content

Anthropic Integration

Add governance to your Anthropic Claude calls with one line of code. The SDK wraps your Anthropic client and automatically enforces whatever policies you have defined in the Control Zero dashboard.

Setup

pip install controlzero anthropic
import controlzero
from controlzero.integrations.anthropic import wrap_anthropic
import anthropic

cz = controlzero.init()
client = wrap_anthropic(anthropic.Anthropic(), cz)

# Use client exactly as before -- all calls are now governed
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(response.content[0].text)

Two lines added. Zero changes to your application logic.

What Gets Enforced Automatically

API MethodPolicy ActionPolicy Resource
messages.create(model="claude-sonnet-4-20250514")llm.generatemodel/claude-sonnet-4-20250514
messages.stream(model="claude-sonnet-4-20250514")llm.generatemodel/claude-sonnet-4-20250514

The wrapper extracts the model parameter, checks it against your policies, and blocks the call if denied.

Example Policy

Define this in the Control Zero dashboard:

{
"name": "anthropic-model-governance",
"rules": [
{ "effect": "allow", "action": "llm.generate", "resource": "model/claude-sonnet-4-20250514" },
{ "effect": "deny", "action": "llm.generate", "resource": "model/claude-*-opus*" },
{ "effect": "deny", "action": "llm.generate", "resource": "model/*" }
]
}

What happens at runtime:

# ALLOWED -- Sonnet is explicitly allowed
client.messages.create(model="claude-sonnet-4-20250514", ...)

# BLOCKED -- Opus is denied
client.messages.create(model="claude-opus-4-20250514", ...)
# Raises PolicyDeniedError

# Also works with streaming
for event in client.messages.stream(model="claude-sonnet-4-20250514", ...):
pass # ALLOWED

Handling Denied Actions

try:
response = client.messages.create(
model="claude-opus-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
except controlzero.PolicyDeniedError:
# Fall back to Sonnet
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)

Controlling Tool Use

When Claude calls tools, you can govern which tools are allowed. Define tool policies in the dashboard:

{
"name": "claude-tool-governance",
"rules": [
{ "effect": "allow", "action": "llm.generate", "resource": "model/claude-sonnet-4-20250514" },
{ "effect": "allow", "action": "tool.call", "resource": "tool/search_web" },
{ "effect": "deny", "action": "tool.call", "resource": "tool/send_email" },
{ "effect": "deny", "action": "tool.call", "resource": "tool/database_query" }
]
}

For tool enforcement, use the enforce() method in your tool handler:

def handle_tool_call(tool_name: str, tool_input: dict) -> str:
# The SDK checks: is tool/{tool_name} allowed by the dashboard policy?
cz.enforce(action="tool.call", resource=f"tool/{tool_name}")

return execute_tool(tool_name, tool_input)

Per-Agent Wrapping

analyst_client = wrap_anthropic(anthropic.Anthropic(), cz, agent_id="analyst")
support_client = wrap_anthropic(anthropic.Anthropic(), cz, agent_id="support")

Using the Secrets Vault

cz = controlzero.ControlZero(secrets_enabled=True)
cz.initialize()

anthropic_key = cz.get_secret("anthropic")
client = wrap_anthropic(anthropic.Anthropic(api_key=anthropic_key), cz)

Next Steps