Metadata-Version: 2.4
Name: mcpguard
Version: 0.1.0
Summary: Security scanner and runtime protection for MCP (Model Context Protocol) servers
Author-email: Aryan Sharma <aryanjp032001@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/aryanjp1/mcpguard
Project-URL: Repository, https://github.com/aryanjp1/mcpguard
Project-URL: Issues, https://github.com/aryanjp1/mcpguard/issues
Project-URL: Changelog, https://github.com/aryanjp1/mcpguard/blob/main/CHANGELOG.md
Keywords: mcp,security,scanner,model-context-protocol,ai-safety,llm-security
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Testing
Classifier: Environment :: Console
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click>=8.0
Requires-Dist: rich>=13.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Dynamic: license-file

# MCPGuard

[![PyPI version](https://img.shields.io/pypi/v/mcpguard.svg)](https://pypi.org/project/mcpguard/)
[![Python versions](https://img.shields.io/pypi/pyversions/mcpguard.svg)](https://pypi.org/project/mcpguard/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![CI](https://github.com/aryanjp1/mcpguard/actions/workflows/ci.yml/badge.svg)](https://github.com/aryanjp1/mcpguard/actions/workflows/ci.yml)

**Security scanner and runtime protection for MCP servers.**

MCP is becoming the TCP/IP of AI agents. MCPGuard makes sure it doesn't become the next Log4j.

MCPGuard scans your MCP server configurations for the vulnerabilities in the [OWASP MCP Top 10](https://owasp.org/www-project-mcp-top-10/) and provides runtime middleware to enforce security policies on every tool call. It works without a running server — pure static analysis of your config files.

---

## Install

```bash
pip install mcpguard
```

## Quick Start

```bash
# Scan your Claude Desktop or Cursor MCP config (auto-detected)
mcpguard scan

# Scan a specific config
mcpguard scan --config ./claude_desktop_config.json

# Get a JSON report
mcpguard scan --format json --output report.json
```

Programmatic usage:

```python
from mcpshield import scan

result = scan()
print(f"Score: {result.score}/100  ({result.rating})")
for finding in result.sorted_findings():
    print(f"[{finding.severity.value}] {finding.rule_id}: {finding.title}")
```

---

## What It Detects

MCPGuard covers all 10 categories of the OWASP MCP Top 10:

| Rule      | Severity | OWASP | Description                                        |
|-----------|----------|-------|----------------------------------------------------|
| MCPS-001  | CRITICAL | MCP01 | Hardcoded API keys and secrets in env vars         |
| MCPS-002  | HIGH     | MCP03 | Filesystem server with access to `/` or `~/.ssh`  |
| MCPS-003  | HIGH     | MCP08 | `npx -y` with unverified or unpinned packages      |
| MCPS-004  | CRITICAL | MCP04 | Shell invocation enabling command injection        |
| MCPS-005  | HIGH     | MCP05 | HTTP/SSE server without authentication or TLS      |
| MCPS-006  | HIGH     | MCP03 | Wildcard tool permissions (`files:*`, `admin:*`)   |
| MCPS-007  | MEDIUM   | MCP02 | Tool descriptions with injection keywords or hidden Unicode |
| MCPS-008  | MEDIUM   | MCP10 | URL parameters without domain allowlisting (SSRF)  |
| MCPS-009  | LOW      | MCP09 | Missing audit logging configuration                |
| MCPS-010  | HIGH     | MCP06 | Shared session memory without user isolation       |
| MCPS-011  | MEDIUM   | MCP05 | stdio server running without sandboxing            |
| MCPS-012  | CRITICAL | MCP08 | Known CVE — mcp-remote, MCP Inspector, filesystem  |

---

## CLI

```bash
# Scan auto-detected config
mcpguard scan

# Scan specific file
mcpguard scan --config ./mcp-config.json

# Output formats
mcpguard scan --format json --output report.json
mcpguard scan --format sarif --output report.sarif   # GitHub Security tab
mcpguard scan --format html --output report.html     # Shareable HTML

# Fail CI on critical/high findings
mcpguard scan --fail-on critical
mcpguard scan --fail-on high

# Skip specific rules
mcpguard scan --skip-rules MCPS-009,MCPS-011

# Check a package against the vulnerability database
mcpguard check-package mcp-remote
mcpguard check-package mcp-remote@0.1.7

# Generate a starter policy file
mcpguard init-policy --output policy.yaml

# Validate an existing policy
mcpguard validate-policy policy.yaml
```

---

## Runtime Guard

Protect your MCP server's tool handlers at runtime with policy enforcement:

```python
from mcpshield.runtime import MCPGuard, Policy

# Load security policy
policy = Policy.from_yaml("policy.yaml")

# Create guard
guard = MCPGuard(policy=policy)

# Protect tool handlers
@guard.protect
async def handle_tool_call(tool_name: str, arguments: dict) -> dict:
    # Blocked tools raise PolicyViolationError before reaching here
    # Path and domain restrictions are enforced automatically
    # Rate limits are checked per-tool
    ...
```

### Policy File

```yaml
# policy.yaml
version: "1.0"
rules:
  blocked_tools:
    - "execute_command"
    - "run_shell"

  tool_restrictions:
    filesystem_read:
      allowed_paths:
        - "/data/**"
        - "/public/**"
      blocked_paths:
        - "~/.ssh/**"
        - "~/.aws/**"
    http_request:
      allowed_domains:
        - "api.github.com"
      blocked_domains:
        - "169.254.169.254"  # AWS metadata
        - "localhost"

  rate_limits:
    default: 100/minute
    per_tool:
      database_query: 20/minute

  approval_required:
    - "delete_file"
    - "send_email"

  audit:
    enabled: true
    log_file: "mcpguard_audit.jsonl"
    log_level: "all"
```

---

## CI/CD Integration

Add to `.github/workflows/security.yml`:

```yaml
name: MCP Security Scan

on: [push, pull_request]

jobs:
  mcpguard:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: pip install mcpguard
      - name: Scan MCP config
        run: mcpguard scan --config ./claude_desktop_config.json --fail-on high
      - name: Generate SARIF report
        run: mcpguard scan --config ./claude_desktop_config.json --format sarif --output mcpguard.sarif
      - name: Upload to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: mcpguard.sarif
```

---

## Writing Custom Rules

Extend `BaseRule` to add your own checks:

```python
from mcpshield.scanner.rules.base import BaseRule
from mcpshield.scanner.finding import Finding, ServerConfig
from mcpshield.utils.severity import Severity

class MyCustomRule(BaseRule):
    rule_id = "CUSTOM-001"
    title = "My Security Check"
    owasp_mapping = "MCP01"

    def check(self, server: ServerConfig) -> list[Finding]:
        findings = []
        if "dangerous_pattern" in str(server.raw):
            findings.append(Finding(
                rule_id=self.rule_id,
                title=self.title,
                description="Dangerous pattern detected",
                severity=Severity.HIGH,
                owasp_mcp=self.owasp_mapping,
                server_name=server.name,
                location="config",
                remediation="Remove the dangerous pattern.",
                references=["https://owasp.org/www-project-mcp-top-10/"],
            ))
        return findings

# Use with the engine
from mcpshield.scanner.engine import ScanEngine
from mcpshield.scanner.rules import DEFAULT_RULES

engine = ScanEngine(rules=DEFAULT_RULES + [MyCustomRule])
```

---

## Security Score

MCPGuard computes a score from 0-100:

| Score   | Rating    |
|---------|-----------|
| 90-100  | EXCELLENT |
| 70-89   | GOOD      |
| 50-69   | FAIR      |
| 25-49   | POOR      |
| 0-24    | CRITICAL  |

Each CRITICAL finding deducts 25 points, HIGH deducts 15, MEDIUM deducts 8, LOW deducts 3.

---

## References

- [OWASP MCP Top 10](https://owasp.org/www-project-mcp-top-10/)
- [MCP Security Best Practices](https://modelcontextprotocol.io/specification/draft/basic/security_best_practices)
- [CVE-2025-6514 — mcp-remote command injection](https://nvd.nist.gov/vuln/detail/CVE-2025-6514)
- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification)

---

## Contributing

Issues and pull requests welcome at [github.com/aryanjp1/mcpguard](https://github.com/aryanjp1/mcpguard).

To run the test suite:

```bash
pip install -e ".[dev]"
pytest --cov
```

To add a new rule, create a file in `src/mcpshield/scanner/rules/`, extend `BaseRule`, and add it to `DEFAULT_RULES` in `src/mcpshield/scanner/rules/__init__.py`.

---

## License

MIT — see [LICENSE](LICENSE).
