Model Context Protocol servers have quickly become the connective tissue between AI agents and the outside world — file systems, databases, APIs, internal tools, you name it. That convenience comes with a catch: the tools, prompts, and schemas an MCP server exposes are a new kind of attack surface, and most traditional scanners simply don't look there. MCPRadar is an open-source project built specifically to close that gap.
Why this matters
A recent academic study examining nearly 1,900 MCP servers found meaningful security issues in a surprising share of them — general vulnerabilities in roughly 7% and MCP-specific tool poisoning in another 5%. Tool poisoning, prompt injection hidden in tool descriptions, and quietly over-permissioned configurations are easy to miss because they don't look like a "normal" vulnerability — there's no CVE, no obvious buffer overflow, just a tool description that quietly tells an agent to do something it shouldn't.
MCPRadar's whole premise is that this class of risk deserves the same rigor as any other part of your CI pipeline.
What it actually scans
MCPRadar isn't a single-purpose linter — it looks at an MCP server from several angles:
Protocol inspection — enumerates tools, prompts, resources, and templates the server exposes, and checks server instructions for suspicious content.
Source analysis — walks Python and JavaScript/TypeScript code looking for SSRF, unsafe deserialization, command/SQL injection, Trojan Source tricks, and mismatches between a tool's description and what its code actually does.
Configuration review — flags poisoned MCP or agent configuration files, risky hooks, and overly broad permission grants.
Supply chain checks — fetches packages without running install scripts, cross-references dependencies against OSV, and can emit a CycloneDX SBOM with hashes and provenance.
Change monitoring — stores snapshots in SQLite and diffs them over time, classifying changes as cosmetic, behavioral, or security-relevant so silent drift doesn't slip past you.
CI integration — outputs JSON, SARIF, or CEF, and supports policy-as-code gates with suppressions for teams that want scanning baked into pull requests.
Getting started
You don't even need to install it to try it out:
bash
uvx mcpradar scan http://localhost:8080
For repeated use, install the CLI directly:
bash
uv tool install mcpradar
mcpradar --help
It requires Python 3.11+, and pip install mcpradar works too if you're not on uv.
A few other common workflows:
bash
Scan a local stdio server inside a disposable sandbox
mcpradar scan "python ./server.py" -t stdio --sandbox
Analyze source code without executing it
mcpradar scan-source ./path/to/server
Check a published package and its dependency tree
mcpradar deps npm:@modelcontextprotocol/server-filesystem
Diff against the previous snapshot
mcpradar diff http://localhost:8080
Emit SARIF for CI pipelines
mcpradar scan http://localhost:8080 --format sarif -o mcpradar.sarif
The security model is refreshingly paranoid — in a good way
MCPRadar treats every server it scans as untrusted input, which is exactly the right instinct for a security tool:
Stdio commands won't run on your host unless you explicitly opt in with --allow-host-exec, or better, run them in --sandbox.
Sandboxed scans happen in a disposable, non-root container with a read-only filesystem, dropped capabilities, and no host mounts.
Source scans of packages download and inspect archives without ever executing install scripts or server code.
Remote fetches are bounded by URL, redirect, size, and timeout limits, with protections against reaching into private networks.
If a container genuinely needs network access to pull a package at startup, that's an explicit, opt-in flag (--sandbox-network bridge) rather than a default.
Risk scoring that doesn't oversell itself
Findings roll up into MRS-v1 (MCPRadar Risk Score), a versioned 0–10 signal that factors in findings, confidence, server capabilities, dependency risk, and how much of the server was actually covered by the scan. Importantly, the project is upfront that this is a risk signal, not proof of exploitability — and an incomplete scan is reported as incomplete rather than quietly treated as "clean."
A public leaderboard
There's also a public leaderboard that publishes reproducible scan results — coverage, findings, and MRS grades — for popular, installable servers from the official MCP Registry, refreshed daily. You can request a server be added by opening a scan request issue; nothing gets executed just by filing the request, and everything is reviewed before it's added.
Worth knowing before you rely on it
The maintainers are honest about the limits of static, pattern-based detection:
Findings can be false positives and may need contextual review.
Attacks that only manifest at runtime might not show up in a point-in-time scan.
Obfuscated or genuinely novel techniques can evade static rules.
That kind of transparency is refreshing for a security tool, and it's a good reminder that a scanner like this complements manual review rather than replacing it.
Try it
bash
uvx mcpradar scan http://localhost:8080
If you're building or deploying MCP servers — or just plugging third-party ones into your agents — it's worth running a scan before you trust them with real credentials and real data. The project is MIT-licensed and open to contributions if you want to add detection rules or extend coverage.
Top comments (6)
Folding coverage into the same 0 to 10 score as findings and confidence is the part I'd want more detail on, because it can quietly recreate the exact problem this kind of tool exists to catch. If a low-coverage scan and a high-coverage clean scan can land on similar numbers, both have few findings, just for different reasons, the MRS number stops distinguishing verified safe from didn't look very hard, and that's the gap between an artifact and a receipt. Reporting an incomplete scan as incomplete has to mean something the number itself can't say, otherwise incomplete is a label attached to a score that already reads as reassuring on its own.
Concretely: would two servers, one fully scanned with zero findings and one scanned at 20% coverage with zero findings, get meaningfully different MRS values, or does the coverage penalty only show up in a separate field a consumer has to go looking for? If it's the latter, most people are going to read the single number the same way a CI gate reads exit code 0, and coverage becomes the same kind of denominator problem a risk score is supposed to prevent one layer up.
That's a fair challenge, probably the sharpest version of this critique you can make against a single blended score. You're right that a 0 to 10 number can't do double duty as "how risky is this" and "how much did we actually look at" without one quietly distorting the other.
To be concrete about current behavior: coverage discounts confidence rather than getting added in as its own term, so a 20%-coverage clean scan and a fully scanned clean scan won't land on the same number. But that's not really the point you're making. "Different" isn't the same as "legible." If someone reads the top-line score the way CI reads exit code 0, a muted low-coverage score can still pass as "fine," just a little lower than it would've been with full coverage. That's the false reassurance you're pointing at.
I don't think the fix is a bigger coefficient. I think it's structural. Coverage shouldn't discount the score, it should gate it. Below some threshold, MRS shouldn't render as a comparable number at all, it should show as "incomplete, N/A" and block a policy gate until coverage clears the bar. That keeps the single number meaningful for scans that actually looked hard, instead of asking one digit to answer two different questions at once.
I'll open an issue to track turning coverage into an explicit gate/label instead of a blended weight, and to document the exact discount function in the scoring model doc instead of leaving it implicit. Appreciate you pushing on this, it's the kind of thing that's easy to get subtly wrong.
This is the layer MCP needed. The server is not just a transport detail once agents can hand it credentials and local files. I like scanners here most when they produce boring evidence, not a scary score, because the next step is usually deciding which tool surface should not exist at all.
Yeah, this is exactly why I want to push the coverage-as-gate thing from the earlier thread even further. A scary score gives people permission to stop at the number. Boring evidence makes them actually read the finding, which is where the real decision sits: does this tool need filesystem write access, or did it get that by accident somewhere along the way.
Most of the interesting security work here won't come from a severity label anyway. It'll be someone reading a tool description next to a code diff and a permission grant, then deciding that surface just shouldn't exist. The scanner's job is making that comparison easy, not making the call for you.
On your stated limitation — "attacks which only manifest at runtime might not show up in a point-in-time scan" — the adjacent question is how fast a scan goes stale, and that one has a measurable answer. I've been sampling it.
I took a seeded random sample of 500 registry-listed servers that complete an anonymous handshake, hashed every tool's
inputSchema, and re-ran it against the identical servers on a schedule:The second number is the interesting one. A constant-rate model predicts 42 by day three; the real answer was 24, about 57% of linear. Twenty-one servers moved in the first 36 hours and only three more in the next 36. None reverted, and three of the original 21 moved again.
So staleness isn't uniform — it's bimodal. A small set of actively-developed servers churns constantly and a large majority is effectively frozen. Which suggests rescan cadence should be per-server rather than global: track which servers have ever moved, hammer those, and check the frozen majority rarely. A uniform re-scan interval is simultaneously too slow for the 5% and pure waste on the rest.
Directly relevant to tool poisoning specifically: the payload for that lives in the tool description, not the schema, and descriptions are a separate hash. My earlier passes only hashed
inputSchema, so I can't yet tell you how often descriptions move independently — I've started capturing them separately and it needs two snapshots before it says anything. If it turns out descriptions drift faster than schemas, that's a genuinely bad result for point-in-time scanning, because it means the injection surface moves faster than the part everyone diffs.One scope note, not a criticism: the ~1,900 figure is roughly 18% of what's currently in the official registry — I count 10,716 unique active remote endpoints. Worth knowing when the percentages get quoted onward, which they will be. I got burned repeating an unsourced ecosystem statistic recently and now measure before I quote.
Some MCP-based solutions allow agents to access databases directly. The agent can generate responses based on the data stored in relational databases, but this raises an important question: could this become a security risk in the future? I do not see sufficient security mechanisms in some of these models to control data access, permissions, and potential misuse.