Enterprise security scanner for Model Context Protocol (MCP) servers
Project description
nyuwaymcpscanner
Enterprise-grade security scanner for Model Context Protocol (MCP) servers.
Catches hardcoded secrets, tool-poisoning instructions, supply-chain attacks, shell-execution backdoors, and data-exfiltration endpoints before they reach production - entirely offline, with no data leaving your machine.
pip install nyuwaymcpscanner
nyuwaymcpscanner scan ./my-mcp-server
Why nyuwaymcpscanner?
MCP servers run with broad access to files, shells, and network sockets on behalf of AI agents. A compromised or malicious MCP server can:
- Exfiltrate secrets and conversation history to attacker-controlled webhooks
- Inject hidden instructions into tool descriptions to hijack agent behaviour ("tool poisoning")
- Execute arbitrary shell commands through unsanitized tool inputs
- Pull in typosquatted dependencies that install malware at install time
nyuwaymcpscanner gives you a repeatable, CI-friendly audit layer that catches these threats statically - no runtime required.
Quick start
# Install
pip install nyuwaymcpscanner
# Scan a local server directory
nyuwaymcpscanner scan ./my-mcp-server
# Scan a single file directly
nyuwaymcpscanner scan ./mcp.json
nyuwaymcpscanner scan ./config.py
# Scan a package from npm or PyPI
nyuwaymcpscanner scan npm:@modelcontextprotocol/server-github
nyuwaymcpscanner scan pypi:weather-mcp
# Scan a GitHub repository
nyuwaymcpscanner scan github:owner/repo
# Scan a claude_desktop_config.json (all declared servers at once)
nyuwaymcpscanner scan ~/Library/Application\ Support/Claude/claude_desktop_config.json --config
# CI: fail the build when HIGH or above findings are found
nyuwaymcpscanner scan ./server --offline --static-only --fail-on high
Sample output
Baseline Scan - ./my-mcp-server
+--------------+-------------------------------------------------------------------+
| Risk Score | 85 / 100 |
| Verdict | HIGH |
| Files scanned| 12 |
+--------------+-------------------------------------------------------------------+
Findings
+----+--------------------+----------+---------------------------------------------+
| # | Type | Severity | Evidence |
+----+--------------------+----------+---------------------------------------------+
| 1 | hardcoded_secret | HIGH | AWS_ACCESS_KEY_ID = "AKIA..." config.py:1 |
| 2 | yara_match | HIGH | os.system call in tool logic tool.py:4 |
+----+--------------------+----------+---------------------------------------------+
Scan modes
Baseline scan (default, fully offline)
The Baseline scan runs entirely on your machine. It makes zero external network calls - no telemetry, no cloud APIs, no data leaves your environment.
Three static layers run in sequence:
| Layer | What it catches |
|---|---|
| Secrets | Hardcoded AWS/GCP/Azure keys, OpenAI/Anthropic tokens, private keys, JWT secrets, generic passwords |
| YARA rules | Tool-poisoning instructions, external logging endpoints, shell execution, plaintext passwords, internal IP leakage |
| Supply chain | Typosquatted dependency names (edit-distance 1 from popular packages); CVE lookup via OSV.dev (skipped in --offline mode) |
| VirusTotal | SHA256 hash lookup for binary files against 70+ AV engines - no file upload, hash only (skipped when VIRUSTOTAL_API_KEY is not set) |
A fifth local LLM layer runs semantic analysis of MCP tool manifests using a locally-hosted Ollama model (skipped with --static-only).
# Static only - no Ollama required, suitable for CI without GPU
nyuwaymcpscanner scan ./server --static-only --offline
# With LLM analysis - requires Ollama running locally
nyuwaymcpscanner scan ./server --offline
Running with LLM analysis
One-time setup to install Ollama and pull the model (~5GB):
nyuwaymcpscanner setup
Then scan without --static-only:
# Full Baseline: static + local LLM, fully air-gapped
nyuwaymcpscanner scan ./my-mcp-server --offline
# Single manifest file with LLM analysis
nyuwaymcpscanner scan ./mcp.json --offline
The LLM layer only activates when an mcp.json manifest is present. If Ollama is not running, the scan falls back to static-only with a warning - it never fails hard.
Requirements: ~8GB RAM, Ollama installed via nyuwaymcpscanner setup.
VirusTotal malware detection
nyuwaymcpscanner can hash-check binary files (wheels, archives, executables, DLLs) against VirusTotal's 70+ AV engines. Only the SHA256 hash is sent - no file content ever leaves your machine.
Setup (free, takes 2 minutes):
- Create a free account at virustotal.com
- Go to your profile and copy your API key
- Set it once in your environment:
export VIRUSTOTAL_API_KEY=your_key_here
Then scan normally - VirusTotal runs automatically on any binary files found:
nyuwaymcpscanner scan npm:some-package
nyuwaymcpscanner scan github:owner/repo
nyuwaymcpscanner scan ./my-mcp-server
If binaries are found but no key is set, the scanner prints a reminder:
Note: 3 binary files not checked for malware - set VIRUSTOTAL_API_KEY for hash-based detection (free at virustotal.com).
You can also pass the key inline for one-off scans:
nyuwaymcpscanner scan ./server --vt-key YOUR_KEY
VirusTotal is skipped automatically when --offline is set. Free tier allows 500 lookups per day.
Scanning a single file
When the target is a single file, findings are scoped to that file only - sibling files in the same directory are ignored:
# Scan a single mcp.json
nyuwaymcpscanner scan ./mcp.json --offline --static-only
# Scan a single source file
nyuwaymcpscanner scan ./server.py --offline --static-only
# Scan a requirements file (CVE lookup runs against its dependencies)
nyuwaymcpscanner scan ./requirements.txt --static-only
| Target type | Recommended command |
|---|---|
Single mcp.json or .py |
scan ./file --offline --static-only |
| Full server directory | scan ./my-server --offline |
requirements.txt / package.json |
scan ./requirements.txt --static-only |
| Claude Desktop config | scan ./claude_desktop_config.json --config --offline --static-only |
Deep Scan (invite only)
Deep Scan is a Nyuway-hosted analysis tier that performs frontier-model semantic analysis, cross-tool exfiltration detection, and behavioral mismatch analysis. It is currently available by invite.
nyuwaymcpscanner scan ./server --deep --token YOUR_TOKEN
Join the waitlist: https://forms.gle/bH8nToK9Zh7ey5F46
CLI reference
nyuwaymcpscanner scan TARGET [OPTIONS]
TARGET
./path/to/server Local directory
./path/to/file.py Single file (findings scoped to that file only)
./mcp.json Single manifest file
github:owner/repo GitHub repository (optionally @ref)
npm:package@version npm package
pypi:package@version PyPI package
/path/to/config.json MCP host config file (requires --config)
/path/to/list.txt Newline-delimited list of targets (requires --batch)
Options
--offline Disable all outbound network calls (OSV.dev CVE lookup skipped)
--static-only Skip local LLM layer; run secrets + YARA + supply chain only
--output FORMAT Output format: summary (default), json, sarif
--fail-on LEVEL Exit non-zero when verdict >= LEVEL (low/medium/high/critical)
--config Treat TARGET as an MCP host config file; scan all declared servers
--batch Treat TARGET as a newline-delimited list of server paths
--deep Run Deep Scan (invite only; requires --token)
--token TOKEN Deep Scan invite token
--model MODEL Ollama model for local LLM layer (default: llama3)
--vt-key KEY VirusTotal API key for binary malware detection (or set VIRUSTOTAL_API_KEY)
nyuwaymcpscanner setup
Download and verify the local Ollama model for LLM-assisted scanning.
Output formats
Summary (default)
Rich terminal table with colour-coded severity, suitable for interactive use.
JSON (--output json)
Machine-readable report. Stable schema for scripting and dashboards.
{
"tool": "nyuwaymcpscanner",
"version": "0.1.0",
"target": "./server",
"scanned_at": "2025-01-15T10:30:00Z",
"risk_score": 85,
"verdict": "HIGH",
"findings": [
{
"type": "hardcoded_secret",
"severity": "high",
"weight": 30,
"file": "config.py",
"line": 1,
"pattern": "AWS Access Key ID",
"evidence": "AWS_ACCESS_KEY_ID = \"AKIA...\"",
"source": "secrets_scanner"
}
]
}
SARIF (--output sarif)
SARIF 2.1.0 format for GitHub Advanced Security, VS Code Problems panel, and any SARIF-aware CI tool.
# .github/workflows/mcp-scan.yml
- name: Scan MCP server
run: |
pip install nyuwaymcpscanner
nyuwaymcpscanner scan ./server --offline --static-only --output sarif > results.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
Scanning MCP host configs
Point nyuwaymcpscanner at your MCP host config to audit every server declared in it:
# Claude Desktop (macOS)
nyuwaymcpscanner scan \
~/Library/Application\ Support/Claude/claude_desktop_config.json \
--config --offline
# Cursor / Windsurf / VS Code
nyuwaymcpscanner scan ~/.cursor/mcp.json --config --offline
Remote SSE/HTTP endpoints are reported as skipped (deep remote scanning is roadmapped for v1.1).
Local LLM setup
The LLM layer uses Ollama running locally. Run setup once:
# Install Ollama from https://ollama.ai, then:
nyuwaymcpscanner setup
This pulls the default model (llama3) and verifies connectivity. The LLM layer only runs when an mcp.json manifest is present in the scanned tree; it is silently skipped otherwise.
Verdicts and scoring
| Verdict | Score range | Meaning |
|---|---|---|
| PASS | 0 | No findings |
| LOW | 1-24 | Informational; review but not urgent |
| MEDIUM | 25-49 | Should be fixed before production |
| HIGH | 50-79 | Block deployment |
| CRITICAL | 80-100 | Immediate action required |
Each finding carries a weight (5-35). The final score is max(weight_sum, severity_floor) capped at 100.
What nyuwaymcpscanner catches
| Finding type | Severity | Description |
|---|---|---|
hardcoded_secret |
HIGH | AWS/GCP/Azure credentials, API keys, private keys, JWT secrets |
yara_match |
CRITICAL-LOW | Tool-poisoning instructions, exfil endpoints, shell exec, passwords, internal IPs |
typosquatting_risk |
MEDIUM | Dependency name 1 edit-distance from a popular package |
dependency_cve |
HIGH | Known CVE in a pinned dependency (requires network; skipped with --offline) |
tool_poisoning |
CRITICAL | LLM-detected hidden instruction in tool description |
malware_detected |
CRITICAL-MEDIUM | Binary file flagged by VirusTotal AV engines (requires VIRUSTOTAL_API_KEY) |
How nyuwaymcpscanner compares
| Capability | nyuwaymcpscanner | Other MCP scanners |
|---|---|---|
| Hardcoded secret detection (8 types) | yes | partial |
| Shell execution patterns across 10 languages | yes (50 patterns) | Python/JS only |
| Supply chain CVE lookup (OSV.dev) | yes | partial |
| Typosquatting detection | yes | - |
| VirusTotal binary malware (hash only, no upload) | yes | partial |
| Tool poisoning / exfiltration instructions | yes | yes |
| Local LLM semantic analysis (fully offline) | yes | cloud only |
| Fully air-gapped / offline mode | yes | no |
| SARIF 2.1.0 output | yes | - |
| JSON output | yes | partial |
| Batch and host config file scanning | yes | - |
| Single-file scoped scanning | yes | - |
| Test/doc-aware false positive tuning | yes | - |
| Open, auditable detection rules (YARA) | yes | no |
| CI/CD fail-on severity gate | yes | - |
| Runtime proxy / tool pinning | roadmap | yes |
| Cloud behavioral analysis | roadmap (Deep Scan) | yes |
Runtime proxy and behavioral analysis are complementary to static scanning - they operate at a different point in the deployment lifecycle. nyuwaymcpscanner is the pre-deploy static layer; runtime tools sit alongside it, not in place of it.
Requirements
- Python 3.11+
- Ollama (optional; only needed for LLM layer)
License
Apache 2.0. See LICENSE.
Links
- Website: https://nyuway.ai
- Deep Scan waitlist: https://forms.gle/bH8nToK9Zh7ey5F46
- Issues: https://github.com/Nyuway-Cybersecurity/nyuwaymcpscanner/issues
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nyuwaymcpscanner-0.2.0.tar.gz.
File metadata
- Download URL: nyuwaymcpscanner-0.2.0.tar.gz
- Upload date:
- Size: 57.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
686c30edeff5c313be9474f1a876c4d8811c0ea76ebcf28e09648c45e82476f0
|
|
| MD5 |
4a87ba29c65cb4971dd17c95a6801999
|
|
| BLAKE2b-256 |
cd507b2a32019fa8a3341fe15b9bc76eb2ed0c5f2d2f472a53de4eb09be4d8a9
|
Provenance
The following attestation bundles were made for nyuwaymcpscanner-0.2.0.tar.gz:
Publisher:
release.yml on Nyuway-Cybersecurity/nyuwaymcpscanner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nyuwaymcpscanner-0.2.0.tar.gz -
Subject digest:
686c30edeff5c313be9474f1a876c4d8811c0ea76ebcf28e09648c45e82476f0 - Sigstore transparency entry: 1619858113
- Sigstore integration time:
-
Permalink:
Nyuway-Cybersecurity/nyuwaymcpscanner@fba4c7e4257364e0c3ffef094cc12438fe93acbe -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Nyuway-Cybersecurity
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fba4c7e4257364e0c3ffef094cc12438fe93acbe -
Trigger Event:
push
-
Statement type:
File details
Details for the file nyuwaymcpscanner-0.2.0-py3-none-any.whl.
File metadata
- Download URL: nyuwaymcpscanner-0.2.0-py3-none-any.whl
- Upload date:
- Size: 43.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b3a3add418463764ab1496f0912fdf75a385bb8dfb5afc0fa0bb998b9687580
|
|
| MD5 |
d2d5bdb5a8825bac6d52d56443c983c8
|
|
| BLAKE2b-256 |
3081b8ee3ad0b3921da2e21fbd852fc43a8585c066db51cd6759672caed01a11
|
Provenance
The following attestation bundles were made for nyuwaymcpscanner-0.2.0-py3-none-any.whl:
Publisher:
release.yml on Nyuway-Cybersecurity/nyuwaymcpscanner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nyuwaymcpscanner-0.2.0-py3-none-any.whl -
Subject digest:
0b3a3add418463764ab1496f0912fdf75a385bb8dfb5afc0fa0bb998b9687580 - Sigstore transparency entry: 1619858230
- Sigstore integration time:
-
Permalink:
Nyuway-Cybersecurity/nyuwaymcpscanner@fba4c7e4257364e0c3ffef094cc12438fe93acbe -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Nyuway-Cybersecurity
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fba4c7e4257364e0c3ffef094cc12438fe93acbe -
Trigger Event:
push
-
Statement type: