Tutorial
March 12, 202614 min readBuilding Secure MCP Servers: Best Practices
A developer's guide to implementing MCP servers that resist tool abuse and privilege escalation.
Introduction
The Model Context Protocol (MCP) enables powerful AI agent capabilities, but with great power comes great responsibility. This guide covers security best practices for building MCP servers.
Principle 1: Never Trust LLM Input
The LLM is a conduit for potentially malicious user input. Every parameter passed to your tools should be validated.
ā Bad Example
tools: [{
name: "read_file",
execute: async ({ path }) => {
return fs.readFileSync(path, 'utf-8'); // Dangerous!
}
}]
ā Good Example
const ALLOWED_DIR = '/safe/directory';
tools: [{
name: "read_file",
execute: async ({ path }) => {
const resolved = path.resolve(ALLOWED_DIR, path);
if (!resolved.startsWith(ALLOWED_DIR)) {
throw new Error("Access denied");
}
return fs.readFileSync(resolved, 'utf-8');
}
}]
Principle 2: Least Privilege
Each tool should have the minimum permissions necessary.
- File tools: Restrict to specific directories
- Network tools: Allowlist domains
- System tools: Avoid shell execution entirely if possible
Principle 3: Input Validation
For File Paths
- Resolve to absolute paths
- Check against allowlisted directories
- Block path traversal sequences (
../)
For URLs
- Allowlist domains
- Block internal IPs (10.x, 172.16.x, 192.168.x, 169.254.x)
- Restrict protocols (https only)
For Commands
- Use allowlists, not blocklists
- Prefer APIs over shell execution
- Never interpolate user input into commands
Principle 4: Rate Limiting
Prevent abuse with per-tool rate limits:
const rateLimiter = new Map();
function checkRateLimit(toolName, limit = 10, window = 60000) {
const now = Date.now();
const calls = rateLimiter.get(toolName) || [];
const recent = calls.filter(t => t > now - window);
if (recent.length >= limit) {
throw new Error("Rate limit exceeded");
}
recent.push(now);
rateLimiter.set(toolName, recent);
}
Principle 5: Audit Logging
Log every tool invocation for security monitoring:
function auditLog(tool, params, result, error) {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
tool,
params: sanitize(params),
success: !error,
error: error?.message
}));
}
Testing Your MCP Server
Use Manta to scan for vulnerabilities:
$ manta scan ./my-mcp-server
š Scanning MCP server...
[!] CRITICAL: Command execution in shell_exec
[!] HIGH: Path traversal in read_file
ā Scan complete ā 2 vulnerabilities found
References
- Anthropic. (2024). MCP Specification
- OWASP. (2024). Secure Coding Practices