MCP (Model Context Protocol)

Use Rune with MCP in two ways: run Rune as an MCP server so any agent can use Rune's security tools natively, or use the MCP proxy to add security scanning to existing MCP servers.

Rune as MCP Server

Expose Rune's security tools (scan, redact, policy management, alert triage) as MCP tools that any MCP-compatible agent can call directly. Works with Claude Code, Cursor, Windsurf, and any MCP client.

Install

Terminalbash
pip install "runesec[mcp]"

Configure in Claude Code

Add to your .claude/settings.json or project MCP config:

MCP configjson
{
  "mcpServers": {
    "rune": {
      "command": "rune-mcp",
      "env": { "RUNE_API_KEY": "rune_live_xxx" }
    }
  }
}

Configure in Cursor / Windsurf

MCP configjson
{
  "mcpServers": {
    "rune": {
      "command": "rune-mcp",
      "env": { "RUNE_API_KEY": "rune_live_xxx" }
    }
  }
}

Available Tools

ToolDescriptionAPI Key?
scan_inputScan text for prompt injection, jailbreaksNo
scan_outputScan LLM output for PII and secretsNo
redactStrip secrets and PII from textNo
validate_policyValidate YAML policy syntaxNo
list_agentsList registered agents and statusYes
list_alertsList open security alertsYes
update_alertTriage an alert (resolve, false positive)Yes
list_policiesList active security policiesYes
create_policyCreate a new policy from YAMLYes

Local tools (scan, redact, validate) work without an API key. Connected tools (agents, alerts, policies) require RUNE_API_KEY.

Standalone Usage

Terminalbash
# Start the MCP server on stdio
rune-mcp

# Or run as a Python module
python -m rune.mcp_server_cli

MCP Security Proxy

The MCP proxy sits between your MCP client and upstream MCP servers, scanning all tool calls and responses passing through. Supports stdio and SSE transports.

Installation

Terminalbash
pip install runesec

The MCP integration is included in the core SDK. No extra dependencies needed.

Quick Start

proxy.pypython
from rune import Shield
from rune.integrations.mcp import ShieldMCPProxy

shield = Shield(api_key="rune_live_xxx")

proxy = ShieldMCPProxy(
    shield=shield,
    agent_id="mcp-proxy",
    agent_tags=["prod"],
)

# Add upstream MCP servers
proxy.add_server(
    "filesystem",
    command="npx @modelcontextprotocol/server-filesystem /tmp",
)
proxy.add_server(
    "github",
    command="npx @modelcontextprotocol/server-github",
)

# Start the proxy — it acts as an MCP server itself
await proxy.start()

The proxy transparently passes through tool listings and calls, adding security scanning at the boundary. Your MCP client connects to the proxy instead of the upstream servers directly.

How It Works

MCP Client (Claude, etc.)
    │
    ▼
┌─────────────────────┐
│   Rune MCP Proxy    │  ← Scans all tool calls + responses
│   (ShieldMCPProxy)  │
└─────────────────────┘
    │           │
    ▼           ▼
┌─────────┐ ┌─────────┐
│ Server1 │ │ Server2 │  ← Upstream MCP servers
│ (files) │ │ (github)│
└─────────┘ └─────────┘

MCP-Specific Threats

MCP servers grant AI agents access to external systems. This creates unique attack surfaces:

File system access abuse

Filesystem MCP servers can read arbitrary files. An injection can cause the agent to read /etc/passwd, .env, or SSH keys. Learn more

Cross-server escalation

An agent reads data from Server A (files) and uses it as input to Server B (github), creating issue with leaked credentials in the body. Learn more

Indirect injection via server responses

MCP servers that fetch web content or read files can return data containing hidden prompt injection payloads. Learn more

Policies for MCP

Restrict which MCP tools the agent can call:

policy.yamlyaml
version: "1.0"
rules:
  - name: restrict-filesystem
    type: tool_access
    allow: ["filesystem.read_file", "filesystem.list_directory"]
    deny: ["filesystem.write_file", "filesystem.delete_file"]
    match:
      agent_id: mcp-proxy
    action: block

  - name: block-sensitive-paths
    type: data_protection
    patterns: ["/etc/*", "*.env", "*.pem", "*/.ssh/*"]
    action: block

SSE Transport

For SSE-based MCP servers, pass the URL instead of a command:

sse_proxy.pypython
proxy.add_server(
    "remote-api",
    url="https://mcp.example.com/sse",
)

Configuration

Optionspython
proxy = ShieldMCPProxy(
    shield=shield,
    agent_id="mcp-proxy",            # Required: unique identifier
    agent_tags=["prod"],             # Optional: for policy targeting
    block_on_error=False,            # Optional: fail open if Rune unreachable
    scan_responses=True,             # Optional: scan server responses (default: True)
)

Complete Runnable Example

Copy, paste, and run to verify your MCP integration:

mcp_rune_test.pypython
import os, asyncio
assert os.environ.get("RUNE_API_KEY"), "Set RUNE_API_KEY"

from rune import Shield
from rune.integrations.mcp import ShieldMCPProxy

async def main():
    shield = Shield()
    proxy = ShieldMCPProxy(shield=shield, agent_id="mcp-test", agent_tags=["test"])
    proxy.add_server("filesystem", command="npx @modelcontextprotocol/server-filesystem /tmp")
    print("MCP proxy configured with filesystem server")
    print("Stats:", shield.stats)

asyncio.run(main())

Next Steps