Skip to main content

A transparent MCP proxy that intercepts dangerous tool calls and requires OTP-based user approval.

Project description

๐Ÿ”ฅ MCP Action Firewall

Python 3.12+ License: MIT MCP Compatible

Works with any MCP-compatible agent

Claude Cursor Windsurf OpenAI Gemini OpenClaw

A transparent MCP proxy that intercepts dangerous tool calls and requires OTP-based human approval before execution. Acts as a circuit breaker between your AI agent and any MCP server.

How It Works

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    stdin/stdout    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    stdin/stdout    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ AI Agent โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚   MCP Action     โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ Target MCP Serverโ”‚
โ”‚ (Claude) โ”‚                    โ”‚   Firewall       โ”‚                    โ”‚ (e.g. Stripe)    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                        โ”‚
                                   Policy Engine
                                  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                  โ”‚ Allow? Block? โ”‚
                                  โ”‚ Generate OTP  โ”‚
                                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

MCP servers don't run like web servers โ€” there's no background process on a port. Instead, your AI agent (Claude, Cursor, etc.) spawns the MCP server as a subprocess and talks to it over stdin/stdout. When the chat ends, the process dies.

The firewall inserts itself into that chain:

Without firewall:
  Claude โ”€โ”€spawnsโ”€โ”€โ–บ mcp-server-stripe

With firewall:
  Claude โ”€โ”€spawnsโ”€โ”€โ–บ mcp-action-firewall โ”€โ”€spawnsโ”€โ”€โ–บ mcp-server-stripe

So you just replace the server command in your MCP client config with the firewall, and tell the firewall what the original command was:

Before (direct):

{ "command": "uvx", "args": ["mcp-server-stripe", "--api-key", "sk_test_..."] }

After (wrapped with firewall):

{ "command": "uv", "args": ["run", "mcp-action-firewall", "--target", "mcp-server-stripe --api-key sk_test_..."] }

Then the firewall applies your security policy:

  1. โœ… Safe calls (e.g. get_balance) โ†’ forwarded immediately
  2. ๐Ÿ›‘ Dangerous calls (e.g. delete_user) โ†’ blocked, OTP generated
  3. ๐Ÿ”‘ Agent asks user for the code โ†’ user replies โ†’ agent calls firewall_confirm โ†’ original action executes

Installation

pip install mcp-action-firewall
# or
uvx mcp-action-firewall --help

Quick Start โ€” MCP Client Configuration

Add the firewall as a wrapper around any MCP server in your client config:

{
  "mcpServers": {
    "stripe": {
      "command": "uv",
      "args": ["run", "mcp-action-firewall", "--target", "mcp-server-stripe --api-key sk_test_abc123"]
    }
  }
}

That's it. Everything after --target is the full shell command to launch the real MCP server โ€” including its own flags like --api-key. The firewall doesn't touch those args, it just spawns the target and sits in front of it.

More Examples

Claude Desktop with per-server rules
{
  "mcpServers": {
    "stripe": {
      "command": "uv",
      "args": [
        "run", "mcp-action-firewall",
        "--target", "uvx mcp-server-stripe --api-key sk_test_...",
        "--name", "stripe"
      ]
    },
    "database": {
      "command": "uv",
      "args": [
        "run", "mcp-action-firewall",
        "--target", "uvx mcp-server-postgres --connection-string postgresql://...",
        "--name", "database",
        "--config", "/path/to/my/firewall_config.json"
      ]
    }
  }
}
Cursor / Other MCP Clients
{
  "mcpServers": {
    "github": {
      "command": "uvx",
      "args": [
        "mcp-action-firewall",
        "--target", "npx @modelcontextprotocol/server-github"
      ]
    }
  }
}

The OTP Flow

When the agent tries to call a blocked tool, the firewall returns a structured response:

{
  "status": "PAUSED_FOR_APPROVAL",
  "message": "โš ๏ธ The action 'delete_user' is HIGH RISK and has been locked by the Action Firewall.",
  "instruction": "To unlock this action, you MUST ask the user for authorization.\n\n1. Tell the user: 'I need to perform a **delete_user** action. Please authorize by replying with code: **9942**'.\n2. STOP and wait for their reply.\n3. When they reply with '9942', call the 'firewall_confirm' tool with that code."
}

The firewall_confirm tool is automatically injected into the server's tool list:

