Skip to main content

Security in MCP

Security fundamentals

This guide explains the security features of the Model Context Protocol:

  • Security model - Understand the core security principles that govern MCP
  • Authentication options - Learn how to implement secure authentication for your MCP server
  • Data protection - Discover techniques for keeping sensitive information secure
  • Best practices - Explore proven approaches to building secure AI applications with MCP

Security Model

Core Security Principles

MCP's security model is designed around several key principles:

  • Principle of Least Privilege - MCP servers and clients should only have access to the resources they need
  • Defense in Depth - Multiple security layers protect against different types of threats
  • Zero Trust - Every access request is verified regardless of source
  • Secure by Default - Default configurations prioritize security over convenience

Authentication and Authorization

Authentication Options

MCP supports multiple authentication methods to fit different security requirements:

  • OAuth 2.0 - Standard protocol for secure delegated access
  • Dynamic Client Registration - Automatic client registration with OAuth servers
  • API Key Authentication - Simple authentication for server-to-server communication

OAuth 2.0 Integration

MCP uses the OAuth 2.0 protocol for authentication and authorization, which enables:

  • Secure access to user data without sharing credentials
  • Fine-grained permission control through scopes
  • Token-based authentication with limited lifetimes
  • Standardized flows for different application types

Example OAuth 2.0 flow in MCP:

// MCP Client with OAuth configuration
const client = new MCPClient({
serverUrl: 'https://example-mcp-server.com',
auth: {
type: 'oauth2',
clientId: 'your-client-id',
scopes: ['tools:read', 'tools:execute']
}
});

// Initiating the OAuth flow
const authUrl = await client.getAuthorizationUrl({
redirectUri: 'https://your-app.com/callback'
});

// Redirect user to authUrl

// After user authorizes and is redirected back:
await client.handleCallback(callbackUrlWithCode);

// Now the client is authenticated and can access protected resources

Dynamic Client Registration

MCP supports OAuth 2.0 Dynamic Client Registration, allowing clients to automatically register with OAuth servers:

// Dynamically register a client
const registration = await client.registerClient({
clientName: 'My MCP Client',
redirectUris: ['https://my-app.com/callback'],
logoUri: 'https://my-app.com/logo.png'
});

// Use the registered client ID and secret
console.log(registration.clientId);
console.log(registration.clientSecret);

API Key Authentication

For simpler scenarios or server-to-server communication, MCP supports API key authentication:

// MCP Client with API key authentication
const client = new MCPClient({
serverUrl: 'https://example-mcp-server.com',
auth: {
type: 'api_key',
apiKey: 'your-api-key'
}
});

Data Protection

Data Security Guide

Follow these practices to protect sensitive data in your MCP implementation:

  1. Use encryption - Implement proper encryption for data in transit and at rest
  2. Minimize data transfer - Only send what's necessary for the task
  3. Set data expiration - Implement automatic cleanup of unused data
  4. Validate all inputs - Prevent injection attacks through thorough validation

Encryption

MCP implements multiple layers of encryption:

  1. Transport Layer Security (TLS): All HTTP and WebSocket communications use TLS for encryption in transit.
  2. Token Encryption: Authentication tokens can be encrypted at rest.
  3. Payload Encryption: Sensitive data within MCP payloads can be encrypted.

Data Minimization

MCP encourages data minimization practices:

  • Only transmit necessary data
  • Limit context window size
  • Implement automatic data expiration
  • Use resource references instead of raw data when possible

Example of data minimization:

// Instead of sending the entire document
const badExample = await client.request({
method: 'tools/call',
params: {
name: 'analyze_document',
arguments: {
document: veryLargeDocument // Bad practice: sending entire document
}
}
});

// Send a reference to the document
const goodExample = await client.request({
method: 'tools/call',
params: {
name: 'analyze_document',
arguments: {
documentId: 'doc-123' // Good practice: sending just a reference
}
}
});

