Securing MCP (Model Context Protocol) Servers
The Model Context Protocol (MCP) enables AI assistants to securely access external data sources and tools. This guide covers securing your MCP servers using Guardhouse.
What is MCP?
MCP is an open protocol that allows AI assistants (like Claude, GPT, etc.) to:
- 📄 Read documents from your databases and file systems
- 🔍 Execute tools - Run commands and scripts
- 🌐 Access APIs - Make HTTP requests to external services
- 💾 Retrieve context - Get relevant data for AI responses
MCP Architecture
┌───────────────────────────────────┐
│ AI Assistant │
│ (Claude, GPT, etc.) │
└────────────┬─────────────────────┘
│
▼
┌───────────────────────────────────┐
│ MCP Client │
│ (Your Application) │
└────────────┬──────────────────────┘
│
▼
┌───────────────────────────────────┐
│ MCP Servers │
│ │
└────────────┬──────────────────────┘
│
▼
┌───────────────────────────────────┐
│ Guardhouse │
│ │
└────────────┬──────────────────────┘
│
│
▼
Token Validation &
Authorization
Security Requirements for MCP Servers
1. Authentication
MCP servers MUST validate all incoming requests using Guardhouse tokens:
- ✅ Verify JWT Signatures - Ensure tokens are signed by Guardhouse
- ✅ Validate Issuer - Check token issuer matches your Guardhouse domain
- ✅ Validate Audience - Ensure audience matches your MCP server ID
- ✅ Check Token Expiration - Reject expired tokens
- ✅ Validate Scopes - Ensure token has required MCP scopes
2. Authorization
Implement proper authorization based on user's MCP permissions:
async function validateToken(token) {
// Verify token with Guardhouse
const introspectionResponse = await fetch('https://your_tenant.guardhouse.cloud/oauth/introspect', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${Buffer.from(clientId + ':' + clientSecret).toString('base64')}`
},
body: new URLSearchParams({
token: token,
token_type_hint: 'access_token'
})
});
const result = await introspectionResponse.json();
// Check if token is active and has MCP scope
if (!result.active || !result.scope?.includes('mcp:read') || !result.scope?.includes('mcp:write')) {
throw new Error('Invalid or unauthorized token');
}
// Check user identity (if needed)
if (result.sub) {
// User is authenticated
return { authorized: true, userId: result.sub };
}
throw new Error('Token validation failed');
}
// Usage in MCP server
function handleRequest(req, res) {
const token = req.headers.authorization?.replace('Bearer ', '');
try {
const auth = await validateToken(token);
if (auth.authorized) {
// Proceed with request
return next();
}
} catch (error) {
res.status(401).json({ error: 'Unauthorized' });
}
}
3. Transport Security
- ✅ HTTPS Only - All MCP communication must use HTTPS
- ✅ TLS 1.2+ - Use modern encryption protocols
- ✅ Certificate Validation - Verify MCP client certificates
- ❌ No HTTP - Never allow unencrypted MCP connections
4. Rate Limiting
Protect your MCP server from abuse:
const rateLimiter = new Map();
function checkRateLimit(userId) {
const now = Date.now();
const windowMs = 60000; // 1 minute
if (!rateLimiter.has(userId)) {
rateLimiter.set(userId, { count: 1, timestamp: now });
return true;
}
const userData = rateLimiter.get(userId);
const elapsed = now - userData.timestamp;
if (elapsed < windowMs && userData.count >= 100) {
return false; // Rate limit exceeded
}
// Reset window
rateLimiter.set(userId, { count: 1, timestamp: now });
return true;
}
5. Audit Logging
Log all MCP operations for security and compliance:
function logMCPEvent(event, userId, resourceId) {
const logEntry = {
timestamp: new Date().toISOString(),
event,
userId,
resourceId,
clientIp: req.ip,
userAgent: req.headers['user-agent'],
success: event.success !== false
};
// Send to your logging system
console.log('[MCP]', JSON.stringify(logEntry));
// Also store in your database for audit
await AuditLog.create(logEntry);
}
Configuring Guardhouse for MCP
Step 1: Create MCP Client
In Guardhouse Admin Console:
- Navigate to Applications (Clients)
- Click Create Application
- Fill in details:
- Name:
MCP Server - Document Processor - Type:
Regular WeborMachine-to-Machine - Description:
MCP server serving documents for AI agents - Grant Type:
Authorization Code(if user-facing) orClient Credentials(for server-to-server)
- Name:
Step 2: Define MCP Scopes
Create specific scopes for MCP operations:
| Scope | Description | Use Case |
|---|---|---|
mcp:read:documents | Read documents from MCP server | AI assistant retrieves content |
mcp:write:documents | Write documents to MCP server | AI assistant creates/modifies content |
mcp:execute:tools | Execute MCP tools | AI assistant runs commands |
mcp:read:config | Read MCP server configuration | AI assistant reads settings |
mcp:write:config | Modify MCP server configuration | AI assistant modifies settings |
Step 3: Configure AI Assistant
Configure your AI assistant to use Guardhouse for MCP authentication:
For Claude Desktop App:
// Configure Claude to use Guardhouse
{
"mcpServers": [
{
"command": "document-processor",
"url": "https://mcp.yourcompany.com",
"transport": "sse",
"auth": {
"type": "oauth",
"provider": "guardhouse",
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"issuer": "https://your_tenant.guardhouse.cloud",
"scopes": ["mcp:read:documents", "mcp:write:documents"]
}
}
]
}
MCP Server Implementation
Node.js Example
const express = require('express');
const { verifyGuardhouseToken } = require('./auth');
const app = express();
// Middleware to verify Guardhouse tokens
app.use(async (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Missing token' });
}
try {
const auth = await verifyGuardhouseToken(token);
if (!auth.authorized) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Add user info to request
req.user = { id: auth.userId, scopes: auth.scopes };
next();
} catch (error) {
console.error('Token verification failed:', error);
return res.status(401).json({ error: 'Invalid token' });
}
});
// MCP endpoint: List available resources
app.get('/mcp/resources', (req, res) => {
if (!req.user.scopes?.includes('mcp:read:documents')) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
const resources = [
{ id: 'docs-1', name: 'User Manual', type: 'document' },
{ id: 'docs-2', name: 'API Reference', type: 'document' },
{ id: 'docs-3', name: 'Troubleshooting Guide', type: 'document' }
];
res.json(resources);
});
// MCP endpoint: Read document
app.get('/mcp/resources/:id', (req, res) => {
if (!req.user.scopes?.includes('mcp:read:documents')) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
const doc = await Document.findById(req.params.id);
res.json(doc);
});
// MCP endpoint: Execute tool
app.post('/mcp/tools/execute', (req, res) => {
if (!req.user.scopes?.includes('mcp:execute:tools')) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
const { toolName, parameters } = req.body;
const result = await ToolExecutor.execute(toolName, parameters);
// Log execution
await logMCPEvent({
event: 'tool_executed',
userId: req.user.id,
resourceId: toolName,
success: result.success
});
res.json(result);
});
app.listen(3000, () => console.log('MCP server running on port 3000'));
Python Example
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
from typing import Optional
import jwt
app = FastAPI()
def verify_guardhouse_token(token: str, client_id: str, client_secret: str) -> dict:
"""Verify Guardhouse token using introspection"""
response = requests.post(
'https://your_tenant.guardhouse.cloud/oauth/introspect',
auth=(client_id, client_secret),
data={'token': token, 'token_type_hint': 'access_token'}
)
result = response.json()
if not result.get('active'):
raise HTTPException(status_code=401, detail="Invalid token")
if not any(scope in result.get('scope', []) for scope in ['mcp:read:documents', 'mcp:write:documents']):
raise HTTPException(status_code=403, detail="Insufficient MCP scopes")
return {
'authorized': True,
'user_id': result.get('sub'),
'scopes': result.get('scope', [])
}
# Dependency for token verification
async def get_current_user(token: str = Depends(verify_guardhouse_token)):
return token
@app.get("/mcp/resources")
async def list_resources(user = get_current_user):
"""List available MCP resources"""
if 'mcp:read:documents' not in user['scopes']:
raise HTTPException(status_code=403, detail="Insufficient permissions")
resources = [
{"id": "docs-1", "name": "User Manual", "type": "document"},
{"id": "docs-2", "name": "API Reference", "type": "document"}
]
return resources
@app.get("/mcp/resources/{resource_id}")
async def read_resource(resource_id: str, user = get_current_user):
"""Read specific MCP resource"""
if 'mcp:read:documents' not in user['scopes']:
raise HTTPException(status_code=403, detail="Insufficient permissions")
document = await Document.get(resource_id)
return document
@app.post("/mcp/tools/execute")
async def execute_tool(tool_request: ToolRequest, user = get_current_user):
"""Execute MCP tool"""
if 'mcp:execute:tools' not in user['scopes']:
raise HTTPException(status_code=403, detail="Insufficient permissions")
result = await ToolExecutor.execute(tool_request.tool_name, tool_request.parameters)
# Log execution
await log_mcp_event(
event="tool_executed",
user_id=user['user_id'],
resource_id=tool_request.tool_name,
success=result.success
)
return result
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
MCP Client Integration
Guardhouse SDK for MCP
Use Guardhouse SDKs to simplify token management:
import { GuardhouseClient } from '@guardhouse/sdk';
const client = new GuardhouseClient({
clientId: process.env.GUARDHOUSE_CLIENT_ID,
clientSecret: process.env.GUARDHOUSE_CLIENT_SECRET,
domain: 'https://your_tenant.guardhouse.cloud'
});
// Get token for MCP operations
async function getMCPUserToken() {
const token = await client.auth.getAccessTokenAsync();
return token.access_token;
}
// Use in MCP client initialization
const mcpClient = {
type: 'oauth',
provider: 'guardhouse',
clientId: process.env.GUARDHOUSE_CLIENT_ID,
clientSecret: process.env.GUARDHOUSE_CLIENT_SECRET,
issuer: 'https://your_tenant.guardhouse.cloud',
scopes: ['mcp:read:documents', 'mcp:write:documents'],
getToken: getMCPUserToken
};
Best Practices
Security
- Always Validate Tokens - Verify every request with Guardhouse
- Use Proper Scopes - Only grant necessary MCP scopes
- Implement Rate Limiting - Prevent abuse and DoS attacks
- HTTPS Only - Never allow HTTP connections
- Audit All Actions - Log all MCP operations
- Secure Token Storage - Store client secrets securely (environment variables)
- Monitor for Anomalies - Detect suspicious activity patterns
Performance
- Use Connection Pooling - Reuse connections to MCP clients
- Implement Caching - Cache frequently accessed documents
- Batch Operations - Group multiple MCP calls when possible
- Lazy Loading - Load large documents on demand
- WebSocket Optimizations - For real-time MCP communication
- Response Compression - Use gzip or brotli for large responses
Reliability
- Graceful Error Handling - Provide clear error messages
- Retry with Backoff - Implement exponential backoff
- Health Checks - Monitor MCP server and Guardhouse API health
- Circuit Breakers - Prevent cascading failures
- Graceful Degradation - Handle partial failures
- Request Queuing - Handle burst traffic
Observability
- Structured Logging - Use consistent log formats (JSON, structured)
- Distributed Tracing - Trace requests across services
- Metrics Collection - Track latency, throughput, error rates
- Alerting - Set up alerts for failures and anomalies
- Dashboard Monitoring - Create visibility into MCP operations
- Performance Profiling - Monitor and optimize slow operations
Troubleshooting
Common Issues
Issue: "Token validation fails"
- Verify Guardhouse domain is correct
- Check client ID and secret
- Ensure token hasn't expired
- Verify introspection endpoint is accessible
Issue: "MCP client cannot authenticate"
- Check token is being passed correctly in Authorization header
- Verify scopes are granted to the MCP client
- Ensure transport (SSE/HTTP) matches server configuration
Issue: "Rate limited"
- Implement proper backoff strategy
- Cache responses to reduce requests
- Check Guardhouse API rate limits
- Use authentication tokens properly
Issue: "High latency"
- Check network connectivity to Guardhouse
- Optimize document loading (pagination, lazy loading)
- Use connection pooling
- Consider CDN for static resources
Security Checklist
- Enable HTTPS for all MCP endpoints
- Implement token verification on every request
- Validate scopes before granting access
- Implement rate limiting per user/agent
- Add audit logging for all operations
- Use secure token storage (no hardcoding)
- Implement proper error handling
- Monitor for suspicious activity
- Regularly rotate client secrets
- Keep dependencies up to date
- Conduct security audits regularly
Related Documentation
- Client Credentials Flow - M2M authentication
- Identities for AI Agents - Creating agent identities
- Introspection for AI Requests - Token validation
- User API - Managing clients and identities
- Authentication API - OAuth 2.0 reference
Support
For issues, questions, or contributions: