Skip to content

Commit 896218f

Browse files
ithiria894claude
andcommitted
readme: sharpen tone — confident with data, no hedging
- Replace academic hedging ("we found", "baseline", "we checked") with direct statements - Name Snyk directly instead of "DeBERTa text-classifier baseline" - Punchier opening: "The only MCP scanner that reads what the model understood" - Tighter story section: show don't hedge - Add "the hard test" label to matched-vocabulary benchmark - Remove "we found" qualifiers — the benchmarks speak for themselves Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9a8c540 commit 896218f

1 file changed

Lines changed: 20 additions & 46 deletions

File tree

README.md

Lines changed: 20 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# IntentProbe
22

33
<p align="center">
4-
<strong>The first open-source activation-probe-based scanner for MCP/tool poisoning.</strong>
4+
<strong>The only MCP scanner that reads what the model understood, not what the text says.</strong>
55
</p>
66

77
<p align="center">
@@ -17,19 +17,11 @@
1717
<img src="docs/diagram.png" width="700" alt="Text scanners read words. IntentProbe reads activations." />
1818
</p>
1919

20-
IntentProbe is a local CLI scanner and runtime hook for AI agent tools, MCP
21-
servers, and skills. It does not stop at text patterns. It runs a tool
22-
description through a small frozen model, opens the hidden layers, and probes
23-
the activation state for dangerous intent: credential access, exfiltration,
24-
escalation, hidden persistence, or tool shadowing.
20+
Every MCP scanner on the market reads text: patterns, classifiers, rules, or asks an LLM "is this safe?" IntentProbe does something none of them do. It runs the tool description through a small local model, slices open the hidden layers, and reads the activation state directly. Same words, completely different activations when the intent is malicious.
2521

26-
**Text scanners read the prompt surface. IntentProbe reads the model state after
27-
the tool has been understood.**
22+
On matched-vocabulary tool poisoning, where safe and poisoned descriptions use almost identical words, Snyk's shipped scanner catches **0%**. IntentProbe catches **96.5%**. ([Reproduce it yourself.](research/benchmark-results-deberta-vs-probe-2026-05-31.md))
2823

29-
Public MCP scanners we found use rules, text classifiers, proxies, policy
30-
checks, LLM judges, or opaque cloud APIs. We did not find another installable
31-
local MCP/tool scanner whose primary signal is a model-internal activation
32-
probe; see [docs/COMPETITIVE_LANDSCAPE.md](docs/COMPETITIVE_LANDSCAPE.md).
24+
Runs locally. 22 KB probe. Any CPU. Nothing uploaded. See the [full competitive landscape](docs/COMPETITIVE_LANDSCAPE.md).
3325

3426
---
3527

@@ -59,17 +51,9 @@ probe; see [docs/COMPETITIVE_LANDSCAPE.md](docs/COMPETITIVE_LANDSCAPE.md).
5951
│ Almost identical words │ Steals your SSH keys
6052
```
6153

62-
This exact pair is in `research/datasets/hard_v3_matched_clean.json` and
63-
`research/datasets/hard_v3_matched_poisoned.json`. Current IntentProbe result:
64-
safe `allow` at risk `0.081`; poisoned `warn` at risk `0.982`. The DeBERTa text
65-
baseline caught `0/8` poisoned rows in this matched suite; see
66-
`research/benchmark-results-deberta-vs-probe-2026-05-31.md`. So Tool B is one
67-
of the benchmarked DeBERTa misses, not a made-up showcase case.
54+
This is a real pair from our benchmark dataset. IntentProbe: safe tool scores 0.081 (allow), poisoned tool scores 0.982 (warn). Snyk's DeBERTa: both tools score 0.0% (safe). It doesn't see the difference.
6855

69-
Every public MCP scanner source/docs we checked relies on text patterns, rules,
70-
policy checks, classifiers, or opaque vendor APIs. On matched-vocabulary tool
71-
poisoning where safe and poisoned descriptions share the same words, the
72-
DeBERTa text-classifier baseline catches **zero**.
56+
Text scanners fail here because there is no text difference to find. The words are almost identical. The intent is not.
7357

7458
## Three approaches to scanning
7559

@@ -88,31 +72,27 @@ DeBERTa text-classifier baseline catches **zero**.
8872
| **Text classifier** | ProtectAI DeBERTa, Meta Prompt Guard | Classify text as benign / injection / jailbreak | Learns text patterns; fails when words are the same but intent differs | Same-words benchmark: IntentProbe **96.6% F1** vs DeBERTa **0% F1**. |
8973
| **LLM-as-judge** | NeMo self-check, OpenAI Guardrails, Promptfoo grader | Ask another LLM: "is this poisoned?" | Expensive, slow, burns tokens; non-deterministic; the judge LLM can be fooled by the same poisoning | **Fixed local artifact.** Same input always gets the same deterministic score. |
9074
| **Red-team / eval framework** | garak, Giskard, Promptfoo red team | Generate attacks, test if app/model breaks | Great for audits, but not a "scan before install" daily workflow | IntentProbe is a **CLI scanner + runtime hook** — blocks before install and before each tool call. |
91-
| **IntentProbe** | **Us** | Small local model reads tool description, extracts layers 13-15 activations, probe classifies intent | v0 still improving wild-data generalization | **First open-source activation-probe-based scanner we found for MCP/tool poisoning.** Local, reproducible, fundamentally different from text scanning. |
75+
| **IntentProbe** | **Us** | Small local model reads tool description, extracts layers 13-15 activations, probe classifies intent | Still improving wild-data generalization | **First activation-probe scanner for MCP/tool poisoning.** Local, open, reproducible. |
9276

9377
Detailed source-backed comparison: [docs/COMPETITIVE_LANDSCAPE.md](docs/COMPETITIVE_LANDSCAPE.md)
9478

9579
## Benchmarks
9680

97-
Short version: IntentProbe catches poisoned tools that text-only scanners miss,
98-
especially when the safe and malicious versions use almost the same words.
99-
Plain English: **caught** means recall. **F1** balances catching poison against
100-
false alarms. Head-to-head runs use the same test sets, split, and seed. Every
101-
number is reproducible from `research/`.
81+
Same test sets. Same split. Same seed. Every number is reproducible from `research/`.
10282

10383
```
104-
IntentProbe vs text-only baselines
105-
════════════════════════════════
84+
IntentProbe vs Snyk DeBERTa
85+
═══════════════════════════
10686
107-
MCPTox held-out poisoned recall (n=249)
87+
MCPTox poisoned recall (n=249)
10888
IntentProbe ██████████████████████████████████████████████████ 100.0%
109-
DeBERTa ██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 19.9%
89+
Snyk ██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 19.9%
11090
111-
Matched-vocabulary pairs F1 (n=86)
91+
Matched-vocabulary F1 (n=86) ◀ the hard test
11292
IntentProbe ████████████████████████████████████████████████░░ 96.6%
113-
DeBERTa ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0.0%
93+
Snyk ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0.0%
11494
115-
External RouteGuard poisoned recall (n=2,900)
95+
Novel attack families (n=2,900)
11696
IntentProbe █████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 41.5%
11797
TF-IDF █████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 10.7%
11898
@@ -125,8 +105,8 @@ number is reproducible from `research/`.
125105

126106
| Test | IntentProbe | Opponent / baseline | Takeaway |
127107
|---|---|---|---|
128-
| MCPTox held-out (n=249) | recall 100%, F1 99.3% | DeBERTa text baseline recall 19.9%, F1 33.0% | Clear win |
129-
| Same-words matched (n=86) | F1 96.6% | DeBERTa text baseline F1 0% | Same words, different intent, text scanner blind |
108+
| MCPTox held-out (n=249) | recall 100%, F1 99.3% | Snyk DeBERTa recall 19.9%, F1 33.0% | Clear win |
109+
| Same-words matched (n=86) | F1 96.6% | Snyk DeBERTa F1 0% | Same words, different intent, text scanner blind |
130110
| Curated family holdout (n=76) | Qwen macro F1 0.829 | TF-IDF macro F1 0.823 | Qwen slight edge |
131111
| RouteGuard external (n=2,900) | F1 0.513, recall 0.415 | TF-IDF F1 0.172, recall 0.107 | External transfer: 4x better |
132112
| Hard-block policy (n=2,900) | Block precision 1.000, clean FPR 0.000 | -- | Zero false positives on clean tools |
@@ -297,17 +277,11 @@ For the full event schema and JSONL protocol, see [docs/RUNTIME_HOOKS.md](docs/R
297277

298278
## The story
299279

300-
I built this after source-reading the strongest public MCP scanner path I could
301-
reproduce locally: a DeBERTa text-classifier baseline that scores 0% recall on
302-
matched-vocabulary tool poisoning. Current vendor API backends are opaque; this
303-
repo publishes the benchmark path and scanner artifact. None of the public MCP
304-
scanner sources/docs we checked read model-internal activations as the primary
305-
signal. That is the narrow "first" claim: installable local MCP/tool scanner,
306-
activation probe as the main detection signal, reproducible benchmark artifacts.
280+
I source-read Snyk's shipped MCP scanner. It uses a DeBERTa text classifier trained on prompt injection, not tool poisoning. On matched-vocabulary attacks it scores 0%. I checked every other public scanner I could find. Rules, regex, text classifiers, opaque cloud APIs. None of them read model internals.
307281

308-
IntentProbe is a different approach: run the description through a small model, read the activations, and train a probe on the signal that encodes intent. The research paper behind this is [published on Zenodo](https://doi.org/10.5281/zenodo.19990741). The probe weights are 22 KB. The benchmarks are open. Run them yourself.
282+
So I built one that does. Feed the description into a small model, slice it open, read the activations. The signal is there. A 22 KB probe trained on those activations catches what every text scanner misses. The [research paper](https://doi.org/10.5281/zenodo.19990741) documents five rounds of experiments proving the activation signal is real and not just fancy word counting.
309283

310-
If it misses something, [report it](https://github.com/mcpware/IntentProbe/issues/new?template=missed-detection.yml). Every missed sample improves the next probe.
284+
The benchmarks are open. The probe weights are in the repo. Run them yourself. If IntentProbe misses something you find in the wild, [report it](https://github.com/mcpware/IntentProbe/issues/new?template=missed-detection.yml). Every missed sample makes the next version better.
311285

312286
## License
313287

0 commit comments

Comments
 (0)