MCP servers run with root-level trust. Most shouldn't.
MCP servers are everywhere now. GitHub integration, file system access, browser automation, API connectors — the ecosystem exploded overnight. But here's what nobody talks about: most of these servers can do anything they want to your system.
I learned this the hard way when a "harmless" file browser MCP server started making network requests. Then I found another one that could execute arbitrary shell commands through a "file preview" feature. The MCP protocol doesn't enforce boundaries — it trusts servers completely.
Here's the security model that actually works:
Assumption: Every MCP server is potentially malicious. Even the ones you trust.
The fix is a security sandbox that wraps every MCP server call. Here's the pattern:
# mcp_sandbox.py
import subprocess
import tempfile
import os
from pathlib import Path
class MCPSandbox:
def __init__(self, allowed_paths=None, network_allowed=False):
self.allowed_paths = allowed_paths or []
self.network_allowed = network_allowed
self.temp_dir = tempfile.mkdtemp(prefix="mcp_sandbox_")
def execute_server(self, server_path, args):
# Create isolated environment
env = os.environ.copy()
env['HOME'] = self.temp_dir
env['TMPDIR'] = self.temp_dir
# Network isolation
if not self.network_allowed:
# Use unshare or docker for network isolation
cmd = ['unshare', '--net', server_path] + args
else:
cmd = [server_path] + args
# File system restrictions via chroot/jail
return subprocess.run(cmd, env=env, cwd=self.temp_dir)But subprocess sandboxing is complex. The simpler approach is permission-based filtering:
# mcp_filter.py
class MCPSecurityFilter:
def __init__(self):
self.allowed_operations = {
'files': ['read'], # No write, no execute
'network': [], # No network by default
'system': [] # No system calls
}
def validate_request(self, tool_name, args):
if tool_name.startswith('file_'):
if 'write' in tool_name or 'delete' in tool_name:
return False, "Write operations not allowed"
if tool_name.startswith('shell_') or tool_name.startswith('exec_'):
return False, "Execution not allowed"
if 'network' in args or 'http' in str(args):
return False, "Network access not allowed"
return True, "OK"The key insight: least privilege by default. Every MCP server starts with zero permissions. You explicitly grant what it needs:
# claude_desktop_config.json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["@modelcontextprotocol/server-filesystem", "/safe/path"],
"permissions": {
"file_read": true,
"file_write": false,
"network": false,
"system": false
}
}
}
}I run a permission audit every week now. Three questions for every MCP server:
- What's the minimum it needs to function? File read vs write vs execute
- Does it need network access? Most don't, despite claiming they do
- Can it escalate privileges? Shell access, system calls, environment variables
The MCP ecosystem moves fast. Security practices don't. Sandbox first, trust later.
I caught two "legitimate" MCP servers trying to write to my home directory last month. The permission model saved me from debugging a corrupted config for hours.
Your agent needs access to be useful. But it needs boundaries to be safe. The MCP security sandbox gives you both.