- Published on
Simplify MCP
- Authors
- Name
- Heefan
After reading the introduction to MCP, some people say MCP is as simple as the USB Protocol. But this left me confused. This article is my attempt to clarify these confusions.
booking system
Starting from a REST Suppose we use a Next.js BFF Web App to build a Booking System. This is very straightforward: the web app directly calls a remote booking REST API.
How do we turn this pattern into the MCP model?
MCP Key Concepts:
Host (Application) – The app that initiates the request, here it’s the Next.js BFF. The Host is the control center of the MCP architecture, responsible for:
- Deciding when to call external tools/services
- Managing user interaction and business logic
- Orchestrating calls to multiple MCP servers
- Handling the final response and rendering
Client (Protocol Handler) – The implementation layer of the MCP protocol, acting as a bridge between Host and Server. The Client is responsible for:
- Implementing the MCP protocol (JSON-RPC 2.0)
- Managing connections to servers (stdio, SSE, WebSocket, etc.)
- Handling protocol-level errors and retries
- Serializing/deserializing requests and responses
Server (MCP Server) – The service provider, here it’s the Booking Server. The Server is responsible for:
- Implementing business tools (search_flights, search_hotels, etc.)
- Integrating with external APIs (flight, hotel, etc.)
- Returning standardized MCP response format
- Handling authentication, authorization, and data validation
Simple Analogy:
- Host = Your phone (initiates requests)
- Client = USB driver (protocol translation)
- Server = USB drive (provides functionality)
After switching to the MCP model, it may seem unnecessarily complicated for a simple problem.
But when the requirements expand—users want to get booking info via chat, check the weather, find hotel deals, and car rental offers—things change. Now you need context.
Multi-Service MCP Architecture
Suppose a user asks via chat: "I want to fly from Singapore to Harstad, please check flights, local weather, hotel deals, and car rental from 8 Aug to 20 Aug." This is where MCP shines:
This architecture shows the real value of MCP:
- Unified protocol: All services communicate via the same MCP protocol
- Parallel processing: Multiple services can be called simultaneously
- Context retention: The AI Agent can synthesize all information for a smart answer
- Extensibility: Easily add new travel services (visa, insurance, etc.)
The Question: Why use MCP when you can just use an Agent?
In enterprise development, many services are proprietary and access-controlled. You might prefer to have an AI Agent call various APIs directly—so why introduce MCP as a middle layer?
1. Standardization & Interoperability
// Traditional Agent approach – each API has a different interface
const flightData = await fetch('/api/flights', {
method: 'POST',
body: JSON.stringify({ from: 'SIN', to: 'EVE' }),
})
const weatherData = await axios.get('/weather/api', {
params: { city: 'Harstad', format: 'json' },
})
const hotelData = await request.post('/booking/hotels', {
location: 'Harstad',
checkin: '2024-08-08',
})
// MCP approach – unified protocol interface
const flightResult = await mcpClient.callTool('flight-server', 'search_flights', {
from: 'SIN',
to: 'EVE',
})
const weatherResult = await mcpClient.callTool('weather-server', 'get_weather', {
location: 'Harstad',
})
const hotelResult = await mcpClient.callTool('hotel-server', 'search_hotels', {
location: 'Harstad',
date: '2024-08-08',
})
2. Development Efficiency & Maintenance
Problems with the traditional Agent approach:
- Each API needs separate error handling
- Different authentication mechanisms
- Data format conversions
- API changes require code changes in multiple places
MCP advantages:
- Unified error handling
- Standardized authentication
- Consistent data formats
- Plugin architecture, easy to extend
3. Security & Authorization
4. Ecosystem Effect
Imagine if every AI app had to:
- Write custom integration code for every service
- Handle all sorts of different API formats
- Maintain multiple authentication systems
It’s like before USB, when every device needed its own driver and interface. MCP is the "USB protocol" for AI tools.
5. Real Business Value
# Traditional approach: adding a new service
- Study API docs (2-3 days)
- Write integration code (3-5 days)
- Test and debug (2-3 days)
- Total: 7-11 days
# MCP approach: adding a new service
- Implement MCP Server (1-2 days)
- Register with MCP ecosystem (minutes)
- Total: 1-2 days
6. Real-World Scenario Comparison
Suppose you want to add 100 new tools to ChatGPT:
Traditional approach:
- OpenAI must review and integrate each tool separately
- Developers must learn OpenAI’s specific API format
- Every tool update requires re-review
MCP approach:
- Any tool conforming to the MCP standard is plug-and-play
- Developers only need to implement the MCP interface once
- Tool updates are transparent to the host app
So the value of MCP is not technical irreplaceability, but:
- Lower integration cost
- Higher development efficiency
- Ecosystem standards
- Tool reusability
Just as we don’t ask "Why use HTTP, why not just TCP?", MCP provides a higher level of abstraction and standardization.
MCP Security and Authorization in Detail
Earlier we mentioned MCP provides a unified security layer, but how is this actually implemented?
1. Transport Layer Security
MCP supports multiple secure transport methods:
2. Authorization and Permission Control
MCP provides multi-layer security at the protocol level:
// MCP Server config example
interface MCPServerConfig {
name: string
version: string
authorization: {
type: 'bearer' | 'apikey' | 'oauth2' | 'custom'
apiKey?: {
header: string
required: boolean
}
oauth2?: {
authUrl: string
tokenUrl: string
scopes: string[]
}
customAuth?: (request: MCPRequest) => Promise<boolean>
}
permissions: {
tools: {
[toolName: string]: {
allowedHosts: string[]
rateLimits: {
requests: number
timeWindow: number
}
requireAuth: boolean
}
}
resources: {
read: boolean
write: boolean
delete: boolean
}
}
}
3. End-to-End Security Flow
Who Provides the Auth Provider?
This is a key question in MCP architecture. The Auth Provider can be provided by different parties, depending on the deployment scenario:
Implementation Scenarios:
1. Enterprise Internal
// Enterprise Auth Provider implementation
class EnterpriseAuthProvider implements AuthProvider {
constructor(private ldapConfig: LDAPConfig) {}
async validateCredentials(hostId: string, credentials: Credentials): Promise<AuthResult> {
// 1. Connect to LDAP/AD
const ldapClient = new LDAPClient(this.ldapConfig)
// 2. Authenticate host app
const appIdentity = await ldapClient.authenticate(credentials.clientId, credentials.secret)
// 3. Get app permissions
const permissions = await this.getAppPermissions(hostId)
return {
valid: true,
hostId,
permissions,
token: this.generateJWT(appIdentity, permissions),
}
}
}
1.1 Microsoft EntraID (Azure AD) Integration
// Microsoft EntraID Auth Provider implementation
class EntraIDAuthProvider implements AuthProvider {
constructor(private entraConfig: EntraIDConfig) {}
async validateCredentials(hostId: string, credentials: Credentials): Promise<AuthResult> {
try {
// 1. Use Client Credentials Flow to authenticate app
const tokenResponse = await this.getAccessToken(credentials)
// 2. Validate token and get app info
const appInfo = await this.validateToken(tokenResponse.access_token)
// 3. Get app role assignments and permissions
const permissions = await this.getApplicationPermissions(appInfo.appId, hostId)
return {
valid: true,
hostId,
permissions,
token: tokenResponse.access_token,
expiresAt: new Date(Date.now() + tokenResponse.expires_in * 1000),
}
} catch (error) {
return { valid: false, error: `EntraID authentication failed: ${error.message}` }
}
}
// ...implementation details omitted for brevity...
}
EntraID Config Example:
# MCP Server config - EntraID integration
mcp_server:
name: 'travel-booking-server'
version: '1.0.0'
auth_provider:
type: 'entraid'
entraid:
tenant_id: '${AZURE_TENANT_ID}'
client_id: '${MCP_SERVER_CLIENT_ID}'
client_secret: '${MCP_SERVER_CLIENT_SECRET}'
scopes:
- 'https://graph.microsoft.com/Application.Read.All'
- 'api://mcp-travel-server/MCP.Access'
role_permissions:
'MCP.FlightSearch.Reader':
permissions: ['search_flights', 'get_prices']
rate_limits:
requests_per_minute: 100
'MCP.TravelAgent.Full':
permissions: ['search_flights', 'search_hotels', 'make_reservation']
rate_limits:
requests_per_minute: 200
'MCP.Admin':
permissions: ['*']
rate_limits:
requests_per_minute: 500
allowed_hosts:
'claude.ai':
entraid_app_id: '${CLAUDE_APP_ID}'
required_roles: ['MCP.TravelAgent.Full']
'openai.com':
entraid_app_id: '${OPENAI_APP_ID}'
required_roles: ['MCP.FlightSearch.Reader']
EntraID App Registration Example:
{
"displayName": "MCP Travel Booking Server",
"signInAudience": "AzureADMyOrg",
"api": {
"requestedAccessTokenVersion": 2,
"oauth2PermissionScopes": [{ "id": "...", "value": "MCP.Access" }]
},
"appRoles": [{ "value": "MCP.FlightSearch.Reader" }, { "value": "MCP.TravelAgent.Full" }]
}
Enhanced Security Flow – EntraID Integration:
EntraID Integration Advantages:
- Enterprise-grade security: Leverage Microsoft’s identity management
- Fine-grained permissions: App Roles for precise access control
- Audit compliance: Integrate with Azure Monitor for full audit
- SSO integration: Seamless with enterprise SSO
- Conditional access: Device/location-based policies
- Multi-tenant support: B2B collaboration
2. Third-Party Service
// Auth0 integration example
class Auth0Provider implements AuthProvider {
constructor(private auth0Config: Auth0Config) {}
async validateCredentials(hostId: string, credentials: Credentials): Promise<AuthResult> {
// 1. Call Auth0 API to validate
const auth0Response = await fetch(`${this.auth0Config.domain}/oauth/token`, {
/* ... */
})
const tokenData = await auth0Response.json()
// 2. Parse permission scopes
const permissions = this.parseScopes(tokenData.scope)
return {
valid: true,
hostId,
permissions,
token: tokenData.access_token,
}
}
}
3. MCP Server Built-in
// MCP Server built-in authentication
class MCPServerAuthProvider implements AuthProvider {
constructor(private apiKeyStore: APIKeyStore) {}
async validateCredentials(hostId: string, credentials: Credentials): Promise<AuthResult> {
// 1. Validate API Key
const apiKeyInfo = await this.apiKeyStore.validate(credentials.apiKey)
if (!apiKeyInfo.valid) {
return { valid: false, error: 'Invalid API key' }
}
// 2. Check host whitelist
if (!apiKeyInfo.allowedHosts.includes(hostId)) {
return { valid: false, error: 'Host not authorized' }
}
// 3. Return permission info
return {
valid: true,
hostId,
permissions: apiKeyInfo.permissions,
token: this.generateSessionToken(hostId, apiKeyInfo),
}
}
}
Deployment Config Example:
# MCP Server config
mcp_server:
name: 'travel-booking-server'
version: '1.0.0'
auth_provider:
type: 'enterprise' # options: enterprise, auth0, okta, builtin, aws_cognito
enterprise:
auth0:
builtin:
permissions:
default_permissions: ['search_flights']
host_specific:
Summary: Choosing an Auth Provider
Scenario | Auth Provider | Advantages | When to Use |
---|---|---|---|
Enterprise | LDAP/AD/SSO | Integrates with existing systems | Large internal apps |
SaaS | Auth0/Okta | Professional identity service | Need enterprise auth |
Simple Deploy | Built-in API Key | Quick setup | Small team or prototype |
Cloud Native | AWS Cognito/Azure AD | Deep cloud integration | Cloud-based projects |
Open Source | JWT + Self-managed | Full control | Open source or self-host |
So the Auth Provider is not provided by the MCP protocol itself, but is chosen and configured by the organization deploying the MCP Server, according to their security requirements and infrastructure.
4. Security Implementation Details
4.1 Token Management
// Secure MCP Client implementation
class SecureMCPClient {
private authTokens: Map<string, AuthToken> = new Map()
async connectToServer(serverConfig: MCPServerConfig) {
// 1. Validate server certificate
const serverInfo = await this.validateServerCertificate(serverConfig)
// 2. Get auth token
const authToken = await this.authenticate(serverConfig.authorization)
// 3. Establish secure connection
const connection = await this.establishSecureConnection(serverConfig, authToken)
return connection
}
private async authenticate(authConfig: AuthConfig): Promise<AuthToken> {
switch (authConfig.type) {
case 'oauth2':
return await this.handleOAuth2Flow(authConfig.oauth2)
case 'apikey':
return await this.handleAPIKeyAuth(authConfig.apiKey)
case 'bearer':
return await this.handleBearerTokenAuth(authConfig.bearer)
default:
throw new Error('Unsupported auth type')
}
}
}
4.2 Permission Validation & Rate Limiting
// MCP Server security middleware
class MCPSecurityMiddleware {
async validateRequest(request: MCPRequest, context: SecurityContext): Promise<boolean> {
// 1. Validate host identity
const hostValid = await this.validateHost(context.hostId, context.credentials)
if (!hostValid) return false
// 2. Check tool permissions
const toolPermissions = this.getToolPermissions(request.method, context.hostId)
if (!toolPermissions.allowed) return false
// 3. Rate limit check
const rateLimitOk = await this.checkRateLimit(context.hostId, request.method)
if (!rateLimitOk) return false
// 4. Parameter validation
const paramsValid = await this.validateParameters(request.params)
if (!paramsValid) return false
return true
}
private async checkRateLimit(hostId: string, method: string): Promise<boolean> {
const key = `${hostId}:${method}`
const current = (await this.redis.get(key)) || 0
const limit = this.rateLimits[method] || 100
if (current >= limit) {
await this.logSecurityEvent('rate_limit_exceeded', { hostId, method })
return false
}
await this.redis.incr(key)
await this.redis.expire(key, 60) // 1 minute window
return true
}
}
5. Data Privacy & Auditing
// Security audit system
class MCPAuditLogger {
async logSecurityEvent(event: SecurityEvent) {
const auditRecord = {
timestamp: new Date().toISOString(),
hostId: event.hostId,
serverId: event.serverId,
action: event.action,
success: event.success,
ipAddress: event.ipAddress,
userAgent: event.userAgent,
requestData: this.sanitizeData(event.requestData),
responseData: this.sanitizeData(event.responseData),
authMethod: event.authMethod,
permissions: event.permissions,
rateLimitStatus: event.rateLimitStatus,
}
await this.writeToAuditLog(auditRecord)
if (this.detectSuspiciousActivity(auditRecord)) {
await this.triggerSecurityAlert(auditRecord)
}
}
private sanitizeData(data: any): any {
// Remove sensitive info: passwords, API keys, PII, etc.
return this.deepClone(data, this.sensitiveFieldsFilter)
}
}
6. Security Advantages over Traditional APIs
Security Aspect | Traditional API | MCP Protocol |
---|---|---|
Auth Standard | Each API is different | Unified protocol |
Permission Granularity | Usually API-level | Tool + parameter-level |
Audit Trail | Scattered | Centralized logs |
Rate Limiting | Each implements | Protocol-level |
Data Privacy | Depends on service | Protocol-level masking |
Anomaly Detection | Extra dev needed | Built-in monitoring |
So the MCP security layer is not just a "wrapper"—it’s a full security framework, providing:
- Standardized authentication and authorization
- Fine-grained permission control
- Unified security auditing
- Built-in threat detection
- Data privacy protection
In theory, MCP offers a more secure solution than traditional API integration.