Skip to main content
Glama
TanvirIslam-BD

permission-aware-mcp

README.md
# Permission-Aware MCP Security System

A production-grade [Model Context Protocol](https://modelcontextprotocol.io) server and client suite that demonstrates **server-enforced permission management**, **risk assessment**, and **tamper-resistant audit logging**.

It exposes four classic file-system tools — `read_file`, `write_file`, `delete_file`, `execute_command` — each classified by risk and gated by a permission policy. Privileged operations cannot run without explicit, single-use human approval.

## Why this design

The defining principle: **the server is the security boundary.** A server can never trust a client to enforce restrictions on its behalf, so every check — policy, path confinement, approval, audit — happens server-side. Clients only *surface* the server's decisions and relay an explicit human approval back.

| Control | How it works |
|---|---|
| **Sandbox confinement** | All file access passes through `safe_resolve`, which rejects absolute paths and `..` traversal and verifies (after symlink resolution) that the target stays inside `data/`. Enforced at execution — even an *approved* escape attempt is blocked. |
| **Permission policy** | Each tool maps to `allow` / `ask` / `deny`. Stored in `config/permissions.json`, hot-reloaded on change, with risk-derived fail-safe defaults for unknown tools. |
| **Risk assessment** | Every tool has an inherent risk (`low`→`critical`) from a single source of truth, shown at approval time and used to derive default policy. |
| **Human-in-the-loop approval** | An `ask` operation returns a **single-use, args-bound, expiring token** instead of executing. It runs only when a human relays the token back via `approve_operation`. The LLM never receives that tool, so it cannot approve its own calls. |
| **Audit trail** | Append-only JSONL with UTC timestamps, stored *outside* the sandbox so the file tools can't tamper with it. Every decision, approval, and outcome is recorded. |

## Architecture

```
┌──────────────────┐        stdio (MCP)        ┌────────────────────────────┐
│  Client / Host   │ ────────────────────────► │  server.py (the boundary)  │
│                  │                            │                            │
│ gui_client.py    │   call_tool ──────────►    │  ┌──────────────────────┐  │
│ host_app.py      │                            │  │ policy → risk → gate │  │
│ cli_demo.py      │   ◄─ approval_required ──   │  │   allow / ask / deny │  │
│                  │                            │  └──────────┬───────────┘  │
│ (one background  │   approve_operation ──►    │   safe_resolve (sandbox)   │
│  event loop owns │                            │   AuditLog (outside data/) │
│  the session)    │   ◄─ ok / denied / error   │   ApprovalStore (tokens)   │
└──────────────────┘                            └────────────────────────────┘
```

Tool results use one uniform JSON envelope:

```json
{"status": "ok",                "result": "..."}
{"status": "denied",            "reason": "...", "risk": "..."}
{"status": "approval_required", "token": "...", "summary": "...", "risk": "...", "expires_in": 180}
{"status": "error",             "message": "..."}
```

## Layout

```
mcp_security/
  config.py       Settings & resolved paths (env-overridable)
  paths.py        Sandbox confinement (safe_resolve)
  risk.py         Risk classification (single source of truth)
  policy.py       allow/ask/deny engine, hot-reloadable
  audit.py        Append-only JSONL audit log
  approvals.py    Single-use, args-bound, expiring tokens
  server.py       FastMCP server — the enforcement boundary
  client.py       Background-loop MCP connection (Gradio-safe)
  gui_client.py   Gradio operator console
  host_app.py     Gradio AI host (OpenAI), human-in-the-loop
  cli_demo.py     Headless end-to-end demonstration
tests/            Unit tests for the deterministic core
config/  data/  logs/   Runtime state (generated)
```

## Setup

```bash
python -m venv .venv
.venv\Scripts\activate          # Windows
# source .venv/bin/activate     # macOS / Linux
pip install -r requirements.txt
```

For the AI host, copy `.env.example` to `.env` and set `OPENAI_API_KEY`.

## Running

All commands run from the project root.

**Headless demo** (no API key needed — the fastest way to see the whole gate):
```bash
python -m mcp_security.cli_demo
```

**Operator console** (browse tools/resources/prompts, manage policy, view audit log) at http://127.0.0.1:7863:
```bash
python -m mcp_security.gui_client
```

**AI host** (chat with an LLM that uses the gated tools) at http://127.0.0.1:7864:
```bash
python -m mcp_security.host_app
```

The clients launch the server (`python -m mcp_security.server`) themselves over stdio. To point a client at a different server, pass a module path or script file:
```bash
python -m mcp_security.gui_client path/to/other_server.py
```

## Tests

```bash
python -m pytest -q
```

The suite covers the deterministic security core — path confinement (traversal/absolute/symlink-escape), policy defaults & hot-reload, token binding/single-use/expiry, audit structure, and risk classification. The MCP/Gradio/OpenAI layers are exercised by `cli_demo.py` and the included smoke checks.

## Configuration

Every setting is overridable via environment variable (see `.env.example`): sandbox/log/config directories, max file size, and approval-token TTL.

## Security notes & limits

- `execute_command` is **simulated** — it never runs a real subprocess, matching the original's stance. It is denied by default regardless.
- Approval tokens live in the server's memory; restarting the server clears pending approvals (by design — stale approvals should not survive a restart).
- The bundled clients run on `127.0.0.1` with no authentication; they are operator tools, not multi-tenant services. For shared deployment, add authentication and per-session state.
- The server's policy file (`config/permissions.json`) is the control plane; protect it with filesystem permissions in production.

TDQS

A4.2/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: read, write, delete files; execute commands; and approve operations. No overlap or ambiguity between them.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern, e.g., read_file, delete_file, approve_operation. This makes the tool surface predictable and easy to navigate.

Tool Count5/5

With 5 tools, the server is well-scoped for its purpose of providing permission-aware file and command operations. Each tool earns its place without unnecessary complexity.

Completeness4/5

Core file operations (read, write, delete) and command execution are covered. Minor gaps include the lack of a list/directory tool and no way to view pending approvals, but the essential workflow is complete.

Maintenance

ActivityInactive
ResponsivenessNo issues