Aegis
Aegis š”ļø
A Blast-Radius Auditor for AI Agents Track 03: Enterprise AI & Workplace Automation ā NitroStack Hackathon
Aegis is a Model Context Protocol (MCP) server built on NitroStack that audits the combined effective permissions of an AI agent across all connected tools. It deterministically detects toxic capability combinations and data-exfiltration vectors before deployment ā at zero LLM token cost.
š“ Live server: https://aegis-6a6d76ee-teamx-srmist.app.nitrocloud.ai/mcp ā connect it to Claude, ChatGPT, or NitroStack Studio and try the walkthrough below for real.
š Table of Contents
š The Problem
Enterprises connect AI agents to dozens of tools ā Gmail, Dropbox, Postgres databases, Slack, filesystem execution. Each integration gets approved individually on its own merits, but nobody audits what the agent can do when tools are combined.
āāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāā
ā Dropbox MCP ā ā Gmail MCP ā
ā (Read Private) ā ā (Send External)ā
āāāāāāāāā¬āāāāāāāāā āāāāāāāāā¬āāāāāāāāā
ā ā
āāāāāāāāāāāāā¬āāāāāāāāāāāāāā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Support Agent ā
āāāāāāāāāāāāāā¬āāāāāāāāāāāāā
ā¼
šØ TOXIC COMBINATION: DATA EXFILTRATION PATHREAD_PRIVATE_DATA(Dropbox) +SEND_EXTERNAL(Gmail) = š“ Data Exfiltration PathREAD_PRIVATE_DATA(Postgres) +WRITE_PUBLIC(Slack) = š Public Leak PathDELETE_DATA(Postgres) +EXECUTE(Filesystem) = š Destructive Automation Vector
This isn't hypothetical ā it's how the Supabase MCP leak happened (an agent with legitimate database read access was steered via prompt injection into exfiltrating private records through an external channel), how the postmark-mcp supply-chain compromise turned an ordinary "send email" tool into a covert BCC-to-attacker exfiltration path, and the exact mechanism behind documented tool-poisoning attacks, where malicious instructions hidden in a tool's own description hijack agent behavior without ever calling an obviously malicious endpoint. Individually-reasonable permissions become dangerous the moment they're combined, and nothing in the stack today computes that union before it's exploited.
šø See It Work
Real, live-rendered screenshots ā the actual widgets, fed the actual output of get_capability_graph / detect_attack_paths after connecting gmail + dropbox to one agent.
The graph ā dangerous capability paths render in red:

The alert ā severity, affected tools, and one-click remediation:

