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.
- agentguardproxy-0.5.0/PKG-INFO +223 -0
- agentguardproxy-0.5.0/README.md +180 -0
- agentguardproxy-0.5.0/agentguard/__init__.py +638 -0
- agentguardproxy-0.5.0/agentguard/adapters/browseruse.py +674 -0
- agentguardproxy-0.5.0/agentguard/adapters/crewai.py +596 -0
- agentguardproxy-0.5.0/agentguard/adapters/langchain.py +470 -0
- agentguardproxy-0.5.0/agentguard/adapters/mcp.py +762 -0
- agentguardproxy-0.5.0/agentguardproxy.egg-info/PKG-INFO +223 -0
- {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/agentguardproxy.egg-info/SOURCES.txt +15 -1
- agentguardproxy-0.5.0/agentguardproxy.egg-info/requires.txt +26 -0
- agentguardproxy-0.5.0/pyproject.toml +102 -0
- {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/tests/test_adapters.py +27 -14
- agentguardproxy-0.5.0/tests/test_adapters_extended.py +450 -0
- agentguardproxy-0.5.0/tests/test_browseruse.py +512 -0
- agentguardproxy-0.5.0/tests/test_crewai.py +543 -0
- agentguardproxy-0.5.0/tests/test_decorator.py +287 -0
- agentguardproxy-0.5.0/tests/test_end_to_end_real_server.py +397 -0
- {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/tests/test_guard.py +23 -0
- agentguardproxy-0.5.0/tests/test_langchain.py +495 -0
- agentguardproxy-0.5.0/tests/test_mcp.py +576 -0
- agentguardproxy-0.5.0/tests/test_mcp_fuzz.py +321 -0
- agentguardproxy-0.5.0/tests/test_mcp_gateway.py +382 -0
- agentguardproxy-0.5.0/tests/test_polling_jitter.py +176 -0
- agentguardproxy-0.5.0/tests/test_redactor_property.py +232 -0
- agentguardproxy-0.5.0/tests/test_sdk_integration.py +196 -0
- agentguardproxy-0.5.0/tests/test_sdk_polish.py +338 -0
- agentguardproxy-0.5.0/tests/test_tenant_routing.py +209 -0
- agentguardproxy-0.5.0/tests/test_wire_format.py +321 -0
- agentguardproxy-0.4.0/PKG-INFO +0 -145
- agentguardproxy-0.4.0/README.md +0 -106
- agentguardproxy-0.4.0/agentguard/__init__.py +0 -235
- agentguardproxy-0.4.0/agentguard/adapters/browseruse.py +0 -128
- agentguardproxy-0.4.0/agentguard/adapters/crewai.py +0 -134
- agentguardproxy-0.4.0/agentguard/adapters/langchain.py +0 -167
- agentguardproxy-0.4.0/agentguard/adapters/mcp.py +0 -303
- agentguardproxy-0.4.0/agentguardproxy.egg-info/PKG-INFO +0 -145
- agentguardproxy-0.4.0/agentguardproxy.egg-info/requires.txt +0 -20
- agentguardproxy-0.4.0/pyproject.toml +0 -48
- agentguardproxy-0.4.0/tests/test_decorator.py +0 -79
- {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/agentguard/adapters/__init__.py +0 -0
- {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/agentguardproxy.egg-info/dependency_links.txt +0 -0
- {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/agentguardproxy.egg-info/top_level.txt +0 -0
- {agentguardproxy-0.4.0 → agentguardproxy-0.5.0}/setup.cfg +0 -0
- {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
|