MCP Security Best Practices: Keeping Your AI Integrations Safe
Essential security guidelines for MCP server developers and users. Learn how to protect your data while leveraging AI-powered integrations.
Security is paramount when building MCP integrations. You're essentially giving AI applications access to your systems — that power needs to come with responsibility. Before diving into security, make sure you understand how MCP works and its architecture. Here are the essential security practices every MCP developer and user should follow.
The Security Mindset
MCP servers act as a bridge between AI and your systems. Unlike traditional APIs where users explicitly make requests, MCP tools can be invoked by AI models based on their interpretation of user requests. This creates unique security challenges.
Key Principle: Assume the AI might be convinced to do something unintended. Design your servers to be safe even when misused.
Server-Side Security
1. Validate All Inputs
Never trust input from AI applications. Always validate and sanitize:
server.tool(
"query_database",
"Query the database",
{
query: z.string()
.max(1000) // Limit length
.refine(q => !q.toLowerCase().includes('drop'), {
message: "Destructive queries not allowed"
}),
},
async ({ query }) => {
// Additional validation
if (containsSqlInjection(query)) {
throw new Error("Invalid query detected");
}
// ... execute safely
}
);
2. Implement Rate Limiting
Prevent abuse by limiting how often tools can be called:
const rateLimiter = new Map();function checkRateLimit(toolName: string, maxCalls: number, windowMs: number): boolean {
const now = Date.now();
const calls = rateLimiter.get(toolName) || [];
const recentCalls = calls.filter(t => t > now - windowMs);
if (recentCalls.length >= maxCalls) {
return false;
}
recentCalls.push(now);
rateLimiter.set(toolName, recentCalls);
return true;
}
3. Principle of Least Privilege
Only expose what's absolutely necessary:
// Bad: Expose entire filesystem
server.tool("read_file", ..., async ({ path }) => {
return fs.readFile(path); // Dangerous!
});// Good: Restrict to specific directories
const ALLOWED_DIRS = ['/home/user/projects', '/tmp/workspace'];
server.tool("read_file", ..., async ({ path }) => {
const resolved = path.resolve(path);
const isAllowed = ALLOWED_DIRS.some(dir => resolved.startsWith(dir));
if (!isAllowed) {
throw new Error("Access denied: Path not in allowed directories");
}
return fs.readFile(resolved);
});
4. Sanitize Outputs
Don't leak sensitive information in responses:
function sanitizeOutput(data: any): any {
// Remove sensitive fields
const sensitiveKeys = ['password', 'token', 'apiKey', 'secret'];
if (typeof data === 'object' && data !== null) {
return Object.fromEntries(
Object.entries(data)
.filter(([key]) => !sensitiveKeys.some(s =>
key.toLowerCase().includes(s)
))
.map(([key, value]) => [key, sanitizeOutput(value)])
);
}
return data;
}
5. Log Everything
Maintain audit trails for debugging and security analysis:
function logToolInvocation(toolName: string, args: any, result: any) {
console.error(JSON.stringify({
timestamp: new Date().toISOString(),
tool: toolName,
arguments: args,
success: !result.isError,
// Don't log full results to avoid leaking data
resultSize: JSON.stringify(result).length,
}));
}
Client-Side Security
1. Review Tool Descriptions
AI models use tool descriptions to decide when to invoke them. Malicious or poorly written descriptions could lead to unexpected behavior:
// Be specific about what tools do
server.tool(
"delete_file",
"PERMANENTLY deletes a file. Cannot be undone. Use with caution.",
// ...
);
2. User Confirmation for Sensitive Operations
For operations with significant impact, implement confirmation:
server.tool(
"send_email",
"Send an email (requires user confirmation)",
{ to: z.string(), subject: z.string(), body: z.string() },
async (args) => {
// Return preview for confirmation
return {
content: [{
type: "text",
text: Ready to send email:\nTo: ${args.to}\nSubject: ${args.subject}\n\nCall confirm_send_email to proceed.,
}],
};
}
);
3. Timeout Long Operations
Don't let tools run indefinitely:
async function withTimeout(
promise: Promise,
timeoutMs: number
): Promise {
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error("Operation timed out")), timeoutMs);
});
return Promise.race([promise, timeout]);
}server.tool("long_operation", ..., async (args) => {
return withTimeout(performOperation(args), 30000); // 30 second timeout
});
Common Vulnerabilities to Avoid
Path Traversal
// Vulnerable
const content = await fs.readFile(/data/${userInput});// Safe
const safePath = path.join('/data', path.basename(userInput));
Command Injection
// Vulnerable
exec(git log ${branch});// Safe
execFile('git', ['log', branch]);
SQL Injection
// Vulnerable
db.query(SELECT * FROM users WHERE id = ${userId});// Safe
db.query('SELECT * FROM users WHERE id = $1', [userId]);
Information Disclosure
// Vulnerable - exposes system info
catch (error) {
return { error: error.stack };
}// Safe - generic error
catch (error) {
console.error(error); // Log internally
return { error: "Operation failed" };
}
Secure Configuration
Environment Variables
Never hardcode secrets:
// Bad
const API_KEY = "sk-12345...";// Good
const API_KEY = process.env.API_KEY;
if (!API_KEY) {
throw new Error("API_KEY environment variable required");
}
Configuration Files
If your server needs a config file, validate it:
const configSchema = z.object({
allowedDirs: z.array(z.string()),
maxFileSize: z.number().max(10 1024 1024),
enableDangerousOperations: z.boolean().default(false),
});const config = configSchema.parse(JSON.parse(configFile));
Security Checklist
Before deploying an MCP server:
Conclusion
Security in MCP is not optional — it's essential. As AI applications become more capable, the potential impact of security vulnerabilities grows. By following these best practices, you can build MCP integrations that are both powerful and safe.
Remember: the goal is to enable AI to help users while preventing it from being tricked into causing harm. Design with paranoia, test thoroughly, and always err on the side of caution.
Ready to build secure MCP servers? Check out our guide to building your first server or explore real-world MCP use cases.