Metadata-Version: 2.1
Name: mcp-aegis
Version: 0.2.0
Summary: Zero-Trust Security Layer for AI Agentic Workflows via the Model Context Protocol
Author: MCP-Aegis Contributors
License: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries
Classifier: Framework :: FastAPI
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: fastapi>=0.115
Requires-Dist: uvicorn[standard]>=0.32
Requires-Dist: pydantic>=2.10
Requires-Dist: authlib>=1.4
Requires-Dist: cryptography>=44
Requires-Dist: httpx>=0.28
Requires-Dist: pyyaml>=6.0
Requires-Dist: sse-starlette>=2.1
Requires-Dist: structlog>=24.4
Provides-Extra: redis
Requires-Dist: redis>=5.2; extra == "redis"
Provides-Extra: dev
Requires-Dist: pytest>=9.0.2; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
Requires-Dist: ruff>=0.8; extra == "dev"
Requires-Dist: mypy>=1.13; extra == "dev"
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
Requires-Dist: pre-commit>=4.0; extra == "dev"
Description-Content-Type: text/markdown

# MCP-Aegis

**The Zero-Trust Security Layer for AI Agentic Workflows.**

MCP-Aegis is an open-source, high-performance security middleware library that acts as a firewall for AI agents. AI assistants like Claude and Cursor are no longer just answering questions. They now directly control your computer — reading files, running commands, querying databases — through something called MCP (Model Context Protocol).

## THE PROBLEM

That remote control has no lock on it.

Right now, if you give an AI agent access to your database, it gets FULL
access. It can read your data, delete your data, or drop entire tables.
There is no way to say "you can read, but you cannot delete."

Worse, a bad actor can trick the AI (called prompt injection) into running
dangerous commands like deleting files or sending your passwords to an
external server. The AI doesn't know it's being tricked.

And nobody is keeping a record of what the AI did, when, or why.

## THE SOLUTION

MCP-Aegis is a security guard that sits between the AI and your tools.

  AI Agent  →  Guardian (checks everything)  →  Your Tools

Before any action reaches your tools, Guardian asks four questions:

  1. Who is asking?         (identity check)
  2. Are they allowed?      (permission check)
  3. Is the request safe?   (scans for dangerous commands)
  4. Are they asking too much? (rate limiting)

If any answer is "no," the request is blocked. It never reaches your tools.


## Features

- **OAuth 2.1 / JWT Authentication** — RS256 validation with scope-based access control and RFC 8707 Resource Indicators.
- **RBAC Policy Engine** — YAML-driven policies with wildcard permissions and principal-to-tool mapping.
- **Argument Sanitization** — Blocks shell injection, path traversal, and other malicious patterns before they reach tool servers.
- **Stateful Rate Limiting** — Token Bucket algorithm with pluggable backends (in-memory, Redis).
- **Transport Agnostic** — Supports both SSE and Stdio transports via a clean abstraction.
- **CLI Security Shim** — Drop-in `mcp-aegis` command wraps any MCP server with zero code changes.
- **Structured Logging** — Full traceability with JSON-RPC request ID correlation.
- **Dependency Injection** — Swap auth providers, policy engines, and rate limiters without touching proxy code.
- **Production Hardened** — Payload size limits, concurrency caps, nesting depth guards, bounded buffers, and Redis failure resilience prevent crashes under any load.

## Installation

```bash
pip install mcp-aegis

# With Redis support for distributed rate limiting
pip install mcp-aegis[redis]
```

### From source

```bash
git clone https://github.com/kalyanganala28/MCP-guardian.git
cd MCP-guardian
pip install -e ".[dev]"
```

## Quick Start

### Option 1: CLI (Stdio Shim) — Recommended

Wrap any MCP server with Guardian in one command. No code changes needed:

```bash
mcp-aegis \
  --target "npx @modelcontextprotocol/server-filesystem /tmp" \
  --policy policies.yaml \
  --principal developer \
  --roles dev \
  --log-level INFO
```

Use it in your Claude Desktop / Cursor MCP config:

