Built by
Siddhant Gahlot • Rupesh Arya Bhatt • Yash Gupta
An AI-powered incident response copilot. Paste in a security alert (suspicious login, leaked API key, malware beacon) and Triagent reasons about it in plain English, proposes a minimal containment plan, and only executes actions after the plan is cryptographically signed via the ArmorIQ SDK. Any action attempted outside the signed plan is blocked and logged. Every action — allowed or blocked — is written to a Notion database as an audit trail.
-
Triage — paste an alert into the chat box. Gemini (
gemini-2.5-flash) reasons about the incident and returns a severity rating, a plain-English explanation, and a minimal set of containment steps (isolate_session,revoke_token,rotate_api_keyonly — never anything destructive).
-
Approve & Sign — review the proposed plan, then sign it. The backend calls ArmorIQ's
capture_plan→get_intent_token, with a policy that explicitly denies anydelete_*action. The resultingplan_hashandmerkle_rootare shown as proof the plan was cryptographically committed.
-
Execute — each step is sent through ArmorIQ's
invoke(). Steps that verify against the signed token run via the actual MCP server (mcp.py), which runs on port 9000 and is registered to the ArmorIQ registry via ngrok. Any action outside the plan is blocked and rejected.
-
Simulate Attack — a separate button attempts
delete_logs, an action that was never part of the signed plan. ArmorIQ's policy denies it, and the real rejection reason is shown live.
-
Audit trail — every attempt, allowed or blocked, is logged to a Notion database and shown in a live-refreshing table.
- Python 3.11+
- Node.js 18+
- An ArmorIQ account/API key with access to the customer proxy
- A Gemini API key (Google AI Studio)
- A Notion integration token with access to a database containing these
properties:
Incident(title),Severity(select: Low/Medium/High),Action(rich text),Status(select: Allowed/Blocked/Error),Detail(rich text),Timestamp(date)
cd backend
cp .env.example .env # fill in the 5 values below
pip install -r requirements.txt
uvicorn main:app --reload --port 8080mcp.py is a separate, real MCP server (using the official mcp
Python SDK / FastMCP) that exposes the four containment actions
(isolate_session, revoke_token, rotate_api_key, delete_logs) as proper
MCP tools over Streamable HTTP. This is what gets registered on the ArmorIQ
dashboard so client.invoke() has a real, reachable server to forward
verified requests to. It runs independently of main.py / the FastAPI app.
-
Run it (in its own terminal, same
venv):cd backend source venv/bin/activate python mcp.py
By default it listens on
http://localhost:9000/mcp. -
Expose it to the internet so ArmorIQ's cloud proxy can reach it. The easiest way for local development is
ngrok:brew install ngrok # if you don't have it ngrok http 9000ngrok will print a forwarding URL like
https://abcd1234.ngrok-free.app. Your full MCP endpoint is that URL +/mcp, e.g.https://abcd1234.ngrok-free.app/mcp. -
Register it on the ArmorIQ dashboard (ArmorIQ registry):
- Go to MCP Servers → Add Server
- Name it exactly
triagent-mock-mcp(must match themcpfield used inarmoriq_client.pyand the plans Gemini generates) - Server URL: the ngrok
/mcpURL from step 2 - Let ArmorIQ scan/validate it — it should discover all four tools
- Save
Once registered, /execute calls in the app will return real Allowed
results (routed through mcp.py's logic via the MCP server) instead of
Error. Note: free ngrok URLs change every time you restart it, so you'll
need to re-register the URL on ArmorIQ's dashboard after each ngrok restart —
or use a paid ngrok static domain / a real deployment if you want this to be
stable.
cd frontend
cp .env.example .env
npm install
npm run devOpen the printed Vite URL (typically http://localhost:5173).
backend/.env
ARMORIQ_API_KEY=
ARMORIQ_PROXY_URL=https://proxy.armoriq.ai
GEMINI_API_KEY=
NOTION_TOKEN=
NOTION_DATABASE_ID=
frontend/.env
VITE_API_BASE_URL=http://localhost:8080
invoke() succeeds for real against MCP servers actually onboarded on the ArmorIQ dashboard. By running mcp.py on port 9000, tunneling it via ngrok, and registering it on the ArmorIQ registry as triagent-mock-mcp, ArmorIQ's proxy will forward the verified requests to your local MCP server. If the server is not onboarded or ngrok is not running, you should expect ArmorIQ to return an error for unregistered-MCP calls in addition to verification failures for out-of-scope actions. Both are treated as meaningful signals by the backend — never faked into a success and never silently swallowed. The real error text from ArmorIQ is always surfaced to the UI and logged to Notion as Blocked or Error.
triagent/
├── backend/
│ ├── main.py # FastAPI app, API routes
│ ├── agent.py # Gemini triage reasoning -> plan JSON
│ ├── armoriq_client.py # ArmorIQ SDK wrapper (capture_plan -> get_intent_token -> invoke)
│ ├── mcp.py # Real standalone MCP server (registers as triagent-mock-mcp on ArmorIQ)
│ ├── mock_mcps.py # Local simulated tool endpoints (used directly + via mcp.py)
│ ├── notion_logger.py # Direct Notion API calls (audit trail)
│ ├── models.py # Pydantic request/response models
│ ├── requirements.txt
│ └── .env.example
└── frontend/
├── src/
│ ├── App.tsx
│ ├── components/
│ │ ├── TriageChat.tsx
│ │ ├── PlanApproval.tsx
│ │ ├── LiveExecutionLog.tsx
│ │ ├── AuditTrail.tsx
│ │ └── SeverityBadge.tsx
│ ├── lib/api.ts
│ ├── types.ts
│ └── index.css
├── tailwind.config.ts
├── package.json
└── .env.example