npx skills add ...
npx skills add wshobson/agents --skill protect-mcp-setup
Configure Cedar policy enforcement and Ed25519 signed receipts for Claude Code tool calls. Use when setting up projects that need cryptographic audit trails, policy-gated tool execution, or compliance-ready evidence of agent actions.
npx skills add wshobson/agents --skill protect-mcp-setup
Cryptographic governance for every Claude Code tool call. Each invocation is evaluated against a Cedar policy and produces an Ed25519-signed receipt that anyone can verify offline.
Claude Code runs powerful tools: Bash, Edit, Write, WebFetch. By default
there is no audit trail, no policy enforcement, and no way to prove what was
decided after the fact. protect-mcp closes all three gaps:
npx @veritasacta/verify. No server, no account,
no trust in the operator.AI agents make decisions that affect money, safety, and rights. The Claude Code session log records what happened, but the log is:
For compliance contexts (finance, healthcare, regulated research), this is not sufficient. You need tamper-evident evidence that can be verified by third parties without trusting you.
Add protect-mcp to your Claude Code project:
Installing the plugin adds both hooks from hooks/hooks.json. Each hook runs a
script bundled with the plugin:
Claude Code passes the hook event to the command as JSON on stdin and does not
set TOOL_NAME or TOOL_INPUT variables. evaluate.sh reads tool_name and
tool_input from that payload and passes them to protect-mcp as flags; sign.sh reads
tool_name only, because the 0.7.4 signer records nothing else. Set
PROTECT_MCP_POLICY, PROTECT_MCP_RECEIPTS, and PROTECT_MCP_KEY to change the
default paths. When the policy file is missing, the PreToolUse hook prints a
warning to stderr and allows the call.
PreToolUse — Runs BEFORE the tool executes. Evaluates the tool call against
your Cedar policy file. If Cedar returns deny, the hook exits with code 2 and
Claude Code blocks the tool call entirely.
PostToolUse — Runs AFTER the tool completes. Signs a receipt containing the
tool name, input hash, output hash, decision, policy digest, and timestamp.
Writes the receipt to ./receipts/<timestamp>.json.
Create ./protect.cedar at the project root:
Verify a single receipt:
Verify the entire chain:
Use the plugin's slash commands from within Claude Code:
Each receipt is a JSON file with this structure:
parent_receipt_id| Before | After |
|---|---|
| "Trust me, the agent only read files" | Cryptographically provable: every Read logged and signed |
| "The log shows it happened" | The receipt proves it happened, and no one can edit it |
| "You'd have to audit our system" | Anyone can verify every receipt offline |
| "Logs might be different by now" | Ed25519 signatures lock the record at signing time |
{
"hooks": {
"PreToolUse": [
{
"matcher": ".*",
"hooks": [
{ "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/evaluate.sh" }
]
}
],
"PostToolUse": [
{
"matcher": ".*",
"hooks": [
{ "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/sign.sh" }
]
}
]
}
}// Allow read-only tools by default
permit (
principal,
action in [Action::"Read", Action::"Glob", Action::"Grep", Action::"WebFetch"],
resource
);
// Require explicit allow for destructive tools
permit (
principal,
action == Action::"Bash",
resource
) when {
// Allow safe commands only
context.command_pattern in ["git", "npm", "ls", "cat", "echo", "pwd", "test"]
};
// Never allow recursive deletion
forbid (
principal,
action == Action::"Bash",
resource
) when {
context.command_pattern == "rm -rf"
};
// Require confirmation for writes outside the project
forbid (
principal,
action in [Action::"Edit", Action::"Write"],
resource
) when {
context.path_starts_with != "."
};npx @veritasacta/verify receipts/2026-04-15T10-30-00Z.json
# Exit 0 = valid
# Exit 1 = tampered
# Exit 2 = malformednpx @veritasacta/verify receipts/*.json/verify-receipt receipts/latest.json
/audit-chain ./receipts/ --last 20{
"receipt_id": "rec_8f92a3b1",
"receipt_version": "1.0",
"issuer_id": "claude-code-protect-mcp",
"event_time": "2026-04-15T10:30:00.000Z",
"tool_name": "Bash",
"input_hash": "sha256:a3f8...",
"decision": "allow",
"policy_id": "autoresearch-safe",
"policy_digest": "sha256:b7e2...",
"parent_receipt_id": "rec_3d1ab7c2",
"public_key": "4437ca56815c0516...",
"signature": "4cde814b7889e987..."
}