Architecture & Design Decisions
Key design decisions and how the MCP OAuth Gateway integrates with the Model Context Protocol.
Core Design Philosophy
Transparent Authentication: Add OAuth 2.1 to MCP services without code changes.
- MCP clients use standard protocols with automatic OAuth discovery
- MCP services receive user context headers (no OAuth code needed)
- Users get familiar OAuth flows (Google, GitHub, etc.)
- Developers focus on MCP logic, not authentication
Key Design Decisions
1. Streamable HTTP Transport Only
Decision: Gateway only supports Streamable HTTP transport.
Why:
- HTTP Proxy Pattern: Gateway acts as reverse proxy - natural fit for HTTP
- OAuth Integration: OAuth 2.1 flows designed for HTTP interactions
- Production Ready: HTTP patterns well-understood for scaling, TLS, load balancing
- Simplicity: Single transport keeps gateway focused and reliable
Implication: Your MCP services must support HTTP transport.
2. Single OAuth Provider
Decision: Only one OAuth provider per gateway instance.
Why:
- OAuth 2.1 Compliance: Resource parameter (RFC 8707) requires consistent token audiences
- Token Scoping: Service-specific tokens prevent privilege escalation
- User Experience: Single sign-on across all MCP services
- Security: Simpler trust model
Configuration:
# ✅ Correct: Single provider
oauth_providers:
google:
client_id: "${GOOGLE_CLIENT_ID}"
client_secret: "${GOOGLE_CLIENT_SECRET}"
mcp_services:
service1:
oauth_provider: "google" # All services use same provider
service2:
oauth_provider: "google"
3. Token Isolation (Critical Security)
Decision: OAuth tokens are never passed to backend MCP services.
Why:
- Security Isolation: Backend services can't access or misuse tokens
- Separation of Concerns: Gateway handles auth, services handle logic
- Token Safety: No risk of backends logging/caching OAuth tokens
- Simplicity: MCP services focus on domain logic
What Services Receive:
GET /mcp HTTP/1.1
MCP-Protocol-Version: 2025-06-18
x-user-id: google_123456789
x-user-email: user@example.com
x-user-name: John Doe
x-user-provider: google
4. Service-Specific Token Audiences
Decision: Each service gets tokens with unique audiences.
Token Structure:
{
"iss": "https://gateway.example.com",
"aud": "https://gateway.example.com/calculator/mcp", // Service-specific
"sub": "google_123456789",
"user_email": "user@example.com"
}
Benefits:
- Service Isolation: Calculator tokens can't access weather service
- Privilege Prevention: Fine-grained access control per service
- Audit Trail: Clear token usage tracking
- RFC 8707 Compliance: Proper resource parameter binding
5. MCP Authorization Compliance
Decision: Full implementation of MCP Authorization Specification.
Complete OAuth 2.1 Flow:
Standards:
- OAuth 2.1: PKCE required, refresh token rotation
- RFC 7591: Dynamic Client Registration
- RFC 8414/9728: Metadata endpoints
- RFC 8707: Resource parameter binding
6. Production Storage Architecture
Decision: Multiple storage backends with automatic fallback.
Strategy:
- Memory: Development, no dependencies
- Redis: Production, clustering support
- Vault: Enterprise, encryption, audit
- Fallback: Gateway continues if storage fails
Implementation Architecture
The gateway uses a component-based architecture. Key components:
Core: McpGateway
orchestrates everything (source)
Auth: OAuth 2.1 server with provider management (auth/)
Proxy: HTTP proxy with token removal and user context injection (proxy/)
Storage: Configurable backends with fallback (storage/)
Security Model
Multi-layer protection:
- Origin Validation: DNS rebinding protection
- Protocol Validation: MCP version enforcement
- Token Isolation: No OAuth tokens reach backends
- PKCE: Required for all authorization flows
- Audience Binding: Service-specific token scoping
7. Browser Client Support
Decision: CORS configuration with exposed headers for browser-based MCP clients.
Why Browser Support Matters:
- Web-based MCP clients need access to custom headers
- Session management requires header visibility
- OAuth flows work naturally in browsers
Configuration for Browser Clients:
cors:
allow_origins: ["https://your-web-app.com"] # Your web app's domain
allow_credentials: true # Required for cookies/auth
expose_headers: # Headers browser can access
- "MCP-Session-ID" # Session tracking
- "X-Request-ID" # Request correlation
- "X-Rate-Limit-Remaining" # Usage tracking
What This Enables:
- Session Persistence: Browser can read and store
MCP-Session-ID
- Request Tracking: Correlate requests with
X-Request-ID
- Rate Limit Awareness: Display usage with rate limit headers
Example Browser Client:
// Browser can now access exposed headers
const response = await fetch('https://gateway.example.com/service/mcp', {
headers: {
'Authorization': 'Bearer ' + accessToken,
'MCP-Protocol-Version': '2025-06-18'
}
});
// Access exposed headers
const sessionId = response.headers.get('MCP-Session-ID');
const requestId = response.headers.get('X-Request-ID');
Security Note: Only expose headers that browser clients need. Avoid exposing sensitive internal headers.
Why These Decisions?
Focus: Do one thing well - transparent OAuth for MCP HTTP services
Security: OAuth 2.1 compliance with enterprise features
Simplicity: Minimal integration effort for MCP services
Standards: MCP specification compliance with proper discovery
Resources
MCP Specs: Authorization · HTTP Transport
OAuth Standards: OAuth 2.1 · RFC 8707 · RFC 7591
Development: GitHub · CLAUDE.md · Tests
→ Quick Start to get hands-on with the gateway