Skip to main content

mcp-triage

CI MCP security PyPI License: MIT Python 3.9+

An MCP security scanner. Audit an MCP server before you let an agent near it.

mcp-triage is a command-line security scanner for MCP (Model Context Protocol) servers. It scans a server for prompt injection in tool descriptions, hardcoded secrets, dangerous code execution paths, and unsafe defaults — locally, with no account and no network calls.

mcp-triage demo

Connecting an agent to a Model Context Protocol server hands that server two things at once: code execution on your machine, and a direct line to the model's context. The second one is what makes MCP different from an ordinary dependency.

A tool's description field isn't documentation for humans — it's fed straight to the LLM as instructions. A server author (or someone who compromised one) can write a description that tells your agent to do something you never asked for, and you'd never see it in a code review that only looked at the implementation. That's a new class of supply-chain risk, and there are now 20,000+ MCP servers in public directories with almost no security review between them and your agent.

mcp-triage is a fast, local, dependency-light first pass over any MCP server — one you didn't write, or one you're about to publish.


Quick start

Audit a server you're thinking about trusting:

pip install mcp-triage

git clone https://github.com/some-org/some-mcp-server
mcp-triage scan ./some-mcp-server

That's the whole workflow. No account, no config file, no network calls — see Runs entirely on your machine.

What it catches

  • 🧠 Prompt-injection-prone tool descriptions — phrasing aimed at the calling model rather than at a human reader ("ignore previous instructions", "do not tell the user"), hidden zero-width unicode, and descriptions long enough to bury instructions in. Checked in static JSON manifests and in the source-embedded string literals where real servers actually keep them.
  • 🔓 Over-broad capabilities — tools advertising shell/exec/arbitrary file access, or accepting unvalidated free-form input (additionalProperties: true).
  • 💣 Dangerous code paths in the implementation — eval, subprocess(..., shell=True), os.system, pickle.loads, unsafe yaml.load.
  • 🔑 Hardcoded secrets — API keys, AWS keys, GitHub/Slack tokens committed into source or config.
  • 🌐 Unsafe defaults — binding to 0.0.0.0, trust / skip_auth flags left on.

Full rule reference below. Every rule ID is stable — see the rule stability policy before you pin one in CI.

Use it in CI

GitHub Action — no pip install boilerplate:

- uses: YashkantG/mcp-triage@main
  with:
    path: ./my-mcp-server
    fail-on: high

SARIF → GitHub Code Scanning, so findings land in the Security tab instead of scrolling past in a build log:

- uses: YashkantG/mcp-triage@main
  with:
    path: ./my-mcp-server
    format: sarif
    upload-sarif: "true"

pre-commit:

repos:
  - repo: https://github.com/YashkantG/mcp-triage
    rev: v0.3.0
    hooks:
      - id: mcp-triage

Any other CImcp-triage scan . --fail-on high exits non-zero when it finds something at or above that severity. --format json for machine-readable output.

Show your posture

If you publish an MCP server, --format badge emits a shields.io endpoint payload you can commit and display, so the people evaluating your server can see it was checked:

mcp-triage scan . --format badge > .github/badges/mcp-security.json
[![MCP security](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/OWNER/REPO/main/.github/badges/mcp-security.json)](https://github.com/YashkantG/mcp-triage)

Grades are deliberately blunt: A clean, B/C medium findings, D/F high findings. The badge at the top of this README is this repo scanning itself, regenerated and verified on every CI run.

Tuning it

A pattern-based scanner will flag things you've already reviewed. Three levers, narrowest first:

Inline, on the offending line:

subprocess.run(cmd, shell=True)  # mcp-triage: ignore[MCP102]
subprocess.run(cmd, shell=True)  # mcp-triage: ignore        ← all rules, this line

Project config.mcptriage.toml at the scan root:

[ignore]
rules = ["MCP004"]              # repo-wide
paths = ["tests/fixtures/**"]

[severity]
MCP003 = "LOW"                  # downgrade rather than silence

[[custom_rules]]                # your own checks, no fork required
id = "CUSTOM001"
pattern = "InternalOnlyApi\\.execute"
message = "Internal-only API called from an MCP tool handler"
severity = "HIGH"

CLI, for one-offs: mcp-triage scan . --ignore-rule MCP004

This repo's own .mcptriage.toml is a worked example.

Runs entirely on your machine

mcp-triage makes zero network calls. It doesn't phone home, doesn't upload your code, and has no telemetry. Runtime dependencies are typer, rich, and tomli (Python < 3.11 only).

Releases publish through PyPI Trusted Publishing (OIDC — no long-lived tokens), so every release traces back to the GitHub Actions run that built it. If your security team needs to approve a new tool: read the source, then run it with no network access at all. It doesn't need any.

Reporting a vulnerability — in this tool, or one you found with it — see SECURITY.md.

What the MCP ecosystem actually looks like

research/ecosystem_scan.py surveys public MCP servers harvested from the curated awesome-lists. Across a seeded sample of 141 successfully scanned repositories (from a pool of 3,701):

Repositories with at least one finding 41%
Most widespread: dangerous code sinks (MCP101/102) 34 and 30 repos
Hardcoded secrets (MCP201) 13 repos
Prompt injection (MCP001) 1 repo

Read those numbers with the caveats they deserve, because they cost something to learn:

  • They are unverified automated output, not audited vulnerabilities. The first pass of this survey reported 37% and 931 HIGH findings — then inspection showed most were fake credentials in test fixtures, eval in benchmark harnesses, and pattern.exec(line) (the JavaScript RegExp API, which alone accounted for 77% of the code-execution hits). Those became precision fixes, not a blog post.
  • MCP001 firing once in 141 repos is the honest headline. Deliberate tool poisoning is an adversarial attack, and public repositories are mostly written by people acting in good faith. The rule exists for the server you didn't expect to be hostile — not because the ecosystem is full of them.
  • No repository is named here. If this tool finds something real in someone else's server, disclose it to them privately.

Reproduce it yourself: python research/ecosystem_scan.py --sample-size 150.

Rules

ID Check
MCP001 Prompt injection / tool poisoning in tool description
MCP002 Hidden/invisible unicode characters in tool description
MCP003 Suspiciously long tool description (payload smuggling risk)
MCP004 Over-broad capability exposed by tool name/description
MCP005 Tool schema accepts arbitrary/unvalidated input
MCP101 Dangerous code execution sink (eval, exec, new Function)
MCP102 Shell command built from untrusted input
MCP103 Unsafe deserialization (pickle.loads, unsafe yaml.load)
MCP201 Hardcoded secret or credential
MCP301 Server bound to all network interfaces
MCP302 Authentication / trust check disabled

Plus any [[custom_rules]] you define.

Design

Deliberately pattern/regex-based rather than a full taint-tracking analyser. That's a real tradeoff, stated plainly:

  • You get: sub-second scans, a rule set you can read end-to-end in one sitting, no compilation or language runtime per target, trivial extensibility.
  • You give up: certainty. It cannot tell you whether attacker-controlled data actually reaches a shell=True call. It is a first pass that tells you where a human should look — not a proof of safety, and it makes no soundness claim.

Anyone selling you a scanner that claims to be complete is selling you something else.

Contributing

Issues and PRs welcome, especially new rules, more language coverage (Python / JS / TS today), and real-world servers that break it. See CONTRIBUTING.md for setup and the fixture-pair pattern every rule follows, or pick up a good first issue.

False positives are treated as real bugs — report them.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_triage-0.4.0.tar.gz (30.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_triage-0.4.0-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file mcp_triage-0.4.0.tar.gz.

File metadata

  • Download URL: mcp_triage-0.4.0.tar.gz
  • Upload date:
  • Size: 30.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcp_triage-0.4.0.tar.gz
Algorithm Hash digest
SHA256 b3e92f80a4970aa2ab83cb2535d535e3f5942fc6ec10709e70c378822a18d90d
MD5 9a9029128739dbd51e0321f0ed376148
BLAKE2b-256 8647c81f2368f5d3f33171ebf7d355df869e48cef423361ed035136dc1764e49

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_triage-0.4.0.tar.gz:

Publisher: publish.yml on YashkantG/mcp-triage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_triage-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_triage-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 25.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcp_triage-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a64846e2e569f469237507cf222b5b6831f2f5e30c7be334f6c55c992bcd7402
MD5 ea3681dfcb798afff2a877b2be9d11ca
BLAKE2b-256 ccd32e36574b9f914c9833f07299f667d28caecf53712447c3e4bd26448aafaf

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_triage-0.4.0-py3-none-any.whl:

Publisher: publish.yml on YashkantG/mcp-triage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page