```json
{
  "mcpServers": {
    "guarded-filesystem": {
      "command": "mcp-aegis",
      "args": [
        "--target", "npx @modelcontextprotocol/server-filesystem /tmp",
        "--policy", "/path/to/policies.yaml",
        "--principal", "developer",
        "--roles", "dev"
      ]
    }
  }
}
```

### Option 2: Python Library (HTTP Proxy)

```python
from mcp_guardian import GuardianProxy, JWTAuthenticator, YAMLPolicyEngine

authenticator = JWTAuthenticator(jwks_uri="https://auth.example.com/.well-known/jwks.json")
policy_engine = YAMLPolicyEngine.from_file("policies.yaml")
proxy = GuardianProxy(authenticator=authenticator, policy_engine=policy_engine)

proxy.serve(host="0.0.0.0", port=8443)
```

### Option 3: Programmatic Stdio Bridge

```python
import asyncio
from mcp_guardian import StdioBridge, YAMLPolicyEngine, LocalAuthenticator
from mcp_guardian.sanitizer import Sanitizer

auth = LocalAuthenticator(principal="my-agent", roles=frozenset({"dev"}))
policy = YAMLPolicyEngine.from_file("policies.yaml")

bridge = StdioBridge(
    ["npx", "@modelcontextprotocol/server-filesystem", "/tmp"],
    claims=auth._claims,
    policy_engine=policy,
    sanitizer=Sanitizer(),
)
asyncio.run(bridge.run())
```

## CLI Reference

```
usage: mcp-aegis [-h] --target TARGET --policy POLICY [--mode {stdio}]
                 [--log-level {DEBUG,INFO,WARNING,ERROR}]
                 [--principal PRINCIPAL] [--roles ROLES]
                 [--rate-limit N] [--max-line-size BYTES]
                 [--max-arg-size BYTES] [--max-depth N] [--version]

Required:
  --target TARGET       Command to launch the downstream MCP server
  --policy POLICY       Path to the YAML policy file

Optional:
  --principal PRINCIPAL Local identity for policy evaluation (default: local-user)
  --roles ROLES         Comma-separated roles (e.g. 'admin,dev')
  --rate-limit N        Max tool calls per minute
  --log-level LEVEL     DEBUG, INFO, WARNING, ERROR (default: INFO)
  --mode {stdio}        Transport mode (default: stdio)
  --max-line-size BYTES Max bytes per stdio JSON-RPC line (default: 10 MB)
  --max-arg-size BYTES  Max bytes per individual argument value (default: 1 MB)
  --max-depth N         Max nesting depth for argument structures (default: 32)
```

## Policy Configuration

Define access control in `policies.yaml`:

```yaml
version: "1"
defaults:
  effect: deny    # deny-by-default (zero trust)

principals:
  admin:
    roles: [admin]
    tools:
      - pattern: "*"           # wildcard: access to everything
        actions: [read, execute]

  developer:
    roles: [dev]
    tools:
      - pattern: "db:read"     # exact match
        actions: [execute]
      - pattern: "fs:*"        # namespace wildcard
        actions: [read]
      - pattern: "git:*"
        actions: [read, execute]

  readonly:
    roles: [observer]
    tools:
      - pattern: "*"
        actions: [read]        # can read anything, execute nothing
```

## Verification

Run the built-in verification script to confirm everything works:

```bash
python verify.py
```

Expected output:

```
1. Import Check
  PASS  All modules import successfully

2. Policy Engine
  PASS  Load policies.yaml
  PASS  Admin wildcard access allowed
  PASS  Junior denied db:write

3. Argument Sanitizer
  PASS  Clean SQL passes
  PASS  Shell injection blocked
  PASS  Path traversal blocked
  PASS  Command substitution blocked

...

============================================================
ALL 30 CHECKS PASSED
MCP-Aegis v0.1.1 is fully operational.
============================================================
```

Run the test suite:

```bash
pytest                                              # quick run
pytest --cov=mcp_guardian --cov-branch              # with coverage
pytest --cov=mcp_guardian --cov-report=html         # HTML report
```

## Production Hardening

MCP-Aegis is designed to **never crash**, regardless of input size or load. Every data path is bounded:

| Protection | Default | Industry Reference | Configurable via |
|---|---|---|---|
| HTTP body size | **100 MB** | gRPC 4 MB, Django 2.5 MB | `max_http_body_bytes` / `GUARDIAN_MAX_HTTP_BODY_BYTES` |
| Stdio line size | **100 MB** | asyncio 64 KB | `max_stdio_line_bytes` / `GUARDIAN_MAX_STDIO_LINE_BYTES` |
| Individual argument | **50 MB** | — | `max_argument_bytes` / `GUARDIAN_MAX_ARGUMENT_BYTES` |
| Total arguments | **100 MB** | — | `max_arguments_total_bytes` / `GUARDIAN_MAX_ARGUMENTS_TOTAL_BYTES` |
| Nesting depth | **128 levels** | JSON parsers ~100 | `max_nesting_depth` / `GUARDIAN_MAX_NESTING_DEPTH` |
| Regex scan cap | **1 MB** | — | `sanitizer_max_scan_bytes` / `GUARDIAN_SANITIZER_MAX_SCAN_BYTES` |
| SSE stream buffer | **100 MB** | — | `max_sse_buffer_bytes` / `GUARDIAN_MAX_SSE_BUFFER_BYTES` |
| Policy file | **10 MB** | — | `max_policy_file_bytes` / `GUARDIAN_MAX_POLICY_FILE_BYTES` |
| Concurrent requests | **10,000** | Uvicorn backlog 2048 | `max_concurrent_requests` / `GUARDIAN_MAX_CONCURRENT_REQUESTS` |
| Transport timeout | **30s** | httpx 5s, K8s probe 30s | `transport_timeout` / `GUARDIAN_TRANSPORT_TIMEOUT` |
| Redis op timeout | **5s** | redis-py None (unsafe) | `redis_operation_timeout` / `GUARDIAN_REDIS_OPERATION_TIMEOUT` |
| Redis failure mode | **fail-closed** | — | `redis_fail_open` / `GUARDIAN_REDIS_FAIL_OPEN` |
| Shutdown grace | **30s** | K8s SIGTERM grace 30s | `target_shutdown_timeout` / `GUARDIAN_TARGET_SHUTDOWN_TIMEOUT` |

### Programmatic configuration

```python
from mcp_guardian import configure_limits

configure_limits(
    max_http_body_bytes=50 * 1024 * 1024,   # 50 MB
    max_concurrent_requests=5000,
    redis_fail_open=True,
)
```

### Environment variable configuration

```bash
export GUARDIAN_MAX_HTTP_BODY_BYTES=52428800
export GUARDIAN_MAX_CONCURRENT_REQUESTS=5000
export GUARDIAN_REDIS_FAIL_OPEN=true
```

## Project Structure

```
src/mcp_guardian/
├── __init__.py         # Public API
├── __main__.py         # python -m mcp_guardian
├── cli.py              # argparse CLI entry point
├── bridge.py           # Stdio message pump (security shim)
├── errors.py           # MCPGuardianError hierarchy
├── limits.py           # GuardianLimits (production safety thresholds)
├── sanitizer.py        # Argument sanitization
├── logging.py          # Structured logging setup
├── core/
│   ├── proxy.py        # GuardianProxy (HTTP mode) + middleware
│   └── transport.py    # Transport ABC + SSE/Stdio
├── auth/
│   ├── jwt.py          # JWTAuthenticator (OAuth 2.1)
│   └── local.py        # LocalAuthenticator (stdio mode)
├── policy/
│   └── engine.py       # YAMLPolicyEngine (RBAC)
└── rate_limit/
    ├── limiter.py       # Token Bucket rate limiter
    └── backends.py      # InMemory + Redis backends
```

## Development

```bash
git clone https://github.com/kalyanganala28/MCP-guardian.git
cd MCP-guardian
pdm install -G dev       # or: pip install -e ".[dev]"

pdm run pytest           # tests
pdm run mypy src/        # type checking
pdm run ruff check src/  # linting
```

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, coding standards, and PR process.

## License

[MIT](LICENSE)
