Skip to content

Commit b6072ad

Browse files
kumar-tigerKumar Rajwanicrivetimihai
authored
HTTP Header Passthrough (forward headers to MCP server) #208 (#626)
* fix: show proper error on gateway register and added hot reload in gunicorn * update the database and routes to passthrough the headers * revert the reload * merge with main * fix: remove redundant code * fix: linting issue * fix: flake8 and ruff * fix: ruff issue * added passthrough header in gateway read Signed-off-by: kumar-tiger <[email protected]> * update the database and routes to passthrough the headers * fix: remove redundant code * fix: linting issue * fix: flake8 and ruff * fix: ruff issue * added passthrough header in gateway read Signed-off-by: kumar-tiger <[email protected]> * Rebase from main, and fix all test issues. Implement UI Signed-off-by: Mihai Criveti <[email protected]> * Add headers to test tool Signed-off-by: Mihai Criveti <[email protected]> * Add docs Signed-off-by: Mihai Criveti <[email protected]> * Added testing and doctest Signed-off-by: Mihai Criveti <[email protected]> * Rebased Signed-off-by: Mihai Criveti <[email protected]> * Fix pylint issues with unused arguments and method calls - Prefix unused request_headers parameter with underscore in tool_service.py - Fix method call in streamablehttp_transport.py to use positional arguments - Achieved 10.00/10 pylint score * Merge branch 'issues/208' of https://github.com/kumar-tiger/mcp-context-forge into issues/208 Signed-off-by: kumar-tiger <[email protected]> --------- Signed-off-by: kumar-tiger <[email protected]> Signed-off-by: Mihai Criveti <[email protected]> Co-authored-by: Kumar Rajwani <[email protected]> Co-authored-by: Mihai Criveti <[email protected]>
1 parent b8dada2 commit b6072ad

23 files changed

+1371
-98
lines changed

.env.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ MCPGATEWAY_UI_ENABLED=true
115115
# Enable the Admin API endpoints (true/false)
116116
MCPGATEWAY_ADMIN_API_ENABLED=true
117117

118+
#####################################
119+
# Header Passthrough Configuration
120+
#####################################
121+
122+
# Default headers to pass through from client requests to backing MCP servers
123+
# Comma-separated list or JSON array format
124+
# Example: ["Authorization", "X-Tenant-Id", "X-Trace-Id"]
125+
DEFAULT_PASSTHROUGH_HEADERS=["Authorization", "X-Tenant-Id", "X-Trace-Id"]
126+
118127
#####################################
119128
# Security and CORS
120129
#####################################

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
todo/
12
*.sarif
23
devskim-results.sarif
34
debug_login_page.png

