AgentGuard Python SDK
Lightweight Python client for AgentGuard — the firewall for AI agents.
- Zero runtime dependencies for the core SDK (stdlib
urllib). - Fail-closed by default — if the proxy is unreachable,
check()returnsDENY. - Framework adapters for LangChain, CrewAI, browser-use, and MCP, gated behind optional extras.
Deep reference:
docs/SDK_PYTHON.md— full API, exception hierarchy, fail-mode details, adapter internals.
Install
pip install agentguardproxy
# With framework adapters
pip install agentguardproxy[langchain]
pip install agentguardproxy[crewai]
pip install agentguardproxy[browser-use]
pip install agentguardproxy[all]
The package is agentguardproxy; the import name is agentguard. agentguard-sdk on PyPI is an unrelated project that also installs an agentguard module — don't install both in the same environment.
Quick start
from agentguard import Guard
guard = Guard(
base_url="http://localhost:8080", # or set AGENTGUARD_URL
agent_id="my-agent",
api_key="…", # or set AGENTGUARD_API_KEY (needed for approve/deny/status)
)
result = guard.check("shell", command="rm -rf ./old_data")
if result.allowed:
execute(command)
elif result.needs_approval:
print(f"Approve at: {result.approval_url}")
# Block until a human resolves it, or 5 min deadline, whichever first
final = guard.wait_for_approval(result.approval_id, timeout=300)
if final.allowed:
# Replay the approval: consumes the one-shot ALLOW, reserves cost,
# and audits the execution. The status poll alone spends nothing.
replay = guard.check("shell", command=command, approval_id=result.approval_id)
if replay.allowed:
execute(command)
else:
print(f"Blocked: {result.reason}")
Environment variables
| Var | Default | Used by |
|---|---|---|
AGENTGUARD_URL |
http://localhost:8080 |
Guard(base_url="") fallback |
AGENTGUARD_API_KEY |
(empty) | Guard(api_key="") fallback; sent as Authorization: Bearer <key> on /v1/approve, /v1/deny, /v1/status |
AGENTGUARD_TENANT_ID |
(empty) | Guard(tenant_id=None) fallback; a value other than local routes calls to /v1/t/<tenant>/… |
Fail mode
# Default: fail closed. Proxy unreachable → CheckResult(decision="DENY", reason="AgentGuard unreachable (deny): …")
guard = Guard("http://localhost:8080")
# Opt in to fail open. Proxy unreachable → CheckResult(decision="ALLOW", reason="AgentGuard unreachable (allow): …")
# Use only when your threat model treats AgentGuard as advisory.
guard = Guard("http://localhost:8080", fail_mode="allow")
Fail mode applies to transport failures — urllib.error.URLError (connection refused / DNS / SSL, and HTTP error statuses), OSError (post-connect timeouts and resets), json.JSONDecodeError (garbage response body) — and to responses that aren't a valid decision (a non-JSON Content-Type, or a body without decision).
The @guarded decorator
from agentguard import Guard, guarded, AgentGuardDenied, AgentGuardApprovalRequired
guard = Guard("http://localhost:8080", agent_id="my-agent")
@guarded("shell", guard=guard)
def run_command(cmd: str):
os.system(cmd)
try:
run_command("ls")
run_command("rm -rf /") # raises AgentGuardDenied
except AgentGuardDenied as e:
log(f"blocked: {e.result.reason}")
On REQUIRE_APPROVAL the decorator raises AgentGuardApprovalRequired immediately. To block until a human resolves it, opt in:
@guarded("cost", guard=guard, wait_for_approval=True, approval_timeout=300)
def expensive_call(prompt: str): ...
All AgentGuard exceptions (AgentGuardDenied, AgentGuardApprovalRequired, AgentGuardApprovalTimeout, AgentGuardAuthError) extend PermissionError, so existing except PermissionError: handlers keep working unchanged.
Framework adapters
LangChain
from agentguard.adapters.langchain import GuardedToolkit
toolkit = GuardedToolkit(
tools=my_tools,
guard_url="http://localhost:8080",
agent_id="langchain-agent",
)
agent = create_react_agent(llm, toolkit.tools, prompt)
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.
CrewAI
from agentguard.adapters.crewai import guard_crew_tools
guarded_tools = guard_crew_tools(
tools=my_crew_tools,
guard_url="http://localhost:8080",
agent_id="crew-agent",
)
Hooks both run and _run (CrewAI calls _run internally).
browser-use
from agentguard.adapters.browseruse import GuardedBrowser
browser = GuardedBrowser(guard_url="http://localhost:8080")
if browser.check_navigation("https://example.com").allowed:
await page.goto("https://example.com")
# Or wrap the page directly so goto() enforces policy for you:
guarded_page = browser.wrap_page(page)
await guarded_page.goto("https://example.com") # raises PermissionError on deny/approval
MCP
from agentguard.adapters.mcp import GuardedMCPServer
server = GuardedMCPServer(guard_url="http://localhost:8080")
server.add_tool("my_tool", "Description", handler=my_handler)
server.run() # stdio JSON-RPC MCP server; pins MCP_PROTOCOL_VERSION
Or as a drop-in stdio server:
python -m agentguard.adapters.mcp --guard-url http://localhost:8080
API reference (summary)
Guard(base_url="", agent_id="", timeout=5, api_key="", fail_mode="deny", tenant_id=None)
| Method | Behavior |
|---|---|
check(scope, *, action, command, path, domain, url, session_id, est_cost, meta, approval_id) |
POST /v1/check (approval_id replays a resolved approval — see the quick start). Returns CheckResult. Transport failure → fail-closed DENY (or ALLOW if fail_mode="allow"). |
approve(id) / deny(id) |
POST /v1/approve/{id} / /v1/deny/{id}. Returns bool success. Sends Bearer if api_key set. |
wait_for_approval(id, timeout=300, poll_interval=2) |
Polls GET /v1/status/{id} until resolved or deadline. Timeout → CheckResult(DENY, "Approval timed out"). |
CheckResult
Fields: decision, reason, matched_rule, approval_id, approval_url. Properties: .allowed, .denied, .needs_approval.
Exception hierarchy (all extend PermissionError)
AgentGuardError— base; carries.result: CheckResult.AgentGuardDenied— policy said DENY.AgentGuardApprovalRequired— policy said REQUIRE_APPROVAL and the decorator was not configured to wait. Carries.approval_id,.approval_url.AgentGuardApprovalTimeout—wait_for_approvaldeadline elapsed. Carries.approval_id.AgentGuardAuthError— await_for_approvalstatus poll got401/403(API key missing or wrong). Carries.status.
License
Apache 2.0
Release files for agentguardproxy 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agentguardproxy-1.1.1.tar.gz | 97.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agentguardproxy-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 142.3 kB
Release files / agentguardproxy-1.1.1.tar.gz
| Download URL | agentguardproxy-1.1.1.tar.gz |
|---|---|
| Size | 97.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7c91924e4dc951018845aa103efe6abe2ad41ed38fd54cc892fb07ad9aa31076
|
|
BLAKE2b-256 checksum How to use checksums |
9fee9b3668c2a473023afeb072b8d2fa32a9b2fc522bb60e82529da5bfc22ec6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / agentguardproxy-1.1.1-py3-none-any.whl
| Download URL | agentguardproxy-1.1.1-py3-none-any.whl |
|---|---|
| Size | 45.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eb28c5b55f35482e25daabdada6af7fd41ae084ab52ae29b0c9fe15106d8dc17
|
|
BLAKE2b-256 checksum How to use checksums |
73a7f4c8f04e5664e374d956a0392dcd436ee3b6f994821ae58fb32a5a150ecb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|