-
Notifications
You must be signed in to change notification settings - Fork 138
Description
Bug Report: MCP Gateway Doesn't Detect HTTPS/TLS Context and Hardcodes HTTP URLs
Summary
MCP Gateway hardcodes http://
protocol in generated URLs and doesn't detect when it's running behind HTTPS (via Gunicorn with TLS or a reverse proxy). While the gateway can be deployed with TLS using Gunicorn's SSL support, internal URL generation still uses http://
causing protocol mismatches and federation issues.
This ticket suggests adding a GATEWAY_PROTOCOL=http|https|auto(default)
When Does This Bug Affect You?
1. Federation Scenarios (federation/discovery.py issue)
Affected when:
- You have multiple MCP Gateways that need to communicate
- You enable
FEDERATION_ENABLED=true
- You want gateways to share tools/resources
Example scenario:
Gateway A (HTTPS) ──federation──> Gateway B (HTTPS)
↓
Generates http:// URL for registration ❌
NOT affected when:
- Running a single standalone gateway
- Adding MCP servers to the gateway
- Federation is disabled (default)
What is Federation in MCP Gateway?
Federation is about connecting multiple MCP Gateways together to share resources. It's NOT about:
- Adding MCP servers to the gateway (that's just normal server registration)
- Clients accessing the gateway via HTTP/SSE/WebSocket
Federation allows:
- Gateway A to discover Gateway B's tools/resources
- Clients connected to Gateway A to use tools from Gateway B
- Automatic peer discovery and synchronization
2. SSE Client Connections (sse_transport.py issue)
Affected when:
- Clients connect using SSE transport
- The gateway runs with HTTPS
- The SSE endpoint URL sent to clients has wrong protocol
Example:
# Client connects to https://gateway.com/sse
# Gateway responds with endpoint URL: http://gateway.com/message?session_id=xxx ❌
# Should be: https://gateway.com/message?session_id=xxx ✓
NOT affected when:
- Using HTTP transport (not SSE)
- Using WebSocket transport
- Running everything as HTTP (no TLS)
Do You Need to Worry?
Probably NOT if you:
- Run a single gateway (no federation)
- Use HTTP or WebSocket transport (not SSE)
- Have
FEDERATION_ENABLED=false
(default) - Run everything as HTTP
You SHOULD care if you:
- Want to federate multiple gateways over HTTPS
- Use SSE transport with HTTPS
- Need proper HTTPS URLs in responses
Quick Check
Run this to see if you're affected:
# Check the current settings
grep -E "FEDERATION_ENABLED|TRANSPORT_TYPE" .env
# If you see:
# FEDERATION_ENABLED=false → Not affected by federation bug
# TRANSPORT_TYPE=http → Not affected by SSE bug
# TRANSPORT_TYPE=ws → Not affected by SSE bug
Most users running a single gateway to aggregate MCP servers won't hit this bug at all!
Current Behavior
When running the gateway with TLS enabled through Gunicorn:
SSL=true CERT_FILE=certs/cert.pem KEY_FILE=certs/key.pem ./run-gunicorn.sh
The gateway:
- Successfully serves HTTPS requests
- But generates HTTP URLs in:
- Federation discovery:
url = f"http://{addresses[0]}:{port}"
- SSE transport base URL:
f"http://{settings.host}:{settings.port}"
- Documentation examples
- Default allowed origins
- Federation discovery:
Expected Behavior
The gateway should:
- Detect when it's running with TLS (via Gunicorn SSL or reverse proxy)
- Generate URLs with the correct protocol (
https://
when using TLS) - Support both HTTP and HTTPS deployments without code changes
- Respect
X-Forwarded-Proto
headers from reverse proxies
Impact
- Severity: Medium
- Components Affected: Federation, SSE Transport, URL generation
- User Impact:
- Federation fails when HTTPS gateway tries to register with
http://
URLs - SSE clients receive incorrect endpoint URLs
- Mixed protocol issues in federated deployments
- Federation fails when HTTPS gateway tries to register with
Root Cause Analysis
1. No Protocol Detection
The gateway doesn't detect:
- When Gunicorn is running with SSL certificates
X-Forwarded-Proto
headers from reverse proxies- The actual protocol being used for incoming requests
2. Hardcoded Protocol in URL Generation
Multiple modules hardcode http://
:
# federation/discovery.py - Line ~211
url = f"http://{addresses[0]}:{port}"
# transports/sse_transport.py - Line ~30
self._base_url = base_url or f"http://{settings.host}:{settings.port}"
# config.py - Default allowed origins
allowed_origins: Set[str] = {
"http://localhost",
"http://localhost:4444",
}
3. Missing Request Context
URL generation happens without access to the current request context, so it can't determine the actual protocol being used.
Proposed Solutions
Solution 1: Protocol Detection via Request Context (Recommended)
Detect protocol from the current request and use it for URL generation.
Implementation Steps:
-
Pass request context to URL generation methods
-
Check for HTTPS indicators:
def get_protocol_from_request(request: Request) -> str: # Check X-Forwarded-Proto header forwarded_proto = request.headers.get("X-Forwarded-Proto") if forwarded_proto: return forwarded_proto # Check if request is secure if request.url.scheme == "https": return "https" # Check for other proxy headers if request.headers.get("X-Forwarded-SSL") == "on": return "https" return "http"
-
Update URL generation to use detected protocol:
# In SSE transport def __init__(self, request: Request, base_url: str = None): protocol = get_protocol_from_request(request) self._base_url = base_url or f"{protocol}://{settings.host}:{settings.port}"
Pros:
- Automatic detection works with any deployment
- No configuration needed
- Works with reverse proxies
Cons:
- Requires request context in more places
- May need refactoring
Solution 2: Configuration-Based Protocol
Add optional protocol configuration that defaults to auto-detection.
Implementation Steps:
-
Add configuration:
# Protocol for generated URLs (http, https, or auto) gateway_protocol: str = "auto" @property def base_url(self) -> str: protocol = self.gateway_protocol if protocol == "auto": # Default to http, but can be overridden by request context protocol = "http" return f"{protocol}://{self.host}:{self.port}"
-
Allow environment override:
GATEWAY_PROTOCOL=https # Force HTTPS URLs
Pros:
- Simple to implement
- Explicit control when needed
- Backward compatible
Cons:
- Requires manual configuration
- Doesn't handle mixed deployments well
Solution 3: External URL Configuration
Allow explicit configuration of the external URL.
Implementation Steps:
- Add configuration:
# External URL for the gateway (auto-detected if not set) external_url: Optional[str] = None @property def base_url(self) -> str: return self.external_url or f"http://{self.host}:{self.port}"
Pros:
- Most flexible
- Handles complex deployments
Cons:
- Requires manual configuration
- Another setting to manage
Recommended Fix
Implement Solution 1 (Protocol Detection) with Solution 2 as fallback:
-
Update SSE Transport to accept request context:
class SSETransport(Transport): def __init__(self, request: Request = None, base_url: str = None): if base_url: self._base_url = base_url elif request: protocol = get_protocol_from_request(request) self._base_url = f"{protocol}://{settings.host}:{settings.port}" else: # Fallback to configuration self._base_url = settings.base_url
-
Add Protocol Detection Helper:
# In utils or transport base def get_protocol_from_request(request: Request) -> str: """Detect protocol from request headers and context.""" # Implementation as shown above
-
Update Federation Discovery:
# For DNS-SD, add protocol hint to service properties properties={ "name": settings.app_name, "version": "1.0.0", "protocol": PROTOCOL_VERSION, "scheme": "https" if running_with_tls() else "http", }
-
Add Optional Configuration:
# For cases where auto-detection isn't sufficient gateway_protocol: str = Field( default="auto", pattern="^(http|https|auto)$", description="Protocol for generated URLs (http, https, or auto)" )
Workaround
Until fixed, when running with TLS:
- Ensure federated peers use full HTTPS URLs in
FEDERATION_PEERS
- Configure reverse proxy to handle protocol translation
- Use explicit
base_url
parameter where possible
Test Cases
- Gateway with Gunicorn SSL generates HTTPS URLs
- Gateway behind HTTPS reverse proxy detects protocol correctly
- Direct HTTP access still generates HTTP URLs
- Federation between HTTP and HTTPS gateways works correctly
- SSE endpoint URL matches the protocol used to access the gateway
- X-Forwarded-Proto header is respected
References
- Gunicorn SSL support:
run-gunicorn.sh
- URL generation:
mcpgateway/federation/discovery.py
,mcpgateway/transports/sse_transport.py
- Configuration:
mcpgateway/config.py