agentguardproxy 0.4.0__tar.gz → 0.5.0__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.
Files changed (44) hide show
  1. agentguardproxy-0.5.0/PKG-INFO +223 -0
  2. agentguardproxy-0.5.0/README.md +180 -0
  3. agentguardproxy-0.5.0/agentguard/__init__.py +638 -0
  4. agentguardproxy-0.5.0/agentguard/adapters/browseruse.py +674 -0
  5. agentguardproxy-0.5.0/agentguard/adapters/crewai.py +596 -0
  6. agentguardproxy-0.5.0/agentguard/adapters/langchain.py +470 -0
  7. agentguardproxy-0.5.0/agentguard/adapters/mcp.py +762 -0
  8. agentguardproxy-0.5.0/agentguardproxy.egg-info/PKG-INFO +223 -0
  9. {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/agentguardproxy.egg-info/SOURCES.txt +15 -1
  10. agentguardproxy-0.5.0/agentguardproxy.egg-info/requires.txt +26 -0
  11. agentguardproxy-0.5.0/pyproject.toml +102 -0
  12. {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/tests/test_adapters.py +27 -14
  13. agentguardproxy-0.5.0/tests/test_adapters_extended.py +450 -0
  14. agentguardproxy-0.5.0/tests/test_browseruse.py +512 -0
  15. agentguardproxy-0.5.0/tests/test_crewai.py +543 -0
  16. agentguardproxy-0.5.0/tests/test_decorator.py +287 -0
  17. agentguardproxy-0.5.0/tests/test_end_to_end_real_server.py +397 -0
  18. {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/tests/test_guard.py +23 -0
  19. agentguardproxy-0.5.0/tests/test_langchain.py +495 -0
  20. agentguardproxy-0.5.0/tests/test_mcp.py +576 -0
  21. agentguardproxy-0.5.0/tests/test_mcp_fuzz.py +321 -0
  22. agentguardproxy-0.5.0/tests/test_mcp_gateway.py +382 -0
  23. agentguardproxy-0.5.0/tests/test_polling_jitter.py +176 -0
  24. agentguardproxy-0.5.0/tests/test_redactor_property.py +232 -0
  25. agentguardproxy-0.5.0/tests/test_sdk_integration.py +196 -0
  26. agentguardproxy-0.5.0/tests/test_sdk_polish.py +338 -0
  27. agentguardproxy-0.5.0/tests/test_tenant_routing.py +209 -0
  28. agentguardproxy-0.5.0/tests/test_wire_format.py +321 -0
  29. agentguardproxy-0.4.0/PKG-INFO +0 -145
  30. agentguardproxy-0.4.0/README.md +0 -106
  31. agentguardproxy-0.4.0/agentguard/__init__.py +0 -235
  32. agentguardproxy-0.4.0/agentguard/adapters/browseruse.py +0 -128
  33. agentguardproxy-0.4.0/agentguard/adapters/crewai.py +0 -134
  34. agentguardproxy-0.4.0/agentguard/adapters/langchain.py +0 -167
  35. agentguardproxy-0.4.0/agentguard/adapters/mcp.py +0 -303
  36. agentguardproxy-0.4.0/agentguardproxy.egg-info/PKG-INFO +0 -145
  37. agentguardproxy-0.4.0/agentguardproxy.egg-info/requires.txt +0 -20
  38. agentguardproxy-0.4.0/pyproject.toml +0 -48
  39. agentguardproxy-0.4.0/tests/test_decorator.py +0 -79
  40. {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/agentguard/adapters/__init__.py +0 -0
  41. {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/agentguardproxy.egg-info/dependency_links.txt +0 -0
  42. {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/agentguardproxy.egg-info/top_level.txt +0 -0
  43. {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/setup.cfg +0 -0
  44. {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/tests/test_integration.py +0 -0
@@ -0,0 +1,223 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentguardproxy
3
+ Version: 0.5.0
4
+ Summary: Python SDK for AgentGuard — the firewall for AI agents
5
+ Author-email: Cauã Ferraz <cauaferrazp@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/Caua-ferraz/AgentGuard
8
+ Project-URL: Repository, https://github.com/Caua-ferraz/AgentGuard
9
+ Project-URL: Documentation, https://github.com/Caua-ferraz/AgentGuard/blob/master/docs/SETUP.md
10
+ Project-URL: Issues, https://github.com/Caua-ferraz/AgentGuard/issues
11
+ Keywords: ai,agents,firewall,policy,guardrails,safety
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Security
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ Provides-Extra: langchain
25
+ Requires-Dist: langchain-core<2.0,>=0.3; extra == "langchain"
26
+ Requires-Dist: langchain<2.0,>=0.3; extra == "langchain"
27
+ Provides-Extra: crewai
28
+ Requires-Dist: crewai<2.0,>=0.80; extra == "crewai"
29
+ Provides-Extra: browser-use
30
+ Requires-Dist: browser-use<1.0,>=0.4; extra == "browser-use"
31
+ Requires-Dist: playwright>=1.40; extra == "browser-use"
32
+ Provides-Extra: mcp
33
+ Requires-Dist: mcp<2.0,>=0.9; extra == "mcp"
34
+ Provides-Extra: all
35
+ Requires-Dist: agentguardproxy[browser-use,crewai,langchain,mcp]; extra == "all"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=7.0; extra == "dev"
38
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
39
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
40
+ Requires-Dist: responses>=0.23; extra == "dev"
41
+ Provides-Extra: integration
42
+ Requires-Dist: agentguardproxy[browser-use,crewai,langchain,mcp]; extra == "integration"
43
+
44
+ # AgentGuard Python SDK
45
+
46
+ Lightweight Python client for [AgentGuard](https://github.com/Caua-ferraz/AgentGuard) — the firewall for AI agents.
47
+
48
+ - **Zero runtime dependencies** for the core SDK (stdlib `urllib`).
49
+ - **Fail-closed by default** — if the proxy is unreachable, `check()` returns `DENY`.
50
+ - **Framework adapters** for LangChain, CrewAI, browser-use, and MCP, gated behind optional extras.
51
+
52
+ > **Deep reference:** [`docs/SDK_PYTHON.md`](../../docs/SDK_PYTHON.md) — full API, exception hierarchy, fail-mode details, adapter internals.
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install agentguardproxy
58
+
59
+ # With framework adapters
60
+ pip install agentguardproxy[langchain]
61
+ pip install agentguardproxy[crewai]
62
+ pip install agentguardproxy[browser-use]
63
+ pip install agentguardproxy[all]
64
+ ```
65
+
66
+ ## Quick start
67
+
68
+ ```python
69
+ from agentguard import Guard
70
+
71
+ guard = Guard(
72
+ base_url="http://localhost:8080", # or set AGENTGUARD_URL
73
+ agent_id="my-agent",
74
+ api_key="…", # or set AGENTGUARD_API_KEY (needed for approve/deny/status)
75
+ )
76
+
77
+ result = guard.check("shell", command="rm -rf ./old_data")
78
+
79
+ if result.allowed:
80
+ execute(command)
81
+ elif result.needs_approval:
82
+ print(f"Approve at: {result.approval_url}")
83
+ # Block until a human resolves it, or 5 min deadline, whichever first
84
+ final = guard.wait_for_approval(result.approval_id, timeout=300)
85
+ if final.allowed:
86
+ execute(command)
87
+ else:
88
+ print(f"Blocked: {result.reason}")
89
+ ```
90
+
91
+ ### Environment variables
92
+
93
+ | Var | Default | Used by |
94
+ |---|---|---|
95
+ | `AGENTGUARD_URL` | `http://localhost:8080` | `Guard(base_url="")` fallback |
96
+ | `AGENTGUARD_API_KEY` | *(empty)* | `Guard(api_key="")` fallback; sent as `Authorization: Bearer <key>` on `/v1/approve`, `/v1/deny`, `/v1/status` |
97
+
98
+ ### Fail mode
99
+
100
+ ```python
101
+ # Default: fail closed. Proxy unreachable → CheckResult(decision="DENY", reason="AgentGuard unreachable (deny): …")
102
+ guard = Guard("http://localhost:8080")
103
+
104
+ # Opt in to fail open. Proxy unreachable → CheckResult(decision="ALLOW", reason="AgentGuard unreachable (allow): …")
105
+ # Use only when your threat model treats AgentGuard as advisory.
106
+ guard = Guard("http://localhost:8080", fail_mode="allow")
107
+ ```
108
+
109
+ Three classes of transport failure are caught: `urllib.error.URLError` (connection refused / DNS / SSL), `OSError` (post-connect timeouts and resets), and `json.JSONDecodeError` (garbage response body).
110
+
111
+ ## The `@guarded` decorator
112
+
113
+ ```python
114
+ from agentguard import Guard, guarded, AgentGuardDenied, AgentGuardApprovalRequired
115
+
116
+ guard = Guard("http://localhost:8080", agent_id="my-agent")
117
+
118
+ @guarded("shell", guard=guard)
119
+ def run_command(cmd: str):
120
+ os.system(cmd)
121
+
122
+ try:
123
+ run_command("ls")
124
+ run_command("rm -rf /") # raises AgentGuardDenied
125
+ except AgentGuardDenied as e:
126
+ log(f"blocked: {e.result.reason}")
127
+ ```
128
+
129
+ On `REQUIRE_APPROVAL` the decorator raises `AgentGuardApprovalRequired` immediately. To block until a human resolves it, opt in:
130
+
131
+ ```python
132
+ @guarded("cost", guard=guard, wait_for_approval=True, approval_timeout=300)
133
+ def expensive_call(prompt: str): ...
134
+ ```
135
+
136
+ All three exceptions (`AgentGuardDenied`, `AgentGuardApprovalRequired`, `AgentGuardApprovalTimeout`) extend `PermissionError`, so existing `except PermissionError:` handlers keep working unchanged.
137
+
138
+ ## Framework adapters
139
+
140
+ ### LangChain
141
+
142
+ ```python
143
+ from agentguard.adapters.langchain import GuardedToolkit
144
+
145
+ toolkit = GuardedToolkit(
146
+ tools=my_tools,
147
+ guard_url="http://localhost:8080",
148
+ agent_id="langchain-agent",
149
+ )
150
+ agent = create_react_agent(llm, toolkit.tools, prompt)
151
+ ```
152
+
153
+ Scope is inferred from each tool's name/description (`http/api`→network, `file/path`→filesystem, `browser`→browser, `shell`→shell) and upgraded at call time if the input dict contains `url`, `domain`, or `path` keys.
154
+
155
+ ### CrewAI
156
+
157
+ ```python
158
+ from agentguard.adapters.crewai import guard_crew_tools
159
+
160
+ guarded_tools = guard_crew_tools(
161
+ tools=my_crew_tools,
162
+ guard_url="http://localhost:8080",
163
+ agent_id="crew-agent",
164
+ )
165
+ ```
166
+
167
+ Hooks both `run` and `_run` (CrewAI calls `_run` internally).
168
+
169
+ ### browser-use
170
+
171
+ ```python
172
+ from agentguard.adapters.browseruse import GuardedBrowser
173
+
174
+ browser = GuardedBrowser(guard_url="http://localhost:8080")
175
+
176
+ if browser.check_navigation("https://example.com").allowed:
177
+ await page.goto("https://example.com")
178
+
179
+ # Or wrap the page directly so goto() enforces policy for you:
180
+ guarded_page = browser.wrap_page(page)
181
+ await guarded_page.goto("https://example.com") # raises PermissionError on deny/approval
182
+ ```
183
+
184
+ ### MCP
185
+
186
+ ```python
187
+ from agentguard.adapters.mcp import GuardedMCPServer
188
+
189
+ server = GuardedMCPServer(guard_url="http://localhost:8080")
190
+ server.add_tool("my_tool", "Description", handler=my_handler)
191
+ server.run() # stdio JSON-RPC MCP server; pins MCP_PROTOCOL_VERSION
192
+ ```
193
+
194
+ Or as a drop-in stdio server:
195
+
196
+ ```bash
197
+ python -m agentguard.adapters.mcp --guard-url http://localhost:8080
198
+ ```
199
+
200
+ ## API reference (summary)
201
+
202
+ ### `Guard(base_url="", agent_id="", timeout=5, api_key="", fail_mode="deny")`
203
+
204
+ | Method | Behavior |
205
+ |---|---|
206
+ | `check(scope, *, action, command, path, domain, url, session_id, est_cost, meta)` | POST `/v1/check`. Returns `CheckResult`. Transport failure → fail-closed `DENY` (or `ALLOW` if `fail_mode="allow"`). |
207
+ | `approve(id)` / `deny(id)` | POST `/v1/approve/{id}` / `/v1/deny/{id}`. Returns `bool` success. Sends Bearer if `api_key` set. |
208
+ | `wait_for_approval(id, timeout=300, poll_interval=2)` | Polls `GET /v1/status/{id}` until `resolved` or deadline. Timeout → `CheckResult(DENY, "Approval timed out")`. |
209
+
210
+ ### `CheckResult`
211
+
212
+ Fields: `decision`, `reason`, `matched_rule`, `approval_id`, `approval_url`. Properties: `.allowed`, `.denied`, `.needs_approval`.
213
+
214
+ ### Exception hierarchy (all extend `PermissionError`)
215
+
216
+ - `AgentGuardError` — base; carries `.result: CheckResult`.
217
+ - `AgentGuardDenied` — policy said DENY.
218
+ - `AgentGuardApprovalRequired` — policy said REQUIRE_APPROVAL and the decorator was not configured to wait. Carries `.approval_id`, `.approval_url`.
219
+ - `AgentGuardApprovalTimeout` — `wait_for_approval` deadline elapsed. Carries `.approval_id`.
220
+
221
+ ## License
222
+
223
+ Apache 2.0
@@ -0,0 +1,180 @@
1
+ # AgentGuard Python SDK
2
+
3
+ Lightweight Python client for [AgentGuard](https://github.com/Caua-ferraz/AgentGuard) — the firewall for AI agents.
4
+
5
+ - **Zero runtime dependencies** for the core SDK (stdlib `urllib`).
6
+ - **Fail-closed by default** — if the proxy is unreachable, `check()` returns `DENY`.
7
+ - **Framework adapters** for LangChain, CrewAI, browser-use, and MCP, gated behind optional extras.
8
+
9
+ > **Deep reference:** [`docs/SDK_PYTHON.md`](../../docs/SDK_PYTHON.md) — full API, exception hierarchy, fail-mode details, adapter internals.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install agentguardproxy
15
+
16
+ # With framework adapters
17
+ pip install agentguardproxy[langchain]
18
+ pip install agentguardproxy[crewai]
19
+ pip install agentguardproxy[browser-use]
20
+ pip install agentguardproxy[all]
21
+ ```
22
+
23
+ ## Quick start
24
+
25
+ ```python
26
+ from agentguard import Guard
27
+
28
+ guard = Guard(
29
+ base_url="http://localhost:8080", # or set AGENTGUARD_URL
30
+ agent_id="my-agent",
31
+ api_key="…", # or set AGENTGUARD_API_KEY (needed for approve/deny/status)
32
+ )
33
+
34
+ result = guard.check("shell", command="rm -rf ./old_data")
35
+
36
+ if result.allowed:
37
+ execute(command)
38
+ elif result.needs_approval:
39
+ print(f"Approve at: {result.approval_url}")
40
+ # Block until a human resolves it, or 5 min deadline, whichever first
41
+ final = guard.wait_for_approval(result.approval_id, timeout=300)
42
+ if final.allowed:
43
+ execute(command)
44
+ else:
45
+ print(f"Blocked: {result.reason}")
46
+ ```
47
+
48
+ ### Environment variables
49
+
50
+ | Var | Default | Used by |
51
+ |---|---|---|
52
+ | `AGENTGUARD_URL` | `http://localhost:8080` | `Guard(base_url="")` fallback |
53
+ | `AGENTGUARD_API_KEY` | *(empty)* | `Guard(api_key="")` fallback; sent as `Authorization: Bearer <key>` on `/v1/approve`, `/v1/deny`, `/v1/status` |
54
+
55
+ ### Fail mode
56
+
57
+ ```python
58
+ # Default: fail closed. Proxy unreachable → CheckResult(decision="DENY", reason="AgentGuard unreachable (deny): …")
59
+ guard = Guard("http://localhost:8080")
60
+
61
+ # Opt in to fail open. Proxy unreachable → CheckResult(decision="ALLOW", reason="AgentGuard unreachable (allow): …")
62
+ # Use only when your threat model treats AgentGuard as advisory.
63
+ guard = Guard("http://localhost:8080", fail_mode="allow")
64
+ ```
65
+
66
+ Three classes of transport failure are caught: `urllib.error.URLError` (connection refused / DNS / SSL), `OSError` (post-connect timeouts and resets), and `json.JSONDecodeError` (garbage response body).
67
+
68
+ ## The `@guarded` decorator
69
+
70
+ ```python
71
+ from agentguard import Guard, guarded, AgentGuardDenied, AgentGuardApprovalRequired
72
+
73
+ guard = Guard("http://localhost:8080", agent_id="my-agent")
74
+
75
+ @guarded("shell", guard=guard)
76
+ def run_command(cmd: str):
77
+ os.system(cmd)
78
+
79
+ try:
80
+ run_command("ls")
81
+ run_command("rm -rf /") # raises AgentGuardDenied
82
+ except AgentGuardDenied as e:
83
+ log(f"blocked: {e.result.reason}")
84
+ ```
85
+
86
+ On `REQUIRE_APPROVAL` the decorator raises `AgentGuardApprovalRequired` immediately. To block until a human resolves it, opt in:
87
+
88
+ ```python
89
+ @guarded("cost", guard=guard, wait_for_approval=True, approval_timeout=300)
90
+ def expensive_call(prompt: str): ...
91
+ ```
92
+
93
+ All three exceptions (`AgentGuardDenied`, `AgentGuardApprovalRequired`, `AgentGuardApprovalTimeout`) extend `PermissionError`, so existing `except PermissionError:` handlers keep working unchanged.
94
+
95
+ ## Framework adapters
96
+
97
+ ### LangChain
98
+
99
+ ```python
100
+ from agentguard.adapters.langchain import GuardedToolkit
101
+
102
+ toolkit = GuardedToolkit(
103
+ tools=my_tools,
104
+ guard_url="http://localhost:8080",
105
+ agent_id="langchain-agent",
106
+ )
107
+ agent = create_react_agent(llm, toolkit.tools, prompt)
108
+ ```
109
+
110
+ Scope is inferred from each tool's name/description (`http/api`→network, `file/path`→filesystem, `browser`→browser, `shell`→shell) and upgraded at call time if the input dict contains `url`, `domain`, or `path` keys.
111
+
112
+ ### CrewAI
113
+
114
+ ```python
115
+ from agentguard.adapters.crewai import guard_crew_tools
116
+
117
+ guarded_tools = guard_crew_tools(
118
+ tools=my_crew_tools,
119
+ guard_url="http://localhost:8080",
120
+ agent_id="crew-agent",
121
+ )
122
+ ```
123
+
124
+ Hooks both `run` and `_run` (CrewAI calls `_run` internally).
125
+
126
+ ### browser-use
127
+
128
+ ```python
129
+ from agentguard.adapters.browseruse import GuardedBrowser
130
+
131
+ browser = GuardedBrowser(guard_url="http://localhost:8080")
132
+
133
+ if browser.check_navigation("https://example.com").allowed:
134
+ await page.goto("https://example.com")
135
+
136
+ # Or wrap the page directly so goto() enforces policy for you:
137
+ guarded_page = browser.wrap_page(page)
138
+ await guarded_page.goto("https://example.com") # raises PermissionError on deny/approval
139
+ ```
140
+
141
+ ### MCP
142
+
143
+ ```python
144
+ from agentguard.adapters.mcp import GuardedMCPServer
145
+
146
+ server = GuardedMCPServer(guard_url="http://localhost:8080")
147
+ server.add_tool("my_tool", "Description", handler=my_handler)
148
+ server.run() # stdio JSON-RPC MCP server; pins MCP_PROTOCOL_VERSION
149
+ ```
150
+
151
+ Or as a drop-in stdio server:
152
+
153
+ ```bash
154
+ python -m agentguard.adapters.mcp --guard-url http://localhost:8080
155
+ ```
156
+
157
+ ## API reference (summary)
158
+
159
+ ### `Guard(base_url="", agent_id="", timeout=5, api_key="", fail_mode="deny")`
160
+
161
+ | Method | Behavior |
162
+ |---|---|
163
+ | `check(scope, *, action, command, path, domain, url, session_id, est_cost, meta)` | POST `/v1/check`. Returns `CheckResult`. Transport failure → fail-closed `DENY` (or `ALLOW` if `fail_mode="allow"`). |
164
+ | `approve(id)` / `deny(id)` | POST `/v1/approve/{id}` / `/v1/deny/{id}`. Returns `bool` success. Sends Bearer if `api_key` set. |
165
+ | `wait_for_approval(id, timeout=300, poll_interval=2)` | Polls `GET /v1/status/{id}` until `resolved` or deadline. Timeout → `CheckResult(DENY, "Approval timed out")`. |
166
+
167
+ ### `CheckResult`
168
+
169
+ Fields: `decision`, `reason`, `matched_rule`, `approval_id`, `approval_url`. Properties: `.allowed`, `.denied`, `.needs_approval`.
170
+
171
+ ### Exception hierarchy (all extend `PermissionError`)
172
+
173
+ - `AgentGuardError` — base; carries `.result: CheckResult`.
174
+ - `AgentGuardDenied` — policy said DENY.
175
+ - `AgentGuardApprovalRequired` — policy said REQUIRE_APPROVAL and the decorator was not configured to wait. Carries `.approval_id`, `.approval_url`.
176
+ - `AgentGuardApprovalTimeout` — `wait_for_approval` deadline elapsed. Carries `.approval_id`.
177
+
178
+ ## License
179
+
180
+ Apache 2.0