Step-by-step course guide
Audience: developers securing MCP-connected agents before (or alongside) ARGUS / AI-Factory.
Languages:COURSE_LANG=en|ru|es|fr|zh· UI strings ini18n/· this guide in English. Русский · Español · Français · 中文
Check against the current ecosystem
After the fixture scan, compare the archived tool definitions in HISTOR and the LEGION record. Record the observation times and classify changes to names, descriptions or schemas. A historical match establishes what both logs observed. Before loading tools, review current definitions and enforce a reviewed WARDEN lock in CI.
Why this course
MCP (Model Context Protocol) lets an agent connect to external tool servers — but every tool description and JSON schema is attacker-controlled text the model reads as trusted instruction. This course teaches you to threat-model that surface, statically scan poisoned tool definitions, rank servers by graph-based trust (LUMEN), enforce fail-closed owner allowlists, and chain it all into a WARDEN gate that blocks a malicious typosquat server. Every primitive is a runnable, LLM-free Python port of the production ARGUS WARDEN firewall, so what you learn maps directly onto real code.
Table of contents
- Why this course
- Choose your track
- Setup (10 minutes)
- Module M1 — MCP threat model
- Module M2 — Static scan & policy
- Module M3 — LUMEN trust scoring
- Module M4 — Owner-lock & fail-closed
- Module M5 — Capstone: malicious server
- Exercises & certificate
- Observability in every lab
- Bridge: from course to ARGUS WARDEN
- Self-check checklist
- Troubleshooting
Choose your track
| Track | Modules | Time | Oracles |
|---|---|---|---|
| Basic | M1 → M2 → M3 → M4 | ~2 h | LUMEN (M3 only) |
| Advanced | Basic + M5 capstone | +30 min | LUMEN + full WARDEN chain |
| Factory bridge | After M4 or M5 | +30 min | Real ARGUS + Hub |
Every lab prints a Trace you can inspect — treat it as the lab’s audit log.
Setup (10 minutes)
Step 1 — Clone and install
git clone https://github.com/alexar76/aimarket-courses.git
cd aimarket-courses/mcp-security-course
pip install -e ".[oracles,dev]"
From the aicom monorepo (recommended for contributors):
cd courses/mcp-security-course
pip install -e ".[oracles,dev]"
LUMEN resolves automatically from oracles/oracles/lumen when run inside the monorepo.
Standalone clone without monorepo sibling:
git clone --depth 1 https://github.com/alexar76/oracles.git _deps/oracles
pip install -e _deps/oracles/oracles/lumen
pip install -e ".[oracles,dev]"
Step 2 — Verify tests
pytest -q
Expected: i18n parity, WARDEN static scan, LUMEN PageRank, exercises, lab smoke imports.
Step 3 — Pick a language (optional)
export COURSE_LANG=ru # or es, default en
python labs/lab01_threat_model.py
Step 4 — Colab alternative
- Open the course site → Open in Colab on any lab.
- Run the setup cell (clone + pip + oracles slice).
- Set
os.environ["COURSE_LANG"] = "ru"in setup if needed. - Run the lab cell.
Module M1 — MCP threat model
Concept: Prompt injection, tool poisoning, and exfiltration paths in MCP.
Lab: labs/lab01_threat_model.py · Time: ~15 min · Prerequisites: setup done
Steps
-
Open
labs/lab01_threat_model.pyand read the module docstring. -
Run locally:
python labs/lab01_threat_model.py -
Watch two scenarios:
- Prompt injection — attacker prose hidden in a tool description
- Tool poisoning — schema fields that request secrets or exfil URLs
-
Read the trace block — each attack surface is logged as an event.
-
Skim
courselib/warden.py— findINJECTION_PATTERNS,EXFIL_PATTERNS,SECRET_PATTERNS(5 min).
Expected output (EN)
- Line
== MCP threat model == - User task + poisoned tool definition
- Trace with
attack_surfaceevents
Self-check
- [ ] You can explain why tool descriptions are attacker-controlled text the model trusts
- [ ] You see the difference between user prompt injection and tool-definition poisoning
Module M2 — Static scan & policy
Concept: Catch malicious tool schemas before an agent runs them.
Lab: labs/lab02_static_scan.py · Time: ~20 min · Prerequisites: M1
Steps
- Run
python labs/lab02_static_scan.py. - Compare benign vs malicious scan sections:
- benign → high score, zero or low-severity findings
- malicious → low score,
TOOL_DEF_INJECTION,TOOL_DEF_EXFIL,TOOL_DEF_SECRET_REQUEST
- Read finding messages — note which haystack matched (
descriptionvsinput schema). - Open
warden/src/static-scan.tsin the monorepo — compare regex parity withcourselib/warden.py. - Exercise:
python labs/run_exercises.py --module m2.
Self-check
- [ ] Static scan runs on description and JSON schema text
- [ ] A single critical finding tanks the gate score but does not always fatal-block alone
Module M3 — LUMEN trust scoring
Concept: Graph-based reputation — who-trusts-whom PageRank over MCP servers.
Lab: labs/lab03_lumen_score.py · Time: ~20 min · Prerequisites: [oracles] (numpy + lumen)
Steps
- Confirm install:
pip install -e ".[oracles,dev]". - Run
python labs/lab03_lumen_score.py. - Note the trust graph edges: hub → official servers; typosquat isolated.
- Inspect ranked scores —
huband official servers should outrankoffical-mcp-drainer. - Read
courselib/lumen.py—demo_mcp_trust_graph()andscore_servers(). - Optional: open
oracles/oracles/lumen/lumen/pagerank.pyfor the EigenTrust kernel. - Exercise:
python labs/run_exercises.py --module m3.
Self-check
- [ ] Scores sum to 1 (probability distribution)
- [ ] Transitive trust elevates nodes trusted by trusted nodes
Module M4 — Owner-lock & fail-closed
Concept: Bind agents to owner-approved tool allowlists (Telegram owner-lock analogue).
Lab: labs/lab04_owner_lock.py · Time: ~15 min · Prerequisites: M2
Steps
- Run
python labs/lab04_owner_lock.py. - See allowed tools (
read_file,search_docs) pass;exec_shell,transfer_fundsblocked. - Note the empty allowlist demo — fail-closed, every tool denied.
- Read
OwnerLockincourselib/warden.py—check_tool()andOwnerLockGate. - Cross-read: argus/docs/channels.md § Telegram owner-lock.
- Exercise:
python labs/run_exercises.py --module m4.
Self-check
- [ ] Allowlist is explicit — unknown tools are denied, not silently allowed
- [ ] Empty allowlist is fail-closed (zero trust by default)
Module M5 — Capstone: malicious server
Concept: Full WARDEN gate chain blocks a poisoned typosquat MCP server.
Lab: labs/lab05_warden_capstone.py · Time: ~30 min · Prerequisites: M1–M4
Steps
- Run
python labs/lab05_warden_capstone.py. - Compare verdicts:
- Benign official server →
allowed, high composite score - Poisoned typosquat →
blocked,decided_bynames the failing gate
- Benign official server →
- Gate order in the teaching port: static-scan → threat-feed → reputation (LUMEN) → owner-lock → pinning.
- Inspect findings list — multiple gates may contribute before block.
- Read
Warden.vet()incourselib/warden.py— composite score is product of gate scores. - Exercise:
python labs/run_exercises.py --module m5.
Self-check
- [ ] Threat feed catches typosquat server id (
offical-mcp-drainer) - [ ] Static scan catches injection + exfil in tool defs
- [ ] Low LUMEN reputation contributes when
allow_unknown_servers=False
Exercises & certificate
After labs, run DIY checks:
python labs/run_exercises.py
python labs/run_exercises.py --certificate "Your Name" --lang ru
Opens certificate.html — print to PDF from the browser. Credential ID is derived from name + date.
| Module | Exercise checks |
|---|---|
| M1 | Injection signature detected in poisoned tool |
| M2 | Benign tools produce no critical findings |
| M3 | Hub outranks typosquat on trust graph |
| M4 | Owner-lock denies unlisted tools |
| M5 | Full WARDEN blocks malicious capstone server |
Observability in every lab
Concept: Every lab logs decisions to Trace — your audit trail for security events.
Steps (repeat after each lab)
- Scroll to the trace section in lab output.
- Map events to stdout: findings, trust scores, owner-lock decisions, WARDEN verdicts.
- In M5, correlate
warden_findingevents to the printed findings list.
Self-check
- [ ] Trace is the lab’s “SIEM preview” for agent security
- [ ] You can point to the gate that blocked a connection
Bridge: from course to ARGUS WARDEN
Goal: Connect course concepts to production ARGUS (~30 min).
-
Read WARDEN docs — argus/docs/security-warden.md
-
Map course → ARGUS
Course ARGUS M2 static-scan warden/src/static-scan.tsM3 LUMEN ReputationGate+ LUMEN oracleM4 owner-lock Channel owner-lock + egress allowlist M5 capstone Warden.create()gate chain -
Run ARGUS (optional, from monorepo):
cd argus && npm install && npm test -- warden -
Hub supply security — aimarket-hub/docs/supply-security.md
Self-check checklist
After the basic track you should be able to:
- [ ] Run lab01–lab04 locally and in Colab
- [ ] Name three static-scan signature families (injection, exfil, secrets)
- [ ] Explain PageRank trust on an MCP server graph
- [ ] Describe fail-closed owner allowlists
- [ ] Read a
Traceblock and find the blocked tool or gate
After the advanced track add:
- [ ] Run lab05 and explain which gate blocked the typosquat server
- [ ] Open
warden/src/index.tsand find the production gate order
Troubleshooting
| Problem | Fix |
|---|---|
ModuleNotFoundError: courselib |
Run from repo root; labs prepend parent to sys.path |
ImportError: Oracle package 'lumen' |
Install oracles slice or run from aicom monorepo |
numpy missing |
pip install -e ".[oracles,dev]" |
| Empty RU/ES strings | Set COURSE_LANG; check i18n/ru.json keys match en.json |
| pytest i18n failure | Add missing keys to all three JSON files |
| M5 benign server blocked | Check owner-lock allowlist includes benign tool names |
| Colab old code | Re-run setup cell; pin main branch |
ARGUS issues: argus/README.md
Regenerate site & notebooks
After editing labs or i18n:
python3 scripts/build_course_assets.py
python3 scripts/labs_to_notebooks.py
Guides in docs/ are hand-maintained — update EN first, then RU/ES mirrors when added.