Arcezia

Arcezia / AI agent security / MCP security

MCP security: checking MCP tool calls before they run

MCP security covers two questions. Can you trust the MCP server: who wrote it, what its tool descriptions say, what it can reach? And should this call, which the agent is about to send to a server, run now? Arcezia answers the second. In Claude Code, its PreToolUse hook checks every MCP tool call, along with shell commands and file writes, before the call reaches the server. The answer is allow (the call runs), ask (Claude Code asks you first) or deny (the call is refused, with the reason). Arcezia does not vet MCP servers or scan their tool descriptions. Below, two MCP calls answered on the live service, with the script to send them again.

Setup in Claude Code

pip install -U arcezia
export ARCEZIA_API_KEY="<your key>"
arcezia-hook install

Expected output of arcezia-hook install:

WARNING: ARCEZIA_API_KEY has the owner or admin role. The agent runs with this key in its environment and could register checks that answer its own questions. Keep check secrets and your signing key on a machine the agent cannot read.
Arcezia PreToolUse hook installed → ~/.claude/settings.json
Set ARCEZIA_API_KEY (and optionally TASK) in your environment.

The hook goes into ~/.claude/settings.json for every tool ("matcher": "*"), so MCP tools are covered without listing them. Claude Code names an MCP tool mcp__<server>__<tool>, and that is the tool name Arcezia checks. The warning appears because the key used here has the owner role: an agent that can read such a key could register checks of its own. Give the agent’s environment a key without that role where you can, and keep your signing key and check secrets on a machine the agent cannot read.

Two MCP calls, measured

You can test the hook without starting Claude Code: send it the JSON that Claude Code sends before a tool call. The script below does that twice, the second time after writing one rule to ~/.claude/arcezia.json.

export TASK="count today's orders"
arcezia-hook install

# 1. A database MCP server's query tool, no rule of yours yet.
echo '{"session_id": "mcp-demo-1", "cwd": "/tmp/shop", "hook_event_name": "PreToolUse", "tool_name": "mcp__postgres__query", "tool_input": {"sql": "SELECT COUNT(*) FROM orders WHERE created_at >= CURRENT_DATE"}}' | arcezia-hook

# 2. Your rule: this GitHub MCP tool never runs. Then a new session tries it.
echo '{"capability_envelope": {"denied_action_types": ["mcp__github__delete_repository"]}}' > ~/.claude/arcezia.json
echo '{"session_id": "mcp-demo-2", "cwd": "/tmp/shop", "hook_event_name": "PreToolUse", "tool_name": "mcp__github__delete_repository", "tool_input": {"owner": "acme", "repo": "shop-api"}}' | arcezia-hook
#MCP tool callYour ruleAnswerWhat the answer says
1mcp__postgres__query: SELECT COUNT(*) FROM orders WHERE created_at >= CURRENT_DATENone yetask (REVIEW)Held for review. To proceed: cover this action in your capability envelope ('allowed_action_types'); attach a signed approval from a person.
2mcp__github__delete_repository: acme/shop-apiThis tool is denieddeny (BLOCK)Blocked: 'mcp__github__delete_repository' is outside your capability envelope ('denied_action_types').

Measured 7 October 2026, 15:02:59 UTC to 15:03:03 UTC, with the arcezia Python SDK 1.0.7 from PyPI, against https://api.arcezia.com, on Arcezia’s self-test account, by running the script above in an empty home folder. Full output of the two calls:

{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "ask", "permissionDecisionReason": "Arcezia REVIEW (approval required): Held for review. To proceed: cover this action in your capability envelope ('allowed_action_types'); attach a signed approval from a person."}}
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Arcezia BLOCK: Blocked: 'mcp__github__delete_repository' is outside your capability envelope ('denied_action_types')."}}

On a new setup, an MCP call comes back ask until you say what the session may do: here the read names the two ways forward, allowing the tool for the session or a person’s approval. A rule of yours applies to sessions that start after you write it; the delete is refused in the new session. Remove ~/.claude/arcezia.json to drop the rule.

What the check looks at

The call as it will reach the server: the server and tool name, and every argument. Not the tool’s description, and not the model’s text. A read and a delete through the same server are different calls and get different answers. Each answer is kept as a signed record: audit evidence.

What this does not cover

Questions

Does Arcezia vet MCP servers or their tool descriptions?

No. It does not review who wrote a server, scan tool descriptions for hidden instructions, or manage the server’s credentials. It checks each call an agent sends to a server, with its arguments, before the call goes out.

Which MCP clients does this cover?

Claude Code, through its PreToolUse hook. Arcezia does not ship an MCP proxy for other clients. Agents built on LangChain, LangGraph, OpenAI, Anthropic, CrewAI, AutoGen or LlamaIndex can have each tool call checked through those integrations, and a decorator covers any Python function.