docs/docs/overview/passthrough.md

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
# HTTP Header Passthrough
2+
3+
The MCP Gateway supports **HTTP Header Passthrough**, allowing specific headers from incoming client requests to be forwarded to backing MCP servers. This feature is essential for maintaining authentication context and request tracing across the gateway infrastructure.
4+
5+
## Overview
6+
7+
When clients make requests through the MCP Gateway, certain headers (like authentication tokens or trace IDs) need to be preserved and passed to the underlying MCP servers. The header passthrough feature provides a configurable, secure way to forward these headers while preventing conflicts with existing authentication mechanisms.
8+
9+
## Key Features
10+
11+
- **Global Configuration**: Set default passthrough headers for all gateways
12+
- **Per-Gateway Override**: Customize header passthrough on a per-gateway basis
13+
- **Conflict Prevention**: Automatically prevents overriding existing authentication headers
14+
- **Admin UI Integration**: Configure passthrough headers through the web interface
15+
- **API Management**: Programmatic control via REST endpoints
16+
17+
## Configuration
18+
19+
### Environment Variables
20+
21+
Set global default headers using the `DEFAULT_PASSTHROUGH_HEADERS` environment variable:
22+
23+
```bash
24+
# JSON array format
25+
DEFAULT_PASSTHROUGH_HEADERS=["Authorization", "X-Tenant-Id", "X-Trace-Id"]
26+
27+
# Or in .env file
28+
DEFAULT_PASSTHROUGH_HEADERS=["Authorization", "X-Tenant-Id", "X-Trace-Id"]
29+
```
30+
31+
### Admin UI Configuration
32+
33+
#### Global Configuration
34+
Access the admin interface to set global passthrough headers that apply to all gateways by default.
35+
36+
#### Per-Gateway Configuration
37+
When creating or editing gateways:
38+
39+
1. Navigate to the **Gateways** section in the admin UI
40+
2. Click **Add Gateway** or edit an existing gateway
41+
3. In the **Passthrough Headers** field, enter a comma-separated list:
42+
```
43+
Authorization, X-Tenant-Id, X-Trace-Id
44+
```
45+
4. Gateway-specific headers override global defaults
46+
47+
### API Configuration
48+
49+
#### Get Global Configuration
50+
```bash
51+
GET /admin/config/passthrough-headers
52+
```
53+
54+
Response:
55+
```json
56+
{
57+
"passthrough_headers": ["Authorization", "X-Tenant-Id", "X-Trace-Id"]
58+
}
59+
```
60+
61+
#### Update Global Configuration
62+
```bash
63+
PUT /admin/config/passthrough-headers
64+
Content-Type: application/json
65+
66+
{
67+
"passthrough_headers": ["Authorization", "X-Custom-Header"]
68+
}
69+
```
70+
71+
## How It Works
72+
73+
### Header Processing Flow
74+
75+
1. **Client Request**: Client sends request with various headers
76+
2. **Header Extraction**: Gateway extracts headers configured for passthrough
77+
3. **Conflict Check**: System verifies no conflicts with existing auth headers
78+
4. **Forwarding**: Allowed headers are added to requests sent to backing MCP servers
79+
80+
### Configuration Hierarchy
81+
82+
The system follows this priority order:
83+
84+
1. **Gateway-specific headers** (highest priority)
85+
2. **Global configuration** (from database)
86+
3. **Environment variable defaults** (lowest priority)
87+
88+
### Example Flow
89+
90+
```mermaid
91+
graph LR
92+
A[Client Request] --> B[MCP Gateway]
93+
B --> C{Check Passthrough Config}
94+
C --> D[Extract Configured Headers]
95+
D --> E[Conflict Prevention Check]
96+
E --> F[Forward to MCP Server]
97+
98+
G[Global Config] --> C
99+
H[Gateway Config] --> C
100+
```
101+
102+
## Security Considerations
103+
104+
### Conflict Prevention
105+
106+
The system automatically prevents header conflicts:
107+
108+
- **Basic Auth**: Skips `Authorization` header if gateway uses basic authentication
109+
- **Bearer Auth**: Skips `Authorization` header if gateway uses bearer token authentication
110+
- **Warnings**: Logs warnings when headers are skipped due to conflicts
111+
112+
### Header Validation
113+
114+
- Headers are validated before forwarding
115+
- Empty or invalid headers are filtered out
116+
- Only explicitly configured headers are passed through
117+
118+
## Use Cases
119+
120+
### Authentication Context
121+
Forward authentication tokens to maintain user context:
122+
```bash
123+
# Client request includes
124+
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
125+
126+
# Forwarded to MCP server if configured
127+
```
128+
129+
### Request Tracing
130+
Maintain trace context across service boundaries:
131+
```bash
132+
# Client request includes
133+
X-Trace-Id: abc123def456
134+
X-Span-Id: span789
135+
136+
# Both forwarded to enable distributed tracing
137+
```
138+
139+
### Multi-Tenant Systems
140+
Pass tenant identification:
141+
```bash
142+
# Client request includes
143+
X-Tenant-Id: tenant_12345
144+
X-Organization: acme_corp
145+
146+
# Forwarded for tenant-specific processing
147+
```
148+
149+
## Configuration Examples
150+
151+
### Basic Setup
152+
```bash
153+
# .env file
154+
DEFAULT_PASSTHROUGH_HEADERS=["Authorization"]
155+
```
156+
157+
### Multi-Header Configuration
158+
```bash
159+
# .env file with multiple headers
160+
DEFAULT_PASSTHROUGH_HEADERS=["Authorization", "X-Tenant-Id", "X-Trace-Id", "X-Request-Id"]
161+
```
162+
163+
### Gateway-Specific Override
164+
```json
165+
// Via Admin API for specific gateway
166+
{
167+
"name": "secure-gateway",
168+
"url": "https://secure-mcp-server.example.com",
169+
"passthrough_headers": ["X-API-Key", "X-Client-Id"]
170+
}
171+
```
172+
173+
## Troubleshooting
174+
175+
### Common Issues
176+
177+
#### Headers Not Being Forwarded
178+
- Verify header names in configuration match exactly (case-sensitive)
179+
- Check for authentication conflicts in logs
180+
- Ensure gateway configuration overrides aren't blocking headers
181+
182+
#### Authentication Conflicts
183+
If you see warnings like:
184+
```
185+
Skipping passthrough header 'Authorization' - conflicts with existing basic auth
186+
```
187+
188+
**Solution**: Either:
189+
1. Remove `Authorization` from passthrough headers for that gateway
190+
2. Change the gateway to not use basic/bearer authentication
191+
3. Use a different header name for custom auth tokens
192+
193+
#### Configuration Not Taking Effect
194+
- Restart the gateway after environment variable changes
195+
- Verify database migration has been applied
196+
- Check admin API responses to confirm configuration is saved
197+
198+
### Debug Logging
199+
200+
Enable debug logging to see header processing:
201+
```bash
202+
LOG_LEVEL=DEBUG
203+
```
204+
205+
Look for log entries containing:
206+
- `Passthrough headers configured`
207+
- `Skipping passthrough header`
208+
- `Adding passthrough header`
209+
210+
## API Reference
211+
212+
### Data Models
213+
214+
#### GlobalConfig
215+
```python
216+
class GlobalConfig(Base):
217+
id: int
218+
passthrough_headers: Optional[List[str]]
219+
```
220+
221+
#### Gateway
222+
```python
223+
class Gateway(Base):
224+
# ... other fields
225+
passthrough_headers: Optional[List[str]]
226+
```
227+
228+
### Admin Endpoints
229+
230+
| Method | Endpoint | Description |
231+
|--------|----------|-------------|
232+
| GET | `/admin/config/passthrough-headers` | Get global configuration |
233+
| PUT | `/admin/config/passthrough-headers` | Update global configuration |
234+
| POST | `/admin/gateways` | Create gateway with headers |
235+
| PUT | `/admin/gateways/{id}` | Update gateway headers |
236+
237+
## Best Practices
238+
239+
1. **Minimal Headers**: Only configure headers you actually need to reduce overhead
240+
2. **Security Review**: Regularly audit which headers are being passed through
241+
3. **Environment Consistency**: Use consistent header configuration across environments
242+
4. **Documentation**: Document which headers your MCP servers expect
243+
5. **Monitoring**: Monitor logs for conflict warnings and adjust configuration accordingly
244+
245+
## Migration Notes
246+
247+
When upgrading to a version with header passthrough:
248+
249+
1. **Database Migration**: Ensure the migration `3b17fdc40a8d` has been applied
250+
2. **Configuration Review**: Review existing authentication setup for conflicts
251+
3. **Testing**: Test header forwarding in development before production deployment
252+
4. **Monitoring**: Monitor logs for any unexpected behavior after deployment
253+
254+
## Testing with the Built-in Test Tool
255+
256+
The MCP Gateway admin interface includes a built-in test tool with passthrough header support:
257+
258+
### Using the Test Tool
259+
260+
1. **Access the Admin UI**: Navigate to the **Tools** section
261+
2. **Select a Tool**: Click the **Test** button on any available tool
262+
3. **Configure Headers**: In the test modal, scroll to the **Passthrough Headers** section
263+
4. **Add Headers**: Enter headers in the format `Header-Name: Value` (one per line):
264+
```
265+
Authorization: Bearer your-token-here
266+
X-Tenant-Id: tenant-123
267+
X-Trace-Id: abc-def-456
268+
```
269+
5. **Run Test**: Click **Run Tool** - the headers will be included in the request
270+
271+
### Example Test Scenarios
272+
273+
**Authentication Testing**:
274+
```
275+
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
276+
```
277+
278+
**Multi-Tenant Testing**:
279+
```
280+
X-Tenant-Id: acme-corp
281+
X-Organization-Id: org-12345
282+
```
283+
284+
**Distributed Tracing**:
285+
```
286+
X-Trace-Id: trace-abc123
287+
X-Span-Id: span-def456
288+
X-Request-Id: req-789xyz
289+
```
290+
291+
The test tool provides immediate feedback and allows you to verify that your passthrough header configuration is working correctly before deploying to production.

