In an April 2026 audit, 27 detections from MCP security scanners boiled down to 6 genuine concerns. The scanners weren't missing attacks — they were crying wolf, which in a CI pipeline is the same failure wearing a different coat.

So I built a scanner whose first feature is its own error profile: every rule publishes a measured precision from a labelled corpus of planted positives and near-miss traps. This is how it works, what it catches — tool poisoning, rug pulls, dangerous sinks, hardcoded secrets — and where its numbers say it still gets things wrong.

One command before you install

Sentinel Scan is a security and conformance scanner for MCP servers and SKILL.md agent skills. One command scans a server or skill for tool poisoning, rug pulls, dangerous code sinks, and hardcoded secrets — and reports each rule's measured precision rather than just asserting it.

pip install sentinel-scan sentinel-scan demo 

The demo scans the bundled 12-tool corpus in about a second: 6 benign tools — two of them near-miss traps — and 6 with planted findings, producing 7 findings. The two traps produce no findings: a tool that "ignores previous cached results" and a security tool that quotes an injection as an example. That restraint is the product.

Pure Python, no API keys, no GPU, no network. Python ≥ 3.10.

What it catches

  • Tool poisoning: hidden instructions in tool descriptions — markup directives, "note to the AI" framing, ignore-instruction phrasing — plus descriptions that direct credentials, files, or conversation contents out, and covert actions a tool takes "without telling the user."
  • Rug pulls: a tool's description changing after you pinned it. Sentinel Scan stores a SHA-256 pin of the normalized description and flags quiet changes (whitespace-only edits ignored).
  • Dangerous sinks: command execution (shell=True, os.system, string commands — fixed argv with shell=False does not fire), dynamic code (eval/exec — ast.literal_eval does not fire), and sensitive-file access (~/.ssh, ~/.aws, /etc/passwd, key files, full environment dumps — a comment merely mentioning a path does not fire).
  • Hardcoded secrets: AWS key ids, GitHub tokens, OpenAI-style keys, private-key blocks, assigned credential literals (${VAR} references do not fire).
  • Skill rules: hidden instructions in a SKILL.md body, capabilities the body uses but the frontmatter does not declare, and filesystem servers scoped to /, ~, or a home directory instead of a project directory.

Measured, not asserted

Each rule is deliberately narrow: it fires on a specific, explainable signal shape. The rates are measured with scripts/measure.py over the labelled corpus in fixtures/corpus/ — 32 cases of planted positives and benign near-miss negatives for every rule. Across all rules: 18 true positives, 0 false positives, 0 false negatives.

Those are corpus rates, not a promise about the open internet. Re-run the measurement yourself:

python3 scripts/measure.py 

Using it

Scan an MCP config, a tools manifest, server sources, or skill folders:

sentinel-scan scan ~/.config/... 

Exit codes are CI-shaped: the scan exits 1 when any finding is at or above --fail-on (default high). Outputs are terminal text, JSON, and SARIF, which renders in the GitHub Security tab. There is also a GitHub Action (ranadhir19/sentinel-scan@v0.1.0) that runs the scan, writes SARIF, and uploads it to the Security tab.

What it does not do: no dynamic execution of scanned servers, no LLM judgement calls, no network calls. It is static analysis. There is also a local web demo in demo/ (React front end, FastAPI backend) if you want to paste a description, a SKILL.md body, or source and watch it scan.

Repo: https://github.com/ranadhir19/sentinel-scan