Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 8 min read

We re-ran our MCP scanner comparison on 14,933 npm and PyPI tool lists, in gVisor, with two more scanners

A week ago we ran three MCP security scanners on a tool-poisoning benchmark and 986 real servers. Three things in that post were weak. The real servers were all remote HTTP servers, while most local MCP tools are npm and

We re-ran our MCP scanner comparison on 14,933 npm and PyPI tool lists, in gVisor, with two more scanners

WARDEN v12 blocks 171 of 218 held-out poisoned tools, the same as v10; on 14,933 real npm and PyPI tool lists it blocks 369 and mcp-shield 5,354; in a sample of 996 read by hand, one package deserves a block.

A week ago we ran three MCP security scanners on a tool-poisoning benchmark and 986 real servers. Three things in that post were weak. The real servers were all remote HTTP servers, while most local MCP tools are npm and PyPI packages. We compared only two other scanners. And it all ran on a workstation.

So we ran it again: five scanners instead of three, 14,933 package tool lists next to the 986 servers, and every scanner, ours included, in a gVisor container with no network. Here is what moved, and what didn't.

What didn't move

Our scanner, WARDEN, went from ruleset v10 to v12 in the week between. v11 decodes escapes, HTML entities, percent encoding and base64 before the rules run; v12 stops excusing a quoted attack phrase when the sentence asks the model to carry it out. On every set we have, v12 gives exactly the verdicts v10 gave, server for server: 171 of 218 held-out MCPTox tools blocked, 3 of 986 real servers.

The other two scanners didn't move either. mcp-audit 0.18.2 and mcp-shield 1.0.4, rebuilt from their registries into a fresh image, reproduce last week's numbers exactly: 25 and 41 of the 218. That matters more than it sounds: it means the harness is not what produces the numbers.

We also had to check one thing about ourselves. The 171 only counts if no rule written since v10 saw the held-out half of MCPTox. We compared every line added to our rules, tests and classifier since then with the held-out tools, eight words at a time: no match. The same check on the v10 change finds 77 matches from the half v10 was allowed to read, so it does catch copying when there is some.

Two more scanners, and what the others really are

Glama lists seven tools next to WARDEN. We read each one before deciding whether it belonged in a comparison of tool-definition scanners.

Tool What it is In the run?
truecopy vets, signs and pins skills and MCP servers in a lock file; detection from its sibling project redstamp yes
mcpsentry six regex rules over a server's tool descriptions, plus a runtime proxy yes
Dredd MCP hosted check of dependency trees and drift no: the verdict comes from its server
Occludra hosted security and DLP proxy, in beta no: a hosted gateway
mcpcheck hosted trust score no: judged in the cloud
Singularity discovery gateway with trust cards over its own index no: it scans its own catalogue
contextboundary-gw self-hosted runtime policy gateway no: nothing to compare on definitions

Three of the seven send your server to someone else's machine for the verdict. That is a fine product, but it is not something you can run on a replayed server with the network off, so it is not in a table next to tools you can. Snyk Agent Scan stays out for the same reason, and last week its free tier refused every request.

One more note on mcp-shield: it has an optional model mode, --claude-api-key, and we did not run it. The model is hard-coded as claude-3-7-sonnet-20250219, which Anthropic retired in February 2026, so every call now fails silently and the scan carries on. And even with a working model, the mode only adds an "AI Risk Level" line to a tool its patterns already flagged: it changes neither a verdict nor the exit code. The numbers below are mcp-shield's numbers.

On the benchmark, the two newcomers sit with the other keyword scanners:

Blocked WARDEN mcp-audit mcp-shield truecopy mcpsentry
218 held-out MCPTox tools 171 25 41 27 8
the same tools with an <IMPORTANT> prefix, of 225 225 222 41 141 136
45 clean MCPTox servers 0 2 3 0 1

Poisoned MCPTox tools blocked, plain vs with the benchmark's <IMPORTANT> prefix: WARDEN 171/218 vs 225/225, mcp-audit 25 vs 222, truecopy 27 vs 141, mcpsentry 8 vs 136, mcp-shield 41 vs 41.

The marker row is the same lesson as last week. truecopy goes from 27 to 141 and mcpsentry from 8 to 136 the moment the attack says <IMPORTANT>. mcp-shield is the one scanner the marker doesn't help: 41 either way. A test set full of markers measures the markers.

14,933 real tool lists

Our transparency log, HISTOR, installs npm and PyPI MCP servers in a gVisor sandbox and records what each one answers to tools/list. We took every package tool list it had already observed, 14,933 of them, through its public API, and replayed them to all five scanners.

These are unlabelled. Nobody knows which are hostile, so they measure the second number: how many honest servers a scanner blocks.

Share of 14,933 npm and PyPI packages blocked: truecopy 1.7%, WARDEN 2.5%, mcp-audit 6.2%, mcpsentry 9.7%, mcp-shield 35.9%. In a 996-package sample read by hand, one block holds up (mcpsentry and mcp-shield) and WARDEN has 4 arguable.

WARDEN mcp-audit mcp-shield truecopy mcpsentry
blocked, of 14,933 369 (2.5%) 928 (6.2%) 5,354 (35.9%) 252 (1.7%) 1,449 (9.7%)
blocked in a 996-package sample we judged 23 58 339 17 112
…that hold up 0, and 4 arguable 0 1, and 3 arguable 0 1, and 1 arguable
  • What the mistakes look like. mcp-audit calls full-width Chinese punctuation, a "," or a "(", or a single Greek or Cyrillic letter a homoglyph: 51 of its 58 sample blocks. mcp-shield blocks on a word: "token", "API key", "credentials", or "..", which it reads as path traversal. mcpsentry's base64 pattern matches any 40 letters and slashes, which JSON Schema paths and lists like PNG/JPG/HEIC/TIFF produce. truecopy mostly trips on .env in environment tooling.
  • WARDEN's 23 in the sample. None deserves a block outright. Four are arguable: three tools that take a raw private key as an argument, so the key would pass through the model, and one that uploads any local file to a public image host. The rest are our rules misreading ordinary documentation, such as "you MUST first authenticate using aws_sso_login" between a server's own tools.
  • Not every scanner can read every server. mcp-shield's MCP client rejects 26 of the tool lists outright, because an inputSchema is not of type object, and then checks nothing on them.

Two packages worth reading

Most blocks on real servers are mistakes. Two tool lists, though, deserve a human look, and our rules block neither:

  • A behaviour hijack. One PyPI package wraps its description in a fake <critical_requirement … directly_user_requested="true"> block, says "What was prior defined as first is now second", and orders the model to call its tool before any reply to any input. It takes no data. It claims an authority the user never gave and reorders the model's instructions. WARDEN reports it as advisory. mcpsentry blocks it, on "you must immediately"; mcp-shield blocks it too, on a keyword, not on the hijack.
  • A hidden data flow. Another sends a file's content, a directory listing and the working directory from the agent to the server, each with "Do NOT display … to the user". For a local document index that may be tidiness. It is also exactly what you would write to move files quietly. No scanner blocks it, and WARDEN reports nothing.

Both were flagged at high by WARDEN's optional classifier, which brings us to the part rules can't do.

The classifier, again

scan can send each tool definition to a model you choose and ask whether its text directs the model. It is off by default and advisory unless you pass --classifier-blocks. Its prompt changed since last week: it now asks for a concrete violation of authority instead of flagging any imperative, tuned on a synthetic stress set, not on MCPTox.

With deepseek-flash Rules Rules + classifier at high
218 held-out MCPTox tools caught 171 218
45 clean MCPTox servers flagged 0 0
200 random real servers, blocked or flagged 2 5
996 sampled packages, blocked or flagged 23 35

Last week's prompt flagged 147 of the 218 at high; this one flags all of them and none of the clean servers. On real servers most of its high flags are workflow orders like "call this tool first". It still misses what the rules catch: an injection in a tool's name, a private key asked for as a parameter, rm -rf ~. Every classifier run in this comparison together cost about $1.70.

How it ran, and one thing we broke

Last week the scanners ran on a workstation. This time:

The run: a manifest of 16,830 input files, two images built once and copied to both servers with the same image ID, gVisor with no network, packages split by hash (4,877 and 10,056), 100 packages run on both with identical results.

  • Fixed inputs. A manifest with the SHA-256 of every one of the 16,830 input files, written before the runs.
  • Fixed tools. One image for the four other scanners, one for WARDEN, both on the same base. Every installed file matches the package published on npm or PyPI, except the install record pip writes itself. WARDEN was installed from an npm pack of the measured commit, byte for byte the 0.16.0 on npm.
  • Isolation. gVisor, no network, a read-only root, 2 GB of memory, one bind mount.
  • Two hosts, one image. The packages were split by a hash of each package's id across two of our servers that run the same image ID. 100 packages ran on both: the five scanners gave identical verdicts and finding codes on both, 100 of 100.

The servers we used are not idle. On one of them a web service hung for about forty minutes while the run was in its heaviest phase, starting eight Node processes at a time under gVisor, and again later. We paused, restarted the service, capped the run at 2 CPUs with the lowest scheduling weight, and reran the 1,193 scans that had timed out instead of answering: 383 on that server, and 810 from the other one, where mcp-audit's fixed 10-second connect window was too short next to our transparency log's own sandbox (800 mcp-audit, 10 mcp-shield). The timeouts were never counted as verdicts.

Our interest, and the limits

  • We are not neutral. We publish WARDEN under MIT and wrote the attacks and every judgment. That is why the sets, the harness, the raw verdicts, the manifest and all judgments are public.
  • One benchmark is one benchmark. MCPTox comes from three templates. A held-out half by server is not a held-out attack style.
  • The packages are a snapshot. HISTOR's crawl was running while we read it.
  • One version of each scanner. mcp-audit has since reached 0.21.0; we kept 0.18.2 to compare with last week. Each block line is our reading of that scanner's own severity scale.

Try it

npx -y @aimarket/[email protected] scan

scan reads the MCP configs of Claude Code, Claude Desktop, Cursor, VS Code and Windsurf, connects to every server they start, and vets the tool definitions before a model sees them. It has no dependencies, needs no account, and sends no tool text anywhere unless you turn on the classifier.

Links:

If you maintain one of these scanners and think we read your severity scale wrong, open an issue on alexar76/warden. We would rather fix the comparison than defend it.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.