Skip to main content

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:

  1. Navigate to Applications (Clients)
  2. Click Create Application
  3. Fill in details:
    • Name: MCP Server - Document Processor
    • Type: Regular Web or Machine-to-Machine
    • Description: MCP server serving documents for AI agents
    • Grant Type: Authorization Code (if user-facing) or Client Credentials (for server-to-server)

Step 2: Define MCP Scopes

Create specific scopes for MCP operations:

ScopeDescriptionUse Case
mcp:read:documentsRead documents from MCP serverAI assistant retrieves content
mcp:write:documentsWrite documents to MCP serverAI assistant creates/modifies content
mcp:execute:toolsExecute MCP toolsAI assistant runs commands
mcp:read:configRead MCP server configurationAI assistant reads settings
mcp:write:configModify MCP server configurationAI 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

  1. Always Validate Tokens - Verify every request with Guardhouse
  2. Use Proper Scopes - Only grant necessary MCP scopes
  3. Implement Rate Limiting - Prevent abuse and DoS attacks
  4. HTTPS Only - Never allow HTTP connections
  5. Audit All Actions - Log all MCP operations
  6. Secure Token Storage - Store client secrets securely (environment variables)
  7. Monitor for Anomalies - Detect suspicious activity patterns

Performance

  1. Use Connection Pooling - Reuse connections to MCP clients
  2. Implement Caching - Cache frequently accessed documents
  3. Batch Operations - Group multiple MCP calls when possible
  4. Lazy Loading - Load large documents on demand
  5. WebSocket Optimizations - For real-time MCP communication
  6. Response Compression - Use gzip or brotli for large responses

Reliability

  1. Graceful Error Handling - Provide clear error messages
  2. Retry with Backoff - Implement exponential backoff
  3. Health Checks - Monitor MCP server and Guardhouse API health
  4. Circuit Breakers - Prevent cascading failures
  5. Graceful Degradation - Handle partial failures
  6. Request Queuing - Handle burst traffic

Observability

  1. Structured Logging - Use consistent log formats (JSON, structured)
  2. Distributed Tracing - Trace requests across services
  3. Metrics Collection - Track latency, throughput, error rates
  4. Alerting - Set up alerts for failures and anomalies
  5. Dashboard Monitoring - Create visibility into MCP operations
  6. 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

Support

For issues, questions, or contributions: