Model Context Protocol (MCP) servers have become essential infrastructure for AI-powered workflows, enabling Claude and similar assistants to interact with external tools, databases, and services. However, exposing these servers in production environments introduces security considerations that many developers overlook. This guide covers practical strategies for securing your MCP servers against common threats.
Understanding the Attack Surface
MCP servers typically expose HTTP endpoints that accept JSON-RPC requests. These endpoints can read files, execute commands, query databases, or interact with third-party APIs. When deployed without proper security measures, they become attractive targets for attackers.
The primary attack vectors include unauthenticated access, command injection through poorly validated inputs, excessive permissions, and exposure of sensitive credentials. Each of these requires a different defensive approach.
Authentication and Authorization
The foundational layer of MCP server security starts with authentication. Never expose an MCP server to the public internet without authentication mechanisms.
Implementing Token-Based Authentication
from functools import wraps
from fastapi import HTTPException, Header
async def verify_token(authorization: str = Header(None)):
if not authorization:
raise HTTPException(status_code=401, detail="Missing authorization header")
scheme, _, token = authorization.partition(" ")
if scheme.lower() != "bearer":
raise HTTPException(status_code=401, detail="Invalid authentication scheme")
if not validate_token(token):
raise HTTPException(status_code=403, detail="Invalid or expired token")
return token
def require_auth(func):
@wraps(func)
async def wrapper(*args, **kwargs):
await verify_token(kwargs.get("authorization"))
return await func(*args, **kwargs)
return wrapper
This pattern ensures only authenticated clients can invoke MCP tools. Store tokens in secure vaults rather than environment variables for production deployments.
Role-Based Access Control
Different Claude skills require different permission levels. The supermemory skill might need read access to knowledge bases, while pdf processing requires file system access. Implement granular permissions:
const toolPermissions = {
'memory-search': ['read'],
'memory-write': ['read', 'write'],
'file-read': ['read'],
'file-write': ['read', 'write'],
'execute-command': ['admin']
};
function checkPermission(toolName, userRole) {
const requiredPermissions = toolPermissions[toolName] || [];
const userPermissions = rolePermissions[userRole] || [];
return requiredPermissions.every(p => userPermissions.includes(p));
}
Input Validation and Sanitization
Command injection remains one of the most critical vulnerabilities in MCP servers. Any tool that accepts user input and passes it to system commands requires rigorous validation.
Safe Parameter Handling
import shlex
import re
def sanitize_command_args(args: dict) -> dict:
"""Prevent command injection through argument sanitization"""
sanitized = {}
for key, value in args.items():
if isinstance(value, str):
# Allow only alphanumeric, dash, underscore, and spaces
if not re.match(r'^[a-zA-Z0-9_\-\s]+$', value):
raise ValueError(f"Invalid characters in argument: {key}")
sanitized[key] = value
return sanitized
def execute_safe_command(tool_name: str, args: dict):
sanitized = sanitize_command_args(args)
# Use parameterized execution instead of shell string building
cmd = [tool_name]
for key, value in sanitized.items():
cmd.extend([f"--{key}", str(value)])
return subprocess.run(cmd, capture_output=True, text=True)
The tdd skill emphasizes writing tests before implementing security controls. Apply this methodology by creating test cases for malicious inputs before deploying sanitization logic.
Network Security
TLS Encryption
Always terminate TLS at your MCP server or behind a reverse proxy. Here’s a minimal Nginx configuration:
server {
listen 443 ssl http2;
server_name mcp.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/mcp.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mcp.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Rate Limiting
Protect against denial-of-service attacks and brute-force attempts by implementing rate limiting:
from collections import defaultdict
from datetime import datetime, timedelta
class RateLimiter:
def __init__(self, max_requests: int = 100, window_seconds: int = 60):
self.max_requests = max_requests
self.window = timedelta(seconds=window_seconds)
self.requests = defaultdict(list)
def is_allowed(self, client_id: str) -> bool:
now = datetime.now()
# Clean old entries
self.requests[client_id] = [
t for t in self.requests[client_id]
if now - t < self.window
]
if len(self.requests[client_id]) >= self.max_requests:
return False
self.requests[client_id].append(now)
return True
Secrets Management
Never hardcode API keys, database credentials, or encryption keys in your MCP server codebase. The frontend-design skill and other Claude capabilities should integrate with proper secrets management:
- Use HashiCorp Vault for enterprise deployments
- AWS Secrets Manager or GCP Secret Manager for cloud-native applications
- Environment variables with runtime injection for simpler setups
import os
from functools import lru_cache
@lru_cache()
def get_secret(secret_name: str) -> str:
"""Retrieve secrets from environment or secrets manager"""
# Check environment first
value = os.environ.get(secret_name)
if value:
return value
# Fall back to secrets manager
from vault import get_vault_secret
return get_vault_secret(secret_name)
Logging and Monitoring
Comprehensive logging enables incident detection and forensic analysis. Log all authentication attempts, tool invocations, and errors:
import logging
import json
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mcp-security")
def log_mcp_request(client_id: str, tool: str, args: dict, success: bool):
event = {
"timestamp": datetime.utcnow().isoformat(),
"client_id": client_id,
"tool": tool,
"args_keys": list(args.keys()), # Never log full args with secrets
"success": success
}
logger.info(json.dumps(event))
Integrate with SIEM systems for production alerting. The supermemory skill can help maintain an audit trail of security events across your infrastructure.
Container Isolation
When deploying MCP servers in containers, apply defense-in-depth principles:
# Run as non-root user
FROM node:20-alpine
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser
# Read-only filesystem where possible
ENTRYPOINT ["node", "server.js"]
Combine with Kubernetes network policies to restrict inter-pod communication:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mcp-server-policy
spec:
podSelector:
matchLabels:
app: mcp-server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: claude-frontend
Regular Security Audits
Schedule periodic reviews of your MCP server configurations. The pdf skill can generate automated security reports, while code analysis tools should scan for vulnerabilities in tool implementations.
Key audit points include:
- Unused exposed tools that should be disabled
- Outdated dependencies with known vulnerabilities
- Overly permissive access controls
- Missing logging on sensitive operations
Summary
Securing MCP servers in production requires a layered approach: authentication at the entry point, input validation at every tool handler, network encryption in transit, secrets management throughout, and comprehensive logging for visibility. These controls work together to reduce your attack surface while maintaining the functionality that makes MCP valuable.
Apply these practices incrementally, starting with authentication and TLS, then adding rate limiting and monitoring. Regular audits ensure your security posture improves over time rather than degrading as your deployment grows.
Related Reading
- Claude Code MCP Server Setup: Complete Guide 2026
- MCP Servers vs Claude Skills: What’s the Difference?
- Claude Code Permissions Model Security Guide 2026
- Claude SuperMemory Skill: Persistent Context Explained
- Advanced Claude Skills Hub
Built by theluckystrike — More at zovo.one