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.
Metadata
Release files for tool-call-warrant 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tool_call_warrant-0.1.0.tar.gz | 19.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tool_call_warrant-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.3 kB
Release files / tool_call_warrant-0.1.0.tar.gz
| Download URL | tool_call_warrant-0.1.0.tar.gz |
|---|---|
| Size | 19.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4bfb17008e8536c2b077025d85822f7963ce708f2ada7fde8e8e65107ab0a2e2
|
|
BLAKE2b-256 checksum How to use checksums |
bd3ca25521925d5e8d8ad272b6204388b87e930036584979c29f23a6e17eff85
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / tool_call_warrant-0.1.0-py3-none-any.whl
| Download URL | tool_call_warrant-0.1.0-py3-none-any.whl |
|---|---|
| Size | 5.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d90249906506fd1e8a193d8a2bb257661f53971f98fe0a32512d29fadacbeee2
|
|
BLAKE2b-256 checksum How to use checksums |
ac2efa1002917c62be5e514f7e43def1d20b4db42f35e74ce065556d8c6f1e5b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|