Skip to content

Secure MCP Servers

The Model Context Protocol (MCP) lets agents connect to data sources and tools via a standardized interface. By default, MCP servers execute any tool call they receive. ShadowAudit's MCP gateway sits between the agent and the MCP server, enforcing policy before any call reaches the server.

The authorization gap

Without ShadowAudit, an MCP server trusts its client completely:

Agent → MCP Client → MCP Server → Filesystem / Database

If the agent is compromised — via prompt injection, hallucination, or a rogue instruction — the MCP server will execute whatever it is told.

The ShadowAudit MCP gateway

Agent → MCP Client → ShadowAudit Gateway → MCP Server → Filesystem / Database
                              │
                         Policy check
                         Audit log

The gateway is a transparent stdio proxy. It intercepts JSON-RPC messages, extracts the tool call arguments, evaluates them against policy, and either forwards the message or returns a standardized error.

Setup

from shadowaudit.mcp.gateway import MCPGatewayServer
from shadowaudit.core.gate import Gate

gateway = MCPGatewayServer(
    upstream_command=["python", "-m", "mcp_server_filesystem", "/data"],
    gate=Gate(),
    policy_path="policies/mcp_policy.yaml",
    agent_id="mcp-agent"
)

gateway.run()

MCP policy example

# policies/mcp_policy.yaml

deny:
  - capability: filesystem.delete
  - capability: filesystem.write
    path_prefix: "/etc"
  - capability: shell.execute

require_approval:
  - capability: filesystem.write
    path_prefix: "/data/prod"

allow:
  - capability: filesystem.read
  - capability: filesystem.write
    path_prefix: "/data/staging"

What the gateway blocks

When a blocked call is intercepted, the gateway returns a JSON-RPC error to the client:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "AgentActionBlocked: capability=filesystem.delete decision=denied reason=destructive_operation"
  }
}

The MCP server never receives the call.

Audit trail

Every MCP tool call — allowed and blocked — is recorded in the audit log with the full JSON-RPC payload hash:

shadowaudit logs --agent mcp-agent