AegisAIGitHub

Wire this into your agent in 5 minutes

AegisAI is a chokepoint, not a framework. If your agent can make one HTTP request before it executes a tool call, it can be guarded.

01

Run AegisAI

No API keys required. Falls back to a mock judge and SQLite automatically, so the whole stack comes up from a clean clone.

terminal
$ git clone https://github.com/Navneet-Scaler/AegisAI
$ cd AegisAI
$ docker compose up
 
# API on :8000, dashboard on :3000
02

Mint a key

No signup. The key is returned once, you keep it. Every call to the guard endpoint authenticates with it.

terminal
$ curl -X POST localhost:8000/v1/keys -d "{}"
 
# { "key": "ag_live_...", "key_id": "..." }
$ export AEGIS_API_KEY=ag_live_...
03

Score a call over curl

Before your agent executes a tool call, send it here first. The response carries the verdict, the three layer scores, and the judge's reasoning.

terminal
$ curl -X POST localhost:8000/v1/guard \
-H "Authorization: Bearer $AEGIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "delete_customer",
"args": {"customer_ids": ["CUST-1002"]},
"user_request": "Remove this customer",
"agent_id": "support-agent"
}'
 
# { "verdict": "hold", "score": 0.81, "reasoning": "..." }

Pick your integration

The guard call is the same everywhere. Only how you insert it changes.

Plain Python

The guard() call that everything else below reduces to: one HTTP request before a tool executes.

guard.py
$ import os, requests
 
$ def guard(tool_name, arguments, user_request, history):
r = requests.post(
"http://localhost:8000/v1/guard",
headers={"Authorization": f"Bearer {os.environ['AEGIS_API_KEY']}"},
json={
"tool": tool_name,
"args": arguments,
"user_request": user_request,
"history": history,
},
)
return r.json()
 
$ verdict = guard("delete_customer", {"customer_ids": ["CUST-1002"]},
"Remove this customer", [])
$ if verdict["verdict"] == "block":
raise RuntimeError(verdict["reasoning"])
$ elif verdict["verdict"] == "hold":
# surface to a human, poll /calls/{call_id} for the decision
...
$ else:
execute_tool(...)

OpenAI function calling

Insert the guard call between the model proposing a tool call and your code executing it. Full runnable example in examples/openai-function-calling/.

agent.py
$ for call in response.choices[0].message.tool_calls:
args = json.loads(call.function.arguments)
verdict = guard(call.function.name, args, user_request, history)
 
if verdict["verdict"] == "block":
output = {"error": f"blocked: {verdict['reasoning']}"}
elif verdict["verdict"] == "hold":
output = {"status": "held_for_review", "call_id": verdict["call_id"]}
else:
output = execute_tool(call.function.name, args)

LangChain

Wrap each StructuredTool so the guard call runs inside the tool's own func, transparent to the agent executor. Full example in examples/langchain/.

guarded_tool.py
$ from langchain_core.tools import StructuredTool
 
$ def guarded(tool_fn, tool_name, user_request):
def wrapped(**kwargs):
verdict = guard(tool_name, kwargs, user_request, [])
if verdict["verdict"] != "allow":
return f"{verdict['verdict']}: {verdict['reasoning']}"
return tool_fn(**kwargs)
return wrapped
 
$ guarded_tools = [
StructuredTool.from_function(
func=guarded(delete_customer, "delete_customer", user_request),
name="delete_customer",
),
# ... one per tool
$ ]

MCP server

Run the bundled MCP server and every tools/call routed through it is guarded before the underlying tool runs, with no changes on the client side. Source in examples/mcp-server/.

terminal
$ export AEGIS_BASE_URL=http://localhost:8000
$ export AEGIS_API_KEY=$(curl -s -X POST $AEGIS_BASE_URL/v1/keys \
-H "Content-Type: application/json" -d '{}' | python3 -c \
"import json,sys; print(json.load(sys.stdin)['key'])")
 
$ uv run examples/mcp-server/server.py
 
# speaks JSON-RPC 2.0 over stdio, same transport Claude Desktop uses

Watch it decide, live.

The dashboard streams every call as it is scored, held, and resolved.

Open the dashboard