MCP Integration
ShadowAudit provides a transparent stdio-proxying gateway for any MCP server. It evaluates every tools/call request against your policy before forwarding it to the upstream server.
How it works
The gateway intercepts JSON-RPC messages on stdin, extracts params.arguments from tools/call requests, evaluates them against the gate, and either forwards the message or returns a standardized −32000 error to the client. The upstream MCP server never receives blocked calls.
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.yaml",
agent_id="mcp-agent"
)
gateway.run()
Client configuration
Run the gateway as the process your MCP client connects to. If you need a CLI wrapper for a specific desktop client, create a small Python entrypoint that constructs MCPGatewayServer with your upstream command and policy path.
from shadowaudit.mcp.gateway import MCPGatewayServer
MCPGatewayServer(
upstream_command=["python", "-m", "mcp_server_filesystem", "/data"],
policy_path="policies/mcp.yaml",
agent_id="desktop-agent",
).run()
Policy for MCP
# policies/mcp.yaml
deny:
- capability: filesystem.delete
- capability: shell.execute
require_approval:
- capability: filesystem.write
path_prefix: "/data/prod"
allow:
- capability: filesystem.read
- capability: filesystem.list
- capability: filesystem.write
path_prefix: "/data/staging"
Blocked call response
When a call is blocked, the client receives a JSON-RPC error:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "AgentActionBlocked: capability=filesystem.delete decision=denied reason=destructive_operation"
}
}
Audit trail
All MCP tool calls are logged with the full JSON-RPC payload hash:
shadowaudit logs --agent mcp-agent