Deny-by-default policy gate for AI agent tool calls: allowlists, argument validation, rate caps, human-approval hooks, dry-run mode, and an audit trail. Framework-agnostic, zero dependencies.
Project description
tool-call-guard (Python)
Deny-by-default policy gate for AI agent tool calls. The Python half of tool-call-guard — same JSON policy model and audit schema as the npm package, so one security review covers both stacks.
from tool_call_guard import Guard, ToolCallDenied
guard = Guard(
{
"defaultAction": "deny", # anything unlisted is blocked
"tools": {
"search_*": {}, # allowlist a group
"send_email": {
"validate": lambda a: a["to"].endswith("@mycompany.com")
or "external recipients need approval",
"maxCallsPerMinute": 5,
},
"deploy": {"action": "approve"}, # human-in-the-loop
"shell_exec": {"action": "deny"},
},
},
approve=lambda req: ask_operator(req), # sync here; async via acheck()
)
@guard.protect("send_email")
def send_email(args):
...
send_email({"to": "attacker@evil.com"}) # raises ToolCallDenied
Install
pip install tool-call-guard
Zero dependencies, fully typed, Python 3.9+. Validators accept plain callables (return True/False/reason-string, or raise) or pydantic-style model classes (anything with model_validate).
What the policy gives you
- Deny-by-default — unlisted tools are blocked; the allowlist is the policy.
- Wildcard rules —
"fs_*"budgets and gates a whole group; exact names beat patterns. - Argument validation — runs before quota, so malformed calls never consume budget.
- Quotas —
maxCallsper guard lifetime,maxCallsPerMinutesliding window (injectable clock). - Approval hooks —
action: "approve"calls your approver; no approver configured means deny, not allow. - Dry-run mode — everything proceeds, but the audit trail records what enforcement would have done. Observe a policy in production before turning it on. Approvers are never invoked during a rehearsal.
- Audit trail — in-memory ring buffer plus optional sinks;
jsonl_audit(path)writes one JSON line per decision, same schema as the JS package.
API sketch
guard = Guard(policy, mode="enforce"|"dry-run", approve=..., on_audit=...,
audit_args=True, max_audit_events=1000, now=time.time)
guard.check(tool, args) -> Decision # sync; sync approvers only
await guard.acheck(tool, args) # async; sync or async approvers
guard.wrap(name, fn) # sync fn -> sync wrapper, async -> async
@guard.protect(name) # decorator form
guard.wrap_tools({name: fn, ...})
guard.audit_log # ring buffer, newest last
guard.reset()
Decision: allowed, action, reason, tool, rule, and in dry-run would_allow + dry_run. Denied wrapped calls raise ToolCallDenied (with .decision).
Policy keys are camelCase (portable JSON, shared with the JS package); snake_case aliases (max_calls, …) are accepted in Python.
See the repository root for the full policy reference and the threat model this addresses.
License
MIT © Binaya Dhakal
Project details
Release history Release notifications | RSS feed
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_guard-0.1.0.tar.gz.
File metadata
- Download URL: tool_call_guard-0.1.0.tar.gz
- Upload date:
- Size: 10.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba60abe8427434a22d68da8b6f3c506a4c20ba8060aec787e78a526683e903be
|
|
| MD5 |
91966fd45d9e857ccc1c91c9978546a8
|
|
| BLAKE2b-256 |
dc9706537bbb827e10935f674e902176023d71e93ef6b2b33035ab7e7640208b
|
File details
Details for the file tool_call_guard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tool_call_guard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb49471d50f19a8cab05f69967e4d4862788ffe40da88e919151cea7dbdda327
|
|
| MD5 |
201bd026e1e39d44fa52e2546c1ae3eb
|
|
| BLAKE2b-256 |
af009fbd3d3fd393ddf1e62307bf8d3594424d711dbdc7b00343b004473e5966
|