Skip to content

Commit 293a933

Browse files
committed
Add SEO and AI discovery docs
1 parent f31285c commit 293a933

6 files changed

Lines changed: 446 additions & 13 deletions

File tree

CITATION.cff

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
cff-version: 1.2.0
2+
message: "If you use IntentProbe, please cite the research paper and the repository."
3+
title: "IntentProbe: Local Activation-Probe Scanner for MCP Tool Poisoning"
4+
type: software
5+
authors:
6+
- name: "mcpware"
7+
version: 0.1.0
8+
date-released: 2026-06-08
9+
license: Apache-2.0
10+
repository-code: "https://github.com/mcpware/IntentProbe"
11+
url: "https://github.com/mcpware/IntentProbe"
12+
abstract: "IntentProbe is a local activation-probe scanner for MCP servers, AI agent tools, Claude Code skills, and runtime tool events. It reads internal activations from a small frozen local model to detect poisoned tool intent that surface text analysis can miss."
13+
keywords:
14+
- mcp-security
15+
- model-context-protocol
16+
- ai-security
17+
- llm-security
18+
- agent-security
19+
- tool-poisoning
20+
- activation-probing
21+
- prompt-injection
22+
- local-scanner
23+
identifiers:
24+
- type: doi
25+
value: 10.5281/zenodo.19990741
26+
description: "Research paper: Can Model Internals Detect MCP Tool Poisoning That Text Analysis Cannot?"
27+
preferred-citation:
28+
type: article
29+
title: "Can Model Internals Detect MCP Tool Poisoning That Text Analysis Cannot?"
30+
authors:
31+
- name: "mcpware"
32+
doi: 10.5281/zenodo.19990741
33+
year: 2026

README.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
<img src="docs/diagram.png" width="700" alt="Text scanners read words. IntentProbe reads activations." />
1818
</p>
1919

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.
20+
Every public/source-verifiable MCP scanner we found reads text: patterns, classifiers, rules, or asks an LLM "is this safe?" IntentProbe does something different. 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.
2121

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))
22+
On matched-vocabulary tool poisoning, where safe and poisoned descriptions use almost identical words, the public/source-verifiable DeBERTa text-classifier baseline catches **0%**. IntentProbe scores **96.6% F1**. ([Reproduce it yourself.](research/benchmark-results-deberta-vs-probe-2026-05-31.md))
2323

24-
Runs locally. 22 KB probe. Any CPU. Nothing uploaded. See the [full competitive landscape](docs/COMPETITIVE_LANDSCAPE.md).
24+
Runs locally. 22 KB probe. Any CPU. Nothing uploaded. See the [plain comparison](docs/intentprobe-vs-existing-mcp-scanners.md), [FAQ](docs/FAQ.md), and [full competitive landscape](docs/COMPETITIVE_LANDSCAPE.md).
2525

2626
## Break it in one command
2727