Tool Security

Tool Security Features

MCP provides several features to ensure tool safety:

  • Permission Models - Control which users or clients can access each tool
  • Input Validation - Verify that all parameters meet security requirements
  • Execution Sandboxing - Isolate tool execution from the rest of the system
  • Rate Limiting - Prevent abuse through usage limits

Tool Permission Models

MCP implements permission models for tools to control access:

  • Public Tools: Available to all authenticated clients
  • Protected Tools: Require specific OAuth scopes
  • Private Tools: Limited to specific clients or users

Example tool definition with permissions:

const sensitiveDataTool = {
name: 'access_sensitive_data',
description: 'Access sensitive user data',
permissions: {
requiredScopes: ['data:sensitive:read'],
requiredRoles: ['admin', 'data-analyst']
},
// ... tool implementation
};

Input Validation

To prevent injection attacks and other security issues, MCP emphasizes thorough input validation:

// Tool with JSON Schema validation
const secureCalculatorTool = {
name: 'calculator',
description: 'Perform calculations',
parameters: {
type: 'object',
properties: {
operation: {
type: 'string',
enum: ['add', 'subtract', 'multiply', 'divide'] // Restricted to known operations
},
a: {
type: 'number',
minimum: -1000, // Set reasonable bounds
maximum: 1000
},
b: {
type: 'number',
minimum: -1000,
maximum: 1000
}
},
required: ['operation', 'a', 'b'],
additionalProperties: false // No unexpected properties allowed
},
execute: async (params) => {
// Validation already happened, so we can safely use the inputs
// ...
}
};

Prompt Security

Prompt Injection Prevention

Prevent prompt injection attacks with these critical practices:

  • Define clear boundaries between system instructions and user content
  • Sanitize user inputs to remove potentially harmful content
  • Validate prompt templates before using them with user-supplied data
  • Use parameter validation to restrict what can be inserted into prompts

Prompt Injection Protection

MCP helps prevent prompt injection attacks through:

  1. Clear Boundaries: Defining strict boundaries between system and user content
  2. Input Sanitization: Removing potentially harmful content from inputs
  3. Template Validation: Validating prompt templates before use

Example of secure prompt handling:

// Secure prompt template with parameter validation
const secureGreetingPrompt = {
name: 'secure_greeting',
description: 'A securely implemented greeting template',
template: `
System: You are a helpful assistant for {{company_name}}.

User's name: {{user_name}}

Please provide a friendly greeting to the user.
`,
parameters: {
type: 'object',
properties: {
company_name: {
type: 'string',
maxLength: 100,
pattern: '^[a-zA-Z0-9 ,.\\-]+$' // Only allow safe characters
},
user_name: {
type: 'string',
maxLength: 50,
pattern: '^[a-zA-Z0-9 ,.\\-]+$'
}
},
required: ['company_name', 'user_name'],
additionalProperties: false
}
};

Network Security

Cross-Origin Resource Sharing (CORS)

MCP servers should implement proper CORS policies to prevent unauthorized web applications from accessing the API:

// Server-side CORS configuration
const server = new MCPServer({
// ...
cors: {
origin: ['https://trusted-app.com', 'https://admin.trusted-app.com'],
methods: ['GET', 'POST'],
allowHeaders: ['Authorization', 'Content-Type'],
maxAge: 86400 // Cache preflight requests for 24 hours
}
});

Rate Limiting

Implement rate limiting to protect against abuse and denial-of-service attacks:

// Server with rate limiting
const server = new MCPServer({
// ...
rateLimit: {
windowMs: 60 * 1000, // 1 minute
max: 100, // Limit each IP to 100 requests per windowMs
standardHeaders: true, // Return rate limit info in the headers
legacyHeaders: false,
handler: (req, res) => {
res.status(429).json({
error: 'Too many requests',
retryAfter: res.getHeader('Retry-After')
});
}
}
});

Next Steps