Aegis
# Aegis š”ļø
[](https://nitrostack.ai)
[](https://modelcontextprotocol.io)
[](https://groq.com)
[](https://aegis-6a6d76ee-teamx-srmist.app.nitrocloud.ai/mcp)
[](LICENSE)
> **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](#-the-problem)
- [See It Work](#-see-it-work)
- [System Architecture](#-system-architecture)
- [Detection & Remediation Lifecycle](#-detection--remediation-lifecycle)
- [Tool & Capability Registry](#-tool--capability-registry)
- [Toxic-Combination Policy Rules](#-toxic-combination-policy-rules)
- [MCP Interface Reference](#-mcp-interface-reference)
- [Try It Yourself](#-try-it-yourself)
- [Quickstart](#-quickstart)
- [FAQ](#-faq)
- [Project Structure](#-project-structure)
---
## š 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 PATH
```
> [!WARNING]
> - `READ_PRIVATE_DATA` (Dropbox) + `SEND_EXTERNAL` (Gmail) = š“ **Data Exfiltration Path**
> - `READ_PRIVATE_DATA` (Postgres) + `WRITE_PUBLIC` (Slack) = š **Public Leak Path**
> - `DELETE_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
```mermaid
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
```mermaid
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 |
|:---:|---|---|:---:|
| š§ | `gmail` | `READ_PRIVATE_DATA`, `SEND_EXTERNAL` | š High |
| š¦ | `dropbox` | `READ_PRIVATE_DATA`, `WRITE_DATA`, `SEND_EXTERNAL` | š High |
| šļø | `postgres` | `READ_PRIVATE_DATA`, `WRITE_DATA`, `DELETE_DATA` | š“ Critical |
| š¬ | `slack` | `WRITE_PUBLIC`, `SEND_EXTERNAL` | š” Medium |
| š» | `filesystem` | `READ_PRIVATE_DATA`, `WRITE_DATA`, `EXECUTE` | š“ Critical |
| š
| `calendar` | `READ_PRIVATE_DATA`, `WRITE_DATA` | š¢ Low |
## šÆ Toxic-Combination Policy Rules
| Rule ID | Source | Sink | Severity | Violation |
|---|---|---|:---:|---|
| `exfiltration` | `READ_PRIVATE_DATA` | `SEND_EXTERNAL` | š“ **Critical** | Agent can read private data AND transmit it externally. |
| `public-leak` | `READ_PRIVATE_DATA` | `WRITE_PUBLIC` | š **High** | Agent can read private records AND post them publicly. |
| `destructive` | `DELETE_DATA` | `EXECUTE` | š **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 in `OAuthGuard` (scaffolded ā enforced only if `OAUTH_REQUIRED=true` is 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`** ā takes `agentId` + `ruleId`, returns a plain-English explanation via Groq (`llama-3.1-8b-instant`). Only fires on an already-detected path, cached by `SHA-256(ruleId + sorted(viaTools))` ā this is the *only* step in the entire system that spends a token.
### MCP surface map
```mermaid
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:
1. **Connect a tool.** *"Connect gmail to demo-agent."* ā `connect_tool` fires. One tool, nothing alarming yet.
2. **Connect a second tool.** *"Now connect dropbox to demo-agent."* ā the agent can now both read private data and send it externally.
3. **See the risk.** *"Show me its capability graph."* ā `get_capability_graph` renders ā the exfiltration edge is red. This step is pure deterministic rule matching: zero LLM tokens spent to catch it.
4. **Get a plain-English explanation.** *"What does this mean?"* ā `explain_attack_path` fires ā the *only* step in the whole system that calls an LLM, and only because a rule already matched.
5. **Fix it.** *"Fix it."* ā `apply_policy_fix` disconnects every tool supplying the dangerous capability. The graph goes back to `riskScore: 0`.
Use any `agentId` you like ā it's just an in-memory key, not a real account.
---
## š Quickstart (run it locally)
```bash
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 dev
```
Verify without any GUI:
```bash
bash scripts/test-mcp.sh
```
Or connect [NitroStack Studio](https://nitrostack.ai/studio) ā **Add Server** ā **Nitro Project** ā select this folder, then run through the [walkthrough above](#-try-it-yourself) 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
```
---
## š License
[MIT](LICENSE)
TDQS
Scored across 4 tools
Each tool has a clearly distinct purpose: connect_tool adds capabilities, get_capability_graph inspects state, detect_attack_paths analyzes for risks, and apply_policy_fix remediates. There is no overlap or ambiguity.
All tool names follow a consistent verb_noun pattern with underscore separation: connect_tool, get_capability_graph, detect_attack_paths, apply_policy_fix. The style is uniform and predictable.
Four tools is well-scoped for a focused security/capability management server. Each tool covers a necessary step and none feel extraneous.
The set covers the core lifecycle: connect, inspect, detect, and remediate. The only minor gap is the lack of a direct 'disconnect_tool' independent of attack-path remediation, but apply_policy_fix effectively serves that purpose.