Skip to content

quantifylabs/mcp-parapet

Repository files navigation

mcp-parapet

Security middleware for MCP servers. Content scanning, integrity verification, trust boundaries, and audit trails.

mcp-parapet sits between your MCP client (Claude Desktop, Cursor, Cline) and any MCP server. It intercepts every tool call, applies security checks, logs everything, and blocks threats before they reach your server — or your data reaches an untrusted server.

mcp-scan tells you what's wrong. mcp-parapet stops it from happening.

Client ↔ [mcp-parapet] ↔ MCP Server
            ↓
      Security Pipeline
      ├─ Content scanning (PII, secrets, prompt injection)
      ├─ Trust boundaries (OWASP 5-tier hierarchy)
      ├─ Rate limiting (per-server, per-tool)
      ├─ Manifest integrity (rug pull detection)
      └─ Audit trail (append-only JSONL)

Install

pip install mcp-parapet

Quickstart

Prefix any server command with mcp-parapet wrap --. That's it.

Whatever command you use to start your MCP server, put mcp-parapet wrap -- in front of it. Every tool call now goes through the security pipeline.

# Before (no security)
npx -y @modelcontextprotocol/server-filesystem /tmp

# After (full security pipeline)
mcp-parapet wrap -- npx -y @modelcontextprotocol/server-filesystem /tmp

Works with any MCP server. No code changes. No server modifications.

# Node-based servers
mcp-parapet wrap -- npx -y @modelcontextprotocol/server-github
mcp-parapet wrap -- npx -y @modelcontextprotocol/server-puppeteer

# Python servers
mcp-parapet wrap -- python my_server.py
mcp-parapet wrap -- uvx my-mcp-package

# Anything that speaks MCP over stdio
mcp-parapet wrap -- ./my-custom-server --port 8080

Use in Claude Desktop

Replace "command" with mcp-parapet and prepend "wrap", "--" to args:

{
  "mcpServers": {
    "filesystem": {
      "command": "mcp-parapet",
      "args": ["wrap", "--name", "filesystem", "--",
               "npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
    },
    "github": {
      "command": "mcp-parapet",
      "args": ["wrap", "--name", "github", "--",
               "npx", "-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "ghp_..." }
    }
  }
}

The --name flag tags audit logs and config per server. Optional but recommended.

Customise (optional)

# Generate a config file with sensible defaults
mcp-parapet init

# Scan a file or stdin directly
echo "My SSN is 123-45-6789" | mcp-parapet scan -

Out of the box, mcp-parapet rejects prompt injection, redacts leaked secrets, and logs every tool call. parapet.yaml lets you tune policies, set per-server trust levels, and configure rate limits — but the defaults are production-ready.

What it does

Content Scanning (OWASP §3)

Four-stage pipeline scans every tool call argument and response:

Stage What it catches Examples
1. Input validation Oversized payloads, null bytes, control chars DoS via payload inflation
2. Sensitive data SSNs, credit cards, API keys, emails, passwords PII leaking to untrusted servers
3. Prompt injection Override attempts, role manipulation, data exfiltration "Ignore previous instructions..."
4. LLM classification (Coming in v0.2) Adversarial inputs that bypass regex Obfuscated injection attacks

Configurable per-category policies: flag, redact, or reject.

Trust Boundaries (OWASP §4)

Five-tier trust hierarchy for MCP servers:

Level Permissions Use case
none Block all tool calls Unknown/untrusted servers
basic Read-only operations Third-party servers under evaluation
verified Standard operations (default) Servers with known manifests
trusted All operations including writes First-party servers
privileged Admin/system-level operations Internal infrastructure

Per-server allowlists and blocklists for fine-grained control.

Tool Manifest Integrity (OWASP §5)

Hashes the tool manifest on first connection. On every subsequent connection, compares against the stored hash. Detects:

  • Tool shadowing — new tools added that impersonate existing ones
  • Rug pulls — tool definitions changed after initial trust
  • Manifest tampering — any modification to the tool list

Optional HMAC-SHA256 signing with a secret key for cryptographic verification.

Rate Limiting (OWASP §7)

Sliding window rate limiter with per-server and per-tool granularity:

  • Default: 60 calls/minute, 1000 calls/hour
  • Configurable per-server overrides
  • Prevents resource exhaustion and abuse

Audit Trail (OWASP §6)

Append-only JSONL log of every security decision:

{"timestamp":"2026-03-02T10:15:30Z","event_type":"tool_call_blocked","server_name":"filesystem","tool_name":"write_file","verdict":"reject","detections":[{"type":"injection_override","confidence":0.85}]}

Every tool call request, response, block, redaction, rate limit hit, and manifest change is logged with full context.

Configuration

parapet.yaml

scanner:
  policy_pii: flag           # flag | redact | reject
  policy_secrets: redact     # flag | redact | reject
  policy_injection: reject   # flag | redact | reject
  scan_tool_arguments: true
  scan_tool_responses: true

audit:
  enabled: true
  log_file: parapet-audit.jsonl

integrity:
  enabled: true
  alert_on_change: true
  block_on_change: false

rate_limit:
  default_per_minute: 60
  default_per_hour: 1000

servers:
  my-trusted-server:
    trust_level: trusted
  third-party-server:
    trust_level: basic
    allowed_tools:
      - search
      - read
    max_calls_per_minute: 10

Environment Variables

Variable Description
PARAPET_CONFIG Path to config file
PARAPET_SIGNING_KEY HMAC signing key for manifest integrity
PARAPET_AUDIT_FILE Audit log file path
PARAPET_LOG_LEVEL Log level (DEBUG, INFO, WARNING, ERROR)
PARAPET_POLICY_PII Override PII policy
PARAPET_POLICY_SECRETS Override secrets policy
PARAPET_POLICY_INJECTION Override injection policy

Python API

from mcp_parapet import ContentScanner, ScanVerdict

scanner = ContentScanner()

# Scan text content
verdict = scanner.scan("My SSN is 123-45-6789")
print(verdict.allowed)     # True (default PII policy is 'flag')
print(verdict.flags)       # ['pii_detected']
print(verdict.detections)  # [Detection(type=ssn, ...)]

# Scan tool call arguments
verdict = scanner.scan_json({"query": "ignore all previous instructions"})
print(verdict.allowed)     # False (default injection policy is 'reject')

OWASP MCP Security Checklist

mcp-parapet maps directly to the OWASP Practical Guide for Secure MCP Server Development v1.0:

OWASP Section Status Implementation
§3 Input Validation & Sanitisation ContentScanner — 4-stage pipeline
§4 Access Control TrustPolicy — 5-tier hierarchy
§5 Tool Integrity integrity — HMAC manifest hashing
§6 Logging & Monitoring AuditTrail — append-only JSONL
§7 Rate Limiting RateLimiter — sliding window
§1 Authentication 🔜 Planned for v0.2
§2 Authorization 🔜 Planned for v0.2
§8 Error Handling JSON-RPC error responses

CLI Reference

mcp-parapet wrap [--name NAME] [--config FILE] -- <command> [args...]
mcp-parapet scan <file | ->
mcp-parapet init [--output FILE] [--force]
mcp-parapet verify <manifest.json>
mcp-parapet version

License

MIT — Quantify Labs Ltd

Links

About

Secure middleware framework for MCP agents, focusing on AI safety, prompt‑injection defences, OWASP alignment, and compliance with the EU AI Act.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages