tool-call-warrant v0.1.0
Pre-execution guard layer for AI agent tool calls. Warrants are structured confirmations that pass through a rule engine before a tool is allowed to execute.
Why this exists
AI coding agents (Claude Code, Cursor, AGY, Gemini CLI) execute tool calls
with zero pre-execution confirmation. When an agent decides to strace
a production server, delete 50k files, DROP TABLE, or POST to a webhook,
the human sees only the retrospective log. This is the core "rogue agent"
failure mode that developers complain about in production.
tool-call-warrant is a zero-dependency, cross-runtime (Python + Node)
plugin that intercepts tool calls before execution and returns a structured
warrant verdict: ALLOW, DENY, MODIFY, or CONFIRM.
Install
Python
pip install -e .
warrant-check --help
Node
npm install -g .
warrant-check --help # uses scripts/warrant-check.mjs via package.json `bin`
Zero runtime dependencies in both languages.
dependencies = []/"dependencies": {}.
Quickstart
As a CLI
# Simulate a destructive call — returns DENY (exit 1)
warrant-check check --tool bash --args '{"command":"rm -rf /app"}' --format json
# Simulate a safe call — returns ALLOW (exit 0)
warrant-check check --tool mcp__filesystem__ls --args '{"path":"/app/docs"}'
# Force ALLOW despite DENY (human override; logs to audit trail)
warrant-check check --tool bash --args '{"command":"rm -rf /app"}' --confirm
# Inspect audit log
warrant-check audit --since 2026-08-09T00:00:00Z --format json
As a library (Python)
import sys; sys.path.insert(0, "src")
from warrant_core import classify_risk, get_verdict
risk, matched = classify_risk("bash", {"command": "rm -rf /"})
# risk = "DESTROY"; matched = ["destroy:recursive delete"]
verdict = get_verdict(risk) # "DENY"
As a library (Node)
import { classifyRisk, getVerdict } from 'tool-call-warrant';
const [risk, matched] = classifyRisk("bash", { command: "rm -rf /" });
// risk = "DESTROY"; matched = ["destroy:recursive delete"]
const verdict = getVerdict(risk); // "DENY"
Risk Classification
| Risk | Patterns | Verdict | Exit code |
|---|---|---|---|
DESTROY |
rm -rf, DROP TABLE, TRUNCATE, kill -9, shutdown, mkfs |
DENY | 1 |
EXFILTRATE |
Private IPs, curl | sh, eval(base64_decode(...)), ~/.env, DB dumps, printenv |
CONFIRM | 2 |
EXECUTE |
bash -c, eval(, subprocess, child_process.spawn, gcc/go build/rustc/npm install, | sh |
CONFIRM | 2 |
OBSERVE |
ps aux, SELECT *, chmod 777, ls -R /, cat ~/.ssh/id_rsa |
ALLOW | 0 |
BENIGN |
ls, git status, cat README.md, find ., npm test |
ALLOW | 0 |
UNKNOWN |
(no pattern matched) | CONFIRM | 2 |
Global blocks (always force DENY → DESTROY):
curl <anything> | shwget -O- | sheval(base64_decode(...))
CLI Subcommands
| Subcommand | Purpose | Exit codes |
|---|---|---|
check |
Evaluate a tool call against rules | 0=ALLOW/MODIFY, 1=DENY, 2=CONFIRM |
diff |
Show arg changes (proposed → allowed) | 0=ok, 1=invalid input |
audit |
Read historical warrant decisions | 0=ok, 1=invalid input |
parse |
Validate warrant JSON schema | 0=valid, 1=invalid |
check flags
| Flag | Effect |
|---|---|
--tool NAME |
(required) Tool name |
--args JSON |
Tool arguments as JSON object (default {}) |
--rules PATH |
Path to rules file (reserved for v0.2) |
--format json|text |
Output format (default text) |
--confirm |
Human override: force ALLOW |
--reject |
Human override: force DENY |
--modify |
Human override: emit MODIFY verdict |
--dry-run |
Do not write audit log entry |
--audit |
With --dry-run: still write audit log |
Hook Integration (per-agent)
{
"event": "pre_tool_call",
"tool": "*",
"handler": "warrant-check --tool ${TOOL_NAME} --args ${TOOL_ARGS} --format json",
"on_allow": "proceed",
"on_deny": "block_and_log",
"on_confirm": "pause_and_notify"
}
See skills/tool-call-warrant/SKILL.md for per-framework integration notes.
Cited Evidence (Honest Pillar)
- HN Ask: How do you enforce permissions for AI agent tool calls in production? — 350+ points, multiple "how do I prevent rogue agents" threads
- HN signal: "AgentWard — After an AI agent deleted files, I built a runtime enforcer."
- HN signal: "Runtime security for AI agents (injection, tool abuse, data exfiltration)"
- GitHub Search:
@twire/guard(Edge/Node only, 0.1.1) and@jc4649/pi-toolcall-guard(pi-specific, 0.1.0) — both narrow in scope, neither Python+Node cross-runtime - npm + PyPI:
tool-call-guard,tool-call-confirm,tool-interceptor— no cross-runtime distribution found
Named Competitors
| Package | Runtime | Limit |
|---|---|---|
@twire/guard |
Edge/Node only | No Python, narrow scope |
@jc4649/pi-toolcall-guard |
pi-agent only | Framework-locked, no general plugin |
Limitations / Non-Goals
- Rule engine is synchronous, single-process. No distributed enforcement across hosts.
- Rules file is plaintext. No schema validation beyond JSON syntax (custom rules loader is v0.2).
- First-run UX requires manual
--auditto inspect behavior before enabling auto-confirm. - Not an OS-level sandbox. Use seccomp, firejail, gVisor for kernel-level isolation.
- Not an LLM-side guardrail. Does not filter agent output or detect prompt-injection.
- Not a substitute for human review. Edge cases (compound operations, multi-step plans) need operator judgment.
Plugin Manifest
See plugin.json for the full agent-runtime plugin declaration (hooks,
scripts, rules, skills). The manifest declares dependencies: [] per spec.
Architecture
tool-call-warrant/
├── plugin.json # Agent-runtime manifest
├── pyproject.toml # Python packaging (zero deps)
├── package.json # Node packaging (zero deps)
├── src/
│ ├── warrant_core.py # Python rule engine (zero deps)
│ ├── index.mjs # Node rule engine mirror (zero deps)
│ └── index.d.ts # TypeScript definitions
├── scripts/
│ ├── warrant_check.py # Python CLI
│ ├── warrant-check.mjs # Node CLI
│ └── pre-push-gate.sh # Mechanical gate before push
├── rules/
│ └── tool-warrant-rules.md
├── skills/
│ └── tool-call-warrant/SKILL.md
├── tests/
│ ├── COVERAGE.md # 50 enumerated acceptance criteria
│ ├── test_warrant.py # 128 pytest tests
│ └── test_warrant.mjs # 107 Node tests
├── README.md
├── LICENSE # MIT
├── CHANGELOG.md
└── QA_REPORT.md # Self-review (build card)
Verification
pytest -q # 128 passed
npm test # 107 passed
warrant-check --help # CLI works
node scripts/warrant-check.mjs --help
License
MIT — see LICENSE.
Changelog
See CHANGELOG.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tool_call_warrant-0.1.0.tar.gz.
File metadata
- Download URL: tool_call_warrant-0.1.0.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bfb17008e8536c2b077025d85822f7963ce708f2ada7fde8e8e65107ab0a2e2
|
|
| MD5 |
bda097d31eb827fb0f6196e7a14cab59
|
|
| BLAKE2b-256 |
bd3ca25521925d5e8d8ad272b6204388b87e930036584979c29f23a6e17eff85
|
File details
Details for the file tool_call_warrant-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tool_call_warrant-0.1.0-py3-none-any.whl
- Upload date:
- Size: 5.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d90249906506fd1e8a193d8a2bb257661f53971f98fe0a32512d29fadacbeee2
|
|
| MD5 |
ff297208a1f2844549acb1ebc3d83be8
|
|
| BLAKE2b-256 |
ac2efa1002917c62be5e514f7e43def1d20b4db42f35e74ce065556d8c6f1e5b
|