{
  "name": "firewall_confirm",
  "description": "Call this tool ONLY when the user provides the correct 4-digit approval code to confirm a paused action.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "otp": {
        "type": "string",
        "description": "The 4-digit code provided by the user."
      }
    },
    "required": ["otp"]
  }
}

Configuration

The firewall ships with sensible defaults. Override with --config:

{
  "global": {
    "allow_prefixes": ["get_", "list_", "read_", "fetch_"],
    "block_keywords": ["delete", "update", "create", "pay", "send", "transfer", "drop", "remove", "refund"],
    "default_action": "block"
  },
  "servers": {
    "stripe": {
      "allow_prefixes": [],
      "block_keywords": ["refund", "charge"],
      "default_action": "block"
    },
    "database": {
      "allow_prefixes": ["select_"],
      "block_keywords": ["drop", "truncate", "alter"],
      "default_action": "block"
    }
  }
}

Rule evaluation order:

  1. Tool name starts with an allow prefix โ†’ ALLOW
  2. Tool name contains a block keyword โ†’ BLOCK (OTP required)
  3. No match โ†’ fallback to default_action

Per-server rules extend (not replace) the global rules. Use --name stripe to activate server-specific overrides.

CLI Reference

--target (required)

The full command to launch the real MCP server. This is the server you want to protect:

mcp-action-firewall --target "mcp-server-stripe --api-key sk_test_abc123"
mcp-action-firewall --target "npx @modelcontextprotocol/server-github"
mcp-action-firewall --target "uvx mcp-server-postgres --connection-string postgresql://localhost/mydb"

--name (optional)

Activates per-server rules from your config. Without it, only global rules apply:

mcp-action-firewall --target "mcp-server-stripe" --name stripe

--config (optional)

Custom config file path. Without it, uses firewall_config.json in your current directory, or the bundled defaults:

mcp-action-firewall --target "mcp-server-stripe" --config /path/to/my_rules.json

-v / --verbose (optional)

Turns on debug logging (written to stderr, won't interfere with MCP traffic):

mcp-action-firewall --target "mcp-server-stripe" -v

Project Structure

src/mcp_action_firewall/
โ”œโ”€โ”€ __init__.py          # Package version
โ”œโ”€โ”€ __main__.py          # python -m support
โ”œโ”€โ”€ server.py            # CLI entry point
โ”œโ”€โ”€ proxy.py             # JSON-RPC stdio proxy
โ”œโ”€โ”€ policy.py            # Allow/block rule engine
โ”œโ”€โ”€ state.py             # OTP store with TTL
โ””โ”€โ”€ default_config.json  # Bundled default rules

Try It โ€” Interactive Demo

See the firewall in action without any setup:

git clone https://github.com/starskrime/mcp-action-firewall.git
cd mcp-action-firewall
uv sync
uv run python demo.py

The demo simulates an AI agent and walks you through the full OTP flow:

  1. โœ… Safe call (get_balance) โ†’ passes through instantly
  2. ๐Ÿ›‘ Dangerous call (delete_user) โ†’ blocked, OTP generated
  3. ๐Ÿ”‘ You enter the code โ†’ action executes after approval

Development

# Install dev dependencies
uv sync

# Run tests
uv run pytest tests/ -v

# Run the firewall locally
uv run mcp-action-firewall --target "your-server-command" -v

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_action_firewall-0.2.0.tar.gz (20.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_action_firewall-0.2.0-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file mcp_action_firewall-0.2.0.tar.gz.

File metadata

  • Download URL: mcp_action_firewall-0.2.0.tar.gz
  • Upload date:
  • Size: 20.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for mcp_action_firewall-0.2.0.tar.gz
Algorithm Hash digest
SHA256 dba1644e343c8693177a90879c97e8be646691e58a0bcb157c519dfd6451fd66
MD5 1089ba61c0cde7d8ecc5496e969a2fe6
BLAKE2b-256 1f31adf35b2bb06a2c43a39552b0601855a32b53d2eaa9e40e1873155e9e705c

See more details on using hashes here.

File details

Details for the file mcp_action_firewall-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_action_firewall-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 17.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for mcp_action_firewall-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8ee21e94189117e31b4e0c14cdd24d20ad4980595eb1203c3b98260640a3afff
MD5 5f852a7dd2765b0f4def2ac01d6a3054
BLAKE2b-256 d069e487e31c3e678172e107cfab2448fa9ff00486de864ba4c1c9980e1df6f6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page