After clicking Fix, apply_policy_fix disconnects every tool supplying the sink capability and the graph goes back to riskScore: 0 ā no attack paths, clean state.
š System Architecture
flowchart TD
subgraph Host["Chat Host / Client"]
Client["User / AI Agent Host\n(NitroStudio, ChatGPT, Claude)"]
end
subgraph MCP["Aegis MCP Server (NitroStack)"]
Tools["Governance Tools Controller\n(connect_tool, get_capability_graph,\ndetect_attack_paths, apply_policy_fix)"]
Guard["OAuthGuard\n(scaffolded, opt-in via OAUTH_REQUIRED ā\nopen by default for the hackathon build)"]
Resources["Resource Server\n(aegis://policies)"]
Prompts["Prompt Controller\n(explain_attack_path)"]
Guard -. wraps connect_tool only .-> Tools
end
subgraph Engine["Deterministic Engine ā 0 Tokens"]
Registry["Tool Capability Registry\n(gmail, dropbox, postgres, slack,\nfilesystem, calendar)"]
Store["Per-Agent Connected-Tool Store\n(in-memory)"]
Union["Effective Capability Union\n(getEffectiveCapabilities)"]
Detector["Policy Rule Matcher\n(detectAttackPaths)"]
Registry --> Store --> Union --> Detector
end
subgraph UI["NitroStack Widgets"]
GraphWidget["capability-graph\n(live risk graph)"]
AlertWidget["attack-path-alert\n(severity + one-click Fix)"]
end
subgraph LLM["Groq Explanation Layer ā only LLM spend"]
Groq["Llama 3.1 8B Instant\ncached by SHA-256(ruleId + sorted(viaTools))"]
end
Client -->|STDIO / HTTP JSON-RPC| Tools
Tools --> Engine
Detector -->|risk score + danger edges| GraphWidget
Detector -->|threat path list| AlertWidget
Prompts -->|only on a detected path, cache miss| Groq
Groq -->|plain-English finding| Client
AlertWidget -->|Fix click| Toolsš Detection & Remediation Lifecycle
flowchart LR
A["1. Connect Gmail"] -->|connect_tool| B["READ_PRIVATE_DATA + SEND_EXTERNAL"]
B --> C["š¢ SAFE ā riskScore 0"]
C --> D["2. Connect Dropbox"]
D -->|connect_tool| E["+ WRITE_DATA"]
E --> F["Detector checks policy table"]
F -->|source+sink both present| G["šØ exfiltration DETECTED"]
G --> H["3. Render widgets"]
H -->|get_capability_graph| I["Graph edge turns RED ā riskScore 1"]
H -->|explain_attack_path| J["Groq: plain-English summary"]
I --> K["4. Remediate"]
K -->|apply_policy_fix| L["Every tool supplying the sink\ncapability is disconnected"]
L --> M["Status cleared ā riskScore 0"]š ļø Tool & Capability Registry
Tool | Tool ID | Granted Capabilities | Risk Profile |
š§ |
|
| š High |
š¦ |
|
| š High |
šļø |
|
| š“ Critical |
š¬ |
|
| š” Medium |
š» |
|
| š“ Critical |
š |
|
| š¢ Low |
šÆ Toxic-Combination Policy Rules
Rule ID | Source | Sink | Severity | Violation |
|
|
| š“ Critical | Agent can read private data AND transmit it externally. |
|
|
| š High | Agent can read private records AND post them publicly. |
|
|
| š High | Agent can delete data AND execute unvalidated actions. |
Both tables are just data (TOOL_REGISTRY and POLICY_RULES) ā adding a 7th tool or a 4th rule is a one-line change, not new logic.
š MCP Interface Reference
Tools
connect_toolā connects a tool (gmail,dropbox,postgres,slack,filesystem,calendar) to an agent. Wrapped inOAuthGuard(scaffolded ā enforced only ifOAUTH_REQUIRED=trueis set; open by default for local/demo use).get_capability_graphā@Widget('capability-graph'). Returns nodes, danger edges, active attack paths, and risk score.detect_attack_pathsā@Widget('attack-path-alert'). Runs the deterministic rule engine.apply_policy_fixā disconnects every tool supplying a detected rule's sink capability.
Resource
aegis://policiesā the toxic-combination policy table as JSON.
Prompt
explain_attack_pathā takesagentId+ruleId, returns a plain-English explanation via Groq (llama-3.1-8b-instant). Only fires on an already-detected path, cached bySHA-256(ruleId + sorted(viaTools))ā this is the only step in the entire system that spends a token.
MCP surface map
flowchart LR
Agent(("š”ļø Aegis"))
Agent --> T1["š§ connect_tool"]
Agent --> T2["š§ get_capability_graph"]
Agent --> T3["š§ detect_attack_paths"]
Agent --> T4["š§ apply_policy_fix"]
Agent --> R1["š aegis://policies"]
Agent --> P1["š¬ explain_attack_path"]
T2 -.renders.-> W1["šØ capability-graph widget"]
T3 -.renders.-> W2["šØ attack-path-alert widget"]ā¶ļø Try It Yourself
Every step below works against the live server ā no local setup needed. Connect https://aegis-6a6d76ee-teamx-srmist.app.nitrocloud.ai/mcp to Claude (Settings ā Connectors ā Add custom connector) or ChatGPT (Developer Mode ā Plugins ā Add), or point NitroStack Studio at this repo locally, then walk through the same scenario the screenshots above were taken from:
Connect a tool. "Connect gmail to demo-agent." ā
connect_toolfires. One tool, nothing alarming yet.Connect a second tool. "Now connect dropbox to demo-agent." ā the agent can now both read private data and send it externally.
See the risk. "Show me its capability graph." ā
get_capability_graphrenders ā the exfiltration edge is red. This step is pure deterministic rule matching: zero LLM tokens spent to catch it.Get a plain-English explanation. "What does this mean?" ā
explain_attack_pathfires ā the only step in the whole system that calls an LLM, and only because a rule already matched.Fix it. "Fix it." ā
apply_policy_fixdisconnects every tool supplying the dangerous capability. The graph goes back toriskScore: 0.
Use any agentId you like ā it's just an in-memory key, not a real account.
š Quickstart (run it locally)
git clone https://github.com/prince-rai88/aegis-mcp.git
cd aegis-mcp
npm install
cp .env.example .env # add GROQ_API_KEY ā free at console.groq.com
npm run devVerify without any GUI:
bash scripts/test-mcp.shOr connect NitroStack Studio ā Add Server ā Nitro Project ā select this folder, then run through the walkthrough above against your local server instead of the live one.
ā FAQ
Why deterministic rules instead of having the model decide what's risky?
Because then the audit trail is "the model thought so." Detection here is a fixed rule table (source capability ā sink capability), so every flag is reproducible and explainable without re-running an LLM.
Does this scale beyond 6 tools and 3 rules?
TOOL_REGISTRY and the policy rules are both just data ā adding a 7th tool or a 4th rule is a one-line addition, not new logic.
What does the LLM actually do, then?
Only explain_attack_path, only after a rule has already fired, cached by SHA-256(ruleId + sorted(viaTools)) so the same finding is never re-explained twice.
š Project Structure
aegis-mcp/
āāā src/
ā āāā app.module.ts # NitroStack root module
ā āāā index.ts # Server bootstrap
ā āāā modules/governance/ # Security governance engine
ā ā āāā capability.ts # Capability model + TOOL_REGISTRY
ā ā āāā detector.ts # 0-token deterministic detector
ā ā āāā policies.ts # Toxic-combination policy rules
ā ā āāā oauth.guard.ts # Scaffolded OAuth guard
ā ā āāā governance.tools.ts # The 4 MCP tools
ā ā āāā governance.resources.ts # aegis://policies
ā ā āāā governance.prompts.ts # Groq explanation prompt
ā ā āāā governance.module.ts
ā āāā widgets/app/
ā āāā capability-graph/ # Capability graph widget
ā āāā attack-path-alert/ # Attack path alert widget
āāā docs/screenshots/ # Real rendered widget screenshots
āāā scripts/
ā āāā test-mcp.sh # Stdio JSON-RPC smoke test
ā āāā preview-widgets.html # Local widget preview, no Studio needed
āāā README.md