Metadata-Version: 2.4
Name: securai-mcp-scanner
Version: 0.2.0
Summary: Security scanner for Model Context Protocol (MCP) server configs and live tool surfaces. Part of the SecurAI toolkit.
Author: Ellie Bidarigh
License-Expression: MIT
Project-URL: Homepage, https://github.com/ElhamBidarigh/securAI-mcp-scanner
Project-URL: Issues, https://github.com/ElhamBidarigh/securAI-mcp-scanner/issues
Project-URL: Changelog, https://github.com/ElhamBidarigh/securAI-mcp-scanner/blob/main/CHANGELOG.md
Keywords: mcp,model-context-protocol,ai-security,llm-security,prompt-injection,sarif,security-scanner
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Environment :: Console
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: live
Requires-Dist: mcp>=1.0.0; extra == "live"
Provides-Extra: llm
Requires-Dist: anthropic>=0.40.0; extra == "llm"
Provides-Extra: all
Requires-Dist: mcp>=1.0.0; extra == "all"
Requires-Dist: anthropic>=0.40.0; extra == "all"
Dynamic: license-file

# 🔌 SecurAI MCP Scanner

[![Regression](https://github.com/ElhamBidarigh/securAI-mcp-scanner/actions/workflows/regression.yml/badge.svg)](https://github.com/ElhamBidarigh/securAI-mcp-scanner/actions/workflows/regression.yml)

🇮🇷 [راهنمای فارسی](README.fa.md)

A security scanner for **MCP (Model Context Protocol)** server configurations and live tool surfaces - the protocol that connects AI agents (Claude, Cursor, VS Code, ChatGPT plugins, etc.) to external tools.

Part of the **[SecurAI toolkit](https://github.com/ElhamBidarigh/securai)** - this is the member of the family that covers the *agent/tool* layer, complementing the others' focus on AI-generated application code.

This experimental scanner checks configuration and advertised tool metadata. It does not prove
server exploitability or inspect server source code. No false-positive reduction benchmark has
been measured for the optional semantic judge.

## What it checks

1. **Server configuration (static, no connection needed)** - command injection via shell interpreters, unpinned supply-chain dependencies (`npx -y pkg` with no version), plaintext secrets in config files, overly broad filesystem access (`--allow-all`, root paths), missing transport security on remote servers (plain HTTP, no auth headers).
2. **Tool descriptions ("tool poisoning")** - every MCP tool has a `description` field the model reads on every turn, whether or not a human ever sees it. This is a live prompt-injection surface. The scanner pattern-matches for instruction-override language, data-exfiltration instructions, tool-routing hijacks, and hidden/invisible Unicode characters used to smuggle text past a UI.
3. **Drift / "rug pull" detection** - pin a baseline of a server's current tools (name + description + input schema, hashed). Every later scan diffs against it. A tool that silently changes behavior after approval - the core trust-model gap CSA's research flagged in 2026 - shows up as a critical finding instead of going unnoticed.

Optionally:
- **`--live`** connects to the real server (stdio / SSE / streamable-HTTP) via the official `mcp` SDK and pulls its actual, current tool list, rather than trusting what's written in a config file.
- **`--llm-judge`** sends flagged (and unflagged) tool descriptions to a Claude model for a semantic second opinion, to cut down regex false positives.

## Install

```bash
git clone https://github.com/ElhamBidarigh/securAI-mcp-scanner.git
cd securAI-mcp-scanner
python3 -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate

pip install -e ".[all]"         # both --live and --llm-judge
# or:
pip install -e ".[live]"        # just live introspection
pip install -e ".[llm]"         # just the LLM judge
```

To install straight from GitHub without cloning:

```bash
pip install "git+https://github.com/ElhamBidarigh/securAI-mcp-scanner.git"
```

The core (`config_parser`, `static_checks`, `heuristics`, `baseline`, `report`) has **zero external dependencies** - it runs on stock Python 3.10+, so you can run a static scan on a client's machine without installing anything beyond Python itself.

## Quick start

```bash
python -m mcp_trust_scanner.cli scan examples/vulnerable_config.json
```

This scans an intentionally vulnerable demo config and prints a Markdown report to stdout. See [`examples/sample_report.md`](examples/sample_report.md) for a full example of the generated output.

## Usage

```bash
mcp-trust-scanner scan CONFIG.json [options]
```

| Flag | What it does |
|---|---|
| `--live` | Connect to each server and pull its real, current tool list (requires `pip install mcp`) |
| `--live-timeout SECONDS` | Timeout per server in `--live` mode (default: 20) |
| `--llm-judge` | Semantic review of tool descriptions via the Anthropic API (requires `pip install anthropic`) |
| `--api-key KEY` | Anthropic API key (or set `ANTHROPIC_API_KEY`) |
| `--baseline-dir DIR` | Where baselines are stored (default: `.mcp-trust/baselines`) |
| `--pin` | Save/update the baseline for every server scanned |
| `--no-drift-check` | Skip comparing against a saved baseline |
| `--out FILE.md` | Write the Markdown report to a file |
| `--json-out FILE.json` | Also write a machine-readable JSON report |
| `--sarif-out FILE.sarif` | Also write a SARIF 2.1.0 file for GitHub Code Scanning |
| `--client-name NAME` | Name printed on the report header |
| `--consultant-name NAME` | Your name, printed on the report header |
| `--consultant-contact C` | Your email/site, printed on the report header |
| `--fail-on SEVERITY` | Exit non-zero if any finding at/above this severity exists - useful in CI |
| `--quiet` | Suppress the console summary line |

### Typical config file locations

| Client | Config path |
|---|---|
| Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
| Claude Code | `.mcp.json` in the project root, or `~/.claude.json` |
| Cursor | `.cursor/mcp.json` |
| VS Code | `.vscode/mcp.json` |

### Example: full client assessment

```bash
python -m mcp_trust_scanner.cli scan client_config.json \
  --live --llm-judge \
  --client-name "Client Org" \
  --consultant-name "Your Name" \
  --out report.md --json-out report.json
```

### Example: CI gate against rug pulls

```bash
# first run, after manual review: approve and pin the current state
python -m mcp_trust_scanner.cli scan .mcp.json --pin

# every subsequent run (e.g. in a pre-commit hook or GitHub Action):
python -m mcp_trust_scanner.cli scan .mcp.json --fail-on high
```

This fails the build if a tool's description/schema changed since the last approved baseline, or if any new high/critical config issue shows up.

## SARIF and GitHub Code Scanning

```bash
mcp-trust-scanner scan .mcp.json --sarif-out results.sarif
```

SARIF is the standard format for static analysis results. Uploaded to GitHub, the findings appear in the repository's **Security > Code scanning** tab and as annotations on pull requests. Each rule id (`CFG-003`, `TDX-002`, ...) becomes a SARIF rule, severity maps to the alert level, and the location is the line where the server is declared in the config file.

## GitHub Action

This repository is also a reusable GitHub Action. It installs the scanner, scans a config file, uploads the SARIF file to Code Scanning, writes the report to the job summary, and fails the job when a finding reaches the threshold.

```yaml
name: MCP scan
on: [push, pull_request]

permissions:
  contents: read
  security-events: write   # needed to upload to Code Scanning

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ElhamBidarigh/securAI-mcp-scanner@v0.2.0
        with:
          config-path: .mcp.json
          fail-on: high          # critical, high, medium, low, info or none
```

| Input | Default | Meaning |
|---|---|---|
| `config-path` | required | Path to the MCP config file in your repo |
| `fail-on` | `high` | Fail the job at or above this severity, or `none` |
| `baseline-dir` | empty | Directory with pinned baselines, enables rug pull detection |
| `upload-sarif` | `true` | Upload to Code Scanning (needs `security-events: write`) |
| `sarif-out` | `mcp-scanner.sarif` | Where the SARIF file is written |

Code Scanning is free for public repositories. For private repositories it needs GitHub Code Security. Set `upload-sarif: "false"` to skip the upload and still get the job summary and the pass or fail result.

## Related work

This is a small, focused tool and not the only MCP scanner. For a more mature multi-engine scanner, look at
[Cisco AI Defense mcp-scanner](https://github.com/cisco-ai-defense/mcp-scanner), which combines YARA rules,
an LLM judge and an optional Cisco API, and offers a REST API and an SDK. Other open-source MCP scanners
exist as well, for example MCPFang and Ramparts.

What this project aims to do differently (design goals, not benchmark results):

- A dependency-free core, so a static scan runs on stock Python with nothing to install.
- Baseline pinning and diffing, to catch a server that changes its tools after you approved it.
- A client-ready Markdown report written for non-technical readers, next to the JSON output.

Other tools may offer similar features. No benchmark of detection quality against them has been run, so
compare before you choose, and combine tools where it makes sense.

## The full SecurAI toolkit

| Tool | Type | Link |
|---|---|---|
| 🛡️ Audit Tool | Self-assessment (browser) | [Live demo](https://elhambidarigh.github.io/securai/) |
| 🔬 Vuln Lab | Educational (PHP/MySQL) | [Repo](https://github.com/ElhamBidarigh/securAI-vuln-lab) |
| 🔐 JWT Analyzer | CLI (Python) | [Repo](https://github.com/ElhamBidarigh/jwt-analyzer) |
| 🔍 Active Scanner | Live URL scanner (PHP) | [Repo](https://github.com/ElhamBidarigh/securAI-active-scanner) |
| 🔌 MCP Scanner | CLI (Python) | You are here |
| 🧪 Prompt Tester | CLI (Python) | [Repo](https://github.com/ElhamBidarigh/securAI-prompt-tester) |

## Project layout

```
mcp_trust_scanner/
  config_parser.py    Parses Claude Desktop / Cursor / VS Code style MCP config files
  static_checks.py     8 config-level rules (CFG-001..CFG-008)
  heuristics.py         7 tool-description prompt-injection rules (TDX-001..TDX-007)
  live_connector.py     Live stdio/HTTP/SSE introspection via the official mcp SDK
  llm_judge.py           Optional semantic second-opinion layer via the Anthropic API
  baseline.py             Pin/diff logic for rug-pull detection
  report.py               Markdown + JSON report generation
  cli.py                   Command-line interface
examples/
  vulnerable_config.json  Intentionally vulnerable demo config
  sample_report.md        Full example report generated from it
  toy_server.py            Minimal real MCP server, used to test --live end-to-end
tests/
  test_scanner.py          Core tests
  test_regressions.py      Invalid input, incomplete scans, baseline and pagination regressions
```

## Running the tests

```bash
python -m unittest discover -s tests -v
```

## Contributing

Issues and PRs welcome - especially new detection rules (`CFG-*` for config patterns, `TDX-*` for tool-description patterns) backed by a real-world example.

## License

MIT - see [LICENSE](LICENSE).

## Author

Built by [Ellie Bidarigh](https://www.linkedin.com/in/elham-bidarigh/), a full-stack engineer moving into AI/agent security.

## Scan completion and baseline safety

Exit codes: 0 = completed with no requested severity gate hit; 1 = severity gate hit;
2 = invalid input, baseline/output error, or a requested live/LLM layer failed.
Failed live/LLM layers are not reported as completed. `--pin` does not update baselines when
requested layers fail or `--fail-on` rejects findings. Baseline comparison also detects changed
launch commands, arguments, transport or endpoint, in addition to tool definitions.

`--live` starts the configured local process or contacts the configured endpoint: inspect and trust
the configuration before using it. It is not a sandbox. `--llm-judge` sends tool metadata to Anthropic.
A score of 100 means no weighted findings were emitted; it is not proof of safety.