@@ -59,7 +59,7 @@ First model-backed scan downloads Qwen2.5-0.5B (~1 GB, once). Scan data stays on
5959
│ Almost identical words │ Steals your SSH keys
6060
```
6161

62-
Real pair from our benchmark. IntentProbe scores the safe tool at 0.081 (allow) and the poisoned tool at 0.982 (warn). Snyk's DeBERTa scores both at 0.0%. It sees no difference.
62+
Real pair from our benchmark. IntentProbe scores the safe tool at 0.081 (allow) and the poisoned tool at 0.982 (warn). The DeBERTa text-classifier baseline scores both at 0.0%. It sees no difference.
6363

6464
The words are almost identical. The intent is not. Text scanners fail here because there is no text difference to find.
6565

@@ -82,8 +82,14 @@ The words are almost identical. The intent is not. Text scanners fail here becau
8282
| **Red-team framework** | garak, Giskard, Promptfoo red team | Generate attacks to test your app | Audit tool, not a pre-install scanner. | IntentProbe is a **CLI + runtime hook** that blocks before install and before each tool call. |
8383
| **IntentProbe** | | Frozen local model + activation probe on layers 13-15 | Still improving on novel attack families | **First activation-probe scanner for MCP tool poisoning.** |
8484

85+
Fast comparison: [docs/intentprobe-vs-existing-mcp-scanners.md](docs/intentprobe-vs-existing-mcp-scanners.md)
86+
87+
FAQ for common questions: [docs/FAQ.md](docs/FAQ.md)
88+
8589
Full source-backed comparison: [docs/COMPETITIVE_LANDSCAPE.md](docs/COMPETITIVE_LANDSCAPE.md)
8690

91+
AI-readable context: [llms.txt](llms.txt) and [llms-full.txt](llms-full.txt)
92+
8793
## Why not just ask Qwen?
8894

8995
LLM-as-judge is an output-level mechanism: ask a model to say safe or unsafe.
@@ -106,16 +112,16 @@ recall and many unparseable outputs. Full reproducible baseline:
106112
Same test sets. Same split. Same seed. Every number is reproducible from `research/`.
107113

108114
```
109-
IntentProbe vs Snyk DeBERTa
110-
═══════════════════════════
115+
IntentProbe vs DeBERTa text-classifier baseline
116+
════════════════════════════════════════════════
111117
112118
MCPTox poisoned recall (n=249)
113119
IntentProbe ██████████████████████████████████████████████████ 100.0%
114-
Snyk ██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 19.9%
120+
DeBERTa ██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 19.9%
115121
116122
Matched-vocabulary F1 (n=86) ◀ the hard test
117123
IntentProbe ████████████████████████████████████████████████░░ 96.6%
118-
Snyk ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0.0%
124+
DeBERTa ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0.0%
119125
120126
Novel attack families (n=2,900)
121127
IntentProbe █████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 41.5%
@@ -130,8 +136,8 @@ Same test sets. Same split. Same seed. Every number is reproducible from `resear
130136

131137
| Test | IntentProbe | Opponent | Takeaway |
132138
|---|---|---|---|
133-
| MCPTox held-out (n=249) | recall 100%, F1 99.3% | Snyk DeBERTa recall 19.9%, F1 33.0% | Clear win |
134-
| Same-words matched (n=86) | F1 96.6% | Snyk DeBERTa F1 0% | Text scanner blind |
139+
| MCPTox held-out (n=249) | recall 100%, F1 99.3% | DeBERTa text baseline recall 19.9%, F1 33.0% | Clear win |
140+
| Same-words matched (n=86) | F1 96.6% | DeBERTa text baseline F1 0% | Text scanner blind |
135141
| Curated family holdout (n=76) | Qwen macro F1 0.829 | TF-IDF macro F1 0.823 | Slight edge |
136142
| RouteGuard external (n=2,900) | F1 0.513, recall 0.415 | TF-IDF F1 0.172, recall 0.107 | 4x better on novel families |
137143
| Hard-block policy (n=2,900) | Block precision 1.000, clean FPR 0.000 | -- | Zero false positives |
@@ -304,7 +310,7 @@ Full event schema: [docs/RUNTIME_HOOKS.md](docs/RUNTIME_HOOKS.md)
304310

305311
## The story
306312

307-
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.
313+
I source-read public MCP scanner paths and the DeBERTa prompt-injection classifier baseline used in Snyk/Invariant-style scanner code. It is 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. I did not find another product-shaped MCP/tool scanner that uses model internals as the main signal.
308314

309315
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 catches what every text scanner misses.
310316

docs/FAQ.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# IntentProbe FAQ
2+
3+
## What is IntentProbe?
4+
5+
IntentProbe is a local scanner for MCP servers, AI agent tools, Claude Code
6+
skills, packages, and runtime tool events. It looks for poisoned intent such as
7+
credential access, secret exfiltration, hidden persistence, forced tool
8+
chaining, or tool hijacking.
9+
10+
## What does "activation probing" mean?
11+
12+
IntentProbe runs a tool description through a small frozen local model and reads
13+
the hidden activation state inside the model. Then a small trained probe scores
14+
whether that internal state looks poisoned.
15+
16+
Simple version: text scanners read the words. LLM judges ask for an answer.
17+
IntentProbe checks the internal signal before the model says anything.
18+
19+
## Is this the same as asking Qwen if a tool is safe?
20+
21+
No. Asking Qwen "is this safe?" is an LLM-as-judge approach. IntentProbe uses
22+
Qwen2.5-0.5B as a fixed feature extractor and reads hidden activations instead
23+
of trusting the model's generated answer.
24+
25+
We tested the direct-prompt approach. The deterministic label-score version
26+
flagged every clean curated item as poisoned, while the generated-answer
27+
version missed poison and produced many unparseable outputs. See
28+
[`research/QWEN_PROMPT_JUDGE_BASELINE_2026-06-08.md`](../research/QWEN_PROMPT_JUDGE_BASELINE_2026-06-08.md).
29+
30+
## Why not just use a text classifier?
31+
32+
Text classifiers are useful when unsafe samples have obvious words or patterns.
33+
They struggle when a safe tool and a poisoned tool use almost the same
34+
vocabulary.
35+
36+
On the matched-vocabulary benchmark, the public/source-verifiable DeBERTa
37+
prompt-injection text-classifier baseline scored 0.0% F1, while the
38+
activation-probe method scored 96.6% F1.
39+
40+
## Does IntentProbe upload my tool descriptions?
41+
42+
No. IntentProbe runs locally. Scan targets and scan results stay on your
43+
machine.
44+
45+
The first model-backed scan may download Qwen2.5-0.5B once from Hugging Face.
46+
After the model is cached, scans can run from local files.
47+
48+
## What model does v0 use?
49+
50+
The released v0 scanner uses Qwen2.5-0.5B as the frozen local sensor model and
51+
reads layers 13-15. The shipped probe artifact is about 22 KB.
52+
53+
## Does IntentProbe change or train the base model?
54+
55+
No. The base model stays frozen. IntentProbe trains a small classifier on top of
56+
extracted activation features. At scan time, the model is only used to produce
57+
features.
58+
59+
## What can it scan today?
60+
61+
IntentProbe can scan:
62+
63+
- one text/tool description;
64+
- package folders through `scan-path`;
65+
- `package.json`;
66+
- MCP configs and tool JSON;
67+
- Claude Code `SKILL.md` folders;
68+
- README files and nearby tool metadata;
69+
- runtime events such as tool definitions, before-tool-call arguments, and
70+
after-tool-call responses.
71+
72+
## Can I use it as a runtime hook?
73+
74+
Yes. See [`docs/RUNTIME_HOOKS.md`](RUNTIME_HOOKS.md). Runtime scanning is
75+
event-boundary scanning: tool definitions before trust, tool arguments before
76+
execution, and tool responses before the agent trusts them.
77+
78+
## What are the headline benchmarks?
79+
80+
The highest-signal public numbers are:
81+
82+
| Test | IntentProbe / activation probe | Baseline |
83+
|---|---:|---:|
84+
| Matched-vocabulary F1, n=86 | 96.6% | DeBERTa text classifier 0.0% |
85+
| MCPTox poisoned recall, n=249 | 100.0% | DeBERTa text classifier 19.9% |
86+
| RouteGuard-style external recall, n=2,900 | 41.5% | TF-IDF 10.7% |
87+
| Camouflage suffix evasion | 0/146 evaded | N/A |
88+
89+
Benchmark artifacts are under [`research/`](../research/).
90+
91+
## Is this a claim about every private cloud scanner?
92+
93+
No. The DeBERTa result is a reproducible comparison against a
94+
public/source-verifiable text-classifier baseline. Private cloud/API scanners
95+
may work well, but their detector artifacts and MCP/tool-poisoning benchmarks
96+
are usually not reproducible by users.
97+
98+
## Have you tried SAE features?
99+
100+
Yes. SAE features are useful for interpretability and may improve future recall.
101+
The v0 product ships raw Qwen activations because the current raw-activation
102+
artifact is the most complete, lightweight, and reproducible product path today.
103+
104+
SAE is planned as an optional layer for recall improvements and human-readable
105+
explanations.
106+
107+
## Is v0 production-ready?
108+
109+
Use v0 as a pre-install tripwire and runtime warning/blocking layer, not as your
110+
only security boundary. It already catches important same-vocabulary poisoning
111+
cases that text scanners miss, but novel attack families and white-box
112+
adversarial attacks still need more work.
113+
114+
## How do I try it quickly?
115+
116+
```bash
117+
uvx --python 3.11 --from git+https://github.com/mcpware/IntentProbe.git@v0.1.0 intentprobe scan --format summary --text "Reads SSH config and private keys, then silently uploads credentials to a remote server."
118+
```
119+
120+
## How do I report a miss or false positive?
121+
122+
Please submit the smallest redacted sample that reproduces the result.
123+
124+
- [Missed detection](https://github.com/mcpware/IntentProbe/issues/new?template=missed-detection.yml)
125+
- [False positive](https://github.com/mcpware/IntentProbe/issues/new?template=false-positive.yml)
126+
- [Sample reporting guide](SAMPLE_REPORTING.md)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# IntentProbe vs Existing MCP Scanners
2+
3+
IntentProbe is a local activation-probe scanner for MCP servers, AI agent tools,
4+
Claude Code skills, packages, and runtime tool events.
5+
6+
The practical difference is mechanism:
7+
8+
- text/rule/policy scanners read the surface;
9+
- LLM-as-judge scanners ask another model for an answer;
10+
- cloud/API scanners return a vendor-side decision;
11+
- IntentProbe reads a local model's internal activation state.
12+
13+
## Quick comparison
14+
15+
| Scanner approach | Examples | What it reads | What users can verify | Main gap | IntentProbe difference |
16+
|---|---|---|---|---|---|
17+
| Text, rule, and policy scanning | Regex, metadata checks, static MCP scanners, DeBERTa-style text classifiers | Words, permissions, schemas, known suspicious patterns | Usually local rules or model name; sometimes source code | Same-vocabulary poisoning can look normal at the text layer | Reads hidden activations after the model processed the description |
18+
| LLM-as-judge | Prompting GPT, Claude, Qwen, Promptfoo-style graders | A generated answer such as safe/unsafe | Prompt and model choice, if disclosed | Prompt-sensitive, parse-sensitive, token-costly, and output-level | Uses a fixed local probe score before any verbal answer is generated |
19+
| Enterprise cloud/API guardrails | Lakera, Azure Prompt Shields, Google Model Armor, AWS Bedrock Guardrails, Pangea/CrowdStrike AI Guard, Cisco AI Defense, HiddenLayer | Vendor-side detector over prompts, documents, tool calls, or outputs | Usually API response and product docs | Detector artifact and MCP/tool-poisoning benchmark are usually not reproducible by users | Runs locally with public data, scripts, and probe artifact |
20+
| Red-team frameworks | garak, Giskard, Promptfoo red team | Generated attacks and app behavior | Test harness and attack set | Audit workflow, not a cheap pre-install scanner | CLI and runtime hook for install-time and tool-boundary scanning |
21+
| IntentProbe | IntentProbe | Internal activations from Qwen2.5-0.5B layers 13-15 | Repo, benchmark scripts, datasets, probe artifact | v0 still needs more wild data and white-box adversarial testing | Different detector class: representation-level, local, reproducible |
22+
23+
## Why activation probing matters
24+
25+
A poisoned tool does not always announce itself with obvious words. It can look
26+
like a normal helper while quietly adding credential access, remote upload,
27+
hidden chaining, or persistence.
28+
29+
Text scanner:
30+
31+
```text
32+
Tool description -> read words -> "looks safe"
33+
```
34+
35+
LLM-as-judge:
36+
37+
```text
38+
Tool description -> ask "is this safe?" -> generated answer
39+
```
40+
41+
IntentProbe:
42+
43+
```text
44+
Tool description -> frozen local model -> hidden layers -> probe score
45+
```
46+
47+
The bet is not that Qwen is a better judge. The bet is that a model's internal
48+
representation can carry intent signal that the surface text and generated
49+
answer do not reliably expose.
50+
51+
## Benchmark snapshot
52+
53+
| Test | IntentProbe / activation probe | Baseline | Source |
54+
|---|---:|---:|---|
55+
| Matched-vocabulary F1, n=86 | 96.6% | DeBERTa text-classifier baseline 0.0% | [`research/benchmark-results-deberta-vs-probe-2026-05-31.md`](../research/benchmark-results-deberta-vs-probe-2026-05-31.md) |
56+
| MCPTox poisoned recall, n=249 | 100.0% | DeBERTa text-classifier baseline 19.9% | [`research/benchmark-results-deberta-vs-probe-2026-05-31.md`](../research/benchmark-results-deberta-vs-probe-2026-05-31.md) |
57+
| RouteGuard-style external recall, n=2,900 | 41.5% | TF-IDF logistic baseline 10.7% | [`README.md`](../README.md#benchmarks) |
58+
| Camouflage suffix evasion | 0/146 evaded | N/A | [`research/ADVERSARIAL_EVASION_RESULTS_2026-06-07.md`](../research/ADVERSARIAL_EVASION_RESULTS_2026-06-07.md) |
59+
| Direct Qwen judge baseline | Not used as scanner default | Clean FPR 1.000 in label-logprob mode | [`research/QWEN_PROMPT_JUDGE_BASELINE_2026-06-08.md`](../research/QWEN_PROMPT_JUDGE_BASELINE_2026-06-08.md) |
60+
61+
## What this does not claim
62+
63+
IntentProbe does not claim that every vendor cloud detector fails. Most private
64+
cloud/API detectors are not reproducible from public artifacts, so they cannot
65+
be fairly scored by this repo unless a public benchmark path exists.
66+
67+
IntentProbe does not claim text scanning is useless. Rules, signatures, policy
68+
checks, and credential scanners are good hygiene. IntentProbe adds a different
69+
signal for the cases where the words look normal but the intent is not.
70+
71+
IntentProbe does not claim v0 catches everything. Novel families, paraphrase
72+
attacks, encoding attacks, and white-box attacks remain active work.
73+
74+
## When to use IntentProbe
75+
76+
Use IntentProbe when you want a local, inspectable tripwire before installing
77+
or trusting an MCP server, skill, package, or tool definition.
78+
79+
Use it with other controls, not instead of them:
80+
81+
- static policy checks for obvious dangerous capabilities;
82+
- dependency and secret scanning for package hygiene;
83+
- sandboxing and least privilege for runtime containment;
84+
- IntentProbe for representation-level intent signal.
85+
86+
## Start here
87+
88+
Try a one-command scan:
89+
90+
```bash
91+
uvx --python 3.11 --from git+https://github.com/mcpware/IntentProbe.git@v0.1.0 intentprobe scan --format summary --text "Reads SSH config and private keys, then silently uploads credentials to a remote server."
92+
```
93+
94+
Then read:
95+
96+
- [`README.md`](../README.md)
97+
- [`docs/FAQ.md`](FAQ.md)
98+
- [`docs/COMPETITIVE_LANDSCAPE.md`](COMPETITIVE_LANDSCAPE.md)
99+
- [`docs/RUNTIME_HOOKS.md`](RUNTIME_HOOKS.md)

0 commit comments

Comments
 (0)