mcplint
Local-first, CI-native security scanner for MCP servers. OWASP MCP Top 10 rules, a lockfile for rug-pull detection, and an AIBOM export. It never executes your MCP servers.
uvx mcplint-sec scan
PyPI distribution is
mcplint-sec(themcplintname collides with an existing project); the command it installs ismcplint.
Why
MCP went from a few hundred servers to a 10,000+ server ecosystem — and the security model did not keep up:
- ~40% of internet-exposed MCP servers have no authentication (Censys, 2026)
- A single compromised MCP server reaches a 78% attack success rate when five servers share one agent (arXiv 2601.17549)
CVE-2025-6514inmcp-remote(CVSS 9.6) affected a package with 437k+ downloads- Registries accepted typosquatted MCP servers; tool descriptions are mutable after approval ("rug pulls")
Most scanners run on your machine and hand your tool descriptions to a vendor API. mcplint scans the configs in your repo, in CI, with nothing leaving your environment.
Quickstart
# scan the current repo
uvx mcplint scan
# also scan user-level client configs and skills
uvx mcplint scan --home
# pin server fingerprints, detect drift in CI
uvx mcplint lock
uvx mcplint lock --check
# CycloneDX AIBOM of every MCP server
uvx mcplint inventory -o aibom.json
# what do the rules mean?
uvx mcplint rules list
uvx mcplint rules explain MCP004
Exit code is 1 when a finding at --fail-on severity (default high)
exists — drop it into CI as-is.
What it scans
| Input | Examples |
|---|---|
| MCP configs | .mcp.json, mcp.json, .cursor/mcp.json, .vscode/mcp.json, opencode.json[c], .codex/config.toml, ~/.codeium/windsurf/mcp_config.json, ~/.gemini/settings.json |
| Instruction / skill files | SKILL.md, AGENTS.md, CLAUDE.md, .cursorrules, .windsurfrules, .github/copilot-instructions.md, .cursor/rules/*.mdc |
Rules
| Rule | Severity | OWASP MCP Top 10 | Checks |
|---|---|---|---|
| MCP001 | critical | MCP01 Token Mismanagement | hardcoded secrets in env/args/headers |
| MCP002 | medium | MCP01 | unsafe config file permissions |
| MCP003 | medium | MCP04 Supply Chain | unpinned npx/uvx/pipx packages |
| MCP004 | high | MCP04 Supply Chain | typosquat/lookalike package names |
| MCP005 | high | MCP07 Auth | remote endpoint over plain http:// |
| MCP006 | medium | MCP07 Auth | remote endpoint with no auth material |
| MCP007 | medium | MCP02 Scope Creep | filesystem server scoped to /, $HOME, ... |
| MCP008 | high | MCP05 Command Execution | shell / command-execution servers |
| MCP009 | high | MCP03 Tool Poisoning | prompt-injection indicators in instructions |
| MCP010 | critical | MCP03 Tool Poisoning | zero-width / bidi unicode (hidden text) |
| MCP011 | medium | MCP03 Tool Poisoning | cross-config server name shadowing |
| MCP012 | high | MCP03 Tool Poisoning | lockfile drift (rug-pull detection) |
| MCP013 | high | MCP04 Supply Chain | pinned packages matching OSV advisories (--online) |
Rules are data: plain YAML in src/mcplint/rules_data/.
Bring your own with --rules-dir ./my-rules.
GitHub Actions
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: dtduc-git/mcplint@main
with:
fail-on: high
Findings show up as annotations and in the repo's code-scanning tab (SARIF).
Design principles
- Never executes your MCP servers. Scanning is static by default; running arbitrary server commands in CI is not acceptable.
- Nothing leaves your machine unless you opt in with
--online(OSV CVE lookups only). - Pin and diff.
.mcplint.lock.jsonfingerprints every server (salted hashes for env values) so post-approval changes are visible ingit diff. - Rules as data. YAML + a small, tested check engine — contributions do not need to touch the scanner core.
- Non-goals: no gateway, no proxy, no runtime traffic monitoring, no SaaS.
Development
uv sync --all-groups
uv run pytest
uv run ruff check .
uv run mcplint scan fixtures/vulnerable-repo --fail-on none
License
Apache-2.0
Release files for mcplint-sec 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcplint_sec-0.1.0.tar.gz | 56.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcplint_sec-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:94.6 kB
Release files / mcplint_sec-0.1.0.tar.gz
| Download URL | mcplint_sec-0.1.0.tar.gz |
|---|---|
| Size | 56.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f3c5c9fd4d772f347927829611e767b8e0aa1fd1e089852d6f91b8701ec739c8
|
|
BLAKE2b-256 checksum How to use checksums |
ce4cb80bdf7533e67474fc67279e9457f811603f1f4c2c5f74276c770991d6c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / mcplint_sec-0.1.0-py3-none-any.whl
| Download URL | mcplint_sec-0.1.0-py3-none-any.whl |
|---|---|
| Size | 38.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9a96e97cc4f86f2a76c84dea2e05aeffd7ced81a8f6b72579f6896abbe424675
|
|
BLAKE2b-256 checksum How to use checksums |
ed9ae537b1f086c4815d74166c5b8ca1d1eb505aed9476db4432d5b97cc1d384
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|