Skip to main content

Aran

CI License: MIT Python 3.10+ Add to Cursor Claude Code: .mcp.json included

A transparent security proxy for Model Context Protocol (MCP) stdio servers. Wraps any existing MCP server — no server-specific integration — and gates traffic in both directions before it reaches your agent or your machine:

  • Blocks dangerous outbound tool calls — destructive commands (rm -rf, chmod 777, fork bombs, ...), before they ever reach the real server.
  • Redacts prompt-injection payloads in inbound tool results — before they reach your agent's context window.
  • Audits every gated call to a local JSONL log, so you can see what was blocked and tune false positives.

Why

Autonomous coding agents (Claude Code, Cursor, Windsurf, ...) use MCP servers to read files, run shell commands, and hit the network. If an agent ingests untrusted content — a webpage, a repo file, a database row — containing a hidden prompt injection, it can be manipulated into running destructive commands or exfiltrating secrets, using your own valid credentials. Static-analysis tools and traditional network firewalls are both blind to this: neither operates at the live protocol layer where the agent and its tools actually talk to each other.

Aran sits inline at that layer instead, as a stdio proxy your IDE launches transparently.

New to Aran? docs/guide/ is a full, beginner-friendly walkthrough — one concept per page, from "what is MCP" through installing, wiring it into your IDE, and a hands-on session that proves every gate case works, with real command output at each step. The rest of this README is the fast, condensed version of the same information.

Install

pip install aran

Not on PyPI yet — that command doesn't work today. Install from source instead:

git clone https://github.com/aranaisec-cyber/aran.git
cd aran
pip install -e .

This applies to the one-click badges and .mcp.json below too: they configure your IDE correctly, but the server will show as errored until you've run the above at least once. Once published to PyPI, this step goes away — uvx will fetch Aran automatically, the same way it already does for the fetch server these examples wrap.

Usage

Wrap the command you'd normally use to launch an MCP server:

aran -- npx -y @modelcontextprotocol/server-filesystem /path/to/project

Wiring into an MCP-speaking IDE

In your IDE's MCP server config, replace the server's command/args with aran, moving the original command after a -- separator.

Claude Code / Cursor-style JSON config:

{
  "mcpServers": {
    "filesystem": {
      "command": "aran",
      "args": ["--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"]
    }
  }
}

That's the whole integration. Aran spawns the real server as a child process and relays every message between it and your IDE, gating tool calls and tool results as they pass through — nothing else in your IDE config changes, and the downstream server needs no modification.

Every gated message (allowed or blocked) is logged to ~/.aran/audit.jsonl.

One-click / auto-config

Both badges above wrap a real, production MCP server — mcp-server-fetch, the official reference server for fetching web pages — not a toy demo. It's a deliberate choice: fetching an untrusted URL and having whatever text is on that page land in your agent's context is exactly the attack this project defends against, so wrapping it is the most honest possible demonstration of what Aran does. It needs uv installed (uvx specifically) and needs no per-user path or account setup — it works identically for every developer who clicks it.

  • Cursor: the "Add to Cursor" badge above installs aran-fetch directly — one click, no manual config.
  • Claude Code: this repo ships a working .mcp.json at its root wrapping the same server. Claude Code auto-detects project-level .mcp.json files — clone this repo, open it in Claude Code, and you'll get a one-time approval prompt for aran-fetch. There's no click-to-install deep link for Claude Code (unlike Cursor, it doesn't have one), but .mcp.json is the equivalent "ships with the repo, auto-detected" mechanism.

Once it's running, ask your agent to fetch a URL and watch ~/.aran/audit.jsonl — every fetched page's content passes through the input gate exactly like the manual tests in TESTING.md, just against the live internet instead of a canned fixture. A page containing ignore previous instructions (or anything else in src/aran/default-rules.yaml) gets redacted before your agent ever sees it.

To wrap a different server instead — your own, or another server entirely — the pattern is identical: swap the args in .mcp.json, or generate your own Cursor deep link by base64-encoding {"command":"python","args":["-m","aran.cli","--",<your command>,<your args...>]} and using it in cursor://anysphere.cursor-deeplink/mcp/install?name=<name>&config=<that base64> — or run claude mcp add <name> --scope project -- python -m aran.cli -- <your command> <your args...>, which writes the .mcp.json entry for you.

Heads up: mcp-server-fetch's own documentation notes it can reach local/internal network addresses, which is a real consideration for any fetch-capable tool regardless of Aran — worth knowing if you point it at anything beyond public URLs.

How it works

  • Outbound (tools/call requests): the tool name and arguments are checked against a list of destructive-command signatures. A match means the call is never forwarded to the real server — Aran synthesizes a JSON-RPC error response instead.
  • Inbound (tool results and other responses): every string is scanned for prompt-injection signatures. A match in agent-visible content gets replaced with a redaction notice before being relayed; a small, explicit set of protocol-machinery fields (protocolVersion, serverInfo, resource uris, etc.) is exempt from rewriting so a match there doesn't break session negotiation — see the design spec for the exact field list and reasoning.
  • Aran treats the downstream server it wraps as untrusted — the gating guarantees are designed to hold even against a malformed or actively hostile server, not just a well-behaved one. See tests/fixtures/hostile_server.py and tests/test_proxy_hostile.py for the adversarial test suite this is verified against.
  • A blocked outbound call gets a JSON-RPC error with code: -32001 and a data field giving the violation, the matched signature, and a best-effort target_node (which argument the match was found in) — useful context for an agent or developer debugging why a call was blocked. A bare -32000 with no data means Aran itself couldn't safely inspect the message (fail-closed), not a signature match.
  • Optional: scan a public GitHub repo before it's cloned/downloaded (ARAN_SCAN_GITHUB_REPOS=1). If an outbound call references a github.com repo (an HTTPS clone URL or an SSH remote, in any tool's arguments — not tied to a specific tool name), Aran fetches that repo's tarball and scans every file against destructive-command, prompt-injection, hardcoded-secret, and supply-chain (curl | bash-style install hooks) signatures before the call that would clone it is forwarded. A match blocks the call with code: -32002. This is the one feature in Aran that makes outbound network requests, so it's off by default — see Configuration below.