mcpgateway/admin.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@
3434

3535
# First-Party
3636
from mcpgateway.config import settings
37-
from mcpgateway.db import get_db
37+
from mcpgateway.db import get_db, GlobalConfig
3838
from mcpgateway.schemas import (
3939
GatewayCreate,
4040
GatewayRead,
4141
GatewayTestRequest,
4242
GatewayTestResponse,
4343
GatewayUpdate,
44+
GlobalConfigRead,
45+
GlobalConfigUpdate,
4446
PromptCreate,
4547
PromptMetrics,
4648
PromptRead,
@@ -88,6 +90,52 @@
8890
####################
8991

9092

93+
@admin_router.get("/config/passthrough-headers", response_model=GlobalConfigRead)
94+
async def get_global_passthrough_headers(
95+
db: Session = Depends(get_db),
96+
_user: str = Depends(require_auth),
97+
) -> GlobalConfigRead:
98+
"""Get the global passthrough headers configuration.
99+
100+
Args:
101+
db: Database session
102+
_user: Authenticated user
103+
104+
Returns:
105+
GlobalConfigRead: The current global passthrough headers configuration
106+
"""
107+
config = db.query(GlobalConfig).first()
108+
if not config:
109+
config = GlobalConfig()
110+
return GlobalConfigRead(passthrough_headers=config.passthrough_headers)
111+
112+
113+
@admin_router.put("/config/passthrough-headers", response_model=GlobalConfigRead)
114+
async def update_global_passthrough_headers(
115+
config_update: GlobalConfigUpdate,
116+
db: Session = Depends(get_db),
117+
_user: str = Depends(require_auth),
118+
) -> GlobalConfigRead:
119+
"""Update the global passthrough headers configuration.
120+
121+
Args:
122+
config_update: The new configuration
123+
db: Database session
124+
_user: Authenticated user
125+
126+
Returns:
127+
GlobalConfigRead: The updated configuration
128+
"""
129+
config = db.query(GlobalConfig).first()
130+
if not config:
131+
config = GlobalConfig(passthrough_headers=config_update.passthrough_headers)
132+
db.add(config)
133+
else:
134+
config.passthrough_headers = config_update.passthrough_headers
135+
db.commit()
136+
return GlobalConfigRead(passthrough_headers=config.passthrough_headers)
137+
138+
91139
@admin_router.get("/servers", response_model=List[ServerRead])
92140
async def admin_list_servers(
93141
include_inactive: bool = False,

0 commit comments

Comments
 (0)