Claude + Veto
Runtime authorization for Claude agents built with the Anthropic SDK and Claude Agent SDK. Intercept every tool_use call before execution. Block, approve, or audit. Claude never knows.
The problem with Claude agents today
Claude is built for agentic work. The Anthropic SDK gives it tools, the Agent SDK gives it autonomy, and MCP gives it access to external systems. But none of these provide runtime authorization. Claude decides what to do and your code executes it unconditionally.
This is not hypothetical risk. In February 2026, Check Point disclosed two configuration injection flaws in Claude Code (CVE-2025-59536, severity 8.7/10) that let attackers run arbitrary shell commands by exploiting repository-controlled settings. Anthropic's own sandboxing documentation acknowledges that Claude agents need external containment. Safety training helps. Authorization enforces.
The Anthropic SDK's tool_use flow returns tool calls for you to execute. There is no built-in step between "Claude wants to call this" and "your code runs it."
MCP's authentication is optional. Enterprise deployments using Claude Desktop or Claude Code expose MCP tools with no centralized access control or audit trail.
OWASP LLM01:2025 ranks prompt injection as the top LLM vulnerability. Constitutional AI reduces risk but cannot eliminate it. Authorization is the defense-in-depth layer.
Before and after Veto
The left tab shows a standard Claude tool_use loop. Claude calls tools, your code executes them blindly. The right tab adds Veto. Same agent, same tools, but every call goes through policy evaluation first.
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()
const tools = [
{
name: 'send_email',
description: 'Send an email',
input_schema: {
type: 'object',
properties: {
to: { type: 'string' },
subject: { type: 'string' },
body: { type: 'string' },
},
required: ['to', 'subject', 'body'],
},
},
{
name: 'delete_records',
description: 'Delete database records',
input_schema: {
type: 'object',
properties: {
table: { type: 'string' },
where: { type: 'string' },
},
required: ['table', 'where'],
},
},
]
const response = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
tools,
messages: [{ role: 'user', content: userMessage }],
})
// Claude decides to call delete_records.
// Your code executes it. No questions asked.
for (const block of response.content) {
if (block.type === 'tool_use') {
const result = await executeTool(block.name, block.input)
// Hope nothing bad happens.
}
}Claude Agent SDK (Python)
Anthropic's Claude Agent SDK gives Claude full autonomy with tool definitions, MCP server connections, and multi-turn execution loops. Veto validates inside each tool function, so the agent architecture stays unchanged.
from claude_agent_sdk import Agent, tool
from veto import Veto
veto = Veto(api_key="veto_live_xxx")
@tool
def transfer_funds(from_account: str, to_account: str, amount: float) -> str:
"""Transfer funds between accounts."""
# Veto validates before the function body runs
decision = veto.validate(
tool="transfer_funds",
arguments={"from_account": from_account, "to_account": to_account, "amount": amount},
context={"user_role": "agent", "environment": "production"},
)
if decision.action != "allow":
return f"Transfer blocked: {decision.reason}"
return process_transfer(from_account, to_account, amount)
@tool
def query_database(query: str, table: str) -> str:
"""Execute a database query."""
decision = veto.validate(
tool="query_database",
arguments={"query": query, "table": table},
)
if decision.action != "allow":
return f"Query blocked: {decision.reason}"
return execute_query(query, table)
agent = Agent(
model="claude-sonnet-4-6",
tools=[transfer_funds, query_database],
instructions="You are a financial operations assistant.",
)
result = agent.run("Transfer $5000 from ops to marketing")Policy configuration
Define what Claude can and cannot do in declarative YAML. Version control it alongside your code. No prompt engineering. No hoping the model cooperates.
rules:
- name: block_destructive_writes
description: Prevent DELETE/DROP on production tables
tool: delete_records
when: context.environment == "production"
action: deny
message: "Destructive writes blocked in production"
- name: approve_large_transfers
description: Human approval for transfers over $1,000
tool: transfer_funds
when: args.amount > 1000
action: require_approval
approvers: [finance-team]
message: "Large transfer requires finance team approval"
- name: block_external_email
description: Block emails to non-company domains
tool: send_email
when: "!args.to.endsWith('@yourcompany.com')"
action: deny
message: "External emails require manual sending"
- name: read_only_queries
description: Only allow SELECT queries
tool: query_database
when: "!args.query.toUpperCase().startsWith('SELECT')"
action: deny
message: "Only read-only queries are permitted"Quickstart
Install
npm install veto-sdk @anthropic-ai/sdkPython: pip install veto anthropic
Define policies
Create veto/policies.yaml with rules for each tool. Match on tool name, constrain arguments, set actions: allow, deny, or require_approval.
Validate before executing
Call veto.guard() in your tool_use handler. Check the decision. Execute only if allowed. The model, your prompts, and your agent loop stay untouched.
What Veto covers for Claude agents
Tool scoping
Allowlist which tools Claude can call per environment, user role, or time of day. Deny everything else by default. Principle of least privilege for agents.
Argument constraints
Block specific argument values: email domains, SQL statements containing DELETE, payment amounts above a threshold. Enforce at the data level, not the prompt level.
Human-in-the-loop
Route sensitive tool calls to approval queues. Approvers get Slack or email notifications. The agent pauses until a human responds. One-click allow or deny.
Full audit trail
Every tool call logged with tool name, arguments, decision, reason, timestamp, and user context. Queryable via API. Exportable for SOC2 and compliance.
Frequently asked questions
Can Claude bypass Veto's guardrails?
Does Veto work with Claude's MCP support?
Does this add latency to Claude responses?
Does it work with streaming?
Related integrations
Stop hoping Claude behaves. Enforce it.