Configuration

Environment variables, read once at startup:

  • ARAN_MODE=audit — dry-run mode. Gate decisions still run and are still logged (as would_block instead of blocked), but nothing is actually blocked or redacted — every call and response is forwarded unmodified. Use this to tune signatures against real traffic before turning enforcement on.
  • ARAN_PROFILE=1 (or true/yes/on) — prints a timing line to stderr for every gated message: how long the outbound check took, and how many JSON leaf nodes the inbound scan visited and in how long.
  • ARAN_SCAN_GITHUB_REPOS=1 (or true/yes/on) — enables the GitHub repo scan described above. Prints a one-time startup notice, since this is the only toggle that makes Aran reach the network. If the fetch itself fails (offline, rate-limited, repo not found, ...), the call is forwarded anyway — a failed scan is logged as "error", not treated as a match; see docs/guide/08-modes-and-configuration.md for the full reasoning on why this one check fails open instead of closed.
ARAN_MODE=audit aran -- npx -y @modelcontextprotocol/server-filesystem /path
ARAN_SCAN_GITHUB_REPOS=1 aran -- npx -y @modelcontextprotocol/server-filesystem /path

See TESTING.md for worked examples of ARAN_MODE/ARAN_PROFILE.

Rule config

Signatures live in src/aran/default-rules.yaml, which ships inside the installed package, under four keys: input_gate_signatures and output_gate_signatures (the two live gates, described above) plus secret_signatures and supply_chain_signatures (used only by the optional GitHub repo scan). The latter two are optional in a hand-edited rules file — an older file that predates them loads exactly as before, falling back to a small built-in default for whichever it's missing, with no warning. Refresh the prompt-injection and destructive-command signatures from a live labeled dataset with:

python scripts/sync_threat_intel.py

On false positives: the shipped signature set is generated from a public labeled dataset and, like any pattern-based detector, can flag benign content. If something gets redacted/blocked that shouldn't be, please open an issue with the matched signature (visible in the audit log) and the offending text.

Security

This is a security tool — please report vulnerabilities responsibly. See SECURITY.md for scope and reporting instructions.

Development

git clone https://github.com/aranaisec-cyber/aran.git
cd aran
pip install -e ".[dev]"
pytest -v

See CONTRIBUTING.md for project layout and testing philosophy, and TESTING.md for a step-by-step manual verification procedure — including seeing the output/input gates block a destructive command and redact an injection payload live, not just watching pytest pass.

Status

Early (v0.1, pre-1.0). The core proxy — invocation, bidirectional gating, YAML config, audit log — is implemented and tested, including against an adversarial downstream-server test suite. Not yet covered: a multi-server gateway, enterprise/compliance features, and a hosted control plane — see the design spec for what's explicitly in and out of scope for this stage.

License

MIT

Release files for aran 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aran 0.1.0
File Size Uploaded
aran-0.1.0.tar.gz 83.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aran 0.1.0
File Interpreter ABI Platform
aran-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 131.2 kB

Release files / aran-0.1.0.tar.gz

Download URL aran-0.1.0.tar.gz
Size 83.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e550b47433d77ec9272bc3a3d30c6fe1b9002531f26b6f93f7cbc2a2d00907fa
BLAKE2b-256 checksum
How to use checksums
a23ac141287fd7929e6d93e144b16e129672dd7202a2fd791d8edaf6a625f07a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / aran-0.1.0-py3-none-any.whl

Download URL aran-0.1.0-py3-none-any.whl
Size 47.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6930943fcdcf010d4796dbb1601720e4c9321a1b1f5557cd7e47570995e868cb
BLAKE2b-256 checksum
How to use checksums
4183ad1428a40d2db7e79989792cf4cddf3cd3fcc5804e67690f08979784e075
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page